在很多软件中,需要多个进程协同工作,而不是单个进程。这么多进程的协同工作涉及进程间通信。在Windows下,实现进程间通信的方式有很多种,比如管道、邮件槽、剪贴板、内存共享……本文介绍通过消息实现进程间通信。进程之间通过消息进行的通信有一定的局限性。有windows的windows应用程序是基于消息驱动工作的,那么没有windows的程序就不是基于消息驱动工作的。对于非窗口应用程序,进程之间不可能通过消息进行通信。进程间通过消息进行通信这里介绍两种方法,一种是通过自定义消息进行进程间通信,另一种是使用WM_COPYDATA消息进行进程间通信。01通过自定义消息进行进程通信消息有两种,一种是系统定义的消息,一种是用户自定义的消息。系统已经定义的消息从0到0x3ff,用户定义的消息可以从0x400开始。系统中提供了一个宏WM_USER。自定义消息时,在WM_USER的基础上加一个值即可。下面是一个实现自定义消息完成进程间通信的例子。一、实现自定义消息的步骤进程间通信是通过自定义消息进行的,只有有windows的进程才能完成基于消息的进程间通信。既然是进程间通信,至少需要写两个程序,一个是接收消息的服务端,一个是发送消息的客户端,而且两个程序都需要有windows。首先介绍一下程序的功能。在发送消息的客户端,它通过自定义消息向接收消息的服务器端发送两个整数值。接收消息的服务器对接收到的两个值进行简单的加法运算。接收消息的服务器在VC下,使用MFC通过自定义消息完成进程间通信需要三步。首先要定义一条消息,然后添加自定义消息的消息映射,最后添加消息映射对应的消息。处理功能。首先在服务端和客户端定义一个消息,如下:#defineWM_UMSGWM_USER+1然后在接收消息的服务端添加一个消息映射,如下:BEGIN_MESSAGE_MAP(CUserWMDlg,CDialog)//{{AFX_MSG_MAP(CUserWMDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_MESSAGE(WM_UMSG,RevcMsg)//}}AFX_MSG_MAPEND_MESSAGE_MAP()在这个消息映射中,ON_MESSAGE(WM_UMSG,RevcMsg)是自定义消息的消息映射。最后在接收消息的服务器上添加自定义消息的消息响应功能。根据消息映射可知,消息响应函数的函数名为RevcMsg(),定义如下:VOIDCUserWMDlg::RevcMsg(WPARAMwParam,LPARAMlParam){//...}2.完成自定义消息通信代码看两个程序的窗口界面,如图1和图2所示。图1自定义消息服务器(接收端)图2自定义消息客户端(发送端)知道了两个程序的功能和界面后窗口,开始分别对它们进行编码。我们先来看自定义消息服务器的代码,比较简单。消息响应函数代码如下:("%d",nSum);SetDlgItemText(IDC_EDIT_REVCDATA,str);}消息响应函数中有两个参数,分别是WPARAM类型和LPARAM类型。这两个参数可以接收两个4字节的参数。这里的代码接收两个整数值,将它们相加并显示在窗口的编辑框中。在发送端,也需要定义相同的消息类型。这里不再重复介绍,直接复制粘贴response的定义即可。主要看发送消息的函数,代码如下:voidCUserWMCDlg::OnBtnSend(){//这里添加handler代码,FALSE);HWNDhWnd=::FindWindow(NULL,"CustomMessageServer");::SendMessage(hWnd,WM_UMSG,(WPARAM)nNum1,(LPARAM)nNum2);}通过SendMessage()函数完成发送,而且也很简单。SendMessage()函数中,通过第三个参数和第四个参数向目标窗口发送两个整数值。从自定义消息的例子可以看出,自定义消息只能完成进程间通信的简单数值传递,无法完成复杂类型数据的通信。那么,是否可以通过消息来完成字符串等数据的通信呢?答案是肯定的。下面我们来看一个使用WM_COPYDATA消息完成进程间通信的例子。02通过WM_COPYDATA消息进行进程通信自定义消息传递的数据类型过于简单,通过WM_COPYDATA消息进行进程间通信会更加灵活。但是由于发送消息时SendMessage()函数的阻塞机制,使用WM_COPYDATA时不宜发送过多的消息。1.WM_COPYDATA消息介绍应用程序发送WM_COPYDATA消息向其他应用程序传输数据。WM_COPYDATA消息需要使用SendMessage()函数发送,而不是PostMessage()消息。通过SendMessage()函数发送WM_COPYDATA消息的形式如下:SendMessage((HWND)hWnd,WM_COPYDATA,(WPARAM)wParam,(LPARAM)lParam);第一个参数hWnd是接收消息的目标窗口句柄;第二个参数是消息的类型,即当前引入的消息WM_COPYDATA;第三个参数是发送消息的窗口句柄;第四个参数是指向COPYDATASTRUCT结构的指针。COPYDATASTRUCT结构的定义如下:其中dwData为自定义数据,cbData用于指定lpData指向的数据大小,lpData为数据指针。在程序中,WM_COPYDATA消息的发送者仍然会通过调用FindWindow()函数找到目标窗口的句柄,消息的接收者需要响应WM_COPYDATA消息的处理。WM_COPYDATA不是自定义消息,编程时不需要像自定义消息那样定义消息和添加消息映射,这部分工作可以直接由MFC辅助完成。MFC添加WM_COPYDATA消息响应的方法如下:首先在响应WM_COPYDATA消息的窗口对应的类上单击鼠标右键,在弹出的快捷菜单中选择“添加Windows消息处理程序”,如如图3所示。选择该菜单项后,会出现如图4所示的添加消息响应函数对话框。图3选择“AddWindowsMessageHandler”图4添加消息响应函数对话框在“NewWindowsmessages/events:”栏中找到WM_COPYDATA消息,双击添加到“Existingmessage/eventhandlers”:“柱子。最后点击“AddHandler”按钮,MFC自动生成WM_COPYDATA的消息映射和消息响应函数。Windows中其他常用的消息可以通过该对话框辅助生成消息映射和消息响应功能。2、WM_COPYDATA程序接口和介绍程序也分为客户端程序和服务器程序。首先看程序运行的效果,如图5所示。图5WM_COPYDATA服务端和客户端界面客户端进行消息反馈。WM_COPYDATA客户端会通过FindWindow()函数找到WM_COPYDATA服务器,并发送WM_COPYDATA消息,同时也接收并处理来自服务器的WM_COPYDATA消息。3.WM_COPYDATA客户端程序的实现我们来完成程序的编码工作,首先来看WM_COPYDATA客户端。客户端界面中有3个控件,分别是按钮控件、编辑框控件和列表框控件(为列表框控件定义了一个控件变量:CListBoxm_ListRec;)。WM_COPYDATA客户端的代码如下:voidCCopyDataCDlg::OnBtnSend(){//在此处添加处理程序代码//查找接收WM_COPYDATA消息的窗口句柄HWNDhWnd=::FindWindow(NULL,"COPYDATAserver");CStringstrText;GetDlgItemText(IDC_EDIT_SENDDATA,strText);//设置COPYDATASTRUCT结构COPYDATASTRUCTcds;cds.dwData=0;cds.cbData=strText.GetLength()+1;cds.lpData=strText.GetBuffer(cds.cbData);//m_hWnd是CWnd类中的一个成员函数//表示窗口的句柄::SendMessage(hWnd,WM_COPYDATA,(WPARAM)m_hWnd,(LPARAM)&cds);}BOOLCCopyDataCDlg::OnCopyData(CWnd*pWnd,COPYDATASTRUCT*pCopyDataStruct){//这里添加处理代码或者调用默认方法//处理服务器发送的WM_COPYDATA消息CStringstrText;strText.Format("服务器在[%s]收到消息",pCopyDataStruct->lpData);m_ListRec.AddString(strText);returnCDialog::OnCopyData(pWnd,pCopyDataStruct);}4.WM_COPYDATA服务器程序的实现WM_COPYDATA服务器有两个控件,分别是列表框控件和按钮控件。为列表框控件定义一个控件变量:CListBoxm_ListData。WM_COPYDATA服务端代码如下:BOOLCCopyDataSDlg::OnCopyData(CWnd*pWnd,COPYDATASTRUCT*pCopyDataStruct){//在此处添加处理程序代码或调用默认方法CStringstrText;//通过发送消息窗口句柄,即PIDDWORDdwPid=0;::GetWindowThreadProcessId(pWnd->m_hWnd,&dwPid);//格式化字符串,添加到列表框strText.Format("PID=[%d]发送的消息过程是:%s",dwPid,pCopyDataStruct->lpData);m_ListData.AddString(strText);//获取本地时间SYSTEMTIMEst;GetLocalTime(&st);CStringstrTime;strTime.Format("%02d:%02d:%02d",st.wHour,st.wMinute,st.wSecond);//发送本地时间给客户端程序COPYDATASTRUCTcds;cds.dwData=0;cds.cbData=strTime.GetLength()+1;cds.lpData=strTime.GetBuffer(cds.cbData);//注意SendMessage()函数的第三个参数为NULL::SendMessage(pWnd->m_hWnd,WM_COPYDATA,NULL,(LPARAM)&cds);returnCDialog::OnCopyData(pWnd,pCopyDataStruct);}voidCCopyDataSDlg::OnBtnDelall(){//在此处添加处理程序代码//清空列表框的内容while(m_ListData.GetCount()){m_ListData.DeleteString(0);}}通过发送message窗口获取发送消息的进程的PID号,并将接收消息的时间反馈给发送消息的客户端。WM_COPYDATA的server和client的代码里面有比较详细的注释,就不多解释了。这里需要强调一下,WM_COPYDATA消息需要额外的两条消息,即SendMessage()函数的wParam和lParam参数都需要用到。wParam参数表示发送消息的窗口句柄,但该参数可以省略,也可以通过类型转换传递其他数值数据。lParam参数是一个COPYDATASTRUCT结构指针类型,不能省略,否则接收到WM_COPYDATA消息的服务器将无法响应。
