四种线程间的通信(笔记) - yuyunliuhen的专栏 - 优快云Blog

本文介绍了四种进程间通信(IPC)的方法:剪贴板、匿名管道、命名管道和邮槽。详细展示了每种方法的实现步骤及核心代码片段,帮助读者理解不同IPC方式的特点和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读:
  有四种方法
  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.以上4种方法各有优缺点:剪贴板比较简单。邮槽是基于广播的,可以一对多发送。但只能一个发送,一个接收,要想同时发送接收,须写两次代码。
  命名管道和邮槽可以进行网络通信。


 
  Trackback: ttp://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=1561623

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值