第17课 进程间通信

有四种方法
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中增加两个成员 hReadhWrite. 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. 命名管道:还可以跨网络通信,服务器只能在  win2000NT 下运行!而客户端可以在 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. 以上 4  种方法各有优缺点:剪贴板比较简单。邮槽是基于广播的,可以一对多发送。但只能一个发送,一个接收,要想同时发送接收,须写两次代码。
命名管道和邮槽可以进行网络通信。

猜你喜欢

转载自blog.csdn.net/zhang_zxk/article/details/52401951