子按钮以Static控件为父窗口时无法捕获点击消息的问题咨询
Win32控件消息重定向问题解决方法
在Win32开发场景中,当按钮的父窗口设为主窗口hwnd时,点击按钮能正常触发WM_COMMAND消息;但将父窗口改为Static控件hpage1(其父窗口为hwnd)后,主窗口的WndProc无法检测到按钮按下事件。这是因为按钮的WM_COMMAND通知会直接发送给它的直接父窗口hpage1,而系统默认的Static控件窗口过程不会将该消息转发给主窗口。
解决方案:子类化Static控件转发消息
通过子类化Static控件,替换它的窗口过程,让它收到WM_COMMAND时自动将消息转发给主窗口。
修改步骤
- 定义子类窗口过程,处理
WM_COMMAND消息并转发给主窗口,同时在窗口销毁时移除子类避免内存泄漏。 - 创建
hpage1后,调用SetWindowSubclass为其设置自定义的子类过程。
修改后的完整代码
#include <windows.h> #include <commctrl.h> #pragma comment(lib, "comctl32.lib") #define CLIENTS_BUTTON 12 bool isRunning = true; LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK StaticSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&icex); WNDCLASS windowClass = {}; windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = WndProc; windowClass.lpszClassName = L"MAIN_WINDOW"; if (!RegisterClass(&windowClass)) { return 1; } HWND hwnd = CreateWindow(windowClass.lpszClassName, L"Simple Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 510, 345, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nShowCmd); UpdateWindow(hwnd); while (isRunning) { MSG message; while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&message); DispatchMessage(&message); } Sleep(10); } return 0; } LRESULT CALLBACK StaticSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { switch (uMsg) { case WM_COMMAND: // 将WM_COMMAND消息转发给主窗口,dwRefData存储主窗口句柄 SendMessage((HWND)dwRefData, uMsg, wParam, lParam); break; case WM_NCDESTROY: // 窗口销毁时移除子类,防止内存泄漏 RemoveWindowSubclass(hWnd, StaticSubclassProc, uIdSubclass); break; } // 调用原窗口过程处理其他消息 return DefSubclassProc(hWnd, uMsg, wParam, lParam); } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { HWND hpage1 = CreateWindow(L"STATIC", L"Page1", WS_CHILD | WS_VISIBLE, 50, 50, 1000, 1000, hwnd, (HMENU)0, (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL); // 子类化Static控件,传入主窗口句柄作为参考数据 SetWindowSubclass(hpage1, StaticSubclassProc, 1, (DWORD_PTR)hwnd); CreateWindow( L"BUTTON", L"Submit", WS_CHILD | WS_VISIBLE, 50, 100, 60, 20, hpage1, (HMENU)CLIENTS_BUTTON, (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL ); } break; case WM_COMMAND: { if (LOWORD(wParam) == CLIENTS_BUTTON) { MessageBox(hwnd, L"Submit Button Pressed!", L"Debug", MB_OK); } break; } case WM_CLOSE: { isRunning = false; } break; case WM_DESTROY: break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; }
额外说明
原代码的消息循环仅处理主窗口hwnd的消息,修改为PeekMessage(&message, NULL, 0, 0, PM_REMOVE)后,能接收当前线程所有窗口的消息,确保子类化后的Static控件消息能被正常处理。
内容的提问来源于stack exchange,提问作者mal
相关产品推荐
相关产品推荐

