Windows系統編程之進程間通訊

Windows系統編程之進程間通訊
做者:北極星2003
來源:看雪論壇(www.pediy.com)
Windows 的IPC(進程間通訊)機制主要是異步管道和命名管道。(至於其餘的IPC方式,例如內存映射、郵槽等這裏就不介紹了)
管道(pipe)是用於進程間通訊的共享內存區域。建立管道的進程稱爲管道服務器,而鏈接到這個管道的進程稱爲管道客戶端。一個進程向管道寫入信息,而另一個進程從管道讀取信息。
異步管道是基於字符和半雙工的(即單向),通常用於程序輸入輸出的重定向;命名管道則強大地多,它們是面向消息和全雙工的,同時還容許網絡通訊,用於建立客戶端/服務器系統。
1、異步管道(實現比較簡單,直接經過實例來說解)
實驗目標:當前有sample.cpp, sample.exe, sample.in這三個文件,sample.exe爲sample.cpp的執行程序,sample.cpp只是一個簡單的程序示例(簡單求和),以下:php

代碼:
[cpp]  view plain  copy
 
  1. #include <iostream.h>  
  2. int main()  
  3. {  
  4.   int a, b ;  
  5.   while ( cin >> a >> b && ( a || b ) )  
  6.     cout << a + b << endl ;  
  7.   return 0;  
  8. }  
Sample.in文件是輸入文件,內容:
32 433
542 657
0 0
要求根據sample.exe和它的輸入數據,把輸出數據重定向到sample.out
流程分析:實際這個實驗中包含兩個部分,把輸入數據重定向到sample.exe 和把輸出數據重定向到sample.out。在命令行下能夠很簡單的實現這個功能「sample <sample.in >sample.out」,這個命令也是利用管道特性實現的,如今咱們就根據異步管道的實現原理本身來實現這個功能。
管道是基於半雙工(單向)的,這裏有兩個重定向的過程,顯然須要建立兩個管道,下面給出流程圖:
異步管道實現的流程圖說明:
1)。父進程是咱們須要實現的,其中須要建立管道A,管道B,和子進程,整個實現流程分爲4個操做。
2)。管道A:輸入管道
3)。管道B:輸出管道
4)。操做A:把輸入文件sample.in的數據寫入輸入管道(管道A)
5)。操做B:子進程從輸入管道中讀取數據,做爲該進程的加工原料。一般,程序的輸入數據由標準的輸入設備輸入,這裏實現輸入重定向,即把輸入管道做爲輸入設備。
6)。操做C:子進程把加工後的成品(輸出數據)輸出到輸出管道。一般,程序的輸出數據會輸出到標準的輸出設備,通常爲屏幕,這裏實現輸出重定向,即把輸出管道做爲輸出設備。
7)。操做D:把輸出管道的數據寫入輸出文件
須要注意的是,管道的本質只是一個共享的內存區域。這個實驗中,管道區域處於父進程的地址空間中,父進程的做用是提供環境和資源,並協調子進程進行加工。
程序源碼:
代碼:
[cpp]  view plain  copy
 
  1. #include <windows.h>   
  2. #include <iostream.h>  
  3.   
  4. const int BUFSIZE = 4096 ;   
  5. HANDLE  hChildStdinRd, hChildStdinWr, hChildStdinWrDup,   
  6.        hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup,   
  7.     hSaveStdin,    hSaveStdout;   
  8.   
  9. BOOL CreateChildProcess(LPTSTR);   
  10. VOID WriteToPipe(LPTSTR);   
  11. VOID ReadFromPipe(LPTSTR);   
  12. VOID ErrorExit(LPTSTR);   
  13. VOID ErrMsg(LPTSTR, BOOL);   
  14. void main( int argc, char *argv[] )   
  15. {    
  16.   // 處理輸入參數  
  17.   if ( argc != 4 )  
  18.     return ;  
  19.   
  20.   // 分別用來保存命令行,輸入文件名(CPP/C),輸出文件名(保存編譯信息)  
  21.   LPTSTR lpProgram = new char[ strlen(argv[1]) ] ;  
  22.   strcpy ( lpProgram, argv[1] ) ;  
  23.   LPTSTR lpInputFile = new char[ strlen(argv[2]) ];  
  24.   strcpy ( lpInputFile, argv[2] ) ;  
  25.   LPTSTR lpOutputFile = new char[ strlen(argv[3]) ] ;  
  26.   strcpy ( lpOutputFile, argv[3] ) ;      
  27.     
  28.   SECURITY_ATTRIBUTES saAttr;   
  29.   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);   
  30.   saAttr.bInheritHandle = TRUE;   
  31.   saAttr.lpSecurityDescriptor = NULL;   
  32.      
  33.   /************************************************ 
  34.    *    redirecting child process's STDOUT  * 
  35.    ************************************************/  
  36.   hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);   
  37.     
  38.   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))   
  39.     ErrorExit("Stdout pipe creation failed/n");   
  40.       
  41.   if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))   
  42.     ErrorExit("Redirecting STDOUT failed");   
  43.     
  44.   BOOL fSuccess = DuplicateHandle(  
  45.     GetCurrentProcess(),   
  46.     hChildStdoutRd,  
  47.         GetCurrentProcess(),   
  48.     &hChildStdoutRdDup ,  
  49.     0,  
  50.         FALSE,  
  51.         DUPLICATE_SAME_ACCESS);  
  52.     if( !fSuccess )  
  53.         ErrorExit("DuplicateHandle failed");  
  54.     CloseHandle(hChildStdoutRd);  
  55.     
  56.   /************************************************ 
  57.    *    redirecting child process's STDIN    * 
  58.    ************************************************/  
  59.   hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);   
  60.   
  61.   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))   
  62.     ErrorExit("Stdin pipe creation failed/n");   
  63.     
  64.   if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))   
  65.     ErrorExit("Redirecting Stdin failed");   
  66.     
  67.   fSuccess = DuplicateHandle(  
  68.     GetCurrentProcess(),   
  69.     hChildStdinWr,   
  70.     GetCurrentProcess(),  
  71.     &hChildStdinWrDup,   
  72.     0,   
  73.     FALSE,                   
  74.     DUPLICATE_SAME_ACCESS);   
  75.   if (! fSuccess)   
  76.     ErrorExit("DuplicateHandle failed");   
  77.   CloseHandle(hChildStdinWr);     
  78.   
  79.   /************************************************ 
  80.    *      建立子進程(即啓動SAMPLE.EXE)    * 
  81.    ************************************************/  
  82.   fSuccess = CreateChildProcess( lpProgram );  
  83.   if ( !fSuccess )   
  84.     ErrorExit("Create process failed");   
  85.     
  86.   // 父進程輸入輸出流的還原設置  
  87.   if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))   
  88.     ErrorExit("Re-redirecting Stdin failed/n");   
  89.   if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))   
  90.     ErrorExit("Re-redirecting Stdout failed/n");   
  91.   
  92.   WriteToPipe( lpInputFile ) ;  
  93.   ReadFromPipe( lpOutputFile );   
  94.           delete lpProgram ;  
  95.           delete lpInputFile ;  
  96.           delete lpOutputFile ;  
  97. }   
  98.   
  99. BOOL CreateChildProcess( LPTSTR lpProgram )   
  100. {   
  101.   PROCESS_INFORMATION piProcInfo;   
  102.   STARTUPINFO siStartInfo;  
  103.   BOOL bFuncRetn = FALSE;   
  104.     
  105.   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );  
  106.   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );  
  107.   siStartInfo.cb = sizeof(STARTUPINFO);   
  108.     
  109.   bFuncRetn = CreateProcess ( NULL, lpProgram, NULL, NULL, TRUE, /  
  110.                 0, NULL, NULL, &siStartInfo, &piProcInfo);  
  111.   if (bFuncRetn == 0)   
  112.   {  
  113.     ErrorExit("CreateProcess failed/n");  
  114.     return 0;  
  115.   }   
  116.   else   
  117.   {  
  118.     CloseHandle(piProcInfo.hProcess);  
  119.     CloseHandle(piProcInfo.hThread);  
  120.     return bFuncRetn;  
  121.   }  
  122. }  
  123.   
  124. VOID WriteToPipe( LPTSTR lpInputFile )   
  125. {   
  126.   HANDLE hInputFile = CreateFile(lpInputFile, GENERIC_READ, 0, NULL,   
  127.     OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);   
  128.   if (hInputFile == INVALID_HANDLE_VALUE)   
  129.     return ;  
  130.   
  131.   BOOL fSuccess ;  
  132.   DWORD dwRead, dwWritten;   
  133.   CHAR chBuf[BUFSIZE] = {0} ;   
  134.     
  135.   for (;;)   
  136.   {   
  137.     fSuccess = ReadFile( hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ;  
  138.     if ( !fSuccess || dwRead == 0)  
  139.       break;   
  140.   
  141.     fSuccess = WriteFile( hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL) ;  
  142.     if ( !fSuccess )   
  143.       break;   
  144.   }   
  145.       
  146.   if (! CloseHandle(hChildStdinWrDup))   
  147.     ErrorExit("Close pipe failed/n");   
  148.   
  149.   CloseHandle ( hInputFile ) ;  
  150. }   
  151.   
  152. VOID ReadFromPipe( LPTSTR lpOutputFile )   
  153. {   
  154.   HANDLE hOutputFile = CreateFile( lpOutputFile, GENERIC_READ|GENERIC_WRITE,   
  155.     FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);   
  156.   if (hOutputFile == INVALID_HANDLE_VALUE)   
  157.     return ;  
  158.   
  159.   BOOL fSuccess ;  
  160.   DWORD dwRead, dwWritten;   
  161.   CHAR chBuf[BUFSIZE] = { 0 };   
  162.     
  163.   if (!CloseHandle(hChildStdoutWr))   
  164.     ErrorExit("Closing handle failed");   
  165.     
  166.   for (;;)   
  167.   {   
  168.     fSuccess = ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) ;  
  169.     if( !fSuccess || dwRead == 0)   
  170.     {  
  171.       break;   
  172.     }  
  173.     fSuccess = WriteFile( hOutputFile, chBuf, dwRead, &dwWritten, NULL) ;  
  174.     if ( !fSuccess )   
  175.       break;   
  176.   }   
  177.   
  178.   CloseHandle ( hOutputFile ) ;  
  179. }   
  180. VOID ErrorExit (LPTSTR lpszMessage)   
  181. {   
  182.   MessageBox( 0, lpszMessage, 0, 0 );   
  183. }  
2、命名管道
命名管道具備如下幾個特徵:
(1)命名管道是雙向的,因此兩個進程能夠經過同一管道進行交互。
(2)命名管道不但能夠面向字節流,還能夠面向消息,因此讀取進程能夠讀取寫進程發送的不一樣長度的消息。
(3)多個獨立的管道實例能夠用一個名稱來命名。例如幾個客戶端可使用名稱相同的管道與同一個服務器進行併發通訊。
(4)命名管道能夠用於網絡間兩個進程的通訊,而其實現的過程與本地進程通訊徹底一致。
實驗目標:在客戶端輸入數據a和b,而後發送到服務器並計算a+b,而後把計算結果發送到客戶端。能夠多個客戶端與同一個服務器並行通訊。
界面設計:
難點所在:
實現的過程比較簡單,但有一個難點。本來當服務端使用ConnectNamedPipe函數後,若是有客戶端鏈接,就能夠直接進行交互。原來我在實現過程當中,當管道空閒時,管道的線程函數會無限(INFINITE)阻塞。若如今須要中止服務,就必須結束全部的線程,TernimateThread能夠做爲一個結束線程的方法,但我基本不用這個函數。一旦使用這個函數以後,目標線程就會當即結束,但若是此時的目標線程正在操做互斥資源、內核調用、或者是操做共享DLL的全局變量,可能會出現互斥資源沒法釋放、內核異常等現象。這裏我用重疊I/0來解決這個問題,在建立PIPE時使用FILE_FLAG_OVERLAPPED標誌,這樣使用ConnectNamedPipe後會當即返回,但線程的阻塞由等待函數WaitForSingleObject來實現,等待OVERLAPPED結構的事件對象被設置。
客戶端主要代碼:
代碼:
[cpp]  view plain  copy
 
  1. void CMyDlg::OnSubmit()   
  2. {  
  3.   // 打開管道  
  4.   HANDLE hPipe = CreateFile("////.//Pipe//NamedPipe", GENERIC_READ | GENERIC_WRITE, /  
  5.     0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ;  
  6.   if ( hPipe == INVALID_HANDLE_VALUE )  
  7.   {  
  8.     this->MessageBox ( "打開管道失敗,服務器還沒有啓動,或者客戶端數量過多" ) ;  
  9.     return ;  
  10.   }  
  11.   
  12.   DWORD nReadByte, nWriteByte ;  
  13.   char szBuf[1024] = {0} ;  
  14.   // 把兩個整數(a,b)格式化爲字符串  
  15.   sprintf ( szBuf, "%d %d", this->nFirst, this->nSecond ) ;  
  16.   // 把數據寫入管道  
  17.   WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;  
  18.   
  19.   memset ( szBuf, 0, sizeof(szBuf) ) ;  
  20.   // 讀取服務器的反饋信息  
  21.   ReadFile ( hPipe, szBuf, 1024, &nReadByte, NULL ) ;  
  22.   // 把返回信息格式化爲整數  
  23.   sscanf ( szBuf, "%d", &(this->nResValue) ) ;  
  24.   this->UpdateData ( false ) ;  
  25.   CloseHandle ( hPipe ) ;  
  26. }  
服務端主要代碼:
代碼:
[cpp]  view plain  copy
 
  1. // 啓動服務  
  2. void CMyDlg::OnStart()   
  3. {  
  4.   CString lpPipeName = "////.//Pipe//NamedPipe" ;  
  5.   for ( UINT i = 0; i < nMaxConn; i++ )  
  6.   {  
  7.     // 建立管道實例  
  8.     PipeInst[i].hPipe =  CreateNamedPipe ( lpPipeName, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, /  
  9.           PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, nMaxConn, 0, 0, 1000, NULL ) ;  
  10.     if ( PipeInst[i].hPipe == INVALID_HANDLE_VALUE )  
  11.     {  
  12.       DWORD dwErrorCode = GetLastError () ;  
  13.       this->MessageBox ( "建立管道錯誤!" ) ;  
  14.       return ;  
  15.     }  
  16.     // 爲每一個管道實例建立一個事件對象,用於實現重疊IO  
  17.     PipeInst[i].hEvent  =  CreateEvent ( NULL, false, false, false ) ;  
  18.     // 爲每一個管道實例分配一個線程,用於響應客戶端的請求  
  19.     PipeInst[i].hTread = AfxBeginThread ( ServerThread, &PipeInst[i], THREAD_PRIORITY_NORMAL ) ;  
  20.   }  
  21.     
  22.   this->SetWindowText ( "命名管道實例之服務器(運行)" ) ;  
  23.   this->MessageBox ( "服務啓動成功" ) ;  
  24. }  
  25. // 中止服務  
  26. void CMyDlg::OnStop()   
  27. {  
  28.   DWORD dwNewMode = PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT ;  
  29.   for ( UINT i = 0; i < nMaxConn; i++ )  
  30.   {  
  31.     SetEvent ( PipeInst[i].hEvent ) ;  
  32.     CloseHandle ( PipeInst[i].hTread ) ;  
  33.     CloseHandle ( PipeInst[i].hPipe ) ;  
  34.   }  
  35.       
  36.   this->SetWindowText ( "命名管道實例之服務器" ) ;  
  37.   this->MessageBox ( "中止啓動成功" ) ;  
  38. }  
  39.   
  40. // 線程服務函數  
  41. UINT ServerThread ( LPVOID lpParameter )  
  42. {  
  43.   DWORD  nReadByte = 0, nWriteByte = 0, dwByte = 0 ;    
  44.   char  szBuf[MAX_BUFFER_SIZE] = {0} ;  
  45.   PIPE_INSTRUCT  CurPipeInst = *(PIPE_INSTRUCT*)lpParameter ;  
  46.   OVERLAPPED OverLapStruct = { 0, 0, 0, 0, CurPipeInst.hEvent } ;  
  47.   while ( true )  
  48.   {  
  49.     memset ( szBuf, 0, sizeof(szBuf) ) ;    
  50.     // 命名管道的鏈接函數,等待客戶端的鏈接(只針對NT)  
  51.     ConnectNamedPipe ( CurPipeInst.hPipe, &OverLapStruct ) ;  
  52.     // 實現重疊I/0,等待OVERLAPPED結構的事件對象  
  53.     WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ;  
  54.     // 檢測I/0是否已經完成,若是未完成,意味着該事件對象是人工設置,即服務須要中止  
  55.     if ( !GetOverlappedResult ( CurPipeInst.hPipe, &OverLapStruct, &dwByte, true ) )  
  56.       break ;  
  57.   
  58.     // 從管道中讀取客戶端的請求信息  
  59.     if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, &nReadByte, NULL ) )  
  60.     {  
  61.       MessageBox ( 0, "讀取管道錯誤!", 0, 0 ) ;  
  62.       break ;  
  63.     }  
  64.       
  65.     int a, b ;  
  66.     sscanf ( szBuf, "%d %d", &a, &b ) ;  
  67.     pMyDlg->nFirst    = a ;  
  68.     pMyDlg->nSecond    = b ;  
  69.     pMyDlg->nResValue  = a + b ;  
  70.     memset ( szBuf, 0, sizeof(szBuf) ) ;  
  71.     sprintf ( szBuf, "%d", pMyDlg->nResValue ) ;  
  72.     // 把反饋信息寫入管道  
  73.     WriteFile ( CurPipeInst.hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;  
  74.     pMyDlg->SetDlgItemInt ( IDC_FIRST, a, true ) ;  
  75.     pMyDlg->SetDlgItemInt ( IDC_SECOND, b, true ) ;  
  76.     pMyDlg->SetDlgItemInt ( IDC_RESULT, pMyDlg->nResValue, true ) ;  
  77.     // 斷開客戶端的鏈接,以便等待下一客戶的到來  
  78.     DisconnectNamedPipe ( CurPipeInst.hPipe ) ;  
  79.   }  
  80.   
  81.   return 0 ;  
  82. }  
最後特別說明一下,此文章是看雪WIN32安全編程板塊的斑竹北極星的,你們能夠多多關注一下他的技術文章,看了幾篇都認爲很不錯的。
鏈 接: http://bbs.pediy.com/showthread.php?t=26252
http://blog.csdn.net/yiruirui0507/article/details/6457806
相關文章
相關標籤/搜索