进程间通信IPC-Windows消息

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage
参考:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage
参考:https://docs.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues#application-defined-messages

引言

在windows下做开发的话,避免不了会使用windows的消息机制。
windows消息机制被windows封装的还是比较好用的一种通信方式。提供了相对比较完备的:消息发送、读取、超时、同步异步、异常处理(消息队列满等)、消息清理等,这些可以比较全面的支持了线程内,进程间通信的需求。
相比其它通信方法Socket、Pipe、ShareMemmory,消息机制还是完备了很多的,简化了使用的难度。
注:下面的部分结论来源于测试,并不保证完全的正确性,请采纳时自行评估。

Win进程间消息通信

对于在进程间的消息通信,和进程内还是有较大的区别的。不过windows消息是操作系统提供的机制,有全局性的消息队列,是能够支撑进程间消息通信的。
跨进程传递消息的方式有两种:
一种是知道对端的HWND,通过HWND发送消息,这个一般是需要先找出对端进程,然后再找出进程窗口,之后进行消息的发送。
另一种是发送广播消息,广播消息会发送到所有进程的顶层窗口HWND中,顶层窗口接收到消息,对消息进行接收分发处理。这种也是有一定限制的,一是要求对端有窗口HWND,二是对它的消息ID范围有要求。
主要使用函数:
发送消息使用SendMessage/PostMessage/SendMesssageTimeOut;
接受消息使用GetMessage/PeekMessage。

使用消息ID范围

windows下的消息id是按范围划分的,分为四个范围,如下所示。

对于我们做进程间通信来说,主要关注私用窗口消息id范围 与 RegisterWindowsMessage消息id范围。

如果采用HWND发送跨进程消息的话,使用这两者消息id都可以;如果发广播消息到其它进程的话,需要使用RegisterWindowsMessage申请的id范围才可以。

系统消息id范围:0x0000-0x03FF

system reserves message: in the range 0x0000 through 0x03FF (the value of WM_USER – 1)

私有窗口消息id范围:0x0400-0x7FFF

private window classes:in the range 0x0400 (the value of WM_USER) through 0x7FFF

应用4.0版本应用消息id范围:0x8000-0xBFFF

application is marked version 4.0:in the range 0x8000 (WM_APP) through 0xBFFF

使用RegiesterWindowMessage申请的消息id范围:0xC000-0xFFFF

an application calls the RegisterWindowMessage:in the range 0xC000 through 0xFFFF

创建一个简单的窗口

跨进程发送消息,使用广播消息的话,要求接收端需要有窗口,这样才能接收到广播的消息;对于没有窗口的程序,是接受不到消息的;这就需要创建一个简单的windows窗口了,创建不可见的窗口就可以的。

下面是一个创建窗口,个人觉得极简的例子:

// 使用预定义Class类型STAIC来注册一个windows:
// Create the window.
HWND hwnd = CreateWindowEx(
  0,                              // Optional window styles.
  _T("STATIC"),             // Window class
  _T("STATIC Windows"),    // Window text
  WS_OVERLAPPED,   // Window style
  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,  // Size and position
  NULL,       // Parent window   
  NULL,       // Menu
  NULL,       // Instance handle
  NULL        // Additional application data
);

通常创建窗口的话,会涉及注册一个窗口类,设定消息回调函数,下面是一个通常创建窗口的例子:

// callback函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
  if (message = WM_DESTROY){
    PostQuitMessage(0);
    return 0;
  }
  return DefWindowProc(hWnd, message, wParam, lParam);
}

// Register the window class.
const TCHAR CLASS_NAME[] = _T("Empty Windows");
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = NULL;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);

// Create the window.
HWND hwnd = CreateWindowEx(
  0,                              // Optional window styles.
  CLASS_NAME,                     // Window class
  _T("Empty Windows Example"),    // Window text
  WS_OVERLAPPEDWINDOW,            // Window style
  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,  // Size and position
  NULL,       // Parent window   
  NULL,       // Menu
  NULL,       // Instance handle
  NULL        // Additional application data
);

消息发送接受的例子

有了窗口之后,跨进程广播消息就可以传入到进程中了,就可以接受消息,处理消息了:

// 发送侧样例
const UINT MY_MSG = ::RegisterWindowMessage(_T("MY_MSG_TEST"));
for (int i = 0; i < 10000; i++) {
  if (PostMessage(HWND_BROADCAST, MY_MSG, (WPARAM)i, (LPARAM)i)) {
    printf("Post Message %d\n", i);
  }
  else {
    printf("Post Message %d failed\n", i);
  }
  Sleep(100);
}

// 接收侧样例:
const UINT MY_MSG = ::RegisterWindowMessage(_T("MY_MSG_TEST"));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
  if (msg.message == MY_MSG)
  {
    printf("Get Message, hwnd: %x, param: %d, %d\n", msg.hwnd, (int)msg.wParam, (int)msg.lParam);
  }
}

测试使用

做了一些跨进程通信测试,看到的一些约束项,或和预期有点差异的点;

简单罗列如下,仅供参考:

  1. PostMessage经过测试,当发到10000条之后,还没有接收者的话,会报:配额不足,无法处理此命令。
  2. PostMessage的进程终止后,发出的消息也随之被销毁了;未被接收的消息也就收不到了。
  3. 位于WM_USER范围的消息,使用广播模式时,发送成功,但是其它进程接收不到;指定HWND时,可以发送成功;
  4. 接收方进程必须有窗口才能接收消息,否则Get/Peek不到消息;
  5. 使用RegisterWindowMessage范围的消息,使用广播模式时,发送成功,其它进程也接收成功;另外接受消息时HWND要使用NULL或主窗口HWND。
  6. 发广播消息,如果消息没有被及时接收处理,在其它窗口中也感受到明显的卡顿感;消息过于频繁的情况,可能不太适合发广播消息;
  7. ChangeWindowMessageFilter在接收端的使用好像用处不大,是否执行都未受影响,也可能是因为我所用测试程序没有权限差异
    ChangeWindowMessageFilter(MY_MSG, MSGFLT_ADD)

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春夜喜雨

稀罕你的喜欢!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值