首页 > 系统相关 >深入理解和实现Windows进程间通信(消息队列)

深入理解和实现Windows进程间通信(消息队列)

时间:2024-06-20 16:59:59浏览次数:28  
标签:Windows hWnd WM 间通信 队列 消息 进程 message

常见的进程间通信方法

常见的进程间通信方法有:

  1. 管道(Pipe)
  2. 消息队列
  3. 共享内存
  4. 信号量
  5. 套接字

下面,我们将详细介绍消息队列的原理以及具体实现。

什么是消息队列?

Windows操作系统使用消息机制来促进应用程序与操作系统之间的通信。每当发生事件(如键盘按键、鼠标移动或系统事件)时,操作系统都会生成相应的消息。这些消息被发送到一个特定的消息队列中,随后由应用程序的消息循环处理。

消息队列

每个创建了窗口的线程都拥有一个消息队列,用于存储等待处理的消息。这些消息包括用户动作(如鼠标点击、键盘操作)和系统通知(如窗口重绘请求、系统关闭通知)。

消息循环

线程通过一个循环机制,称为消息循环或消息泵,从其消息队列中检索消息。消息循环的基本操作包括:

  • 检索消息:使用GetMessagePeekMessage
  • 翻译消息TranslateMessage转换键盘输入。
  • 分发消息DispatchMessage将消息派发给目标窗口的窗口过程。

消息分类

消息主要包含:

  • 系统消息:涉及窗口生命周期管理,如WM_CLOSEWM_QUIT等。
  • 硬件消息:反映用户与硬件的交互,如WM_KEYDOWNWM_MOUSEMOVE等。

除了上面提到的消息外,用户还可以自定义消息,自定义消息一般从WM_USER(0x0400)开始,到0x7FFF这样一个范围内,比如:
#define WM_CUSTOM_MSG WM_USER+100

接口介绍

GetMessagePeekMessageSendMessagePostMessage这四个接口是Windows消息处理的核心,它们各自承担着不同的角色和功能。

GetMessage

原型

BOOL GetMessage(
  LPMSG lpMsg,
  HWND  hWnd,
  UINT  wMsgFilterMin,
  UINT  wMsgFilterMax
);

参数解释

  • lpMsg:指向MSG结构的指针,该结构将接收消息的详细信息。
  • hWnd:指定窗口的句柄,如果为NULL,则接收属于调用线程的任何窗口的消息。
  • wMsgFilterMinwMsgFilterMax:指定要检索的消息范围的最小值和最大值。如果两者都为0,函数将返回所有可用的消息。

功能和特点

  • GetMessage用于从调用线程的消息队列中检索消息。该函数在有消息到达时返回,如果遇到退出消息WM_QUIT,则返回FALSE
  • 该函数是阻塞的,如果没有消息,它会等待消息的到来。

PeekMessage

原型

BOOL PeekMessage(
  LPMSG lpMsg,
  HWND  hWnd,
  UINT  wMsgFilterMin,
  UINT  wMsgFilterMax,
  UINT  wRemoveMsg
);

参数解释

  • lpMsg:指向MSG结构的指针,该结构将接收消息的详细信息。
  • hWnd:指定窗口的句柄,如果为NULL,则获取属于调用线程的任何窗口的消息。
  • wMsgFilterMinwMsgFilterMax:指定要检索的消息范围的最小值和最大值。
  • wRemoveMsg:指定消息如何处理。常用值有:PM_REMOVEPM_NOREMOVE

功能和特点

  • PeekMessage用于非阻塞地检查调用线程的消息队列,允许你查看消息队列中的消息而不必移除它。
  • 可以配置为从队列中移除消息或仅检查消息而不移除。
  • 常用于动画或游戏编程中,确保应用程序保持响应用户操作,同时继续进行其它处理。

SendMessage

原型

LRESULT SendMessage(
  HWND   hWnd,
  UINT   Msg,
  WPARAM wParam,
  LPARAM lParam
);

参数解释

  • hWnd:接收消息的窗口的句柄。
  • Msg:消息的标识符。
  • wParamlParam:消息特定的附加信息。

功能和特点

  • SendMessage同步发送消息,调用方在接收窗口处理该消息之前会阻塞。
  • 可用于发送任何类型的消息,并且能够获取消息处理的结果。

PostMessage

原型

BOOL PostMessage(
  HWND   hWnd,
  UINT   Msg,
  WPARAM wParam,
  LPARAM lParam
);

参数解释

  • hWnd:接收消息的窗口的句柄。
  • Msg:消息的标识符。
  • wParamlParam:消息特定的附加信息。

功能和特点

  • PostMessage异步发送消息,将消息放入消息队列后立即返回,不等待消息被处理。
  • 适合用于那些不需要立即反馈的消息发送,如状态更新或通知消息。

实现

问题

实现两个进程,进程1发送消息给进程2,相关代码如下:

// 进程1
int main()
{
  HWND hWnd = FindWindow(NULL, L"WindowsProject1"); // 找到进程2的句柄
  const wchar_t* message = L"Hello!";
  if (hWnd != NULL) {
    SendMessage(hWnd, WM_USER+100, 0, (LPARAM)message);
  }
  std::cin.get();
}
// 进程2
#define WM_CUSTOMMSG   (WM_USER+100)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
	{
	case WM_CUSTOMMSG: {
		wchar_t* receivedMessage = reinterpret_cast<wchar_t*>(lParam);
        SetWindowText(hLabel, receivedMessage);
	}
    break;
  ...
    }
    return 0;
}

进程1通过自定义消息(WM_USER+100)发送了一个字符串给进程2,进程2的确收到了消息,但是数据获取失败。

因为进程1准备发送的数据存储在进程1的虚拟内存中,进程2是无法通过地址来获取这个数据的,所以进程2虽然收到消息了,但是无法得到对应的数据。

所以在进程间通过消息队列通信时需要保证传递的数据的安全性。

WM_COPYDATA

WM_COPYDATA是一个Windows消息,用于在Windows应用程序之间传递数据。这个消息特别适用于跨进程通信,允许一个应用程序向另一个应用程序的窗口发送数据,无论这些数据是简单的数值、字符串还是更复杂的结构通。

原理

当一个应用程序需要向另一个应用程序发送数据时,它可以将数据封装在COPYDATASTRUCT结构中,并通过SendMessage函数发送WM_COPYDATA消息。当接收窗口的窗口过程收到这条消息时,它可以从COPYDATASTRUCT结构中提取数据。

COPYDATASTRUCT结构

这个结构用于封装要传递的数据,其定义如下:

typedef struct tagCOPYDATASTRUCT {
    ULONG_PTR dwData;  // 任意值,由发送方设置,接收方可以用它来识别数据
    DWORD cbData;      // lpData指向的数据的大小,以字节为单位
    PVOID lpData;      // 指向要传递的数据的指针
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
  • dwData:这是一个用户定义的数据值,发送方可以使用它来传递额外的信息或数据类型标识,接收方则可以用它来决定如何解释接收到的数据。
  • cbData:这表示 lpData 指向的数据的大小(以字节为单位)。
  • lpData:这是一个指针,指向实际要传输的数据。

实现代码

进程1代码

int main()
{
	HWND hWnd = FindWindow(NULL, L"WindowsProject1");
	const wchar_t* message = L"Hello!";
	if (hWnd != NULL) {
		COPYDATASTRUCT cds;
		cds.dwData = 1;  // 用于识别数据的自定义标识
		cds.cbData = (wcslen(message) + 1) * sizeof(wchar_t);  // 包括终止符的大小
		cds.lpData = (void*)message;
        SendMessage(hWnd, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);
    }
    std::cin.get();
}

进程2代码

HWND hLabel;
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   hLabel = CreateWindowW(L"STATIC", L"Waiting for message...",
	   WS_CHILD | WS_VISIBLE | SS_LEFT,
	   10, 10, 300, 20,
	   hWnd, (HMENU)1, hInstance, nullptr);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
	{
	case WM_COPYDATA: {
		PCOPYDATASTRUCT pCds = (PCOPYDATASTRUCT)lParam;
		if (pCds->dwData == 1) {
			const wchar_t* receivedStr = (const wchar_t*)pCds->lpData;
			SetWindowText(hLabel, receivedStr);
		}
	}
    break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

效果

image.png

标签:Windows,hWnd,WM,间通信,队列,消息,进程,message
From: https://blog.csdn.net/LeoLei8060/article/details/139837629

相关文章

  • 苹果嘲笑微软在 Windows 召回计划中 AI 失败
    在科技界,苹果和微软一直是两大巨头,彼此之间的竞争从未停止。最近,苹果公司公开嘲笑微软在其Windows召回计划中使用人工智能(AI)失败的事件,再次引发了业界的广泛关注和讨论。本文将探讨这一事件的背景、原因及其对两家公司和整个科技行业的影响。事件背景微软在其最新的Windows......
  • python的queue队列获取数据
    一概念Python中的队列(Queue)数据结构提供了get()方法用于获取队列的头部项,而不会删除该项。importqueue#创建一个队列对象q=queue.Queue()#向队列中添加一些元素q.put(1)q.put(2)q.put(3)#获取队列的头部项head=q.get()print("队列的头部项是:",head)......
  • 【windows|007】DHCP服务详解
    ......
  • Windows下 Modelsim10.7下载安装及破解
    下载链接: 【免费】EDA工具,modelsim资源-CSDN文库https://download.csdn.net/download/mojixin123/89452381?spm=1001.2014.3001.5501安装及破解步骤:1、解压后打开安装包,点击exe文件右键以管理员身份运行2、出现以下界面点击下一步 3、选择安装路径,尽量不要放在c盘,然后......
  • 数据结构_栈和队列
    目录一、栈1.1 栈的使用1.2模拟实现栈二、队列2.1 队列的使用2.2 环形队列2.3 双端队列总结一、栈栈是只允许在固定的一端进行元素的插入和删除操作的一种特殊线性表。其中进行元素的插入和删除操作的一端称为栈顶,另一端称为栈底。栈遵循先进后出(后进先出) ......
  • leetcode225用队列实现栈
    本文主要讲解用队列实现栈的要点与细节,按照步骤思考更方便理解,同类型用栈实现队列c++与java代码如下,末尾请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。实现 MyStack 类:voidpush(intx) 将元素x压入栈顶。intp......
  • Windows安全加固总结(非常详细)零基础入门到精通,收藏这一篇就够了
    为了达到安全的目的,一般来说我们需要关注操作系统的八个方面:补丁管理>账号漏洞>授权管理>服务管理>功能优化>文件管理>远程访问控制>日志审计其中:补丁管理使用最新版的补丁,避免使系统存在已知的漏洞,从而被攻击者利用。账号口令梳理出系统中正在使......
  • Windows中在commond如何设置系统环境变量
    最近测试项目中需要配置一个python环境用来发workjob,配置过程中有一个步骤需要增加系统变量:addtwosystemenvvarsforthetestapplicationbydifferentenvironments(dev/stg/prod):FORGE_TEST_CLIENT_IDFORGE_TEST_CLIENT_SECRET处理方法:1、查看已经设置了哪......
  • api-ms-win-shcore-scaling-l1-1-1.dll解析及缺失解决策略:确保Windows高DPI显示正常
    api-ms-win-shcore-scaling-l1-1-1.dll是Windows操作系统中的一个API接口库文件,属于WindowsShellCommonDLLs(Shell核心动态链接库)的一部分。这个特定的DLL文件与Windows的高DPI(每英寸点数)缩放功能紧密相关,支持应用程序在不同分辨率和缩放设置下正确显示界面元素,确保UI的清晰......
  • python队列实例解析
    一队列的概念1创建队列:importqueueq=queue.Queue()#创建Queue队列 2入队和出队foriinrange(3):q.put(i)#在队列中依次插入0、1、2元素foriinrange(3):print(q.get())#依次从队列中取出插入的元素,数据元素输出顺序为2、1、0......