进程的通信 - WM_COPYDATA

在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。其中使用SendMessage向另一进程发送WM_COPYDATA消息是一种比较经济实惠的方法。

WM_COPYDATA通信需要将传递的消息封装在COPYDATASTRUCT结构体中,在SendMessage消息传递函数时作为lParam参数传递

COPYDATASTRUCT的结构如下:

typedef struct tagCOPYDATASTRUCT {
  ULONG_PTR dwData;
  DWORD     cbData;
  PVOID     lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
  • 参数dwData

为自定义数据,按照自己习惯设置就好,不影响对象的传输;

  • 参数cbData

表示传递数据的大小(以字节为单位

  • 参数PVOID

表示传递的数据,是一个void*类型 

使用WM_COPYDATA时要用SendMessage而不能使用PostMessage,因为SendMessage是阻塞的,会等待消息响应窗体处理消息完毕后再返回;而PostMessage是异步的,这样就可能会导致当消息响应窗体接收到WM_COPYDATA的时候,COPYDATASTRUCT对象已经被析构了,导致访问数据发生异常。

LRESULT SendMessage(
  [in] HWND   hWnd,
  [in] UINT   Msg,
  [in] WPARAM wParam,
  [in] LPARAM lParam
);
  • 参数hWnd

表示接收消息的窗体句柄

  • 参数Msg

指定附加的消息特定信息。在WM_COPYDATA进程通信中这个参数设置成WM_COPYDATA

  • 参数wParam

指定附加消息的特定信息。在WM_COPYDATA通信中这个参数设置为当前窗体的句柄

可以通过AfxGetApp()->m_pMainWnd获得,但需要强转成WPARAM

  • 参数lParam

指定附加消息的特定信息。在WM_COPYDATA通信中这个参数设置为COPYDATASTRUCT结构体,也需要强转成lParam

使用WM_COPYDATA进行进程间的通信需要提前获得另一个进程的窗体句柄。虽然spy++工具可以每次查到窗体的句柄,但是进程每次运行后句柄可能会改变,因此我们要先获得窗体的名称,再通过FindWindow函数获得窗体句柄

strWindowTitle是窗体的名称 

HWND FindWindowW(
  [in, optional] LPCWSTR lpClassName,
  [in, optional] LPCWSTR lpWindowName
);
  • 参数lpClassName

一般设置为NULL

  • 参数lpWindowName

表示窗口的名称 

发送数据端 

返回值

函数成功,返回窗口的句柄

函数失败,返回NULL

在判断函数返回值时,不仅要判断返回的句柄是否为空,还需要判断句柄标识的是否是现有的窗口

通过调用IsWindow函数判断长提是否还存在

BOOL IsWindow(
  [in, optional] HWND hWnd
);
  • 参数hWnd

表示窗口的句柄

返回值

如果窗口句柄标识现有窗口,则返回值非零

如果窗口句柄未标识现有窗口,则返回零 

接收端

接收端通过消息响应函数来接收数据

带xxxDlg文件中添加WM_COPYDATA消息响应函数

f667c983b13a40fe8acfc48d5831f73a.png

 当有消息发送过来时就会响应这个函数,

BOOL CWMCOPYDATADlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}

通过解析参数COPYDATASTRUCT* pCopyDataStruct就可以获得发送过来的数据了

需要注意的是

  1. 使用WM_COPYDATA时要用SendMessage而不能使用PostMessage,因为SendMessage是阻塞的,会等待消息响应窗体处理消息完毕后再返回;而PostMessage是异步的,这样就可能会导致当消息响应窗体接收到WM_COPYDATA的时候,COPYDATASTRUCT对象已经被析构了,导致访问数据发生异常。
  2. COPYDATA结构体的实质依然是共享内存,区别是这一片特殊的共享内存由操作系统管理而不用用户手动申请管理。
  3. WM_COPYDATA适合小数据量的进程间通信,大数据量可能造成内存问题,以及界面卡死,因为消息的发送形式是同步的。

Demo示例:

8f8d95b3a5a14d93811fe951cfe2c65c.png

374f1656e6ab44659da4532bea29dc9b.png

新建两个基于对话框的MFC应用,将其中一个窗体名称定为“MFCRecv”,当点击第二个应用的发送按钮后,会向第一个应用发送一段数据,第一个应用会将消息通过消息框显示出来 。

发送端:

CString strWindowTitle = _T("MFCRecv");
HWND hRecvWnd = ::FindWindow(NULL, strWindowTitle.GetBuffer(0));

CString strDataToSend = _T("Hello,这是霸道小明");

if (hRecvWnd != NULL && ::IsWindow(hRecvWnd)) {
	//如果不是空,并且是一个窗口
		
	//数据的封装
	COPYDATASTRUCT cpd;
	cpd.dwData = 0;//要传递给应用程序的数据
	cpd.cbData = strDataToSend.GetLength() * sizeof(TCHAR);//传递数据的大小
	cpd.lpData = (PVOID)strDataToSend.GetBuffer(0);
	//AfxGetApp函数可以
	::SendMessage(hRecvWnd, WM_COPYDATA, (WPARAM)(AfxGetApp()->m_pMainWnd), (LPARAM)&cpd);
}
strDataToSend.ReleaseBuffer();

接收端:

//消息响应函数
BOOL CWMCOPYDATADlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	//窗口接收到消息,就会响应这个函数
	//解析数据
	LPCTSTR szText = (LPCTSTR)(pCopyDataStruct->lpData);
	DWORD dwLength = (DWORD)pCopyDataStruct->cbData;
	TCHAR szRecvText[1024] = { 0 };
	memcpy(szRecvText, szText, dwLength);
	MessageBox(szRecvText, _T("收到消息"), MB_OK);
	return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}

运行结果:

11f8ab557e2048cea59d71e2d8f41ad8.gif 

右端进程点击发送,左端继承就会将接收到的数据显示出来,此时右端继承会进入阻塞状态,直到左边进程对消息做出处理。 

 

 

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值