第17章 进程间通信
转自: https://blog.csdn.net/u014162133/article/details/46573873
进程间通信有6种方法
1.剪贴板(只能在本机上的进程间通信)
a.创建个ClipBoard的对话框应用程序,加两EditBox和两个Button发送接收。
b.具体代码:
发送端代码:
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(); }
接收端代码:
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(); } }
2.匿名管道:只能在父子进程之间进行通信(只能在本机上的进程间通信)
a.先建一个Parent的单文档应用程序,增加“创建管道”“读取数据”“写入数据”三个菜单
b.增加成员变量HANDLE类型的hRead,hWrite,初始化变量,并在析构函数中释放句柄
c.响应菜单代码:
void CParentView::OnPipeCreate() 菜单“创建管道”代码 { // TOD Add your command handler code here 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));将数据清0! 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))创建子进程 { CloseHandle(hRead); CloseHandle(hWrite);关闭句柄,将内核对象的使用计数减少1,这样当操作系统发现内核对象的使用计数为0时,将清除内核对象。 hRead=NULL; hWrite=NULL; MessageBox("创建子进程失败!"); return; } else { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } }
菜单“读取数据”代码
void CParentView::OnPipeRead() { // TOD Add your command handler code here char buf[100]; DWORD dwRead; if(!ReadFile(hRead,buf,100,&dwRead,NULL)) { MessageBox("读取数据失败!"); return; } MessageBox(buf); } void CParentView::OnPipeWrite() 菜单“写入数据”代码 { // TOD Add your command handler code here char buf[]="http://www.sunxin.org"; DWORD dwWrite; if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL)) { MessageBox("写入数据失败!"); return; } }
d.再建一个Child的单文档,在View中增加两个成员hRead和hWrite.在OnInitialUpdate()中得到句柄的值。
void CChildView::OnInitialUpdate() { CView::OnInitialUpdate(); // TOD Add your specialized code here and/or call the base class hRead=GetStdHandle(STD_INPUT_HANDLE);注意这句代码! hWrite=GetStdHandle(STD_OUTPUT_HANDLE); }
e.加菜单“读取数据”“写入数据”其代码如下:
void CChildView::OnPipeRead() { // TOD Add your command handler code here char buf[100]; DWORD dwRead; if(!ReadFile(hRead,buf,100,&dwRead,NULL)) { MessageBox("读取数据失败!"); return; } MessageBox(buf); } void CChildView::OnPipeWrite() { // TOD Add your command handler code here char buf[]="匿名管道测试程序"; DWORD dwWrite; if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL)) { MessageBox("写入数据失败!"); return; } }
3.命名管道:还可以跨网络通信,服务器只能在win2000和NT下运行!而客户端可以在95下运行。关键函数CreateNamedPipe
a.先建一个NamedPipeSRV单文档应用程序,加菜单“创建管道”“读取数据”“写入数据”
b.在View中增加Handle变量hPipe,注意在析构函数中释放它!
c.响应菜单,创建命名管道
void CNamedPipeSrvView::OnPipeCreate() { // TOD 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); }
void CNamedPipeSrvView::OnPipeRead() { // TOD Add your command handler code here char buf[100]; DWORD dwRead; if(!ReadFile(hPipe,buf,100,&dwRead,NULL)) { MessageBox("读取数据失败!"); return; } MessageBox(buf); } void CNamedPipeSrvView::OnPipeWrite() { // TOD Add your command handler code here char buf[]="http://www.sunxin.org"; DWORD dwWrite; if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL)) { MessageBox("写入数据失败!"); return; } }
d.再建一个NamedPipeCLT单文档工程,加菜单“连接管道”“读取数据”“写入数据”,当然别忘记成员变量hPipe的定义和初始化
e.响应菜单代码
void CNamedPipeCltView::OnPipeConnect() 连接管道 { // TOD 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; } } void CNamedPipeCltView::OnPipeRead() 读取数据 { // TOD Add your command handler code here char buf[100]; DWORD dwRead; if(!ReadFile(hPipe,buf,100,&dwRead,NULL)) { MessageBox("读取数据失败!"); return; } MessageBox(buf); } void CNamedPipeCltView::OnPipeWrite() 写入数据 { // TOD Add your command handler code here char buf[]="命名管道测试程序"; DWORD dwWrite; if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL)) { MessageBox("写入数据失败!"); return; } }
4.邮槽,可以跨网络通信,使用时应将消息长度限制在424字节以下,关键函数CreateMailSlot()
a.先建一个MailSlotSRV工程,加菜单“接收数据”
b.消息响应代码:
菜单“接收数据”的代码
void CMailslotSrvView::OnMailslotRecv() { // TOD 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); }
c.加工程MailSlotCLT,加菜单“发送数据”
d.加消息响应,添加代码,客户端也比较简单。
void CMailslotCltView::OnMailslotSend() 菜单“发送数据”的代码 { // TOD 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[]="http://www.sunxin.org"; DWORD dwWrite; if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL)) { MessageBox("写入数据失败!"); CloseHandle(hMailslot); return; } CloseHandle(hMailslot); }
5. 共享内存(只能在本机上的进程间通信)
6、Socket通信,可以跨网络通信
7、以上6种方法各有优缺点:剪贴板比较简单。邮槽是基于广播的,可以一对多发送。但只能一个发送,一个接收,要想同时发送接收,须写两次代码。
匿名管道、剪贴板和共享内存只能在本机上的进程间通信
命名管道、邮槽和Socket通信可以进行跨网络通信。