進程間通訊二:管道技術之匿名管道

轉載自:http://blog.csdn.net/morewindows/article/details/7390441web

計算機中管道pipe相似於現實世界中的水管道,在一端放入水流,另外一端就會流出來。在計算機機中水流天然被數據流所代替了。計算機中管道分爲匿名管道和命名管道,本篇將主要介紹用匿名管道來完成這一重定向輸出任務,命名管道就留給下一篇來介紹了。windows

先來看看如何建立和使用匿名管道。安全

 

第一個 CreatePipeapp

函數功能:建立管道異步

函數原型:ide

BOOLWINAPICreatePipe(函數

  PHANDLEhReadPipe,oop

  PHANDLEhWritePipe,this

  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()會形成阻塞,直到有其它線程將數據寫入管道。一樣,當有線程正在管道中讀取數據時,其它試圖將數據寫入管道的的線程也會被阻塞。

 

所以對上一篇的示例程序進行重定向時,能夠先建立二個管道,一個用來存放輸入數據,稱爲數據輸入管道,另外一個用來存放輸出數據,稱爲數據輸出管道。而後從輸入文件中讀取數據並寫入數據輸入管道。再啓動示例程序做爲子進程,子進程的輸入輸出已經改爲從數據輸入管道中讀取和輸出到數據輸出管道。子進程運行結束後,從數據輸出管道中將數據寫入到輸出文件便可。整個流程圖以下所示:

 

下面給出使用管道的示例代碼:

[cpp]   view plain copy
  1. //用管道來完成子進程的重定向。  
  2. //流程以下:  
  3. // infile.txt -> Input管道 -> 標準程序.exe -> Output管道 -> outfile.txt  
  4. #include <windows.h>  
  5. #include <stdio.h>  
  6. int main()  
  7. {  
  8.     printf("   使用管道來重定向子進程的輸入輸出\n");    
  9.     printf("  --by MoreWindows( http://blog.csdn.net/MoreWindows )--\n\n");   
  10.   
  11.     char sz[3][50] = {"示例程序.exe""infile.txt""outfile.txt"};  
  12.     HANDLE hPipeInputRead, hPipeInputWrite, hPipeOutputRead, hPipeOutputWrite;  
  13.   
  14.     //建立兩個管道  
  15.     SECURITY_ATTRIBUTES sa;    
  16.     sa.nLength              = sizeof(SECURITY_ATTRIBUTES);  
  17.     sa.lpSecurityDescriptor = NULL;  
  18.     sa.bInheritHandle       = TRUE;  
  19.     //數據輸入管道  
  20.     CreatePipe(&hPipeInputRead, &hPipeInputWrite, &sa, 0);  
  21.     //數據輸出管道  
  22.     CreatePipe(&hPipeOutputRead, &hPipeOutputWrite, &sa, 0);  
  23.       
  24.     printf("建立數據輸入管道和數據輸出管道完畢\n");  
  25.   
  26.     //從文件中讀取數據,寫入管道ReadFile中.  
  27.     const int BUFSIZE  = 4096;  
  28.     CHAR chBuf[BUFSIZE] = {0};   
  29.     DWORD dwRead, dwWritten;  
  30.     BOOL  fSuccess;  
  31.     HANDLE hInputFile = CreateFile(sz[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);  
  32.     while (true)   
  33.     {  
  34.         //從文件中讀取數據  
  35.         fSuccess = ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL);  
  36.         if (!fSuccess || dwRead == 0)  
  37.             break;   
  38.         //將數據寫入管道  
  39.         fSuccess = WriteFile(hPipeInputWrite, chBuf, dwRead, &dwWritten, NULL);  
  40.         if (!fSuccess)   
  41.             break;   
  42.     }   
  43.     //關閉輸入數據管道  
  44.     CloseHandle(hInputFile);  
  45.     hInputFile = NULL;  
  46.     CloseHandle(hPipeInputWrite);  
  47.     hPipeInputWrite = NULL;  
  48.       
  49.     printf("已經從文件中讀取數據並寫入數據輸入管道\n");  
  50.     printf("啓動示例程序並重定向到管道中\n....\n");  
  51.     //啓動示例程序做爲子進程  
  52.     STARTUPINFO si;  
  53.     si.cb = sizeof(STARTUPINFO);  
  54.     GetStartupInfo(&si);   
  55.     si.hStdInput   = hPipeInputRead;   //輸入由標準輸入 -> 從管道中讀取  
  56.     si.hStdOutput  = hPipeOutputWrite; //輸出由標準輸出 -> 輸出到管道  
  57.     si.wShowWindow = SW_HIDE;    
  58.     si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;    
  59.     PROCESS_INFORMATION pi;   
  60.     CreateProcess( sz[0], NULL, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi);  
  61.     WaitForSingleObject(pi.hProcess, INFINITE);  
  62.   
  63.     //關閉輸入數據管道  
  64.     CloseHandle(hPipeInputRead);  
  65.     hPipeInputRead = NULL;  
  66.     CloseHandle(hPipeOutputWrite);  
  67.     hPipeOutputWrite = NULL;  
  68.       
  69.     printf("示例程序完成處理,如今將數據輸出管道中的數據寫入文件\n");  
  70.   
  71.     //將輸出數據管道中的數據寫入到文件中  
  72.     HANDLE hOutputFile = CreateFile(sz[2], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);   
  73.     while (true)  
  74.     {   
  75.         //從管道中讀取  
  76.         fSuccess = ReadFile(hPipeOutputRead, chBuf, BUFSIZE, &dwRead, NULL);  
  77.         if( !fSuccess || dwRead == 0)   
  78.             break;   
  79.         //寫入輸出文件  
  80.         fSuccess = WriteFile(hOutputFile, chBuf, dwRead, &dwWritten, NULL);  
  81.         if (!fSuccess)   
  82.             break;   
  83.     }   
  84.     //關閉輸出數據管道  
  85.     CloseHandle(hOutputFile);  
  86.     hOutputFile = NULL;  
  87.     CloseHandle(hPipeOutputRead);  
  88.     hPipeOutputRead = NULL;  
  89.       
  90.     printf("數據輸出管道中的數據寫入文件完畢\n");  
  91.     return 0;  
  92. }  

運行結果以下圖:

結果徹底正確,說明咱們的程序已經完成了啓動其它程序並對它進行重定向這一功能。

 

對匿名管道總結一下:匿名管道有讀取端和寫入端。匿名管道建立(CreatePipe)後就能夠像讀寫文件同樣的對管道中的數據讀寫(ReadFileWriteFile),但要注意讀寫順序。匿名管道在關閉兩端後會由系統銷燬並回收資源。

 

匿名管道的使用比較常見,下面是二個安裝程序的截圖。

QQ遊戲的安裝過程截圖:

五筆編碼及時查的安裝過程截圖:

對比下這二個截圖,顯示的內容都差很少,都是解壓縮文件並移動到指定地方。惟一不一樣的是一個是控制檯界面,另外一個是圖形界面。聯想上面的程序,不可貴知QQ遊戲的安裝實際也是使用管道將一個控制檯程序的輸出內容顯示到圖形界面,這樣既美觀又便於維護。

相關文章
相關標籤/搜索