Windows编程模型-消息循环机制

本文详细介绍了Windows编程模型,包括其事件驱动机制、消息循环、常见消息类型以及关键API如窗口注册、创建、显示、消息处理等。通过实例展示了如何处理系统消息和自定义消息,以实现桌面应用的开发。
摘要由CSDN通过智能技术生成

1 Windows编程模型

  • Windows编程模型是一种基于事件驱动的编程架构(程序的执行流程主要依赖于外部事件的发生),它主要用于构建桌面应用程序和其他类型的应用,这些应用运行在Microsoft Windows操作系统之上。
  • Windows编程模型强调了窗口、消息处理和系统API的使用,通过这些机制,开发者能够构建出功能丰富、响应用户交互的桌面应用程序。

2 Windows消息循环机制

  • Windows消息循环机制是Windows操作系统中GUI编程的核心部分,它是事件驱动编程模型的基础。
  • 每个运行中的Windows应用程序都有与之关联的一个消息队列,当产生消息后(比如点击鼠标就会产生鼠标按下的消息),操作系统会将消息放入消息队列,应用程序会循环从消息队列中检索消息并处理。这就是Windows消息循环机制。
  • 模型示意图
    请添加图片描述

3 消息

  • 消息是如何产生的?当用户与应用程序窗口进行交互或系统事件发生时,硬件中断或者软件事件触发器会生成相应的消息,并将其放入应用程序的消息队列,这一步是由操作系统完成的。
  • Windows定义了成百上千个不同的消息类型,常见的有以下几种
    • WM_CHAR: 从键盘输入字符
    • WM_COMMAND: 用户选择菜单内的某项
    • WM_CREATE: 生成窗口
    • WM_DESTROY: 撤销窗口
    • WM_LBUTTONDOWN: 按下鼠标左键
    • WM_LBUTTONUP: 释放鼠标左键
    • WM_MOUSEMOVE: 移动鼠标
    • WM_PAINT: 窗口需要重新绘制
    • WM_QUIT: 应用程序将结束
    • WM_SIZE: 窗口尺寸被调整

4 相关API

4.1 入口函数

  • 函数原型
    •   int WinMain ( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
      
  • 参数
    • hInstance: 模块实例句柄
    • hPrevInstance: Win16遗留,现在基本不使用
    • lpCmdLine: 命令行参数
    • nShowCmd: 主窗口初始化时的显示方式

4.2 注册窗口

  • 函数原型
    •   ATOM WINAPI RegisterClassEx(const WNDCLASSEXA *lpwcx);
      
  • 参数
    • lpwcx: 说明窗口属性的结构体
      • cbSize: 结构体大小
      • style: 窗口类样式
      • lpfnWndProc: 指向窗口过程函数的指针。
      • cbClsExtra: 类附加内存的字节数。
      • cbWndExtra: 窗口附加内存的字节数。
      • hInstance : 应用程序实例句柄。
      • hIcon : 窗口左上角图标的句柄
      • hCursor : 光标句柄
      • hbrBackground : 背景画刷句柄。
      • lpszMenuName : 菜单名
      • lpszClassName : 窗口类名
      • hIconSm : 小图标句柄
  • 返回值
    • 函数成功注册窗口类后,会返回一个 ATOM 类型的值,它是类名称在系统中的内部标识符。如果注册失败,则返回零。

4.3 创建窗口

  • 函数原型
    •   HWND
        WINAPI
        CreateWindowEx(
            _In_ DWORD dwExStyle,
            _In_opt_ LPCWSTR lpClassName,
            _In_opt_ LPCWSTR lpWindowName,
            _In_ DWORD dwStyle,
            _In_ int X,
            _In_ int Y,
            _In_ int nWidth,
            _In_ int nHeight,
            _In_opt_ HWND hWndParent,
            _In_opt_ HMENU hMenu,
            _In_opt_ HINSTANCE hInstance,
            _In_opt_ LPVOID lpParam
        );
      
  • 参数
    • dwExStyle: 指定窗口的扩展样式
    • lpClassName: 窗口类名
    • lpWindowName: 窗口标题
    • dwStyle: 指定窗口的基本样式
      • WS_BORDER: 创建一个单边框的窗口
      • WS_CAPTION: 创建一个有标题框的窗口
      • WS_CHILD: 创建一个子窗口
      • WS_DISABLED: 创建一个初始状态为禁止的子窗口,该窗口不能接收来自用户的输入信息
      • WS_DLGFRAME: 创建一个带对话框边框风格的窗口
      • WS_HSCROLL: 创建一个有水平滚动条的窗口
      • WS_VSCROLL: 创建一个有垂直滚动条的窗口
      • WS_ICONIC: 创建一个初始状态为最小化状态的窗口
      • WS_MAXIMIZE: 创建一个具有最大化按钮的窗口
      • WS_OVERLAPPED: 创建一个层叠的窗口
      • WS_POPUP: 创建一个弹出式窗口
      • WS_SIZEBOX: 创建一个可调边框的窗口
      • WS_SYSMENU: 创建一个在标题条上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格
      • WS_THICKFRAME: 创建一个具有可调边框的窗口
      • WS_VISIBLE: 创建一个初始状态为可见的窗口
      • WS_MINIMIZEBOX: 创建一个具有显示最小化按钮的窗口
      • WS_MAXIMIZEBOX: 创建一个具有显示最大化按钮的窗口
      • WS_OVERLAPPEDWINDOW: 创建一个具有WS_OVERLAPPED、WS_CAPTION、WS_SYSMENU、WS_THICKFRAME、WS_MINIMIZEBOX、WS_MAXIMIZEBOX风格的层叠窗口
      • WS_POPUPWINDOW: 创建一个具有WS_BORDER、WS_POPUP、WS_SYSMENU风格的窗口。必须同时设定WS_CAPTION风格
    • X: 初始X坐标
    • Y: 初始Y坐标
    • nWidth: 窗口宽度
    • nHeight: 窗口高度
    • hWndParent: 父窗口句柄
    • hMenu: 菜单句柄
    • hInstance: 程序实例句柄
    • lpParam: 用户数据
  • 返回值: 窗口创建成功返回窗口句柄,创建失败返回NULL

4.4 显示窗口

  • 函数原型
    •   BOOL WINAPI ShowWindow(_In_ HWND hWnd, _In_ int nCmdShow);
      
  • 参数
    • hWnd: 窗口句柄
    • nCmdShow: 系统传递给WinMain函数的参数
  • 返回值
    • 成功返回TRUE,失败返回FALSE

4.5 更新窗口

  • 函数原型
    •   BOOL WINAPI UpdateWindow(_In_ HWND hWnd);
      
  • 参数
    • hWnd: 窗口句柄
  • 返回值
    • 成功返回TRUE,失败返回FALSE

4.6 从消息队列中检索消息

  • 函数原型
    •   BOOL WINAPI GetMessage(_Out_ LPMSG lpMsg, _In_opt_ HWND hWnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax);
      
  • 参数
    • lpMsg: 指向 MSG 结构体的指针, 用于接收从消息队列取出的消息
    • hWnd: 窗口句柄,如果不为空,则只检索发给该窗口或其子窗口的消息;如果为 NULL,则检索所有线程的消息。
    • wMsgFilterMin, wMsgFilterMax: 定义了要检索的消息范围,通常设置为 0 到 0xFFFF,表示检索所有消息类型
  • 返回值
    • 从消息队列中获取的消息如果不是WM_QUIT, 则返回非零值

4.7 翻译消息

  • 函数原型
    •   BOOL WINAPI TranslateMessage(_In_ CONST MSG *lpMsg);
      
  • 参数
    • lpMsg: 指向一个 MSG 结构的指针
  • 说明
    • 它的主要作用是对来自消息队列的键盘消息(通常是 WM_KEYDOWN 和 WM_SYSKEYDOWN 消息)进行翻译,将其转换成字符消息(如 WM_CHAR 和 WM_SYSCHAR),以便应用程序能够更容易地识别和处理用户的字符输入。

4.8 分发消息

  • 函数原型
    •   LRESULT WINAPI DispatchMessage(_In_ CONST MSG *lpMsg);
      
  • 参数
    • lpMsg: 指向一个 MSG 结构的指针
  • 备注
    • 主要功能是将从消息队列中取出的消息分发给相应的窗口过程进行处理。

4.9 窗口过程函数

  • 函数原型
    •   LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
      
  • 参数
    • hWnd:窗口句柄,表示该消息是发送给哪个窗口的。
    • uMsg:消息标识符,用于指示需要处理的具体消息类型。
    • wParam, lParam:这两个参数的内容取决于消息类型,通常包含了消息的附加信息。
  • 说明
    • 窗口过程函数是每个窗口类注册时必须指定的一个回调函数,它负责处理发送给窗口的所有消息。

4.10 查找窗口

  • 函数原型
    •   HWND WINAPI FindWindow(LPCSTR lpClassName, LPCSTR lpWindowName);
      
  • 参数
    • lpClassName:指向一个以NULL结束的宽字符字符串,表示窗口类名。如果该参数为NULL,则函数仅根据窗口标题查找窗口。
    • lpWindowName:指向一个以NULL结束的宽字符字符串,表示窗口标题。如果该参数为NULL,则函数仅根据窗口类名查找窗口。
  • 返回值
    • 如果函数成功找到匹配的窗口,则返回该窗口的句柄。
    • 如果没有找到匹配的窗口,则返回 NULL 或 0。

4.11 发送消息-同步接口

  • 函数原型
    •   LRESULT WINAPI SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
      
  • 参数
    • hWnd:指向接收消息的窗口的句柄。
    • Msg:一个无符号整数,代表要发送的消息标识符,它可以是预定义的标准Windows消息(如 WM_PAINT、WM_KEYDOWN 等),也可以是自定义的消息(从 WM_USER 开始的范围)。
    • wParam 和 lParam:这两个参数根据具体的消息类型有不同的含义,通常用来携带额外的消息相关数据。

4.12 发送消息-异步接口

  • 函数原型
    •   BOOL WINAPI PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
      
  • 参数
    • hWnd:指向接收消息的窗口的句柄。
    • Msg:一个无符号整数,代表要发送的消息标识符,它可以是预定义的标准Windows消息(如 WM_PAINT、WM_KEYDOWN 等),也可以是自定义的消息(从 WM_USER 开始的范围)。
    • wParam 和 lParam:这两个参数根据具体的消息类型有不同的含义,通常用来携带额外的消息相关数据。
  • 备注
    • SendMessage 和 PostMessage 都是发送消息到消息队列中。但SendMessage必须等消息被处理后才会返回,而PostMessage执行后,不管消息是否被处理都会立即返回。

5 编程示例-处理系统消息

  • 创建工程时需要创建一个Win32项目
  • 源代码
  •   #include <windows.h>
    
      // 定义窗口过程函数
      LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
      {
      	switch (msg)
      	{
      	case WM_DESTROY:
      		//处理窗口销毁消息
      		PostQuitMessage(0); // 当窗口被销毁时发送WM_QUIT消息
      		break;
      	case WM_LBUTTONDOWN:
      		// 处理鼠标左键按下消息
      		MessageBox(NULL, L"Mouse Left Press", L"Info", MB_OK);
      		break;
      	case WM_KEYDOWN:
      		// 处理键盘按下消息
      		{
      			UINT keyCode = (UINT)wParam;
      			if (keyCode == VK_RETURN) {
      				// 回车键按下
      				MessageBox(NULL, L"Enter Press", L"Info", MB_OK);
      			}
      		}
      		break;
      	case WM_CHAR:
      		// 处理键盘输入的字符
      		{
      			UINT keyCode = (UINT)wParam;
      			if (keyCode == 0x41) {
      				//	大写字母A按下
      				MessageBox(NULL, L"A Press", L"Info", MB_OK);
      			}
      		}
      		break;
      	}
      	// 其他消息转交默认窗口过程处理
      	return DefWindowProc(hwnd, msg, wParam, lParam);
      }
      
      int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
      {
      	WNDCLASSW wc;
      
      	// 初始化窗口类结构体
      	wc.style = CS_HREDRAW | CS_VREDRAW;
      	wc.lpfnWndProc = WndProc;
      	wc.cbClsExtra = 0;
      	wc.cbWndExtra = 0;
      	wc.hInstance = hInstance;
      	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 
      	wc.lpszMenuName = NULL; // 如果没有菜单,则设为NULL
      	wc.lpszClassName = L"MyWndClass"; 
      
      	// 注册窗口类
      	if (!RegisterClass(&wc))
      	{
      		MessageBox(NULL, L"Failed to register window class.", L"Error", MB_OK | MB_ICONERROR);
      		return 0;
      	}
      
      	// 创建窗口
      	HWND hWnd = CreateWindowW(L"MyWndClass", L"Wnd", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
      	if (!hWnd)
      	{
      		MessageBox(NULL, L"Failed to create window.", L"Error", MB_OK | MB_ICONERROR);
      		return 0;
      	}
      
      	// 显示并更新窗口
      	ShowWindow(hWnd, nCmdShow);
      	UpdateWindow(hWnd);
      
      	// 进入消息循环
      	MSG msg;
      	while (GetMessage(&msg, NULL, 0, 0))
      	{
      		// 翻译消息
      		TranslateMessage(&msg);
      		// 分发消息(由窗口过程函数处理)
      		DispatchMessage(&msg);
      	}
      
      	return msg.wParam;
      }
    
  • 效果演示
    在这里插入图片描述

6 编程示例-发送和处理自定义消息

  • Win32编程时不仅可以处理系统消息,还可以发送和处理自定义消息。
  • 我们在进程A中创建一个窗口,并处理对应的自定义消息。在进程B中,查找进程A窗口,并向进程A发送自定义消息,实现两个窗口之间的通信。
  • 进程A
  •   #include <windows.h>
      
      // 定义自定义消息
      #define WM_MYCUSTOMMESSAGE (WM_USER + 100)
      
      // 定义窗口过程函数
      LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
      {
      	switch (msg)
      	{
      	case WM_MYCUSTOMMESSAGE:
      		// 处理自定义消息
      		MessageBox(NULL, L"WM_MYCUSTOMMESSAGE", L"Info", MB_OK);
      		break;
      	case WM_DESTROY:
      		//处理窗口销毁消息
      		PostQuitMessage(0); // 当窗口被销毁时发送WM_QUIT消息
      		break;
      	case WM_LBUTTONDOWN:
      		// 处理鼠标左键按下消息
      		MessageBox(NULL, L"Mouse Left Press", L"Info", MB_OK);
      		break;
      	case WM_KEYDOWN:
      		// 处理键盘按下消息
      	{
      		UINT keyCode = (UINT)wParam;
      		if (keyCode == VK_RETURN) {
      			// 回车键按下
      			MessageBox(NULL, L"Enter Press", L"Info", MB_OK);
      		}
      	}
      	break;
      	case WM_CHAR:
      		// 处理键盘输入的字符
      	{
      		UINT keyCode = (UINT)wParam;
      		if (keyCode == 0x41) {
      			//	大写字母A按下
      			MessageBox(NULL, L"A Press", L"Info", MB_OK);
      		}
      	}
      	break;
      	}
      	// 其他消息转交默认窗口过程处理
      	return DefWindowProc(hwnd, msg, wParam, lParam);
      }
      
      int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
      {
      	WNDCLASSW wc;
      
      	// 初始化窗口类结构体
      	wc.style = CS_HREDRAW | CS_VREDRAW;
      	wc.lpfnWndProc = WndProc;
      	wc.cbClsExtra = 0;
      	wc.cbWndExtra = 0;
      	wc.hInstance = hInstance;
      	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
      	wc.lpszMenuName = NULL; // 如果没有菜单,则设为NULL
      	wc.lpszClassName = L"MyWndClass";
      
      	// 注册窗口类
      	if (!RegisterClass(&wc))
      	{
      		MessageBox(NULL, L"Failed to register window class.", L"Error", MB_OK | MB_ICONERROR);
      		return 0;
      	}
      
      	// 创建窗口
      	HWND hWnd = CreateWindowW(L"MyWndClass", L"Wnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
      	if (!hWnd)
      	{
      		MessageBox(NULL, L"Failed to create window.", L"Error", MB_OK | MB_ICONERROR);
      		return 0;
      	}
      
      	// 显示并更新窗口
      	ShowWindow(hWnd, nCmdShow);
      	UpdateWindow(hWnd);
      
      	// 进入消息循环
      	MSG msg;
      	while (GetMessage(&msg, NULL, 0, 0))
      	{
      		// 翻译消息
      		TranslateMessage(&msg);
      		// 分发消息(由窗口过程函数处理)
      		DispatchMessage(&msg);
      	}
      
      	return msg.wParam;
      }
    
    
  • 进程B
  •   #include <windows.h>
      #include <stdio.h>
      // 定义自定义消息,ID值必须和进程A中的一致
      #define WM_MYCUSTOMMESSAGE (WM_USER + 100)
      
      int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
      	// 找到进程A的窗口句柄
      	HWND hwndTarget = FindWindow(TEXT("MyWndClass"), TEXT("Wnd"));
      	if (!hwndTarget) {
      		MessageBox(NULL, L"Failed to find target window.", L"Error", MB_OK | MB_ICONERROR);
      		return -1;
      	}
      
      	// 发送自定义同步消息
      	SendMessage(hwndTarget, WM_MYCUSTOMMESSAGE, 0, 0);
      	return 0;
      }
    
  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: libevent是一个开源的C语言网络编程库,主要用于处理高并发网络连接。它提供了对事件驱动的支持,使得开发者可以方便地编写高效的并发网络应用程序。 libevent的核心是事件循环机制。在传统的网络编程中,通常需要使用多线程或多进程来处理并发连接,而使用libevent可以通过一个事件循环来处理多个连接。在事件循环中,可以注册多个事件,并定义回调函数来处理事件的触发。当有事件发生时,libevent会调用相应的回调函数来处理事件的处理逻辑。这样可以大大简化并发编程的复杂性,并提高程序的性能。 libevent的事件模型基于操作系统提供的I/O多路复用机制,如select、poll和epoll等。它可以在不同的操作系统平台上运行,并提供一致的接口和高效的事件处理机制。借助这些机制,libevent可以同时处理大量的并发连接,并保持低延迟和高吞吐量。 除了处理网络连接,libevent还提供了其他常用的功能,如定时器和信号处理等。它允许开发者在事件循环中注册定时器事件,可以用于定时任务的调度。同时,libevent还可以处理来自操作系统的信号,并提供了对信号的处理接口,以便开发者能够处理各种系统事件。 总之,libevent是一个功能强大、简单易用的高并发网络编程库,适用于开发各种类型的网络应用。无论是开发服务器、代理、聊天程序还是实时应用,libevent都能帮助开发者快速编写高性能的并发网络程序。 ### 回答2: libevent是一个开源的C/C++网络库,用于高性能的事件驱动编程。它提供了一个轻量级、可移植的框架,用于开发高并发的网络应用程序。 它的设计目标是提供一个高效的事件处理器,可以处理成千上万个并发连接,并且支持多线程并发处理。libevent基于事件驱动模型,通过异步I/O和回调函数来实现高并发处理网络请求。 libevent提供了一系列的函数来注册和监听各种网络事件,包括读、写、超时和信号等等。当一个事件发生时,libevent会调用相应的回调函数来处理事件。通过这种方式,我们可以非常方便地处理并发连接,并实现高性能的网络编程。 libevent的优点主要包括: 1. 高性能:libevent使用异步I/O和事件驱动模型,能够处理成千上万个并发连接,具有很高的处理能力。 2. 可移植性:libevent提供了统一的接口,可以在多种操作系统上运行,包括Linux、Windows、Mac等。 3. 易用性:libevent简单易用,只需注册感兴趣的事件和相应的回调函数,就可以实现高效的网络编程。 4. 多线程支持:libevent支持多线程并发处理,可以充分利用多核CPU的性能优势。 总之,libevent是一款非常适合高并发网络编程的开源库,它可以帮助我们实现高性能的服务器程序,提升系统的并发处理能力。无论是开发网络服务器还是网络应用程序,libevent都是一个不错的选择。 ### 回答3: libevent 是一个用于高并发网络编程的 C/C++ 库。它提供了一个跨平台的异步事件驱动的网络编程框架,能够实现高效地处理大量并发连接的需求。 libevent 的主要特点包括: 1. 异步事件驱动:libevent 使用事件驱动模型,主要利用非阻塞 I/O 和事件回调机制,能够高效地处理大量并发事件。 2. 跨平台支持:libevent 提供了跨不同操作系统的支持,包括 Windows、Linux、Unix 等,并且提供了统一的 API 接口,方便开发者进行跨平台开发。 3. 支持多种网络协议:libevent 支持 TCP、UDP、HTTP 等多种网络协议,为开发者提供了丰富的网络编程能力。 4. 高性能:libevent 的设计目标之一是高性能,它通过使用多路复用技术,将系统资源高效地利用起来,能够同时处理大量并发连接,并且保持低延迟。 5. 灵活易用:libevent 提供了简洁的 API,使用起来非常方便,可以快速实现高并发网络编程的需求。 总之,libevent 是一个强大而灵活的 C/C++ 库,适用于各种需要处理高并发连接的网络应用程序。无论是开发高性能服务器、代理、负载均衡器还是其他类似应用,libevent 都是一个值得推荐的选择。它的高效性能、跨平台支持和简洁易用的 API 接口使得开发者能够快速构建稳定可靠的高并发网络应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值