windows进程通信之消息和WM_COPYDATA

本文由danny发表于 http://blog.csdn.net/danny_share

 

说明:建议先下载本文配套工程,其中

MessageMain工程、MessageSubA工程、MessageSubAs工程分别用于演示进程间通信的主进程和两个子进程

下载地址:http://download.csdn.net/detail/danny_share/7710705

 注意:

(1)不要F5直接运行

(2)编译生成debug目录或者release目录以后,双击其中的MessageMain.exe运行

 

一.Windows消息机制

(1)       概念

Windows程序以消息为基础,以事件驱动(message basedevent driven),这里的消息就是如下一个数据结构

typedef struct tagMSG{
            HWND hwnd;  //窗口句柄

            UINT  message;  
            WPARAM  wParam;         

LPARAM  LParam; 
            DWORD  time;//消息被传递时候的时间
            POINT  pt; //消息被传递时,光标位置
} MSG;


 

(2)原生Windows消息循环

老生常谈的话题,原生的Windows程序通过注册窗口类RegisterClass,注册时指定窗口回调函数WndProc,创建窗口CreateWindow,再使用消息循环,即可成功创建Windows程序。

其中窗口过程函数形式如下:

LRESULT CALLBACK WndProc( HWND hWnd , UINT message , WPARAM wParam , LPARAM lParam)

{

switch( message)

{

  case MessageType:

{
}

Break; 

}
}

 


消息循环形式如下:

while(GetMessag(&msg,NULL,0,0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);
}


 

3MFC消息循环

MFC封装了原生Windows消息循环,通过DECLEAR_MESSAGE_MAP定义了指向父类的消息映射指针,以及本身消息和消息处理的数据结构,再通过BEGIN_MESSAGE_MAPEND_MESSAGE_MAP填入刚刚定义的结构。

 

二.Message相关函数

(1)       消息函数

ID

函数

含义

1

GetMessage

从消息队列获取消息,阻塞

2

PeekMessage

从消息队列获取消息,不管有没有消息,立即返回

3

TranslateMessage

实际是读取WM_KEYDOWNWM_KEYUP消息

翻译产生WM_CHAR消息

4

DispatchMessage

把消息交给对应的窗口回调函数处理

5

SendMessage

把消息发送到窗口消息队列中,等其处理完成才返回

6

PostMessage

把消息发送到窗口消息队列中,立即返回

7

SendDlgItemMessage

向控件发送消息,等其处理完成才返回

8

RegisterWindowMessage

注册一个消息,成功则返回值在0xC0000xFFFF之间

9

BroadcastSystemMessage

注册一个消息,成功则返回值在0xC000到0xFFFF之间

10

BroadcastSystemMessageEx

 

11

GetInputState

 

12

GetMessageExtraInfo

 

13

GetMessagePos

 

14

GetMessageTime

 

15

GetQueueStatus

 

16

InSendMessage

 

17

InSendMessageEx

 

18

PostQuitMessage

 

19

PostThreadMessage

发送消息到拥有消息队列的线程

20

RegisterWindowMessage

 

21

ReplyMessage

 

22

SendAsyncProc

传入某消息的额外的回调函数

23

SendMessageCallback

 

24

SendMessageTimeout

把消息发送到窗口消息队列中,等其处理完成或是超时才返回

25

SendNotifyMessage

若目标窗口由调用线程创建,则发送并等待其处理完成后返回;若目标窗口由其他线程创建,则异步发送

26

SetMessageExtraInfo

 

27

WaitMessage

PeekMessage相比,在无消息时多了交出线程控制权的操作

 

 

(2)       MFC消息宏

ID

函数

含义

1

DECLARE_MESSAGE_MAP

在头文件中声明,本质是定义父类消息映射指针和自己的消息映射数据结构

2

BEGIN_MESSAGE_MAP

填充DECLARE_MESSAGE_MAP中定义的数据结构

3

ON_MESSAGE

自定义消息的映射

 

三.使用消息实现进程通信

1.  关于自定义消息

(1)       我们可能会想当然地自定义一个结构体,如下

struct MsgInfoStruct

{

  char sender;

  char receiver;

  char command;

  string data;

};

 (2)再自定义一个消息比如#define WM_MSG_WORK (WM_USER+30)

(3)然后在主进程中PostMessage一个new的MsgInfoStruct,

 (4)  在子进程的WM_MSG_WORK响应函数中去MsgInfoStruct* originalStruct=(MsgInfoStruct*)lParam;

(5)这样主进程PostMessage给子进程A后,可直接再PostMessage给B,相比剪贴板而言,对于子进程中耗时处理的情况,这种方式可以很好的做到让两个子进程同时运行

(6)但实际上不可行,因为两个进程拥有不同的虚拟地址空间,导致子进程不认主进程中传的地址,因此这种方式是不信的

7)因此使用自定义消息可以发命令(通过消息来标识不同的命令),但无法传输数据

 

2.  关于WM_COPYDATA

(1)       由于WM_COPYDATA消息底层是通过文件映射实现的,且CreateFileMapping时共享内存名称都为”MSName”,所以为了防止数据紊乱,只能采用SendMessage而无法使用PostMessage来发送

(2)       这就和剪贴板一样,对于耗时的操作,主进程必须等一个子进程处理完成才能命令下一个子进程处理。

(3)       从这里也可以看出,WM_COPYDATA适合一个主进程和一个子进程通信的情况

下面使用WM_COPYDATA来实现进程间相互通信,COPYDATASTRUCT结构如下

struct COPYDATASTRUCT

{

DWORD dwData;//任意数据

DWORD cbData;//传输的数据长度

PVOID lpData;//cbData数据的指针
};


 

我们使用上篇文章设计的协议,”MA10”表示主进程M向子进程A发送命令1,数据为0

我们将数据存放在lpData

贴上主进程M的主要代码

发送数据和命令

void CMessageMainDlg::sendDataASYNC(CWnd* WndReceive_In,int CommandType)
{
	if(NULL!=WndReceive_In)
	{
		string content;
		content+='M';
		if(WndReceive_In==this->m_ASubWnd)
		{
			content+='A';
		}
		else
		{
			if(WndReceive_In==this->m_BSubWnd)
			{
				content+='B';
			}
		}
		switch(CommandType)
		{
		case 1:
			{
				content+='1';
			}
			break;
		case 2:
			{
				
				content+='2';
			}
			break;
		}
		content+='0';
		COPYDATASTRUCT tempDataStruct;
		tempDataStruct.lpData=(PVOID)(content.c_str());
		tempDataStruct.cbData=content.length();

		::SendMessage(WndReceive_In->m_hWnd,WM_COPYDATA,0,(LPARAM)&tempDataStruct);
	}
}


响应接收代码

BOOL CMessageMainDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	if(NULL!=pCopyDataStruct)
	{
		string tempContent=(LPSTR)pCopyDataStruct->lpData;
		tempContent=tempContent.substr(0,pCopyDataStruct->cbData);
		if(tempContent.length()==4)
		{
			if(tempContent[1]=='M')
			{
				string info;
				if(tempContent[0]=='A')
				{
					info="A sub process response Work";
				}
				else
				{
					if(tempContent[0]=='B')
					{
						info="B sub process response Work";
					}
				}
				info+=tempContent[2];
				MessageBox(info.c_str(),"Info",MB_OK);
			}
		}
	}
	return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}


再贴上子进程A中的处理

BOOL CMessageSubADlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	CWnd* tempWnd=FindWindow(NULL,"MessageMain");


	string tempContent=(LPSTR)pCopyDataStruct->lpData;
	tempContent=tempContent.substr(0,pCopyDataStruct->cbData);
	if(tempContent.length()==4)
	{
		if(tempContent[0]='M')
		{
			if(tempContent[1]='A')
			{
				string response="AM";
				response+=tempContent[2];
				response+=tempContent[3];
				COPYDATASTRUCT copyData = { 0 };
				copyData.lpData =(PVOID) response.c_str();
				copyData.cbData = response.length();
				::SendMessage(tempWnd->m_hWnd,WM_COPYDATA,0,LPARAM(&copyData));
			}
		}
	}
	return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}


详见工程 

 

 

四.总结

1)自定义消息虽然看似灵活,但由于不同进程间虚拟地址空间不同,所以只能用于传递命令,无法传递复杂结构的数据

2WM_COPYDATA相比于自定义消息而言,解决了无法传送复杂数据的问题,但由于其本质是共享名为”MSName”的文件映射,所以只能使用SendMessage,无法使用PostMessage,和剪贴板一样,由两个后台进程无法同时工作的缺陷

 

 

Danny

2014年8月3号

于天津河西七天酒店

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值