在运行时创建事件处理程序而不使用 WndProc win32 c++
Posted
技术标签:
【中文标题】在运行时创建事件处理程序而不使用 WndProc win32 c++【英文标题】:Create event handler at runtime without using WndProc win32 c++ 【发布时间】:2021-01-05 05:10:22 【问题描述】:在使用 C# 时,之前很容易在运行时创建事件处理程序,例如:
Button button1 = new button1();
button1.click += Button_Click(); //Create handler
button1.click -= Button_Click(); //Remove handler
public void Button_Click()
//Button clicked
但在 win32 中,我遇到了必须处理所有事件的 WndProc 回调。我想为特定消息创建一个处理程序并将其附加到特定的 void。
目前,我正在使用 WndProc 来捕获 WM_CREATE 消息并绘制控件:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
switch (message)
case WM_CREATE:
Draw(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
return 0;
void Draw(HWND hWnd)
HWND button4 = CreateWindow(L"button", L"button4", WS_CHILD | WS_VISIBLE , 329, 118, 112, 67, hWnd, (HMENU)1001, hInst, NULL);
HWND button3 = CreateWindow(L"button", L"button3", WS_CHILD | WS_VISIBLE , 212, 118, 112, 67,
...
但我想创建或删除事件处理程序,而不是在运行时使用 WndProc,例如:
AddHandler WM_CREATE , Draw(hWnd);
DelHandler WM_CREATE , Draw(hWnd);
我尝试了什么?
SetWindowsHookEx 的问题在于它像 WndProc 一样处理整个消息。我不想要一个处理整个窗口消息并跳过其中一些消息的处理程序。这可能会导致性能或内存泄漏问题。
编辑:来自answer的实现示例:
#include <unordered_map>
using msgHandler = LRESULT(*)(HWND, UINT, WPARAM, LPARAM);
std::unordered_map<UINT, msgHandler> messageHandlers;
LRESULT handleCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
//Draw some buttons to see whether event WM_CREATE called or not
HWND button4 = CreateWindow(L"button", L"button4", WS_CHILD | WS_VISIBLE, 329, 118, 112, 67, hWnd, (HMENU)1001, hInst, NULL);
HWND button3 = CreateWindow(L"button", L"button3", WS_CHILD | WS_VISIBLE, 212, 118, 112, 67, hWnd, (HMENU)1002, hInst, NULL);
HWND button2 = CreateWindow(L"button", L"button2", WS_CHILD | WS_VISIBLE, 329, 46, 112, 67, hWnd, (HMENU)1003, hInst, NULL);
HWND button1 = CreateWindow(L"button", L"button1", WS_CHILD | WS_VISIBLE, 212, 46, 112, 67, hWnd, (HMENU)1004, hInst, NULL);
return 0;
LRESULT handleClose(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
//Quit form
PostQuitMessage(0);
return 0;
void AddHandler()
messageHandlers[WM_CREATE] = handleCreate;
messageHandlers[WM_DESTROY] = handleClose;
void DelHandler()
messageHandlers.erase(WM_CREATE);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
auto handler = messageHandlers.find(msg);
if (handler != messageHandlers.end()) return handler->second(hWnd, msg, wParam, lParam);
return DefWindowProc(hWnd, msg, wParam, lParam);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
AddHandler();
//DelHandler();
...
【问题讨论】:
一种选择是编写自己的 WndProc,它保存一个处理程序表以及它们注册的消息。另一种方法是编写一个 WndProc,为每条消息引发一个事件,并让每个人都注册他们想要处理的事件。 我需要尝试注册特定消息的第一个选项。你能指导我更多吗? 在此示例中,c# 事件处理程序是在编译时创建的。//Create handler
和 //Remove handler
和错误。
也许“原始”win32 不是您所需要的。 C/C++/Native Windows 编程还有其他选项。例如,您可以尝试 MFC docs.microsoft.com/en-us/cpp/mfc/mfc-desktop-applications 它更接近 .NET 事件,或者至少研究它以获取信息。
This answer 填写选项1的详细信息。
【参考方案1】:
消息 ID 只是一个无符号整数,因此并没有什么特别之处。尽管巨大的 switch 语句是处理消息的一种常用方法,但如果您愿意,您可以做完全不同的事情。为了支持处理程序的动态插入/删除,一种可能性是使用std::unordered_map
:
// a message handler receives the normal parameters:
using msgHandler = LRESULT(*)(HWND, UINT, WPARAM, LPARAM);
// a map from message numbers to the handler functions:
std::unordered_map<UINT, msgHandler> messageHandlers;
// A couple of message handler functions:
LRESULT handleCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
// ...
LRESULT handleDraw(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
// ...
// register them to handle the appropriate messages:
messageHandlers[WM_CREATE] = handleCreate;
messageHandlers[WM_PAINT] = handleDraw;
// and then our (now really tiny) window proc that uses those:
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
auto handler = messageHandlers.find(msg);
if (handler != messageHandlers.end())
return handler->second(hWnd, msg, wParam, lParam);
return DefWindowProc(hWnd, msg, wParam, lParam);
由于我们只是在 std::unordered_map
中存储指向函数的指针,因此添加、查找或删除处理程序都只需使用常规操作在 std::unodered_map
中添加、查找或删除某些内容(例如,@987654326 @ 从地图中删除 WM_CREATE
处理程序)。
如果您想对此进行更详细的说明,您可以创建特定类型来处理不同的消息,因此(例如)在其lParam
中没有收到任何有意义的消息的人根本不会收到lParam
根本上,而另一个接收两个“平滑”在一起的东西,一个在lParam
的低位字中,另一个在lParam
的高位字中,可以将它们分成两个单独的参数。但这还有很多工作要做。
您可能还想查找 WindowsX.h,它是 Microsoft 在 SDK 中提供(或至少曾经提供)的头文件,它处理映射有点像我上面概述的(后一个版本,每个处理程序接收参数表示它接收的逻辑数据,而不是用于编码该数据的WPARAM
和LPARAM
。
【讨论】:
return handler->second
是干什么用的?以及如何删除使用 messageHandlers[WM_CREATE]
创建的处理程序?
@GrayProgrammerz:find
查找特定消息的处理程序。返回值是映射中的迭代器,因此handler->first
为您提供刚刚查找的消息 ID,handler->second
为您提供(指向)您为该消息注册的函数。 return handler->second(...);
调用该函数,检索它返回的任何值(如果有)并将其传递回调用者。
至于“你如何删除句柄”:重点是messageHandlers
记录了哪些处理程序负责哪些消息。如果要修改处理程序,请修改messageHandlers
。要添加处理程序,请将其添加到 messageHandlers
。要删除一个,请将其从messageHandlers
中删除。要搜索处理程序,请在 messageHandlers
中搜索。以此类推。
:) 谢谢大家。我试过这段代码,我解决了 1 个错误,第二个是很难。请参阅:i.ibb.co/tYhbQvX/Capture.png
@GrayProgrammerz:这些是赋值语句,所以它们需要在函数内部。【参考方案2】:
您可以像这样动态处理消息:
typedef void (*FHANDLE)();
std::vector<FHANDLE> handles;
void AddHandler(FHANDLE handle)
handles.push_back(handle);
void DelHandler(FHANDLE handle)
auto it = std::find(handles.begin(), handles.end(), handle);
handles.erase(it);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
switch (message)
case WM_CREATE:
for (int i = 0; i < handles.size(); i++)
handles[i]();
break;
...
并添加/删除句柄:
void myclick1()
MessageBox(0, L"test1", L"message", 0);
void myclick2()
MessageBox(0, L"test2", L"message", 0);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
hInst = hInstance; // Store instance handle in our global variable
AddHandler(myclick1);
AddHandler(myclick2);
//DelHandler(myclick2);
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
...
【讨论】:
@raymond-chen 你能看看这个选项是什么吗?这是 ?顺便说一句,它看起来很干净。以上是关于在运行时创建事件处理程序而不使用 WndProc win32 c++的主要内容,如果未能解决你的问题,请参考以下文章
DefWndProc/WndProc/IMessageFilter的区别
WinForm中DefWndProcWndProc与IMessageFilter的区别