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