郵槽和命名管道的使用方法也很簡單,只需幾個有限的函數就可以實現雙方的通信。
第三、郵槽
郵槽----進程間通信機制。
通過郵槽客戶進程可以將消息通過廣播給一個或多個服務進程。這是一個單向通信機制,缺點是只允許從客戶機到服務器,優點也是這個原理,使客戶機應用能夠非常容易地將廣播消息發送給一個或多個服務器應用。郵槽是一種無連接方式,是一種”不可靠“的數據傳輸。
郵槽名也使用UNC路徑,第二個關鍵字是Mailslot,不可改變
\\\\server\\Mailslot\\[path]name
服務器實現過程:
CreateMailslot();//創建一個郵槽句柄
ReadFile();//接受任何客戶機的數據
CloseHandle();//關閉郵槽句柄
服務器端郵槽實現
1 //server1.cpp 2 //郵槽的實現 3 #include "windows.h" 4 #include "winbase.h" 5 #include "stdio.h" 6 7 void main() 8 { 9 HANDLE Mailslot; 10 char buffer[256]; 11 DWORD NumberOfBytesRead; 12 13 //創建郵槽 14 if ((Mailslot = CreateMailslot("\\\\.\\Mailslot\\Myslot",0,MAILSLOT_WAIT_FOREVER,NULL)) 15 == INVALID_HANDLE_VALUE) 16 { 17 printf("Failed to create a mailslot %d\n",GetLastError()); 18 getchar(); 19 return ; 20 } 21 22 //從郵槽中讀取數據,只有服務器才能從郵槽中讀取數據 23 while(ReadFile(Mailslot,buffer,256,&NumberOfBytesRead,NULL) != 0) 24 { 25 printf("%.*s\n",NumberOfBytesRead,buffer); 26 } 27 if (!Mailslot) 28 { 29 CloseHandle(Mailslot); 30 } 31 }
客戶端實現過程
CreateFile();//打開指向郵槽的句柄
WriteFile();//寫入數據
CloseHandle();//關閉句柄
客戶端郵槽代碼實現
1 //client.cpp 2 //郵槽客戶端 3 4 #include "windows.h" 5 #include "stdio.h" 6 7 void main() 8 { 9 HANDLE Mailslot; 10 DWORD ByteWritten; 11 CHAR ServerName[256]; 12 13 if((Mailslot=CreateFile("\\\\.\\Mailslot\\Myslot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIB UTE_NORMAL,NULL)) == INVALID_HANDLE_VALUE)//這里就是郵槽的創建 14 { 15 printf("Createfile failed with error %d\n",GetLastError()); 16 getchar(); 17 return ; 18 } 19 20 if (WriteFile(Mailslot,"This is a test",15,&ByteWritten,NULL) == 0)//寫入數據 21 { 22 printf("WriteFile failed with error %d\n",GetLastError()); 23 getchar(); 24 return ; 25 } 26 27 printf("Write %d bytes\n",ByteWritten); 28 getchar(); 29 CloseHandle(Mailslot); 30 }
下面在服務器上添加線程,用於自己終止程序運行,而不是讓其一直處於掛起狀態。這種方式是為了避免服務器在ReadFile()時服務器程序因某種原因而終止,此時ReadFile()沒有完成,程序會一直處於掛起狀態,直到有數據可讀。
服務器端可以自己終止接受數據
1 //server2.cpp 2 //郵槽的線程實現 3 #include "windows.h" 4 #include "winbase.h" 5 #include "stdio.h" 6 #include "conio.h" 7 8 BOOL StopProcessing; 9 10 DWORD WINAPI ServerMailslot(LPVOID lpParameter); 11 void SendMessageToMailslot(void);//自己向郵槽發送數據 12 13 void main() 14 { 15 HANDLE MailslotThread; 16 DWORD ThreadID; 17 StopProcessing = FALSE; 18 MailslotThread = CreateThread(NULL,0,ServerMailslot,NULL,0,&ThreadID); 19 20 printf("Press a key to stop the server\n"); 21 _getch(); 22 23 //按下按鍵以后,賦值為TRUE,在線程中終止程序運行 24 StopProcessing = TRUE; 25 26 //發送消息,之后線程會進入while()循環,並且會終止循環 27 SendMessageToMailslot();//自己向郵槽發送數據 28 29 // 30 if (WaitForSingleObject(MailslotThread,INFINITE) == WAIT_FAILED) 31 { 32 printf("WaitForSingleObject Failed with error %d\n",GetLastError()); 33 getchar(); 34 return ; 35 } 36 } 37 38 //線程入口函數 39 DWORD WINAPI ServerMailslot(LPVOID lpParameter) 40 { 41 char buffer[2048]; 42 DWORD NumberOfBytesRead; 43 DWORD Ret; 44 HANDLE Mailslot; 45 46 if ((Mailslot = CreateMailslot("\\\\.\\mailslot\\myslot",2048,MAILSLOT_WAIT_FOREVER,NULL)) 47 == INVALID_HANDLE_VALUE) 48 { 49 printf("Failed to create a mailslot %d\n",GetLastError()); 50 getchar(); 51 return 0; 52 } 53 54 while ((Ret = ReadFile(Mailslot,buffer,2048,&NumberOfBytesRead,NULL)) != 0) 55 { 56 if(StopProcessing) 57 break; 58 printf("Receive %d bytes \n",NumberOfBytesRead); 59 } 60 CloseHandle(Mailslot); 61 return 0; 62 } 63 64 //發送終止信息 65 void SendMessageToMailslot() 66 { 67 HANDLE Mailslot; 68 DWORD BytesWritten; 69 if ((Mailslot = CreateFile("\\\\.\\mailslot\\myslot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING 70 ,FILE_ATTRIBUTE_NORMAL,NULL)) == INVALID_HANDLE_VALUE) 71 { 72 printf("CreateFile Failed with error %d\n",GetLastError()); 73 getchar(); 74 return ; 75 } 76 if (WriteFile(Mailslot,"STOP",4,&BytesWritten,NULL) == 0) 77 { 78 printf("WriteFile Failed with error %d\n",GetLastError()); 79 getchar(); 80 return ; 81 } 82 CloseHandle(Mailslot); 83 }
這樣可以避免服務器進入無限的等待。
郵槽總結:
使用郵槽,應用程序可以在Windows重定向器的幫助下,實現簡單的單向進程間數據通信。對郵槽來說,它最有價值的一項功能便是通過網絡,將一條消息廣播給一台或多台計算機。然而,郵槽並未提供對數據可靠傳輸的保障。是一種不可靠的數據傳輸。
第四、命名管道
命名管道實際上建立一個簡單的客戶機/服務器數據通信體系,可在其中可靠地傳輸數據。
規則:
命名管道的標識是采用 U N C格式進行的:\ \ server\pipe\ [ path ]name
其中的pipe是一個標記,不能改變,不區分大小寫。例子如下:
\\\\ myserver\\pipe\\mypipe
通信方式
命名管道提供了兩種基本通信模式:字節模式和消息模式。
在字節模式中,消息以一個連續的字節流的形式,在客戶機與服務器之間流動。在一方寫入某個數量的字節,並不表示在另一方會讀出等量的字節。這樣一來,客戶機和服務器在傳輸數據的時候,便不必關心數據的內容。
在消息模式中,客戶機和服務器則通過一系列不連續的數據單位,進行數據的收發。每次在管道上發出了一條消息后,它必須作為一條完整的消息讀入。
服務器與客戶機的區別
命名管道的最大特點就是建立了一個基於服務器/客戶機的程序設計體系。在這個體系結構中,數據既可以單向流動,也可以雙向流動。但是服務器是唯一一個有權利創建命名管道的進程,也只有它有權利接受來自客戶端的鏈接請求。
服務器的實現過程
CreateNamedPipe();//創建命名管道實例句柄
ConnectNamedPipe();//監聽來自客戶機的鏈接請求
ReadFile(),WriteFile();//讀寫數據
DisconnectNmaePipe();//關閉命名通道連接
CloseHandle();//關閉命名管道實例句柄
客戶端實現過程
WaitNamedPipe();// 等候一個命名管道實例可供自己使用
CreateFile();// 建立與命名管道的連接
WriteFile(); ,ReadFile();//讀寫數據
CloseHandle();// 關閉命名管道會話
簡單命名管道的實現
1 //命名管道的實現 2 3 #include "windows.h" 4 #include "stdio.h" 5 6 void main() 7 { 8 HANDLE PipeHnadle; 9 DWORD BytesRead; 10 CHAR buffer[256]; 11 12 if ((PipeHnadle = CreateNamedPipe("\\\\.\\Pipe\\Song",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE | PIPE_READMODE_BYTE 13 ,1,0,0,1000,NULL)) == INVALID_HANDLE_VALUE)//創建 14 { 15 printf("CreateNamedPipe failed with error %d\n",GetLastError()); 16 getchar(); 17 return ; 18 } 19 printf("Server is now running!\n"); 20 21 if (ConnectNamedPipe(PipeHnadle,NULL) == 0)//連接客戶端 22 { 23 printf("ConnectNamedPipe failed with error %d\n",GetLastError()); 24 CloseHandle(PipeHnadle); 25 getchar(); 26 return ; 27 } 28 29 if (ReadFile(PipeHnadle,buffer,sizeof(buffer),&BytesRead,NULL) <= 0)//讀取數據 30 { 31 printf("ReadFile failed with error %d\n",GetLastError()); 32 CloseHandle(PipeHnadle); 33 getchar(); 34 return ; 35 } 36 37 printf("%.*s\n",BytesRead,buffer); 38 39 if (DisconnectNamedPipe(PipeHnadle) == 0)//關閉 40 { 41 printf("DisconnectNamedPipe failed with error %d\n",GetLastError()); 42 getchar(); 43 return ; 44 } 45 46 CloseHandle(PipeHnadle); 47 getchar(); 48 getchar(); 49 }
同時控制多個管道實例,命名管道可以實現多個實例的連接。這個數量有函數CreateNamedPipe()控制
1 HANDLE WINAPI CreateNamedPipe( 2 _In_ LPCTSTR lpName,//管道名稱 3 _In_ DWORD dwOpenMode,//打開模式,如PIPE_ACCESS_DUPLES 雙向管道,PIPE_FLAG_OVEERLAPPED(重疊I/O) 4 _In_ DWORD dwPipeMode,//管道模式,字節流還是信息流 5 _In_ DWORD nMaxInstances,//最大連接實例數量 6 _In_ DWORD nOutBufferSize,//輸出緩沖區大小 7 _In_ DWORD nInBufferSize,//輸入緩沖區大小 8 _In_ DWORD nDefaultTimeOut,//超時設置 9 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes//安全描述符 10 );
多管道實現,使用線程
1 //命名管道的實現,多線程 2 3 #include "windows.h" 4 #include "stdio.h" 5 #include "conio.h" 6 7 #define NUM_PIPES 5 8 9 DWORD WINAPI PipeInstanceProc(LPVOID lpParameter); 10 void main() 11 { 12 HANDLE ThreadHandle; 13 INT i; 14 DWORD ThreadID; 15 16 for(i = 0 ; i < NUM_PIPES ;i ++ ) 17 { 18 //創建線程保存管道實例 19 if ((ThreadHandle = CreateThread(NULL,0,PipeInstanceProc, 20 NULL,0,&ThreadID)) == NULL) 21 { 22 printf("CreateThread failed with error %d \n",GetLastError()); 23 getchar(); 24 return ; 25 } 26 CloseHandle(ThreadHandle); 27 } 28 29 printf("Press any key to stop the server!\n"); 30 _getch(); 31 } 32 33 //入口函數 34 35 DWORD WINAPI PipeInstanceProc(LPVOID lpParameter) 36 { 37 HANDLE PipeHandle; 38 DWORD BytesRead; 39 DWORD BytesWritten; 40 CHAR buffer[256]; 41 42 if ((PipeHandle = CreateNamedPipe("\\\\.\\Pipe\\Song",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE | PIPE_READMODE_BYTE 43 ,NUM_PIPES,0,0,1000,NULL)) == INVALID_HANDLE_VALUE) 44 { 45 printf("CreateNamedPipe failed with error %d \n",GetLastError()); 46 getchar(); 47 return 0; 48 } 49 //一直嘗試連接客戶端 50 while (true) 51 { 52 if (ConnectNamedPipe(PipeHandle,NULL) == 0) 53 { 54 printf("ConnectNamedPipe failed with error %d \n",GetLastError()); 55 getchar(); 56 break; 57 } 58 //讀取數據 59 while (ReadFile(PipeHandle,buffer,sizeof(buffer),&BytesRead,NULL) > 0) 60 { 61 printf("Echo %d bytes to client \n",BytesRead); 62 if (WriteFile(PipeHandle,buffer,BytesRead,&BytesWritten,NULL) == 0) 63 { 64 printf("WriteFile failed with error %d \n",GetLastError()); 65 getchar(); 66 break; 67 } 68 } 69 if (DisconnectNamedPipe(PipeHandle) == 0) 70 { 71 printf("DisconnectNamedPipe failed with error %d \n",GetLastError()); 72 getchar(); 73 break; 74 } 75 } 76 CloseHandle(PipeHandle); 77 return 0; 78 }
最后看一下基於重疊I/O模式的管道通信
這個重疊I/O的設置在CreateNamedPipe();的第二個參數 dwOpenMode,只需在里面包含 PIPE_FLAG_OVEERLAPPED就行。
1 //overlapped_server.cpp 2 3 //重疊I/O方式是下命名管道 4 5 #include "windows.h" 6 #include "stdio.h" 7 8 #define NUM_PIPES 5 9 #define BUFFER_SIZE 256 10 11 void main() 12 { 13 HANDLE PipeHandles[NUM_PIPES]; 14 DWORD BytesTransferred; 15 CHAR buffer[NUM_PIPES][BUFFER_SIZE]; 16 int i; 17 OVERLAPPED ovlap[NUM_PIPES]; 18 HANDLE Event[NUM_PIPES]; 19 20 ////////////////////////////////////////////////////////////////////////// 21 22 BOOL DataRead[NUM_PIPES]; 23 DWORD Ret; 24 DWORD Pipe; 25 26 for (i = 0; i < NUM_PIPES ; i ++) 27 { 28 //創建命名管道實例 29 if ((PipeHandles[i] = CreateNamedPipe("\\\\.\\pipe\\Song",PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 30 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,NUM_PIPES,0,0,1000,NULL)) == INVALID_HANDLE_VALUE) 31 { 32 printf("CreateNamedPipe for pipe %d failed with error %d\n",i,GetLastError()); 33 getchar(); 34 return ; 35 } 36 //創建事件 37 if ((Event[i] = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) 38 { 39 printf("CreateEvent for pipe %d failed with error %d\n",i,GetLastError()); 40 getchar(); 41 continue ; 42 } 43 //保存事件狀態 44 DataRead[i] = FALSE; 45 ZeroMemory(&ovlap[i],sizeof(OVERLAPPED)); 46 47 //監聽事件 48 if (ConnectNamedPipe(PipeHandles[i],&ovlap[i]) == 0) 49 { 50 if (GetLastError() != ERROR_IO_PENDING) 51 { 52 printf("ConnectNamedPipe for pipe %d failed with error %d\n",i,GetLastError()); 53 CloseHandle(PipeHandles[i]); 54 getchar(); 55 return ; 56 } 57 } 58 } 59 60 // 61 printf("Server is running!\n"); 62 ////////////////////////////////////////////////////////////////////////// 63 64 //讀取數據 65 while (true) 66 { 67 if ((Ret = WaitForMultipleObjects(NUM_PIPES,Event,FALSE,INFINITE)) == WAIT_FAILED) 68 { 69 printf("WaitForMultipleObjects failed with error %d\n",GetLastError()); 70 getchar(); 71 return ; 72 } 73 Pipe = Ret - WAIT_OBJECT_0; 74 ResetEvent(Event[Pipe]); 75 76 //檢查I/O狀態,如果失敗,就斷開連接並重新嘗試讀取數據 77 if (GetOverlappedResult(PipeHandles[Pipe],&ovlap[Pipe],&BytesTransferred,TRUE) == 0) 78 { 79 printf("GetOverlappedResult failed with error %d\n",GetLastError()); 80 //斷開連接 81 if (DisconnectNamedPipe(PipeHandles[Pipe]) == 0) 82 { 83 printf("DisconnectNamedPipe failed with error %d\n",GetLastError()); 84 return ; 85 } 86 if (ConnectNamedPipe(PipeHandles[Pipe],&ovlap[pipe]) == 0) 87 { 88 if (GetLastError() != ERROR_IO_PENDING) 89 { 90 //服務器出錯,關閉句柄 91 printf("ConnectNamedPipe for pipe %d failed with error %d\n",i,GetLastError()); 92 CloseHandle(PipeHandles[Pipe]); 93 } 94 } 95 96 DataRead[Pipe] = FALSE; 97 } 98 else 99 { 100 //如果管道上有數據就讀取並發送客戶端,如果沒有就一直嘗試讀取 101 if (DataRead[Pipe] == FALSE) 102 { 103 ZeroMemory(&ovlap[Pipe],sizeof(OVERLAPPED)); 104 ovlap[Pipe].hEvent = Event[Pipe]; 105 106 if (ReadFile(PipeHandles[Pipe],buffer[Pipe],BUFFER_SIZE,NULL,&ovlap[Pipe]) == 0) 107 { 108 if (GetLastError() != ERROR_IO_PENDING) 109 { 110 printf("ReadFile failed with error %d\n",GetLastError()); 111 112 } 113 } 114 DataRead[Pipe] = TRUE; 115 } 116 else 117 { 118 //向管道寫入數據 119 printf("Received %d bytes ,echo bytes back \n",BytesTransferred); 120 ZeroMemory(&ovlap[Pipe],sizeof(OVERLAPPED)); 121 ovlap[Pipe].hEvent = Event[Pipe]; 122 123 if (WriteFile(PipeHandles[Pipe],buffer[Pipe],BUFFER_SIZE,NULL,&ovlap[Pipe]) == 0) 124 { 125 if (GetLastError() != ERROR_IO_PENDING) 126 { 127 printf("WriteFile failed with error %d\n",GetLastError()); 128 129 } 130 } 131 DataRead[Pipe] = FALSE; 132 } 133 134 } 135 } 136 }
再看一下客戶端的實現
1 //client3.cpp 2 3 //簡單命名管道客戶機 4 5 6 #include "windows.h" 7 #include "stdio.h" 8 #include "conio.h" 9 10 #define PIPE_NAME "\\\\.\\pipe\\Song" 11 12 void main() 13 { 14 15 HANDLE PipeHandle; 16 DWORD BytesWritten; 17 DWORD BytesRead; 18 CHAR buffer[256]; 19 memset(&buffer,0,sizeof(buffer)); 20 //等待可用的命名管道 21 if (WaitNamedPipe(PIPE_NAME,NMPWAIT_WAIT_FOREVER) == 0) 22 { 23 printf("WaitNamedPipe failed with error %d \n",GetLastError()); 24 getchar(); 25 return ; 26 } 27 //創建命名管道句柄 28 29 if ((PipeHandle = CreateFile(PIPE_NAME,GENERIC_READ | GENERIC_WRITE, 30 0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)) == INVALID_HANDLE_VALUE) 31 { 32 printf("CreateFile failed with error %d \n",GetLastError()); 33 getchar(); 34 return ; 35 } 36 //寫入數據 37 if ((WriteFile(PipeHandle,"This is a test",14,&BytesWritten,NULL)) == 0) 38 { 39 printf("WriteFile failed with error %d \n",GetLastError()); 40 CloseHandle(PipeHandle); 41 getchar(); 42 return ; 43 } 44 45 printf("Wrote %d bytes\n",BytesWritten); 46 47 //讀取數據 48 if (ReadFile(PipeHandle,buffer,sizeof(buffer),&BytesRead,NULL) > 0) 49 { 50 printf("Receive:dfj %s\n",buffer); 51 printf("Read %d bytes\n",BytesWritten); 52 getchar(); 53 return ; 54 } 55 56 getchar(); 57 CloseHandle(PipeHandle); 58 59 }
最后還有幾個簡化后的API,使用起來更加方便
CallNamedPipe();//客戶機,可同時讀寫數據
TransactNamedPipe();//客戶機、服務器,可同時讀寫數據
1 BOOL WINAPI CallNamedPipe( 2 _In_ LPCTSTR lpNamedPipeName,//管道名稱,采用UNC格式 3 _In_ LPVOID lpInBuffer,//發送數據緩存區 4 _In_ DWORD nInBufferSize,//數據緩存區大小 5 _Out_ LPVOID lpOutBuffer,//接受數據緩存區 6 _In_ DWORD nOutBufferSize,//接受數據緩存區大小 7 _Out_ LPDWORD lpBytesRead,//從管道中讀取的數據大小 8 _In_ DWORD nTimeOut//超時 9 );
其中nTimeOut 的參數可供選擇為:
NMPWAIT_NOWAIT //不可用則直接退出,並返回錯誤
WMPWAIT_WAIT_FOREVER//一直等待
NMPWAIT_USE_DEFAULT_WAIT//使用默認的超時設置
1 BOOL WINAPI TransactNamedPipe( 2 _In_ HANDLE hNamedPipe,//命名管道句柄 3 _In_ LPVOID lpInBuffer,//發送數據緩沖區 4 _In_ DWORD nInBufferSize,//緩沖區大小 5 _Out_ LPVOID lpOutBuffer,//接受數據緩沖區 6 _In_ DWORD nOutBufferSize,//緩沖區大小 7 _Out_ LPDWORD lpBytesRead,//實際讀取的數據多少 8 _Inout_opt_ LPOVERLAPPED lpOverlapped//重疊I/O 9 );
GetNamedPipeHandleState();//
用於接收與一個指定命名管道對應的信息,比如運行模式(消息或字節模式)、管道實例數以及緩沖區信息等等。
1 BOOL WINAPI GetNamedPipeHandleState( 2 _In_ HANDLE hNamedPipe,//命名管道句柄 3 _Out_opt_ LPDWORD lpState,//管道狀態PIPE_NOWAIT,PIPE_READMODE_MESSAGE 4 _Out_opt_ LPDWORD lpCurInstances,//當前管道的實例數量 5 _Out_opt_ LPDWORD lpMaxCollectionCount,//實際最大字節數 6 _Out_opt_ LPDWORD lpCollectDataTimeout,//超時 7 _Out_opt_ LPTSTR lpUserName,//客戶機名稱 8 _In_ DWORD nMaxUserNameSize//客戶機數量 9 );
SetNamedPipeHandleState ();//設置管道的一些參數,傳輸模式
BOOL WINAPI SetNamedPipeHandleState( _In_ HANDLE hNamedPipe,//命名管道句柄 _In_opt_ LPDWORD lpMode,//傳輸模式,字節流或者信息流 _In_opt_ LPDWORD lpMaxCollectionCount,//最大字節數 _In_opt_ LPDWORD lpCollectDataTimeout//超時 );
lpMode的參數有兩個PIPE_READMODE_BYTE(字節流),PIPE_READMODE_MESSAGE(消息流)
GetNamedPipeInfo();//獲得緩沖區大小以及管道實例最大數量信息
1 BOOL WINAPI GetNamedPipeInfo( 2 _In_ HANDLE hNamedPipe, 3 _Out_opt_ LPDWORD lpFlags,//管道類型,服務器或者客戶端 4 _Out_opt_ LPDWORD lpOutBufferSize, 5 _Out_opt_ LPDWORD lpInBufferSize, 6 _Out_opt_ LPDWORD lpMaxInstances//管道最大實例數量 7 );
lpFlags的參數選擇為:
PIPE_CLIENT_END//客戶機
PIPE_SERVER_END//服務器
PIPE_TYPE_BYTE//字節流
PIPE_TYPE_MESSAGE//消息流
PeekNamedPipe();//可用它對命令管道內的數據進行瀏覽,同時毋需將其從管道的內部緩沖區挪出。
1 BOOL WINAPI PeekNamedPipe( 2 _In_ HANDLE hNamedPipe, 3 _Out_opt_ LPVOID lpBuffer,//讀取數據緩沖區 4 _In_ DWORD nBufferSize,//緩沖區大小 5 _Out_opt_ LPDWORD lpBytesRead,//讀取數據大小 6 _Out_opt_ LPDWORD lpTotalBytesAvail,//接收可從管道發出的字節總數 7 _Out_opt_ LPDWORD lpBytesLeftThisMessage//於接收消息內尚存的字節數量(前提是管道用消息模式打開 8 );
