进程通信之二 管道技术第二篇 匿名管道
上一篇《進(jìn)程通信之二 管道技術(shù)第一篇 輸入輸出的重定向》示范了增加若干程序代碼來(lái)完成程序輸入輸出的重定向,并提出了如果沒(méi)有程序源代碼,只有程序文件如何來(lái)完成重定向。本篇就介紹如何使用匿名管道來(lái)完成這一任務(wù)。
?
計(jì)算機(jī)中管道pipe類似于現(xiàn)實(shí)世界中的水管道,在一端放入水流,另一端就會(huì)流出來(lái)。在計(jì)算機(jī)機(jī)中水流自然被數(shù)據(jù)流所代替了。計(jì)算機(jī)中管道分為匿名管道和命名管道,本篇將主要介紹用匿名管道來(lái)完成這一重定向輸出任務(wù),命名管道就留給下一篇來(lái)介紹了。
先來(lái)看看如何創(chuàng)建和使用匿名管道。
?
第一個(gè) CreatePipe
函數(shù)功能:創(chuàng)建管道
函數(shù)原型:
BOOLWINAPICreatePipe(
? PHANDLEhReadPipe,
? PHANDLEhWritePipe,
? LPSECURITY_ATTRIBUTESlpPipeAttributes,
? DWORDnSize
);
函數(shù)說(shuō)明:
第一個(gè)參數(shù)返回新創(chuàng)建的管道的讀取端句柄。
第二個(gè)參數(shù)返回新創(chuàng)建的管道的寫(xiě)入端句柄。
注意不能在管道的讀取端寫(xiě)入數(shù)據(jù)也不能在寫(xiě)入端讀取數(shù)據(jù)。
第三個(gè)參數(shù)表示管道的安全屬性,通常可以作如下設(shè)置:
?????? SECURITY_ATTRIBUTES sa;?
?????? sa.nLength???????????? = sizeof(SECURITY_ATTRIBUTES);
?????? sa.lpSecurityDescriptor? ?= NULL;
?????? sa.bInheritHandle?????? = TRUE;
第四個(gè)參數(shù)表示管道的緩沖區(qū)容量,為0表示使用默認(rèn)大小。
函數(shù)執(zhí)行成功返回TRUE,否則返回FALSE。
?
第二個(gè) ReadFile
函數(shù)功能:從管道中讀取數(shù)據(jù)
函數(shù)原型:
BOOLReadFile(
? HANDLEhFile,
? LPVOIDlpBuffer,
? DWORDnNumberOfBytesToRead,
? LPDWORDlpNumberOfBytesRead,
? LPOVERLAPPEDlpOverlapped
);
函數(shù)說(shuō)明:
第一個(gè)參數(shù)為句柄,可以是創(chuàng)建文件函數(shù)CreateFile()的返回值也可以是管道。
第二個(gè)參數(shù)是一個(gè)指向緩沖區(qū)的指針,函數(shù)將讀取的數(shù)據(jù)寫(xiě)入該緩沖區(qū)。
第三個(gè)參數(shù)的表達(dá)非常好,光從名字上就可以知道這是用來(lái)指定讀取的字節(jié)數(shù)。
第四個(gè)參數(shù)將返回實(shí)際讀取到的字節(jié)數(shù)。
第五個(gè)參數(shù)是用于異步操作方面,一般傳入NULL即可。
?
第三個(gè) WriteFile
函數(shù)功能:向管道寫(xiě)入數(shù)據(jù)
函數(shù)原型:
BOOLWriteFile(
? HANDLEhFile,
? LPCVOIDlpBuffer,
? DWORDnNumberOfBytesToWrite,
? LPDWORDlpNumberOfBytesWritten,
? LPOVERLAPPEDlpOverlapped
);
函數(shù)說(shuō)明:
第一個(gè)參數(shù)為句柄,可以是創(chuàng)建文件函數(shù)CreateFile()的返回值也可以是管道。
第二個(gè)參數(shù)是一個(gè)指針,該指針指向待寫(xiě)入管道的數(shù)據(jù)。
第三個(gè)參數(shù)表示要寫(xiě)入的字節(jié)數(shù)。
第四個(gè)參數(shù)將返回實(shí)際寫(xiě)入管道的字節(jié)數(shù)。
第五個(gè)參數(shù)是用于異步操作方面,一般傳入NULL即可。
?
第四個(gè)CloseHandle
函數(shù)功能:關(guān)閉管道的一端
函數(shù)原型:BOOLCloseHandle(HANDLEhObject);
函數(shù)說(shuō)明:當(dāng)讀取和寫(xiě)入端都關(guān)閉后,系統(tǒng)會(huì)關(guān)閉管道并回收資源。
?
從后面三個(gè)函數(shù)可以看出,向管道中讀取和寫(xiě)入數(shù)據(jù)就和向文件中讀取和寫(xiě)入數(shù)據(jù)是一樣的(事實(shí)上管道也是一種特殊的文件——內(nèi)存映射文件)。
?
使用管道要注意的一個(gè)地方是:讀取和寫(xiě)入數(shù)據(jù)時(shí),一定要注意順序,MSDN上說(shuō),如果管道中沒(méi)有數(shù)據(jù),調(diào)用ReadFile()會(huì)造成阻塞,直到有其它線程將數(shù)據(jù)寫(xiě)入管道。同樣,當(dāng)有線程正在管道中讀取數(shù)據(jù)時(shí),其它試圖將數(shù)據(jù)寫(xiě)入管道的的線程也會(huì)被阻塞。
?
因此對(duì)上一篇的示例程序進(jìn)行重定向時(shí),可以先創(chuàng)建二個(gè)管道,一個(gè)用來(lái)存放輸入數(shù)據(jù),稱為數(shù)據(jù)輸入管道,另一個(gè)用來(lái)存放輸出數(shù)據(jù),稱為數(shù)據(jù)輸出管道。然后從輸入文件中讀取數(shù)據(jù)并寫(xiě)入數(shù)據(jù)輸入管道。再啟動(dòng)示例程序作為子進(jìn)程,子進(jìn)程的輸入輸出已經(jīng)改成從數(shù)據(jù)輸入管道中讀取和輸出到數(shù)據(jù)輸出管道。子進(jìn)程運(yùn)行結(jié)束后,從數(shù)據(jù)輸出管道中將數(shù)據(jù)寫(xiě)入到輸出文件即可。整個(gè)流程圖如下所示:
下面給出使用管道的示例代碼:
//用管道來(lái)完成子進(jìn)程的重定向。 //流程如下: // infile.txt -> Input管道 -> 標(biāo)準(zhǔn)程序.exe -> Output管道 -> outfile.txt #include <windows.h> #include <stdio.h> int main() {printf(" 使用管道來(lái)重定向子進(jìn)程的輸入輸出\n"); printf(" --by MoreWindows( http://blog.csdn.net/MoreWindows )--\n\n"); char sz[3][50] = {"示例程序.exe", "infile.txt", "outfile.txt"};HANDLE hPipeInputRead, hPipeInputWrite, hPipeOutputRead, hPipeOutputWrite;//創(chuàng)建兩個(gè)管道SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES);sa.lpSecurityDescriptor = NULL;sa.bInheritHandle = TRUE;//數(shù)據(jù)輸入管道CreatePipe(&hPipeInputRead, &hPipeInputWrite, &sa, 0);//數(shù)據(jù)輸出管道CreatePipe(&hPipeOutputRead, &hPipeOutputWrite, &sa, 0);printf("創(chuàng)建數(shù)據(jù)輸入管道和數(shù)據(jù)輸出管道完畢\n");//從文件中讀取數(shù)據(jù),寫(xiě)入管道ReadFile中.const int BUFSIZE = 4096;CHAR chBuf[BUFSIZE] = {0}; DWORD dwRead, dwWritten;BOOL fSuccess;HANDLE hInputFile = CreateFile(sz[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);while (true) {//從文件中讀取數(shù)據(jù)fSuccess = ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL);if (!fSuccess || dwRead == 0)break; //將數(shù)據(jù)寫(xiě)入管道fSuccess = WriteFile(hPipeInputWrite, chBuf, dwRead, &dwWritten, NULL);if (!fSuccess) break; } //關(guān)閉輸入數(shù)據(jù)管道CloseHandle(hInputFile);hInputFile = NULL;CloseHandle(hPipeInputWrite);hPipeInputWrite = NULL;printf("已經(jīng)從文件中讀取數(shù)據(jù)并寫(xiě)入數(shù)據(jù)輸入管道\n");printf("啟動(dòng)示例程序并重定向到管道中\(zhòng)n....\n");//啟動(dòng)示例程序作為子進(jìn)程STARTUPINFO si;si.cb = sizeof(STARTUPINFO);GetStartupInfo(&si); si.hStdInput = hPipeInputRead; //輸入由標(biāo)準(zhǔn)輸入 -> 從管道中讀取si.hStdOutput = hPipeOutputWrite; //輸出由標(biāo)準(zhǔn)輸出 -> 輸出到管道si.wShowWindow = SW_HIDE; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; PROCESS_INFORMATION pi; CreateProcess( sz[0], NULL, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi);WaitForSingleObject(pi.hProcess, INFINITE);//關(guān)閉輸入數(shù)據(jù)管道CloseHandle(hPipeInputRead);hPipeInputRead = NULL;CloseHandle(hPipeOutputWrite);hPipeOutputWrite = NULL;printf("示例程序完成處理,現(xiàn)在將數(shù)據(jù)輸出管道中的數(shù)據(jù)寫(xiě)入文件\n");//將輸出數(shù)據(jù)管道中的數(shù)據(jù)寫(xiě)入到文件中HANDLE hOutputFile = CreateFile(sz[2], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); while (true){ //從管道中讀取fSuccess = ReadFile(hPipeOutputRead, chBuf, BUFSIZE, &dwRead, NULL);if( !fSuccess || dwRead == 0) break; //寫(xiě)入輸出文件fSuccess = WriteFile(hOutputFile, chBuf, dwRead, &dwWritten, NULL);if (!fSuccess) break; } //關(guān)閉輸出數(shù)據(jù)管道CloseHandle(hOutputFile);hOutputFile = NULL;CloseHandle(hPipeOutputRead);hPipeOutputRead = NULL;printf("數(shù)據(jù)輸出管道中的數(shù)據(jù)寫(xiě)入文件完畢\n");return 0; }運(yùn)行結(jié)果如下圖:
結(jié)果完全正確,說(shuō)明我們的程序已經(jīng)完成了啟動(dòng)其它程序并對(duì)它進(jìn)行重定向這一功能。
?
對(duì)匿名管道總結(jié)一下:匿名管道有讀取端和寫(xiě)入端。匿名管道創(chuàng)建(CreatePipe)后就可以像讀寫(xiě)文件一樣的對(duì)管道中的數(shù)據(jù)讀寫(xiě)(ReadFile與WriteFile),但要注意讀寫(xiě)順序。匿名管道在關(guān)閉兩端后會(huì)由系統(tǒng)銷毀并回收資源。
?
匿名管道的使用比較常見(jiàn),下面是二個(gè)安裝程序的截圖。
QQ游戲的安裝過(guò)程截圖:
五筆編碼及時(shí)查的安裝過(guò)程截圖:
對(duì)比下這二個(gè)截圖,顯示的內(nèi)容都差不多,都是解壓縮文件并移動(dòng)到指定地方。唯一不同的是一個(gè)是控制臺(tái)界面,另一個(gè)是圖形界面。聯(lián)想上面的程序,不難得知QQ游戲的安裝實(shí)際也是使用管道將一個(gè)控制臺(tái)程序的輸出內(nèi)容顯示到圖形界面,這樣既美觀又便于維護(hù)。
?
下一篇《進(jìn)程通信之二 管道技術(shù)第三篇 命名管道》將介紹命名管道的使用,歡迎參閱。
?
注:不知道程序代碼的情況下還可以使用批處理來(lái)完成。批處理使用>和<來(lái)重定向,>為輸出到文件,如果文件不存在就創(chuàng)建,已存在就清空原文件后再寫(xiě)入,<為從文件讀取。批處理文件的內(nèi)容可以這樣寫(xiě):
@echo off
<infile.txt 標(biāo)準(zhǔn)程序.exe >outfile.txt
也可以這樣寫(xiě):
@echo off
標(biāo)準(zhǔn)程序.exe <infile.txt >outfile.txt
批處理重定向的內(nèi)部實(shí)現(xiàn)原理當(dāng)然也是使用匿名管道。
?
轉(zhuǎn)載請(qǐng)標(biāo)明出處,原文地址:http://blog.csdn.net/morewindows/article/details/7390441
如果覺(jué)得本文對(duì)您有幫助,請(qǐng)點(diǎn)擊‘頂’支持一下,您的支持是我寫(xiě)作最大的動(dòng)力,謝謝。
?
轉(zhuǎn)載于:https://www.cnblogs.com/long12365/p/9731284.html
總結(jié)
以上是生活随笔為你收集整理的进程通信之二 管道技术第二篇 匿名管道的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Stack Overflow:研究发现访
- 下一篇: Winform判断一个窗口是否以模态化方