一、背景

  一个进程的线程窗口(window1)如何在不知道另一个进程的线程窗口(window2)的情况下接收到window2发送的自定义消息呢?

二、自定义消息

  Windows系统除了预定义一些系统的消息外,还为用户预留了自定义消息的范围(WM_USER~0x7FFF)。通过RegisterWindowMessage函数,我们可以注册一个系统唯一的新消息。两个不同的进程注册了相同的消息字符串,这些应用将会返回相同的消息。直到整个消息会话结束。

三、广播消息

  SendMessage或PostMessage函数可以向指定的窗口句柄发送窗口消息。如果窗口句柄是HWND_BOARDCAST,就会向系统所有顶层窗口发生该广播消息。

四、代码验证

  广播消息的代码:

 1 #include <iostream>
 2 #include <Windows.h>
 3 
 4 int main()
 5 {
 6     // 注册窗口消息
 7     UINT seewoDesktopProxyMsg = ::RegisterWindowMessageW(L"Seewo_Desktop_Proxy_Message");
 8 
 9     if (0 == seewoDesktopProxyMsg)
10     {
11         std::cout << "RegisterWindowMessageW fail. error code:" << ::GetLastError();
12         return 1;
13     }
14 
15     // 广播消息
16     HWND hDesktop = GetDesktopWindow();
17     std::cout << "desktop window:" << hDesktop << std::endl;
18     ::PostMessageW(HWND_BROADCAST, seewoDesktopProxyMsg, reinterpret_cast<WPARAM>(hDesktop), 0);
19     system("pause");
20 
21     std::cout << "Hello World!\n";
22 }

接收广播消息:

#include <iostream>
#include <thread>
#include <Windows.h>

UINT seewoDesktopProxyMsg = 0;

void ListenRegistryWindowMessage()
{
    WNDCLASSEX wndClass;
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
    wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(DefWindowProc);
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hIcon = NULL;
    wndClass.hbrBackground = NULL;
    wndClass.hCursor = LoadCursor(0, IDC_ARROW);
    std::wstring className(L"SeewoDesktopProxy_recevie");
    wndClass.lpszClassName = className.c_str();
    wndClass.lpszMenuName = NULL;
    wndClass.hIconSm = NULL;
    if (!RegisterClassEx(&wndClass))
    {
        std::cout << "RegisterClassEx err:" << GetLastError() << std::endl;
    }
    HWND proxyHwnd = CreateWindowEx(WS_EX_NOACTIVATE,
                                    className.c_str(), NULL,
                                    WS_POPUP,
                                    0, 0, 0, 0,
                                    NULL, NULL, 0, NULL);
    if (proxyHwnd == NULL)
    {
        std::cout << "CreateWindowEx err:" << GetLastError() << std::endl;
    }
    std::cout << "create proxy windows success hWnd :" << proxyHwnd << std::endl;
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (msg.message == seewoDesktopProxyMsg)
        {
            std::cout << "receive registry window message." << (HWND)(msg.wParam) << std::endl;
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

int main()
{
    // 注册窗口消息
    seewoDesktopProxyMsg = ::RegisterWindowMessageW(L"Seewo_Desktop_Proxy_Message");

    if (0 == seewoDesktopProxyMsg)
    {
        std::cout << "RegisterWindowMessageW fail. error code:" << ::GetLastError();
        return 1;
    }

    // 接收消息的窗口
    std::thread listen(ListenRegistryWindowMessage);

    listen.join();

    std::cout << "Hello World!\n";
}

运行效果:

 

如果使用Spy++监控窗口消息会更加详细:

 

 五、注意事项

  接收方必须在发送方发送广播消息窗口消息队列创建完成,否则创建前的所有广播消息都无法正常接收到。

参考地址:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerwindowmessagea