引言
内部进程间通讯和数据交换有消息、共享内存、匿名(命名)管道、邮槽、Windows套接字等多种技术。其中利用消息机制实现IPC虽然同其他方法相比有交换的数据量小、携带的信息少等缺点,但由于其实现方便、应用灵活而广泛应用于无须大量、频繁数据交换的内部进程通讯系统之中,尤其是对于在上层主控软件与底层工作软件之间的命令与响应上更能充分显示其良好的性能。本文就通过编制一个主控软件和一个受其操作的底层工作软件来阐述如何用VC++6.0通过消息来实现内部进程通信。
Windows消息机制
Windows是一种面向对象的体系结构,Windows环境和应用程序都是通过消息来交互的。Windows应用程序开始执行后,Windows为该程序创建一个“消息队列(message queue)”,用以存放邮寄给该程序可能创建的各种不同窗口的消息。消息队列中消息的结构(MSG)为:
typedef struct tagMSG{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
其中第一个成员变量是用以标识接收消息的窗口的窗口句柄;第二个参数便是消息标识号,如WM_PAINT;第三个和第四个参数的具体意义同message值有关,均为消息参数。前四个参数是非常重要和经常用到的,至于后两个参数则分别表示邮寄消息的时间和光标位置(屏幕坐标)。
把消息传送到应用程序有两种方法:一种是由系统将消息“邮寄(post)”到应用程序的“消息队列”这是“进队消息”Win32 API有对应的函数:PostMessage(),此函数不等待该消息处理完就返回;而另一种则是由系统在直接调用窗口函数时将消息“发送(send)”给应用程序的窗口函数,属于“不进队消息”对应的函数是SendMessage()其必须等待该消息处理完后方可返回。消息是在消息循环中被处理的,下面这段代码就是一个典型的消息循环:
MSG msg; //定义消息名
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ; //翻译消息
DispatchMessage (&msg) ; //撤去消息
}
return msg.wParam ;
消息循环以GetMessage调用开始,它从消息队列中取出一个消息。该函数的四个参数可以有控制地获取消息,第一个参数指定要接收消息的MSG结构的地址,第二个参数表示窗口句柄,一般将其设置为空,表示要获取该应用程序创建的所有窗口的消息;第三、四参数用于指定消息范围。后面三个参数被设置为默认值,用于接收发送到属于这个应用程序的任何一个窗口的所有消息。在接收到除WM_QUIT之外的任何一个消息后,GetMessage()返回TRUE;如果GetMessage收到一个WM_QUIT消息,则返回FALSE以退出消息循环,终止程序运行。因此,在接收到WM_QUIT之前,带有GetMessage()的消息循环可以一直循环下去。
当除WM_QUIT的消息用GetMessage读入后,首先要经过函数TranslateMessage()对其进行解释,但对大多数消息来说并不起什么作用。这里起关键作用的是DispatchMessage()函数,把由GetMessage获取的Windows消息传送给在MSG结构中为窗口所指定的窗口过程。在消息处理函数处理完消息之后,代码又循环到开始去接收另一个消息,这样就完成了一个完整的消息循环。
主控程序的实现
本文将设计一个主控程序和一个底层工作程序,由主控程序通过消息来控制底层工作程序的工作状态。这里首先对主控程序的设计过程进行介绍:
首先创建一个单文档工程,添加三个菜单“命令一”、“命令二”、“命令三”及与之对应的命令响应函数:
OnSendComm1()
{
CString str="Receiver";
CWnd *pWnd=CWnd::FindWindow(NULL,str);
if(pWnd)
pWnd-〉SendMessage(WM_COMM,0,0);
}
OnSendComm2()
{
CWnd *pWnd=CWnd::FindWindow(NULL,str);
if(pWnd)
pWnd-〉SendMessage(WM_COMM,0,1);
}
OnSendComm3()
{
CString str="Receiver";
CWnd *pWnd=CWnd::FindWindow(NULL,str);
if(pWnd)
pWnd-〉SendMessage(WM_COMM,1,0);
}
这里,WM_COMM是自定义消息,用于在主控程序和底层通信程序之间进行联系,为了能够使用该消息,必须首先添加预定义语句:
#define WM_COMM WM_USER + 100
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)
上述几个函数的结构没有什么区别,首先通过FindWindow()返回由str变量指定窗口标题的应用程序主窗口句柄并将其保存到pWnd。然后再通过该句柄调用SendMessage()函数并发送刚才定义的WM_COMM消息及其消息参数。如果函数被正确执行,底层程序将收到来发自主控程序的WM_COMM消息并可在消息响应函数中完成对消息参数的判断处理以及进一步的操作。
底层工作程序的实现
最后,来介绍一下底层工作程序的实现过程。底层工作程序也是一个单文档应用程序(当然,对于多文档应用程序也是一样)。由于主控程序是通过对应用程序主窗口标题的捕获来得到底层程序主窗口句柄的。因此需要在底层工作程序应用类的初始化应用函数InitInstance()中添加(在函数末尾添加)下面语句:
m_pMainWnd-〉SetWindowText("Receiver");
以设置底层工作程序的窗口标题。同时还要在主框架类的头文件MainFrm.h中添加与主控程序一样的自定义消息定义:
#define WM_COMM WM_USER+100
由于底层程序需要接收并响应主控程序发出的通知消息,因此还要在底层工作程序中添加对自定义消息WM_COMM的响应处理:
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code !
ON_WM_CREATE()
/