进程间通信

来源:互联网 发布:nba历届总决赛mvp数据 编辑:程序博客网 时间:2024/05/16 04:39

进程间通信的四种方式?
一、剪贴板
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); 
}

原创粉丝点击