学习笔记:第17课 进程间的通信

来源:互联网 发布:生物医学专业数据库 编辑:程序博客网 时间:2024/06/04 00:36
 
      进程间通信的四种方式
剪贴板:所有的进程都可以访问。
匿名管道
命名管道
邮槽
◆BOOL OpenClipboard()如果打开剪贴版,则打开是非0。
打开剪贴版后,别的应用程序就不能再调用剪贴版了。直到调用了 CloseClipboard();
      EmptyClipboard();清空剪贴版,并释放剪贴版的句柄
      SetClipboardData在剪贴版上放置程序
      GlobalAlloc从堆上分配指定数目的字节。WIN32内存管理没有提供一个本地堆和全局堆。他和动态数据交换和剪贴版一起使用。
      void CClipboardDlg::OnBtnSend()
      {
      // TODO: Add your control notification handler code here
      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();//要打开也要关闭
      }
      }
      接收端代码
      void CClipboardDlg::OnBtnRecv()
      {
      // TODO: Add your control notification handler code here
      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();
           }
      }
      }
 
      创建匿名管道:CreatePipe他只能够在父子进程之间进行通信
      BOOL CreatePipe(
  PHANDLE hReadPipe,//读的句并
  PHANDLE hWritePipe,//写的句并
  LPSECURITY_ATTRIBUTES lpPipeAttributes,//安全描述符
  DWORD nSize
);
      启动进程:
BOOL CreateProcess(
  LPCTSTR lpApplicationName,
  LPTSTR lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);
void CParentView::OnPipeCreate()
{
    // TODO: 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));// 将机构体中所有成员置为零可以使用函数
    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); //关闭管道
        hRead=NULL;
        hWrite=NULL;
        MessageBox("创建子进程失败!");
        return;
    }
    else
    {
        CloseHandle(pi.hProcess); //关闭所返回的子进程句柄
        CloseHandle(pi.hThread); //关闭子进程中主线程句柄
    }
}
◆读管道
void CParentView::OnPipeRead()
{
    // TODO: 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()
{
    // TODO: 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;
    }
}
 
      子进程的编写
      为得到子进程的标准输入和标准输出句柄,需要在View类窗口完全创建成功之后去获取,增加一个虚函数OnInitialUpdate();此函数是当我们的窗口创建成功之后第一个调用的函数
代码如下:
void CChildView::OnInitialUpdate()
{
    CView::OnInitialUpdate();
    //得到标准输入和输出句并
    // TODO: Add your specialized code here and/or call the base class
    hRead=GetStdHandle(STD_INPUT_HANDLE);
    hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
}
 
      命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信。
      命名管道充分利用了Windows NT和Windows2000内建的安全机制。
      名管道是围绕Windows文件系统设计的一种机制,采用“命名管道文件系统(Named Pipe File System,NPFS)”接口,因此,客户机和服务器可利用标准的Win32文件系统内核(例如ReadFile和WriteFile)来进行数据的收发。
      命名管道服务器和客户机的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。而客户机只能同一个现成的命名管道服务器建立连接。
      名管道服务器只能在Windows NT或Windows2000上创建,所以,我们无法在两台Windows95或Windows98计算机之间利用管道进行通信。不过,客户机可以是Windows95或Windows98计算机,与Windows NT或Windows2000计算机进行连接通信。
      命名管道提供了两种基本通信模式:字节模式和消息模式。在字节模式中,数据以一个连续的字节流的形式,在客户机和服务器之间流动。而在消息模式中,客户机和服务器则通过一系列不连续的数据单位,进行数据的收发,每次在管道上发出一条消息后,它必须作为一条完整的消息读入。
 
◆服务器进程使用
HANDLE CreateNamedPipe(
  LPCTSTR lpName,               //唯一标拾管道 //./pipe/pipename
  DWORD dwOpenMode,         //访问的方式
  DWORD dwPipeMode,         //指定是字节模式还是消息模式的管道
  DWORD nMaxInstances,      //最大可以创建几个命名管道的实例,对于同一个名字的管道来说
  DWORD nOutBufferSize,
  DWORD nInBufferSize,
  DWORD nDefaultTimeOut,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
对于字节模式的命名管道,他不能和消息读的命名管道一起使用
 
      void CNamePipeView::OnPipeCreate()
{
 // TODO: 在此添加命令处理程序代码
 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 lp;
 ZeroMemory(&lp,sizeof(OVERLAPPED));
 lp.hEvent=hEvent;       //人工从置的事件对象
}
 if(!ConnectNamedPipe(hPipe,&lp))
    {
   if(ERROR_IO_PENDING != GetLastError())  
    //
如果相等表示可能在随后的时间内这个操作会完成
     {
        MessageBox("
等待客户段连接失败!");
        CloseHandle(hPipe);
        CloseHandle(hEvent);
       hPipe=NULL;
       return;
     }
if(WAIT_FAILED= =WaitForSingleObject(hEvent,INFINITE))
    {// INFINITE表示永远等待
         MessageBox("等待对象失败!");
        CloseHandle(hPipe);
        CloseHandle(hEvent);
        hPipe=NULL;
        return;
    }
CloseHandle(hEvent);
}
◆服务器等待连接请求到来,使用函数:
BOOL ConnectNamedPipe(
      HANDLE hNamedPipe,
      LPOVERLAPPED lpOverlapped);
如果ConnectNamedPipe返回一个零值而ERROR_IO_PENDING= =GetLastError()表示这个操作没有失败,可能在随后的时间内这个操作会完成
调用CreateEvent创建一个人工或自动重置的事件对象
HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,
  BOOL bManualReset,
  BOOL bInitialState,
  LPCTSTR lpName
);
      WaitForSingleObject等待事件对象变为有信号状态
      客户机端:WaitNamedPipe等待,直到一个超时值的发生
      客户端程序要相对简单,只需要等待网络上命名管道实例,如果连上就打开命名空间
代码如下:
void CNamedPipeClientView::OnPipeConnect()
{
 // TODO: 在此添加命令处理程序代码
 if(!WaitNamedPipe("////.//pipe//MyTest",NMPWAIT_WAIT_FOREVER))
 {
  MessageBox("当前没有可利用的命名管道实例!");
  return ;
 }
 hPipe=CreateFile("////.//pipe//MyTest",FILE_SHARE_READ | FILE_SHARE_WRITE,
  0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
//点那部分表示服务器名字,用“.”表示本地主机,如果要连接其他主机就要把点//换成主机名。注意双引号中的反斜杠,四个表示两个
 if(INVALID_HANDLE_VALUE==hPipe)
 {
  MessageBox("打开命名空间失败!");
  hPipe=NULL;
  return ;
 }
}
void CNamedPipeClientView::OnPipeRead()
{
 // TODO: 在此添加命令处理程序代码
 char buf[100];
 DWORD dwRead;
 if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
 {
  MessageBox ("读取数据失败!");
  return ;
 }
 else MessageBox (buf);
}
void CNamedPipeClientView::OnPipeWrite()
{
 // TODO: 在此添加命令处理程序代码
 char buf[]="命名管道测试程序!!";
 DWORD dwWrite;
 if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox ("写入数据失败!");
  return ;
 }
}
 
◆邮槽
邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。
邮槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据,为保证邮槽在各种Windows平台下都能正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。
要创建邮槽,可以使用函数
HANDLE CreateMailslot(
  LPCTSTR lpName,           ////./mailslot/MyMail",
  DWORD nMaxMessageSize,
  DWORD lReadTimeout,       //
读超时的间隔
  LPSECURITY_ATTRIBUTES lpSecurityAttributes
); 
服务器端代码:
void CMailsoltSrvView::OnMailRecv()
{
 // TODO: 在此添加命令处理程序代码
// HANDLE hMail;
 hMail=CreateMailslot("////.//mailslot//MyMail",0,MAILSLOT_WAIT_FOREVER,NULL);
 if(INVALID_HANDLE_VALUE==hMail)
 {
  MessageBox("创建邮槽失败!");
  return;
 }
 char buf[100]="0";
 DWORD dwRead;
 if(!ReadFile(hMail,buf,100,&dwRead,NULL))
 {
  MessageBox ("读取数据失败!");
  CloseHandle(hMail); 
  return ;
 }
 MessageBox (buf);
 CloseHandle(hMail);
}
      在客户端口,CreateFile也可以打开邮槽
      客户端代码:
void CMailsoltCltView::OnMailSend()
{
 // TODO: 在此添加命令处理程序代码
 HANDLE hMail;
 hMail=CreateFile("////.//mailslot//MyMail",GENERIC_READ,FILE_SHARE_READ,
  NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 if(INVALID_HANDLE_VALUE==hMail)
 {
  MessageBox("打开邮槽失败!");
  return; 
 }
 
 char buf[]="邮槽测试程序!";
 DWORD dwWrite;
 if(!WriteFile(hMail,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox ("写入数据失败!");
  CloseHandle(hMail);
  return ;
 }
 CloseHandle(hMail);
}
 
原创粉丝点击