進(jìn)程間通信的四種方式:
Ø 剪貼板
Ø 匿名管道
Ø 命名管道
Ø 郵槽
1. 剪貼板:
剪貼板其實(shí)是系統管理的一個(gè)內存區域,當一個(gè)程序發(fā)生拷貝的時(shí)候,將是該內存區域得到填充,使用粘貼的時(shí)候是重該區域取出數據,然后顯示的對應窗口上。
將指定內容賦值到剪貼板上:
a. 打開(kāi)剪貼板:OpenClipboard,注意:一旦打開(kāi)了剪貼版,其它運用程序將無(wú)法修改剪貼板,直到調用了CloseClipboard。
b. 清空剪貼板:EmptyClipboard,清空剪切板,并將所有權交付給打開(kāi)剪貼板的運用程序
c. 為即將拷貝的內容分配內存空間:GlobalAlloc,第一個(gè)參數指示分配內存的類(lèi)型,重要的有兩類(lèi),GMEM_FIXED:Allocates fixed memory. The return value is a pointer;GMEM_MOVEABLE:Allocates movable memory. In Win32, memory blocks are never moved in physical memory, but they can be moved within the default heap. The return value is a handle to the memory object. To translate the handle into a pointer, use the GlobalLock function. This flag cannot be combined with the GMEM_FIXED flag.
本例中采用GMEM_MOVEABLE,其返回值是一個(gè)指向內存對象的句柄。
d. 將句柄轉換為指針:GlobalLock,將指定內存塊鎖定。
The internal data structures for each memory object include a lock count that is initially zero. For movable memory objects, GlobalLock increments the count by one, and the GlobalUnlock function decrements the count by one. For each call that a process makes to GlobalLock for an object, it must eventually call GlobalUnlock. Locked memory will not be moved or discarded, unless the memory object is reallocated by using the GlobalReAlloc function. The memory block of a locked memory object remains locked until its lock count is decremented to zero, at which time it can be moved or discarded.
e. 將字符串的內容拷貝到可移動(dòng)堆中:strcpy
f. 釋放內存塊鎖定:GlobalUnlock
g. 放置數據:SetClipboardData, The SetClipboardData function places data on the clipboard in a specified clipboard format. The window must be the current clipboard owner, and the application must have called the OpenClipboard function.
(本例程序沒(méi)有采用)SetClipboardData的第一個(gè)參數可以是指定的格式或NULL,如果是NULL,則采用的是延遲提交的技術(shù),所謂延遲提交表示的是為了避免下面這種情況:當一個(gè)拷貝數據到剪貼板的動(dòng)作發(fā)生時(shí),直到下一個(gè)從剪貼板上取出數據的過(guò)程中,數據一直占用著(zhù)內存空間,造成了資源浪費。為了改善這種情況,延遲提交技術(shù)采用SetClipboardData調用一個(gè)空的內存區塊,當下一個(gè)從剪貼板取出數據的動(dòng)作發(fā)生時(shí),自動(dòng)發(fā)送一個(gè)WM_RENERFORMAT消息,剪貼板的所有者程序再次調用具有實(shí)際內存區塊參數的SetClipboardData方法,發(fā)生實(shí)際剪貼動(dòng)作。第二次調用前不用再調用OpenClipboard方法。
h. 關(guān)閉剪貼板:CloseClipboard
實(shí)現代碼如下:
if(OpenClipboard()) //打開(kāi)剪貼板
{
CString str;
HANDLE hClip; //剪貼板句柄
char* pBuf;
EmptyClipboard();
GetDlgItemText(IDC_EDIT_SEND,str);
//分配內存的長(cháng)度一般是字符串的長(cháng)度加1用來(lái)存放空字符,否則系統將自動(dòng)覆蓋掉現有字符串的最后一位用來(lái)存放空字符,空字符作為結尾標識
hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
pBuf=(char*)GlobalLock(hClip); //將句柄轉換為指針,如果GlobalAlloc參數是GMEM_FIXED,則這樣不需要這樣的轉換。該語(yǔ)句將增長(cháng)Lock數
strcpy(pBuf,str); //為分配好的內存空間填充想賦的值
GlobalUnlock(hClip); //如果GlobalAlloc參數是GMEM_FIXED,則不起作用。該語(yǔ)句將減少Lock數,如果Lock數為0,則指定動(dòng)態(tài)內存區域將可被移動(dòng)和拋棄
SetClipboardData(CF_TEXT,hClip); //以指定格式存放數據,不完成指定格式轉換,不能完成粘貼
CloseClipboard();
}
從剪貼板上提取數據:
具體代碼如下:
if(OpenClipboard())
{
if(IsClipboardFormatAvailable(CF_TEXT)) //指定格式數據在接貼板上是否存在
{
HANDLE hClip=GetClipboardData(CF_TEXT); //從剪貼板上得到數據,且拿到了數據塊的句柄
char* pBuf;
pBuf=(char*)GlobalLock(hClip);
GlobalUnlock(hClip);
SetDlgItemText(IDC_EDIT_RECV,pBuf);
}
CloseClipboard();
}
2. what is the pipes
A pipe is a section of shared memory that processes use for communication. The process that creates a pipe is the pipe server. A process that connects to a pipe is a pipe client. One process writes information to the pipe, then the other process reads the information from the pipe. This overview describes how to create, manage, and use pipes.
3. 匿名管道
創(chuàng )建父進(jìn)程:
a. CreatePipe:其中第三個(gè)參數代表安全屬性結構體SECURITY_ATTRIBUTES的指針,在前幾章的運用中,都是運用了NULL,代表返回的安全句柄不可以被子進(jìn)程所繼承。但在本運用中,涉及到的是匿名管道。匿名管道就是父子進(jìn)程之間的通信,所以結構體必須設置相應的值。子進(jìn)程要想獲得匿名管道的讀寫(xiě)句柄,只能從父進(jìn)程繼承而來(lái)。一旦子進(jìn)程有了繼承而來(lái)的讀寫(xiě)句柄,就可以和父進(jìn)程進(jìn)行通信了。對于機構體SECURITY_ATTRIBUTES,最重要的是第三個(gè)參數bInheritHandle,表示Specifies whether the returned handle is inherited when a new process is created. If this member is TRUE, the new process inherits the handle.
b. CreateProcess:如果創(chuàng )建管道成功,則創(chuàng )建子進(jìn)程,并將管道的讀寫(xiě)句柄傳遞給子進(jìn)程。
創(chuàng )建匿名管道具體代碼:
SECURITY_ATTRIBUTES sa;
//總共就三個(gè)參數
sa.bInheritHandle=TRUE; //表示可被子進(jìn)程所繼承
sa.lpSecurityDescriptor=NULL; //安全描述符號一般都設置成NULL,即默認描述符
sa.nLength=sizeof(SECURITY_ATTRIBUTES); //管道長(cháng)度
if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
MessageBox("創(chuàng )建匿名函數失??!");
return;
}
//管道創(chuàng )建成功后,接著(zhù)創(chuàng )建子進(jìn)程,并將讀寫(xiě)句柄傳遞給子進(jìn)程
STARTUPINFO sui;
PROCESS_INFORMATION pi;
//調用ZeroMemory方法將該結構體中的所有成員都置為0,這是因為這個(gè)結構體的成員很多,如果開(kāi)始的時(shí)候沒(méi)有置為0的話(huà),那它的值是隨機的,將這樣的結構體傳給CreateProcess,可能會(huì )影響到執行的結果。
ZeroMemory(&sui,sizeof(STARTUPINFO));
sui.cb=sizeof(STARTUPINFO); //設置結構體的大小
sui.dwFlags=STARTF_USESTDHANDLES; //該標識表示標準輸入句柄,標準輸出句柄和錯誤句柄是有用的
sui.hStdInput=hRead; //將子進(jìn)程的輸入句柄設置成父進(jìn)程的讀句柄
sui.hStdOutput=hWrite; //將子進(jìn)程的輸出句柄設置成父進(jìn)程的寫(xiě)句柄
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE); //得到標準錯誤句柄,是父進(jìn)程的錯誤句柄,該行代碼在本程序中沒(méi)有實(shí)際的用途意義
//因為是匿名管道,是沒(méi)有名稱(chēng)的管道,只有通過(guò)CreateProcess由上而下的傳遞管道操作句柄。
if(!CreateProcess("..\\Child\\Debug\\Child.exe",NULL,NULL,NULL,
TRUE,0,NULL,NULL,&sui,&pi))
{
MessageBox("創(chuàng )建子進(jìn)程失??!");
CloseHandle(hRead);
CloseHandle(hWrite);
//避免在析構函數中再次關(guān)閉,析構函數采用:
//if(hRead) CloseHandle(hRead)
hRead=NULL;
hWrite=NULL;
return;
}
else
{
//創(chuàng )建一個(gè)新的進(jìn)程的時(shí)候,系統會(huì )創(chuàng )建一個(gè)進(jìn)程內核對象和一個(gè)線(xiàn)程內核對象,內核對象都有一個(gè)使用基數,初始調用的時(shí)候,都設置為1。在CreateProcess返回之前,該函數打開(kāi)進(jìn)程和線(xiàn)程的內核對象,,并將進(jìn)程相關(guān)的句柄放置到結構體PROCESS_INFORMATION的hProcess和hThread中,當Process在內部打開(kāi)這些對象的時(shí)候,使得每個(gè)對象的使用基數增加到2了。如果在父進(jìn)程中不需要使用這兩個(gè)句柄,就將這個(gè)句柄進(jìn)行關(guān)閉,使得使用基數減1。當子進(jìn)程終結的時(shí)候,系統會(huì )在將使用基數減1,使得子進(jìn)程的進(jìn)程內核對象和線(xiàn)程內核對象的使用基數變?yōu)?/span>0,這樣內核對象就可以被釋放了。
CloseHandle(pi.hProcess); //關(guān)閉子進(jìn)程的句柄
CloseHandle(pi.hThread); //關(guān)閉子進(jìn)程中主線(xiàn)程的句柄
}
父進(jìn)程讀匿名管道:
char *buf="hello world";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("匿名管道寫(xiě)入數據失??!");
return;
}
父進(jìn)程寫(xiě)匿名管道:
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("匿名管道讀取數據失??!");
return;
}
MessageBox(buf);
創(chuàng )建子進(jìn)程程序:
可以將獲取父進(jìn)程的匿名管道的讀寫(xiě)句柄操作放在CView類(lèi)的OnInitialUpdate方法中實(shí)現,該方法是在CView完全構造后調用的第一個(gè)方法。代碼如下:
hRead=GetStdHandle(STD_INPUT_HANDLE);
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
子進(jìn)程的讀寫(xiě)匿名管道的代碼和父進(jìn)程的一樣,這里不再累述。
4. 命名管道:
命名管道是通過(guò)網(wǎng)絡(luò )來(lái)完成進(jìn)程間的通信,它屏蔽了底層的網(wǎng)絡(luò )協(xié)議細節。我們在不了解網(wǎng)絡(luò )協(xié)議的情況下,也可以利用命名管道來(lái)實(shí)現進(jìn)程間的通信。而上述的匿名管道只能在本地機器上,且連個(gè)父子進(jìn)程間通信。命名管道也具有匿名管道的功能。
命名管道服務(wù)器和客戶(hù)機的區別在于:服務(wù)器是唯一一個(gè)有權創(chuàng )建命名管道的進(jìn)程,也只有它才能接受管道客戶(hù)機的連接請求。而客戶(hù)機只能同一個(gè)現成的命名管道服務(wù)器建立連接。
命名管道提供了兩種基本通信模式:字節模式和消息模式。在字節模式中,數據以一個(gè)連續的字節流的形式,在客戶(hù)機和服務(wù)器之間流動(dòng)。而在消息模式中,客戶(hù)機和服務(wù)器則通過(guò)一系列不連續的數據單位,進(jìn)行數據的收發(fā),每次在管道上發(fā)出了一條消息后,它必須作為一條完整的消息讀入。
命名管道服務(wù)器端代碼(核心為命名管道的創(chuàng )建與等待客戶(hù)端的連接):
說(shuō)明:CreateNamedPipe,創(chuàng )建命名管道,其中第一個(gè)參數管道的名稱(chēng)是格式為"\\.\pipe\pipename", 在VC中使用的時(shí)候,因涉及到轉義符,作為字符串,應使用"\\\\.\\pipe\\pipename",其中pipe不能更改,大小寫(xiě)沒(méi)有區分
hPipe=CreateNamedPipe("\\\\.\\pipe\\MyPipe",PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,0,1,1024,1024,0,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("創(chuàng )建命名管道失??!");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
HANDLE hEvent;
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
MessageBox("創(chuàng )建事件對象失??!");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
OVERLAPPED ovlap;
//這里調用ZeroMemory和上一章的意義是一樣的,為了避免ConnectNamedPipe在調用該結構中使用的是一些不可欲知的參數值,防止有影響
ZeroMemory(&ovlap,sizeof(OVERLAPPED));
ovlap.hEvent=hEvent;
說(shuō)明:等待客戶(hù)端連接到一個(gè)命名管道實(shí)例。If hNamedPipe was created with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the OVERLAPPED structure pointed to by lpOverlapped must contain a handle to a manual-reset event object (which the server can create by using the CreateEvent function).這是為什么上面需要申明一個(gè)自動(dòng)的事件的對象。
if(!ConnectNamedPipe(hPipe,&ovlap))
{
if(ERROR_IO_PENDING!=GetLastError())
{
MessageBox("等待客戶(hù)端的連接失??!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
}
}
//等待事件狀態(tài)有效,如果當前無(wú)效,INFINITE參數表明則一直等待下去
if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
{
MessageBox("等待對象失??!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
}
CloseHandle(hEvent); //說(shuō)明已經(jīng)有客戶(hù)端連接到了
命名管道的讀寫(xiě)和上一章是類(lèi)似的,這里就省略掉了
命名管道的客戶(hù)端實(shí)現(核心為命名管道的連接):
//WaitNamePipe的參數NMPWAIT_WAIT_FOREVER一直等待下去,直到等待到可用的連接,當然也可以設置超時(shí)的時(shí)間,但前提是所有的程序里所有的命名管道的超時(shí)時(shí)間必須一樣
if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",NMPWAIT_WAIT_FOREVER))
{
MessageBox("當前沒(méi)有可用的命名管道實(shí)例");
return;
}
//打開(kāi)命名管道,建立連接
hPipe=CreateFile("\\\\127.0.0.1\\pipe\\MyPipe",GENERIC_READ|GENERIC_WRITE, 0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("打開(kāi)命名管道失??!");
hPipe=NULL;
}
注意:程序的運行方式為先點(diǎn)擊服務(wù)端的創(chuàng )建命名管道,然后點(diǎn)擊客戶(hù)端的連接管道,再點(diǎn)擊服務(wù)端的發(fā)送數據,再在客戶(hù)端點(diǎn)擊接收數據。
5. 郵槽:
Ø 郵槽是基于廣播通信體系設計出來(lái)的,它采用無(wú)連接的不可靠的數據傳輸。
Ø 郵槽是一種單向通信機制,創(chuàng )建郵槽的服務(wù)器進(jìn)程讀取數據,打開(kāi)郵槽的客戶(hù)機進(jìn)程寫(xiě)入數據。
Ø 為保證郵槽在各種Windows平臺下都能夠正常工作,我們傳輸消息的時(shí)候,應將消息的長(cháng)度限制在424字節以下。
服務(wù)器端的代碼實(shí)現:
HANDLE hMailslot;
hMailslot=CreateMailslot("\\\\.\\mailslot\\MyMailSlot",0,MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("創(chuàng )建郵槽失??!");
return;
}
char buf[100];
DWORD dwRead;
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
MessageBox("讀取數據失??!");
CloseHandle(hMailslot);
hMailslot=NULL;
return;
}
MessageBox(buf);
CloseHandle(hMailslot);
客戶(hù)端的代碼實(shí)現:
HANDLE hMailslot;
//因為郵槽的客戶(hù)是負責寫(xiě)入數據,所以訪(fǎng)問(wèn)方式只需要是寫(xiě),但對于服務(wù)器端而言,是讀取數據,所以客戶(hù)端共享方式設置為讀FILE_SHARE_READ
hMailslot=CreateFile("\\\\.\\mailslot\\MyMailSlot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("打開(kāi)郵槽失??!");
return;
}
char buf[]="this is test!";
DWORD dwWrite;
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("寫(xiě)入數據失敗");
CloseHandle(hMailslot);
return;
}
CloseHandle(hMailslot);
注意:程序的運行方式為先點(diǎn)擊服務(wù)器端的接收數據以此創(chuàng )建一個(gè)郵槽,然后在點(diǎn)擊客戶(hù)端的發(fā)送數據,最終服務(wù)器端會(huì )接收到數據。
聯(lián)系客服