news 2026/9/7 19:20:36

WIN32基础

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WIN32基础

1、VS2026生成的默认Win32程序

// HelloWindows.cpp : 定义应用程序的入口点。 // #include "framework.h" #include "HelloWindows.h" #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此处放置代码。 // 初始化全局字符串 LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_HELLOWINDOWS, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HELLOWINDOWS)); MSG msg; // 主消息循环: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } // 函数: MyRegisterClass() // 目标: 注册窗口类。 ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOWINDOWS)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_HELLOWINDOWS); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex); } // 函数: InitInstance(HINSTANCE, int) // 目标: 保存实例句柄并创建主窗口 // 注释: // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 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; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // 目标: 处理主窗口的消息。 // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { 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; BeginPaint(hWnd, &ps); // TODO: 在此处添加任何绘图代码... EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // “关于”框的消息处理程序。 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }

1.1、消息队列和消息循环

Windows程序运行是由消息驱动的,系统为每一个GUI线程创建消息队列。程序通过一个循环等待消息,获取消息后,DispatchMessage函数将消息发送到对应的窗口函数进行处理。

(1)消息

消息是一种数据结构,包括接收消息窗口的句柄,消息标识符和相关的附加信息

typedef struct tagMSG { HWND hwnd; //收消息的窗口的句柄 UINT message; //消息的标识符。 WPARAM wParam; //消息的附加信息,WORD类型参数 LPARAM lParam; //消息的附加信息,LONG类型参数 DWORD time; //消息的发布时间。 POINT pt; //发布消息时的光标位置 #ifdef _MAC DWORD lPrivate; #endif } MSG

(2)消息队列

Win32的消息队列从系统架构层面可分为‌两大类‌,系统为每个 GUI 线程维护一个系统消息队列和一个特定于线程的消息队列。

系统消息队列‌:由Windows操作系统全局维护,统一存放鼠标、键盘等硬件输入产生的原始消息,是所有应用程序输入消息的第一入口。

线程(应用程序)消息队列‌:属于每个GUI线程独立维护的私有队列,系统会将对应线程的消息从系统队列转发至此,供线程的消息循环读取处理。

此外也可按消息处理逻辑,将消息分为队列消息非队列消息两类,二者的流转路径存在明显差异‌。

  1. ‌队列消息 (Queued Messages)‌:由 Windows 放入‌线程消息队列‌,通过消息循环(GetMessage/PeekMessage + DispatchMessage)按‌先进先出‌顺序异步分发给窗口过程;多为硬件事件触发,支持跨线程投递。‌典型代表‌:鼠标/键盘输入(WM_MOUSEMOVE, WM_KEYDOWN)、WM_PAINT、WM_TIMER、WM_QUIT 。
  2. ‌非队列消息 (Non-Queued Messages)‌:‌绕过消息队列‌,由系统直接同步调用目标窗口的窗口过程(WndProc),处理完毕才返回;多由特定 API 调用或窗口状态变更触发。‌典型代表‌:WM_CREATE、WM_DESTROY、WM_SIZE、WM_ACTIVATE、WM_SETFOCUS、WM_WINDOWPOSCHANGED 。

PostMessage发送的是‌队列消息‌(异步,立即返回);SendMessage发送的是‌非队列消息‌(同步,阻塞等待结果,同线程内不入队,跨线程时挂起发送者)。

Windows不会在创建线程时就为其分配消息队列,所有线程初始状态下都没有消息队列,以此避免非GUI线程的不必要资源开销。系统会在‌该线程首次调用特定的USER或GDI类Win32用户态图形函数时‌,才为其创建专属的线程级消息队列,将该线程标记为GUI线程。部分场景下,线程首次创建窗口(调用CreateWindow系列API)时也会触发该创建逻辑,这是因为创建窗口的底层实现本身就依赖相关USER函数调用。

以下是创建一个标准 GUI 线程的完整步骤和核心代码逻辑:

核心步骤

  1. ‌创建普通线程‌:使用 CreateThread(或 _beginthreadex)创建一个新线程。
  2. ‌触发消息队列创建‌:在线程入口函数中,调用任意一个 USER 模块的 API(最典型的是 PeekMessage 或 CreateWindow)。
  3. ‌建立消息循环‌:编写标准的 while(GetMessage/PeekMessage) 循环来分发和处理消息。
  4. ‌创建窗口(可选但常见)‌:在消息循环之前或之中,调用 CreateWindowEx 创建属于该线程的窗口。

(3)消息循环

MSG msg; // 主消息循环: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); //DispatchMessage 会将窗口句柄、消息标识符 //以及两个消息参数传递给窗口过程,但不会传递消息的 //投递时间或鼠标光标位置。应用程序可以在处理消息时, //通过调用GetMessageTime 和 GetMessagePos 函数来获取这些信息。 } }

1.1.1 获取消息

BOOL GetMessage( [out] LPMSG lpMsg, [in, optional] HWND hWnd, //要检索其消息的窗口的句柄。 窗口必须属于当前线程。 [in] UINT wMsgFilterMin, [in] UINT wMsgFilterMax );

从调用该函数的线程中消息队列中获取一个消息,如果队列为空则一直等待。

If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.

If hWnd is -1, GetMessage retrieves only messages on the current thread's message queue whose hwnd value is NULL, that is, thread messages as posted by PostMessage (when the hWnd parameter is NULL) or PostThreadMessage.

当接收到WM_QUIT消息时函数返回0,退出消息循环,其他消息返回非0值。发生错误时返回-1。

BOOL PeekMessageA( [out] LPMSG lpMsg, [in, optional] HWND hWnd, [in] UINT wMsgFilterMin, [in] UINT wMsgFilterMax, [in] UINT wRemoveMsg );

PeekMessage与GetMessage类似,用于检查消息队列中是否有消息,没有消息时直接返回,有消息时提取消息(根据wRemoveMsg中的PM_NOREMOVE,PM_REMOVE指定是否从队列中移除消息)。通常可以在空闲期间进行额外的处理,队列有消息时返回TRUE,无消息时返回FALSE。该函数不会产生阻塞。消息循环变成:

MSG msg; while(TRUE){ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { OnIdle(); } }

1.1.2转换快捷键消息

int TranslateAcceleratorA( [in] HWND hWnd, [in] HACCEL hAccTable, [in] LPMSG lpMsg );

处理菜单命令的快捷键。 该函数将 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息转换为 WM_COMMAND 或 WM_SYSCOMMAND 消息(如果指定快捷键表中有项),然后将 WM_COMMAND 或 WM_SYSCOMMAND 消息直接发送到指定的窗口过程。TranslateAccelerator 在窗口过程处理消息之前不会返回。

1.1.3 转换消息

BOOL TranslateMessage( [in] const MSG *lpMsg );

将虚拟按键消息转换为字符消息。字符消息将发布到调用线程的消息队列,以便下次线程调用 GetMessage 或 PeekMessage 函数时读取(增加一个字符消息)。

1.1.4 分发消息

LRESULT DispatchMessage( [in] const MSG *lpMsg );

将消息调度到窗口过程,将消息传给操作系统,由操作系统调用窗口函数。 它通常用于调度 GetMessage 函数检索到的消息。如果 lpmsg 参数指向WM_TIMER消息,并且WM_TIMER消息的 lParam 参数不为 NULL,则 lParam 指向调用的函数,而不是窗口过程。
DispatchMessage 函数在窗口过程处理完消息之前不会返回。

1.2、创建窗口

(1)注册窗口类

创建窗口前要先注册窗口类。窗口类和窗口的关系类似C++中类和对象的关系,基于一个窗口类可以创建多个窗口。窗口类的数据结构如下:

typedef struct tagWNDCLASSEXW { UINT cbSize; UINT style; //窗口类样式 WNDPROC lpfnWndProc; //指向窗口消息处理函数的指针。 int cbClsExtra; //为窗口类分配的额外字节数 int cbWndExtra; //为窗口类实例分配的额外字节数 HINSTANCE hInstance; //包含类的窗口过程的实例的句柄。 HICON hIcon; //窗口图标 HCURSOR hCursor; //窗口光标 HBRUSH hbrBackground; //窗口背景画刷 LPCWSTR lpszMenuName; //窗口菜单字符串 LPCWSTR lpszClassName; //窗口类的名字 HICON hIconSm; //窗口小图标 } WNDCLASSEXW, *PWNDCLASSEXW, *NPWNDCLASSEXW, *LPWNDCLASSEXW;

窗口类样式定义所有窗口类对象共有的样式,主要有:CS_DBLCLKS,向窗口发送双击消息;CS_HREDRAW,CS_VREDRAW如果移动或大小调整更改了工作区的宽度或高度,则重新绘制整个窗口;CS_OWNDC,为 类中的每个窗口分配唯一的设备上下文;CS_CLASSDC分配一个设备上下文,以便类中的所有窗口共享。

注册一个窗口类,以便在调用 CreateWindow 或 CreateWindowEx 函数时使用。

ATOM RegisterClassExW( [in] const WNDCLASSEXW *unnamedParam1 );

(2)创建窗口

HWND CreateWindowW( [in, optional] LPCWSTR lpClassName, //窗口类名称 [in, optional] LPCWSTR lpWindowName, //窗口名称。 如果窗口样式指定标题栏,lpWindowName 指向的窗口标题将显示在标题栏中。 [in] DWORD dwStyle, //窗口样式 [in] int x, //窗口的初始水平位置。CW_USEDEFAULT 使用默认值 [in] int y, [in] int nWidth, //窗口的宽度,CW_USEDEFAULT 使用默认值 [in] int nHeight, [in, optional] HWND hWndParent, //父窗口,指定则变成子窗口 [in, optional] HMENU hMenu, //窗口菜单 [in, optional] HINSTANCE hInstance, [in, optional] LPVOID lpParam );

hMenu标识要与窗口一起使用的菜单;如果要使用类菜单,它可以 NULL

窗口样式,常用的窗口样式有:

WS_CHILDWINDOW:与WS_CHILD意义相同,表示创建一个子窗口。子窗口不能有菜单条,hMenu作为一个整数来标识不同的子窗口。并且不能同时指定WS_POPUP风格。

WS_OVERLAPPED:创建一个重叠式窗口,拥有标题栏和边框。WS_OVERLAPPEDWINDOW:WS_OVERLAPPED 风格、WS_CAPTION 风格、WS_SYSMENU风格 WS_THICKFRAME 风格、WS_MINIMIZEBOX 风格和WS_MAXIMIZEBOX风格的组合。

WS_POPUP:创建一个弹出式窗口。不能同时指WS_CHILD风格。

WS_CAPTION:创建的窗口拥有标题栏(其中包含了WS_BORDER风格)。

WS_SYSMENU:创建的窗口拥有标题栏菜单。必须同时指定WS_CAPTION风格。

WS_BORDER:创建的窗口拥有边框。

WS_SIZEBOX:创建的窗口拥有边框,可变大小。

WS_THICKFRAME:与WS_SIZEBOX风格相同。

WS_VSCROLL:创建的窗口拥有垂直滚动条。

WS HSCROLL:创建的窗口拥有水平滚动条。

WS_MAXIMIZE:创建的窗口初始为最大化。

WS_MAXIMIZEBOX:创建的窗口拥有最大化按钮。必须同时指定WS_SYSMENU风格。

WS_MINIMIZE:创建的窗口初始为最小化。

WS_MINIMIZEBOX:创建的窗口拥有最小化按钮。必须同时指定WS_SYSMENU风格。

WS_TABSTOP:当用户单击Tab键时,所创建的窗口可以接收键盘输入焦点。

WS_VISIBLE:创建的窗口初始化为可见。可以通过ShowWindow函数来改变窗口的可见性。

WS_DISABLED:创建的窗口初始化为不可用。可以通过EnableWindow函数来改变窗口的可用性。不可用的窗口不接受任何用户输入。

窗口可大致分为:重叠窗口,子窗口,弹出窗口,仅消息处理窗口等几类。一个顶层窗口的非用户区就是由许多子窗口构成的,如标题栏、状态栏,最大化按钮等。

最后一个参数 ‌lpParam‌ 的主要用途是‌传递自定义的创建数据‌。数据透传至WM_CREATE,lpParam是一个LPVOID类型的指针,指向应用程序定义的任意数据。当CreateWindow被调用时,系统会向窗口的过程函数(Window Procedure)发送 ‌WM_CREATE‌ 消息。常见用途:在面向对象编程中,通常需要将窗口消息路由到特定的 C++ 类实例方法中。由于 Windows 回调函数必须是静态的或全局的,无法直接访问非静态成员变量,因此常用lpParam传递this指针。

// 创建窗口时传递 this 指针 HWND hWnd = CreateWindow( szClassName, L"Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, this // lpParam: 传递当前类实例指针 ); // 在窗口过程中接收 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_CREATE) { CREATESTRUCT* cs = (CREATESTRUCT*)lParam; MyClass* pApp = (MyClass*)cs->lpCreateParams; // 获取传递的 this 指针 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pApp); // 保存以便后续使用 } // ... 其他消息处理 }

(3)显示窗口

BOOL ShowWindow( [in] HWND hWnd, [in] int nCmdShow );

设置指定窗口的显示状态。SW_RESTORE,SW_HIDE,SW_SHOW,SW_MAXIMIZE,SW_MINIMIZE等。不进入消息队列,直接调用窗口处理函数?

BOOL UpdateWindow( [in] HWND hWnd );

如果窗口的更新区域不为空,UpdateWindow函数通过向窗口发送 WM_PAINT 消息来更新指定窗口的工作区。函数绕过应用程序队列,将 WM_PAINT 消息直接发送到指定窗口的窗口过程。 如果更新区域为空,则不发送任何消息。

窗口处理函数

默认的窗口处理函数,收到WM_CLOSE消息,调用DestroyWindow销毁窗口拥有的资源,向窗口发送WM_DESTROY消息,通常处理此消息时调用PostQuitMessage,发送WM_QUITx消息结束消息循环。

Windows程序的生命周期

1.主线程调用CreateWindow,创建一个窗口,作为程序的主窗口。CreateWindow 产生窗口后以WM _CREATE消息直接调用窗口函数,后者于是可以在此时做些初始化操作(例如配置内存、打开文件、读初始数据.....)。
2.在程序运行的过程中,不断以GetMessage从消息队列中获取消息。如果这个消息是WM_QUIT,GetMessage会返回0从而结束while循环,进而结束整个程序。
3. DispatchMessage 通过 Windows USER模块的协助与监督,把消息分派至窗口函数。消息将在该处被判别并处理。
4.程序不断进行2.和3.的操作。
5.当使用者按下系统菜单中的Close命令项时,系统送出WM_CLOSE。通常程序的窗口函数不处理此消息,于是DefWindowProc 处理它。
6. DefWindowProc 收到 WM_CLOSE后,调用 DestroyWindow把窗口销毁,然后发送WM_DESTROY消息。
7.程序对 WM_DESTROY 的标准反应是调用 PostQuitMessage。
8. PostQuitMessage 没什么其它操作,只发送 WM_QUIT 消息,步骤2消息循环中的GetMessage获取该消息后结束消息循环。

1.3、对话框

对话框是一种窗口,对话框可以包含许多子窗口,例如输入框,按钮,滚动条等,这些都是系统提供的、已经具备某些特定功能的子窗口,称为常用控件。系统已经预定义了常用控件的窗口类,使用时不再需要注册窗口类。在除对话框以外的窗口中使用常用控件,仍然需要使用CreateWindow创建它们。

父窗口与常用控件间的通信:

当用户与控件进行交互时,控件会将有关的交互信息以通知消息的形式发送给父窗口,这种消息在Windows 中定义为 WM_COMMAND消息,其wParam 和 IParam参数的意义如下: LOWORD(wParam) 控件ID

HIWORD(wParam) 通知码

IParam 控件句柄

Windows提供了多种预定义的通知码,每一种常用控件都有不同的通知码。

反之,父窗口也可以向控件发送消息使其执行指定的操作或返回指定的当前状态。例如,语句:

hFont = SendMessage( hwndEdit, WM_GETFONT, 0, OL );

用于获得编辑框控件在显示文本时所使用的字体句柄。而语句:

SendMessage( hwndEdit, WM_SETFONT,(WPARAM)hFont, 0L);用于设置控件字体。

(1)模态对话框

void DialogBoxW( [in, optional] hInstance, [in] lpTemplate, [in, optional] hWndParent, [in, optional] lpDialogFunc );

DialogBox根据对话框模板开启对话框,指定父窗口和对话框消息处理函数。

BOOL EndDialog( [in] HWND hDlg, [in] INT_PTR nResult );

EndDialog不会立即销毁对话框。 相反,它设置一个标志,并允许对话框过程将控制权返回到系统。 在尝试从应用程序队列检索下一条消息之前,系统会检查标志。 如果设置了标志,系统将结束消息循环,销毁对话框,并使用nResult中的值作为创建对话框的函数的返回值。

典型应用:"关于"窗口、确认删除提示、必须用户立即响应的警告框。

模态对话框内部维护一个专用的消息循环。自动处理对话框特有的键盘消息(如Tab键切换焦点、Enter键触发默认按钮、Esc键取消等)。

函数内部自行检索和分发消息,直到对话框关闭才返回。

对话框消息处理函数返回true表示已经处理了该消息,返回false表示未处理该消息,由系统自动调用DefDlgPro函数进行处理。通常只处理WM_INITDIALOG消息和WM_COMMAND消息。

对话框的每个控件也是一个窗口,有窗口类,窗口处理函数,消息循环。这些由系统管理,控件通过向对话框发送WM_COMMAND消息与对话框进行通信。

(2)非模态对话框

通常包含标题栏和系统菜单栏,常用的窗口样式为WS_POPUP WS_CAPTION WS_SYSMENU WS_VISIBLE,如果不包括WS_VISIBLE,则需要调用ShowWindow才能显示对话框。

CreateDialog创建,DestroyWindow结束对话框。非模式对话框的消息要经过主线程的消息循环,使用IsDialogMessage将属于该对话框的消息发送给对话框的窗口处理函数。

void CreateDialogA( [in, optional] hInstance, [in] lpName, [in, optional] hWndParent, [in, optional] lpDialogFunc );

CreateDialog 函数使用 CreateWindowEx 函数来创建对话框。 CreateDialog 然后向对话框过程发送 WM_INITDIALOG 消息。

典型应用:查找/替换窗口、属性面板、工具箱、浮动调色板。

2、从启动程序到单击关闭按钮

记录消息的调用,增加函数TraceWindowsMessage,用于打印收到的消息类型。

#define TRACE_ID(iD) case iD: return L#iD; std::wstring WINAPI TraceWindowsMessage(UINT uMsg) { switch (uMsg) { TRACE_ID(WM_NULL); TRACE_ID(WM_CREATE); TRACE_ID(WM_DESTROY); TRACE_ID(WM_MOVE); TRACE_ID(WM_SIZE); TRACE_ID(WM_ACTIVATE); TRACE_ID(WM_SETFOCUS); TRACE_ID(WM_KILLFOCUS); TRACE_ID(WM_ENABLE); TRACE_ID(WM_SETREDRAW); TRACE_ID(WM_SETTEXT); TRACE_ID(WM_GETTEXT); TRACE_ID(WM_GETTEXTLENGTH); TRACE_ID(WM_PAINT); TRACE_ID(WM_CLOSE); TRACE_ID(WM_QUERYENDSESSION); TRACE_ID(WM_QUERYOPEN); TRACE_ID(WM_ENDSESSION); TRACE_ID(WM_QUIT); TRACE_ID(WM_ERASEBKGND); TRACE_ID(WM_SYSCOLORCHANGE); TRACE_ID(WM_SHOWWINDOW); TRACE_ID(WM_WININICHANGE); TRACE_ID(WM_DEVMODECHANGE); TRACE_ID(WM_ACTIVATEAPP); TRACE_ID(WM_FONTCHANGE); TRACE_ID(WM_TIMECHANGE); TRACE_ID(WM_CANCELMODE); TRACE_ID(WM_SETCURSOR); TRACE_ID(WM_MOUSEACTIVATE); TRACE_ID(WM_CHILDACTIVATE); TRACE_ID(WM_QUEUESYNC); TRACE_ID(WM_GETMINMAXINFO); TRACE_ID(WM_PAINTICON); TRACE_ID(WM_ICONERASEBKGND); TRACE_ID(WM_NEXTDLGCTL); TRACE_ID(WM_SPOOLERSTATUS); TRACE_ID(WM_DRAWITEM); TRACE_ID(WM_MEASUREITEM); TRACE_ID(WM_DELETEITEM); TRACE_ID(WM_VKEYTOITEM); TRACE_ID(WM_CHARTOITEM); TRACE_ID(WM_SETFONT); TRACE_ID(WM_GETFONT); TRACE_ID(WM_SETHOTKEY); TRACE_ID(WM_GETHOTKEY); TRACE_ID(WM_QUERYDRAGICON); TRACE_ID(WM_COMPAREITEM); TRACE_ID(WM_GETOBJECT); TRACE_ID(WM_COMPACTING); TRACE_ID(WM_COMMNOTIFY); TRACE_ID(WM_WINDOWPOSCHANGING); TRACE_ID(WM_WINDOWPOSCHANGED); TRACE_ID(WM_POWER); TRACE_ID(WM_COPYDATA); TRACE_ID(WM_CANCELJOURNAL); TRACE_ID(WM_NOTIFY); TRACE_ID(WM_INPUTLANGCHANGEREQUEST); TRACE_ID(WM_INPUTLANGCHANGE); TRACE_ID(WM_TCARD); TRACE_ID(WM_HELP); TRACE_ID(WM_USERCHANGED); TRACE_ID(WM_NOTIFYFORMAT); TRACE_ID(WM_CONTEXTMENU); TRACE_ID(WM_STYLECHANGING); TRACE_ID(WM_STYLECHANGED); TRACE_ID(WM_DISPLAYCHANGE); TRACE_ID(WM_GETICON); TRACE_ID(WM_SETICON); TRACE_ID(WM_NCCREATE); TRACE_ID(WM_NCDESTROY); TRACE_ID(WM_NCCALCSIZE); TRACE_ID(WM_NCHITTEST); TRACE_ID(WM_NCPAINT); TRACE_ID(WM_NCACTIVATE); TRACE_ID(WM_GETDLGCODE); TRACE_ID(WM_SYNCPAINT); TRACE_ID(WM_NCMOUSEMOVE); TRACE_ID(WM_NCLBUTTONDOWN); TRACE_ID(WM_NCLBUTTONUP); TRACE_ID(WM_NCLBUTTONDBLCLK); TRACE_ID(WM_NCRBUTTONDOWN); TRACE_ID(WM_NCRBUTTONUP); TRACE_ID(WM_NCRBUTTONDBLCLK); TRACE_ID(WM_NCMBUTTONDOWN); TRACE_ID(WM_NCMBUTTONUP); TRACE_ID(WM_NCMBUTTONDBLCLK); TRACE_ID(WM_NCXBUTTONDOWN); TRACE_ID(WM_NCXBUTTONUP); TRACE_ID(WM_NCXBUTTONDBLCLK); TRACE_ID(WM_INPUT); TRACE_ID(WM_KEYDOWN); TRACE_ID(WM_KEYUP); TRACE_ID(WM_CHAR); TRACE_ID(WM_DEADCHAR); TRACE_ID(WM_SYSKEYDOWN); TRACE_ID(WM_SYSKEYUP); TRACE_ID(WM_SYSCHAR); TRACE_ID(WM_SYSDEADCHAR); TRACE_ID(WM_UNICHAR); TRACE_ID(WM_IME_STARTCOMPOSITION); TRACE_ID(WM_IME_ENDCOMPOSITION); TRACE_ID(WM_IME_COMPOSITION); TRACE_ID(WM_INITDIALOG); TRACE_ID(WM_COMMAND); TRACE_ID(WM_SYSCOMMAND); TRACE_ID(WM_TIMER); TRACE_ID(WM_HSCROLL); TRACE_ID(WM_VSCROLL); TRACE_ID(WM_INITMENU); TRACE_ID(WM_INITMENUPOPUP); TRACE_ID(WM_MENUSELECT); TRACE_ID(WM_MENUCHAR); TRACE_ID(WM_ENTERIDLE); TRACE_ID(WM_MENURBUTTONUP); TRACE_ID(WM_MENUDRAG); TRACE_ID(WM_MENUGETOBJECT); TRACE_ID(WM_UNINITMENUPOPUP); TRACE_ID(WM_MENUCOMMAND); TRACE_ID(WM_CHANGEUISTATE); TRACE_ID(WM_UPDATEUISTATE); TRACE_ID(WM_QUERYUISTATE); TRACE_ID(WM_CTLCOLORMSGBOX); TRACE_ID(WM_CTLCOLOREDIT); TRACE_ID(WM_CTLCOLORLISTBOX); TRACE_ID(WM_CTLCOLORBTN); TRACE_ID(WM_CTLCOLORDLG); TRACE_ID(WM_CTLCOLORSCROLLBAR); TRACE_ID(WM_CTLCOLORSTATIC); TRACE_ID(MN_GETHMENU); TRACE_ID(WM_MOUSEMOVE); TRACE_ID(WM_LBUTTONDOWN); TRACE_ID(WM_LBUTTONUP); TRACE_ID(WM_LBUTTONDBLCLK); TRACE_ID(WM_RBUTTONDOWN); TRACE_ID(WM_RBUTTONUP); TRACE_ID(WM_RBUTTONDBLCLK); TRACE_ID(WM_MBUTTONDOWN); TRACE_ID(WM_MBUTTONUP); TRACE_ID(WM_MBUTTONDBLCLK); TRACE_ID(WM_MOUSEWHEEL); TRACE_ID(WM_XBUTTONDOWN); TRACE_ID(WM_XBUTTONUP); TRACE_ID(WM_XBUTTONDBLCLK); TRACE_ID(WM_PARENTNOTIFY); TRACE_ID(WM_ENTERMENULOOP); TRACE_ID(WM_EXITMENULOOP); TRACE_ID(WM_NEXTMENU); TRACE_ID(WM_SIZING); TRACE_ID(WM_CAPTURECHANGED); TRACE_ID(WM_MOVING); TRACE_ID(WM_POWERBROADCAST); TRACE_ID(WM_DEVICECHANGE); TRACE_ID(WM_MDICREATE); TRACE_ID(WM_MDIDESTROY); TRACE_ID(WM_MDIACTIVATE); TRACE_ID(WM_MDIRESTORE); TRACE_ID(WM_MDINEXT); TRACE_ID(WM_MDIMAXIMIZE); TRACE_ID(WM_MDITILE); TRACE_ID(WM_MDICASCADE); TRACE_ID(WM_MDIICONARRANGE); TRACE_ID(WM_MDIGETACTIVE); TRACE_ID(WM_MDISETMENU); TRACE_ID(WM_ENTERSIZEMOVE); TRACE_ID(WM_EXITSIZEMOVE); TRACE_ID(WM_DROPFILES); TRACE_ID(WM_MDIREFRESHMENU); TRACE_ID(WM_IME_SETCONTEXT); TRACE_ID(WM_IME_NOTIFY); TRACE_ID(WM_IME_CONTROL); TRACE_ID(WM_IME_COMPOSITIONFULL); TRACE_ID(WM_IME_SELECT); TRACE_ID(WM_IME_CHAR); TRACE_ID(WM_IME_REQUEST); TRACE_ID(WM_IME_KEYDOWN); TRACE_ID(WM_IME_KEYUP); TRACE_ID(WM_MOUSEHOVER); TRACE_ID(WM_MOUSELEAVE); TRACE_ID(WM_NCMOUSEHOVER); TRACE_ID(WM_NCMOUSELEAVE); TRACE_ID(WM_WTSSESSION_CHANGE); TRACE_ID(WM_TABLET_FIRST); TRACE_ID(WM_TABLET_LAST); TRACE_ID(WM_CUT); TRACE_ID(WM_COPY); TRACE_ID(WM_PASTE); TRACE_ID(WM_CLEAR); TRACE_ID(WM_UNDO); TRACE_ID(WM_RENDERFORMAT); TRACE_ID(WM_RENDERALLFORMATS); TRACE_ID(WM_DESTROYCLIPBOARD); TRACE_ID(WM_DRAWCLIPBOARD); TRACE_ID(WM_PAINTCLIPBOARD); TRACE_ID(WM_VSCROLLCLIPBOARD); TRACE_ID(WM_SIZECLIPBOARD); TRACE_ID(WM_ASKCBFORMATNAME); TRACE_ID(WM_CHANGECBCHAIN); TRACE_ID(WM_HSCROLLCLIPBOARD); TRACE_ID(WM_QUERYNEWPALETTE); TRACE_ID(WM_PALETTEISCHANGING); TRACE_ID(WM_PALETTECHANGED); TRACE_ID(WM_HOTKEY); TRACE_ID(WM_PRINT); TRACE_ID(WM_PRINTCLIENT); TRACE_ID(WM_APPCOMMAND); TRACE_ID(WM_THEMECHANGED); TRACE_ID(WM_HANDHELDFIRST); TRACE_ID(WM_HANDHELDLAST); TRACE_ID(WM_AFXFIRST); TRACE_ID(WM_AFXLAST); TRACE_ID(WM_PENWINFIRST); TRACE_ID(WM_PENWINLAST); TRACE_ID(WM_APP); default: TCHAR buff[20]; ZeroMemory(buff, 20 * sizeof(TCHAR)); wsprintf(buff, L"0x%04x", uMsg); return std::wstring(buff); } } void PrintMsgInfo(HWND hwnd, UINT message) { auto msgStr = TraceWindowsMessage(message); TCHAR buff[100]; ZeroMemory(buff, 100 * sizeof(TCHAR)); wsprintf(buff, L"hwnd: 0x%x , message: %s \r\n", hwnd, msgStr.c_str()); std::wstring info(buff); OutputDebugString(info.c_str()); }

在执行中,增加跟踪消息的函数调用:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // 将实例句柄存储在全局变量中 OutputDebugString(L"before CreateWindowW: \r\n"); HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } OutputDebugString(L"after CreateWindowW: \r\n"); ShowWindow(hWnd, nCmdShow); OutputDebugString(L"after ShowWindow: \r\n"); UpdateWindow(hWnd); OutputDebugString(L"after UpdateWindow: \r\n"); return TRUE; }

消息循环:

MSG msg; // 主消息循环: while (GetMessage(&msg, nullptr, 0, 0)) { OutputDebugString(L"GetMessage : "); PrintMsgInfo(msg.hwnd,msg.message); if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } OutputDebugString(L"Exit GetMessage : "); PrintMsgInfo(msg.hwnd, msg.message);

窗口处理函数:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { OutputDebugString(L"WndProc message: "); PrintMsgInfo(hWnd, message); ...... }

调试窗口的输出如下:

before CreateWindowW: WndProc message: hwnd: 0x1b0802 , message: WM_GETMINMAXINFO WndProc message: hwnd: 0x1b0802 , message: WM_NCCREATE WndProc message: hwnd: 0x1b0802 , message: WM_NCCALCSIZE WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: 0x0094 WndProc message: hwnd: 0x1b0802 , message: 0x0094 WndProc message: hwnd: 0x1b0802 , message: WM_CREATE after CreateWindowW: WndProc message: hwnd: 0x1b0802 , message: WM_SHOWWINDOW WndProc message: hwnd: 0x1b0802 , message: WM_WINDOWPOSCHANGING WndProc message: hwnd: 0x1b0802 , message: WM_WINDOWPOSCHANGING WndProc message: hwnd: 0x1b0802 , message: WM_ACTIVATEAPP WndProc message: hwnd: 0x1b0802 , message: WM_NCACTIVATE WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: WM_GETICON WndProc message: hwnd: 0x1b0802 , message: WM_GETICON WndProc message: hwnd: 0x1b0802 , message: WM_GETICON WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: 0x0091 WndProc message: hwnd: 0x1b0802 , message: 0x0092 WndProc message: hwnd: 0x1b0802 , message: 0x0092 WndProc message: hwnd: 0x1b0802 , message: WM_ACTIVATE WndProc message: hwnd: 0x1b0802 , message: WM_SETFOCUS WndProc message: hwnd: 0x1b0802 , message: WM_NCPAINT WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: 0x0091 WndProc message: hwnd: 0x1b0802 , message: 0x0092 WndProc message: hwnd: 0x1b0802 , message: 0x0092 WndProc message: hwnd: 0x1b0802 , message: WM_ERASEBKGND WndProc message: hwnd: 0x1b0802 , message: WM_WINDOWPOSCHANGED WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: WM_SIZE WndProc message: hwnd: 0x1b0802 , message: WM_MOVE after ShowWindow: WndProc message: hwnd: 0x1b0802 , message: WM_PAINT after UpdateWindow: WndProc message: hwnd: 0x1b0802 , message: WM_GETICON WndProc message: hwnd: 0x1b0802 , message: WM_SYNCPAINT WndProc message: hwnd: 0x1b0802 , message: WM_GETICON GetMessage : hwnd: 0x1b0802 , message: 0x031f WndProc message: hwnd: 0x1b0802 , message: 0x031f WndProc message: hwnd: 0x1b0802 , message: WM_GETICON GetMessage : hwnd: 0x1b0802 , message: 0xc120 WndProc message: hwnd: 0x1b0802 , message: 0xc120 GetMessage : hwnd: 0x2300a6 , message: 0x0465 GetMessage : hwnd: 0x20056e , message: 0x83f1 GetMessage : hwnd: 0x20056e , message: 0x83f1 GetMessage : hwnd: 0x20056e , message: 0x83f1 GetMessage : hwnd: 0x20056e , message: 0x83f1 GetMessage : hwnd: 0xc0784 , message: 0x0060 GetMessage : hwnd: 0x20056e , message: 0x83f0 GetMessage : hwnd: 0x0 , message: WM_TIMER GetMessage : hwnd: 0xc0784 , message: 0x0060 WndProc message: hwnd: 0x1b0802 , message: 0xc053 WndProc message: hwnd: 0x1b0802 , message: WM_IME_REQUEST GetMessage : hwnd: 0xc0784 , message: 0x0060 GetMessage : hwnd: 0x0 , message: WM_TIMER GetMessage : hwnd: 0x0 , message: WM_TIMER GetMessage : hwnd: 0x170526 , message: WM_TIMER WndProc message: hwnd: 0x1b0802 , message: WM_NCHITTEST WndProc message: hwnd: 0x1b0802 , message: WM_SETCURSOR GetMessage : hwnd: 0x1b0802 , message: WM_NCMOUSEMOVE WndProc message: hwnd: 0x1b0802 , message: WM_NCMOUSEMOVE WndProc message: hwnd: 0x1b0802 , message: WM_SETCURSOR GetMessage : hwnd: 0x1b0802 , message: WM_NCLBUTTONDOWN WndProc message: hwnd: 0x1b0802 , message: WM_NCLBUTTONDOWN WndProc message: hwnd: 0x1b0802 , message: WM_CAPTURECHANGED WndProc message: hwnd: 0x1b0802 , message: WM_SYSCOMMAND WndProc message: hwnd: 0x1b0802 , message: WM_CLOSE WndProc message: hwnd: 0x1b0802 , message: 0x0090 WndProc message: hwnd: 0x1b0802 , message: WM_WINDOWPOSCHANGING WndProc message: hwnd: 0x1b0802 , message: WM_WINDOWPOSCHANGED WndProc message: hwnd: 0x1b0802 , message: WM_NCACTIVATE WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: 0x0093 WndProc message: hwnd: 0x1b0802 , message: 0x0091 WndProc message: hwnd: 0x1b0802 , message: 0x0092 WndProc message: hwnd: 0x1b0802 , message: 0x0092 WndProc message: hwnd: 0x1b0802 , message: WM_ACTIVATE WndProc message: hwnd: 0x1b0802 , message: WM_ACTIVATEAPP WndProc message: hwnd: 0x1b0802 , message: WM_KILLFOCUS WndProc message: hwnd: 0x1b0802 , message: WM_IME_SETCONTEXT WndProc message: hwnd: 0x1b0802 , message: WM_IME_NOTIFY WndProc message: hwnd: 0x1b0802 , message: WM_DESTROY WndProc message: hwnd: 0x1b0802 , message: WM_NCDESTROY GetMessage : hwnd: 0x0 , message: 0x0466 GetMessage : hwnd: 0x0 , message: 0x0466 Exit GetMessage : hwnd: 0x0 , message: WM_QUIT

可见,大部分的消息都是直接调用消息处理函数,并不经过消息队列。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 19:20:22

水质监测指标计算与自动化分析实战指南

1. 水质指标计算项目概述水质监测是环境保护工作的基础环节,而指标计算代码则是实现自动化分析的核心工具。这个项目主要解决水质监测数据(包括pH值、浊度等关键参数)的批量处理与达标判定问题。在实际环境监测站工作中,每天需要处…

作者头像 李华
网站建设 2026/9/7 19:20:11

AIGC检测原理与降AI率工具实测:从原理到实操的全流程指南

最近研究生圈子里的高频话题之一,就是“降AI率”。无论是课程论文、小论文还是毕业论文,很多学校已经将AIGC检测纳入评审流程,不少同学在提交前发现自己的内容被判定为“高度疑似AI生成”,明明是自己熬夜码的字,却被一…

作者头像 李华
网站建设 2026/9/7 19:17:58

CSS定位全解:relative、absolute、fixed、sticky

CSS 里的 position,我用坏过无数个布局,也救回来过无数个布局。这玩意儿说到底是 CSS 中最容易“一看就会,一用就废”的属性。网上讲 position 的文章多如牛毛,但大多数都在背文档,真正把 relative、absolute、fixed、…

作者头像 李华
网站建设 2026/9/7 19:17:50

基于Copula的多风场出力相关性分析与场景生成聚类削减

先说结论:这套基于 Copula 函数做多风场出力相关性分析、再配合场景生成与聚类削减的流程,在 MATLAB 里跑通并不复杂,真正难的是每一步的参数选择和结果校验。我实际做完一轮之后最深的感受是——Copula 不是万能药,但只要你把边缘…

作者头像 李华
网站建设 2026/9/7 19:17:45

2.4万亿参数MoE模型部署实战:量化、显存与许可证全解析

谁能想到,有一天“下载模型权重”会变成一件需要先算好半天显存、再等一周硬盘的事。最近大家都在讨论那批刚放出来的开放权重,总参数量到了 2.4 万亿,最小的量化文件也要 397GB。说实话,我第一次看到这个数字也愣了一下——许可证…

作者头像 李华