進程間通信的四種方式?
一、剪貼板
BOOL OpenClipboard( );---》打開剪切板,,假如其他程序要打開剪切板,必須調用CloseClipboard
BOOL CloseClipboard(VOID);
BOOL EmptyClipboard(VOID);---》清空剪切板並且釋放裏面的數據,交給當前打開剪切板的所有者
HANDLE SetClipboardData(
UINT uFormat, // clipboard format
HANDLE hMem // data handle---》如果設置爲NULL,則爲延遲方式,暫時不提交,避免提交的數據量過大,浪費空間,直到進程需要,才提交數據
);
剪切板加載數據:
if(OpenClipboard())
{
CString str;
HANDLE hClip;
char *pBuf;
EmptyClipboard();
GetDlgItemText(IDC_EDIT_SEND,str);
hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
pBuf=(char*)GlobalLock(hClip);
strcpy(pBuf,str);
GlobalUnlock(hClip);
SetClipboardData(CF_TEXT,hClip);
CloseClipboard();
}
HANDLE GetClipboardData(
UINT uFormat // clipboard format
);
BOOL IsClipboardFormatAvailable(-----》檢測剪切板的數據格式
UINT format // clipboard format
);
剪切板接收數據:
if(OpenClipboard())
{
if(IsClipboardFormatAvailable(CF_TEXT))
{
HANDLE hClip;
char *pBuf;
hClip=GetClipboardData(CF_TEXT);
pBuf=(char*)GlobalLock(hClip);
GlobalUnlock(hClip);
SetDlgItemText(IDC_EDIT_RECV,pBuf);
}
CloseClipboard();
}
二、匿名管道---->只能在本地實現
BOOL CreatePipe(
PHANDLE hReadPipe, // read handle
PHANDLE hWritePipe, // write handle
LPSECURITY_ATTRIBUTES lpPipeAttributes, // security attributes
DWORD nSize // pipe size
);
BOOL CreateProcess(
LPCTSTR lpApplicationName, // name of executable module
LPTSTR lpCommandLine, // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);
HANDLE GetStdHandle(-----》從標準輸入或者標準輸出或標準錯誤設備上獲取句柄
DWORD nStdHandle // input, output, or error device
);
創建管道:
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
MessageBox("創建匿名管道失敗!");
return;
}
STARTUPINFO sui;
PROCESS_INFORMATION pi;
ZeroMemory(&sui,sizeof(STARTUPINFO));
sui.cb=sizeof(STARTUPINFO);
sui.dwFlags=STARTF_USESTDHANDLES;
sui.hStdInput=hRead;
sui.hStdOutput=hWrite;
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE);
if(!CreateProcess("..//Child//Debug//Child.exe",NULL,NULL,NULL,
TRUE,0,NULL,NULL,&sui,&pi))----》Child.exe爲需要啓動的子進程
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead=NULL;
hWrite=NULL;
MessageBox("創建子進程失敗!");
return;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
CloseHandle有什麼用處?
1,線程和線程句柄(Handle)不是一個東西,線程是在cpu上運行的.....(說不清楚了),線程句柄是一個內核對象。我們可以通過句柄來操作線程,但是線程的生命週期和線程句柄的生命週期不一樣的。線程的生命週期就是線程函數從開始執行到return,線程句柄的生命週期是從CreateThread返回到你CloseHandle()。
2,所有的內核對象(包括線程Handle)都是系統資源,用了要還的,也就是說用完後一定要closehandle關閉之,如果不這麼做,你係統的句柄資源很快就用光了。
3,如果你CreateThread以後需要對這個線程做一些操作,比如改變優先級,被其他線程等待,強制TermateThread等,就要保存這個句柄,使用完了在CloseHandle。如果你開了一個線程,而不需要對它進行如何幹預,CreateThread後直接CloseHandle就行了。
所以
CloseHandel(ThreadHandle );
只是關閉了一個線程句柄對象,表示我不再使用該句柄,即不對這個句柄對應的線程做任何干預了。並沒有結束線程。
BOOL ReadFile(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer
);
讀取信息:
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("讀取數據失敗!");
return;
}
MessageBox(buf);
寫入數據:
char buf[]="主進程管道測試";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("寫入數據失敗!");
return;
}
在類的析構函數中:
if(hRead)
CloseHandle(hRead);
if(hWrite)
CloseHandle(hWrite);
再另外創建一個項目,聲明爲Child
1.void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();
hRead=GetStdHandle(STD_INPUT_HANDLE);
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
}
2.讀取和寫入和以上一樣的
必須由主進程啓動子進程纔有父子的關係,否則通過兩個項目分別打開一個窗口,兩者並沒有關係
三、命名管道
通過網絡來完成進程間的通信,它屏蔽了底層的網絡協議細節。
我們在不瞭解網絡協議的情況下,也可以利用命名管道來實現進程間的通信。
命名管道充分利用了Windows NT和Windows 2000內建的安全機制。
將命名管道作爲一種網絡編程方案時,
它實際上建立了一個客戶機/服務器通信體系,並在其中可靠地傳輸數據。
圍繞Windows文件系統設計的一種機制,採用“命名管道文件系統(Named Pipe File System,NPFS)”接口,
因此,客戶機和服務器可利用標準的Win32文件系統函數(例如:ReadFile和WriteFile)來進行數據的收發。
命名管道服務器和客戶機的區別?
服務器是唯一一個有權創建命名管道的進程,也只有它才能接受管道客戶機的連接請求。
客戶機只能同一個現成的命名管道服務器建立連接。
命名管道服務器只能在Windows NT或Windows 2000上創建,
所以,我們無法在兩臺Windows 95或Windows 98計算機之間利用管道進行通信。
不過,客戶機可以是Windows 95或Windows 98計算機,與Windows NT或Windows 2000計算機進行連接通信。
命名管道提供了兩種基本通信模式:
字節模式---》數據以一個連續的字節流的形式,在客戶機和服務器之間流動。
消息模式----》客戶機和服務器則通過一系列不連續的數據單位,進行數據的收發,每次在管道上發出了一條消息後,它必須作爲一條完整的消息讀入。
HANDLE CreateNamedPipe(
LPCTSTR lpName, // pipe name
DWORD dwOpenMode, // pipe open mode
DWORD dwPipeMode, // pipe-specific modes
DWORD nMaxInstances, // maximum number of instances
DWORD nOutBufferSize, // output buffer size
DWORD nInBufferSize, // input buffer size
DWORD nDefaultTimeOut, // time-out interval
LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD
);
BOOL ConnectNamedPipe(------》讓服務器端的管道等待客戶端的管道數據到來,而不是連接管道
HANDLE hNamedPipe, // handle to named pipe
LPOVERLAPPED lpOverlapped // overlapped structure
);
創建管道:
void CNamedPipeSrvView::OnPipeCreate()
{
// TODO: Add your command handler code here
hPipe=CreateNamedPipe("////.//pipe//MyPipe",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
0,1,1024,1024,0,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("創建命名管道失敗!");
hPipe=NULL;
return;
}
HANDLE hEvent;
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
MessageBox("創建事件對象失敗!");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap,sizeof(OVERLAPPED));
ovlap.hEvent=hEvent;
if(!ConnectNamedPipe(hPipe,&ovlap))
{
if(ERROR_IO_PENDING!=GetLastError())
{
MessageBox("等待客戶端連接失敗!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
}
if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
{
MessageBox("等待對象失敗!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
CloseHandle(hEvent);
}
讀取和寫入和匿名管道都是一樣的
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, // pipe name
DWORD nTimeOut // time-out interval
);
HANDLE CreateFile(
LPCTSTR lpFileName, // file name
DWORD dwDesiredAccess, // access mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to template file
);
客戶端連接版塊:
void CNamedPipeCltView::OnPipeConnect()
{
// TODO: Add your command handler code here
if(!WaitNamedPipe("////.//pipe//MyPipe",NMPWAIT_WAIT_FOREVER))
{
MessageBox("當前沒有可利用的命名管道實例!");
return;
}
hPipe=CreateFile("////.//pipe//MyPipe",GENERIC_READ | GENERIC_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("打開命名管道失敗!");
hPipe=NULL;
return;
}
}
讀取和寫入和匿名管道都是一樣的
四、郵槽
基於廣播通信體系設計出來的,它採用無連接的不可靠的數據傳輸。
郵槽是一種單向通信機制,創建郵槽的服務器進程讀取數據,打開郵槽的客戶機進程寫入數據。
爲保證郵槽在各種Windows平臺下都能夠正常工作,我們傳輸消息的時候,應將消息的長度限制在424字節以下。
HANDLE CreateMailslot(
LPCTSTR lpName, // mailslot name
DWORD nMaxMessageSize, // maximum message size
DWORD lReadTimeout, // read time-out interval
LPSECURITY_ATTRIBUTES lpSecurityAttributes // inheritance option
);
讀取數據:
void CMailslotSrvView::OnMailslotRecv()
{
// TODO: Add your command handler code here
HANDLE hMailslot;
hMailslot=CreateMailslot("////.//mailslot//MyMailslot",0,
MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("創建郵槽失敗!");
return;
}
char buf[100];
DWORD dwRead;
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
MessageBox("讀取數據失敗!");
CloseHandle(hMailslot);
return;
}
MessageBox(buf);
CloseHandle(hMailslot);
}
寫入數據:
void CMailslotCltView::OnMailslotSend()
{
// TODO: Add your command handler code here
HANDLE hMailslot;
hMailslot=CreateFile("////.//mailslot//MyMailslot",GENERIC_WRITE,
FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("打開郵槽失敗!");
return;
}
char buf[]="郵槽測試數據";
DWORD dwWrite;
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("寫入數據失敗!");
CloseHandle(hMailslot);
return;
}
CloseHandle(hMailslot);
}