上篇博文介绍了线程间同步,本博文将介绍进程间通信。实现进程间通信的方式有好几种,其中最常用的而且最可靠的是采用内存映射的方式实现这一功能。
1. 目的:通过内存映射的方式实现进程间通信。
2. 实现:
2.1 平台:WinCE + VS2005
2.2 具体实现过程
为了叙述的方便,将主应用程序对应的进程称为Host进程,程序称为Host程序;将从应用程序对应的进程称为Target进程,程序称为Target程序。
2.2.1 Host端
首先在Host程序的头文件中添加如下自定义消息,注意自定义消息
/************************************************************************/
// 用于访问内存映射文件的用户自定义消息
#define WM_MSG_SEND_COMMAND WM_USER+111 // 发送消息(命令)给Bottom窗口
#define WM_MSG_SEND_DATA WM_USER+112 // 发送消息(数据)给Bottom窗口
#define WM_MSG_RECEIVE_COMMAND WM_USER+113 // 接收来自Bottom的消息(命令)
#define WM_MSG_RECEIVE_DATA WM_USER+114 // 接收来自Bottom的消息(数据)
/************************************************************************/
其次,在Host程序的源文件中添加想要发送到Target程序的消息,比如
::SendMessage(m_hTarget, WM_MSG_SEND_COMMAND, (WPARAM)QueryUSBStatus,(LPARAM)0);
// 参数1表示Target程序对应的窗口句柄,有几种方式可获得窗口的句柄,在本博文小结中,我会给出具体的获取窗口句柄的方法。
// 参数2表示自定义消息,这个消息就是Target程序要响应的,因此该消息同样要在Target程序的头文件中有定义。
// 参数3和参数4是用户可灵活使用的,一般用于告诉接受端访问哪段内存区域,告诉用户指针的偏移地址和数据长度,以方便接受端从指定的内存区域取数据。内存映射访问共享数据就用到了这个方法。
为了方便叙述,给出了Host程序向Target程序发送的部分命令样式。
/************************************************************************/
// WinCE发给USB端的命令
#define QueryUSBStatus 1 // 0x01
#define USBStatusReturn 16 // 0x10表示USB正常,0x12表示USB不正常
#define QueryUSBReady 17 // 0x11表示正常,仅用在USBConnectRigth为FALSE的情况下才使用这个命令
#define WinCEStatusReturn 32 // 0x20表示WinCE正常,0x22表示WinCE端不正常
#define WinCEToUSBStart 33 // 0x21表示WinCE通知USB开始发送数据
#define NotifyWinCEGetData 48 // 0x30表示USB通知WinCE取数据
#define WinCEToUSBStop 49 // 0x31表示WinCE通知USB停止发送数据
#define USBSendDataFinish 64 // 0x40表示USB通知此次测试已经完毕
#define SetUSBWndMinimized 65 // 0x41表示WinCE通知USB窗口最小化
#define CloseUSB 80 // 0x50表示WinCE通知USB退出程序
#define USBReady 97 // 0x61表示USB通知WinCE端USB已经准备好
/************************************************************************/
2.2.2 Target端
接受端Target程序有4处需要注意。
1)在Target的头文件中添加与Host头文件中一样的自定义消息定义,如下所示。
/************************************************************************/
// 用于访问内存映射文件的用户自定义消息,这个定义直接来自WinCE程序
#define WM_MSG_SEND_COMMAND WM_USER+111 // 发送消息(命令)给Bottom窗口
#define WM_MSG_SEND_DATA WM_USER+112 // 发送消息(数据)给Bottom窗口
#define WM_MSG_RECEIVE_COMMAND WM_USER+113 // 接收来自Bottom的消息(命令)
#define WM_MSG_RECEIVE_DATA WM_USER+114 // 接收来自Bottom的消息(数据)
#define WM_MSG_ExitWindow WM_USER+115 // 如果已经有个实例在运行,则直接让之后打开的实例退出
/************************************************************************/
2)在Target的源文件中添加自定义消息映射
BEGIN_MESSAGE_MAP(CTVLFUSBCommunicationDlg, CDialog)
ON_MESSAGE(WM_MSG_SEND_COMMAND, OnReceiveCommand) // 对于Host是发送命令,对于Target则是接收命令
END_MESSAGE_MAP()
3)在Target程序的头文件中添加消息响应函数的定义
afx_msg LRESULT OnReceiveCommand(WPARAM wParam,LPARAM lParam); // 收到来自Top的命令
4)完善自定义消息对应的消息响应函数
LRESULT CTVLFUSBCommunicationDlg::OnReceiveCommand(WPARAM wParam,LPARAM lParam)
{
int CommandID = wParam;
int usedBufferLen = lParam;
switch(CommandID)
{
case QueryUSBStatus:
{ // 向WinCE返回当前USB的状态
int USBstatus = 16;
memset(m_pCommandBuf+sizeof(int), 0 ,sizeof(int));
memcpy(m_pCommandBuf+sizeof(int), &USBstatus, sizeof(int));
break;
}
case WinCEToUSBStart:
{
// 收到WinCE的命令,以及积分次数和每个测点号的数据点个数,然后开始发送数据给WinCE
memcpy(&IntegrationNumbers, m_pCommandBuf+sizeof(int)*10, sizeof(int));
memcpy(&DataNumbers, m_pCommandBuf+sizeof(int)*11, sizeof(int));
currentIntegralCount = 0;
this->SetTimer(1, TimerPeriod, NULL);
break;
}
case WinCEToUSBStop:
{
currentIntegralCount = 0;
this->KillTimer(1);
break;
}
...........
}
return 0;
}
3. 总结:
3.1 在进程A中使用SendMessage()函数向进程B发送消息,需明确进程B对应的窗口句柄。在进程B源程序中获取窗口句柄的方式有:
1)hTarget = ::FindWindow(NULL, TargetExeWindowName); // 其中TargetExeWindowName为CString类型,表示进程B对应的窗口名称
2)hTarget= AfxGetApp()->GetMainWnd()->m_hWnd;
3)hTarget= AfxGetMainWnd()->m_hWnd;
4)hTarget= this->GetSafeHwnd();
3.2 SendMessage()和PostMessage()发送消息的区别在于发送消息是否立即返回,可根据实际需要权衡利弊选择最适合项目程序的发送消息方式。
3.3 文中只介绍了发送命令接受命令的方法,发送数据和接受数据的方法与其类似,不再赘述,区别仅在于发送数据时传递了2个参数,1个参数是指针的起始偏移地址,1个参数是数据长度。