在运行时创建事件处理程序而不使用 WndProc win32 c++
Create event handler at runtime without using WndProc win32 c++
使用 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 一样处理整个消息。我不想要处理整个 window 消息并跳过其中一些消息的处理程序。可能这会造成性能或内存泄漏问题。
编辑:来自 的实施示例:
#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();
...
消息 ID 只是一个无符号整数,因此并没有什么特别之处。尽管巨大的 switch 语句是处理消息的一种常见方式,但如果您愿意,您可以做一些完全不同的事情。要支持动态 insertion/deletion 处理程序,一种可能性是使用 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
中存储指向函数的指针,所以添加、查找或删除处理程序都只需使用正常操作来添加、查找或删除 [=13= 中的内容](例如,messageHandlers.erase(WM_CREATE);
从映射中删除 WM_CREATE
处理程序)。
如果您想对此进行更详细的说明,您可以创建特定类型来处理不同的消息,因此(例如)在其 lParam
中没有收到任何有意义的消息的人根本不会收到一个 lParam
,而另一个接收两个东西的“smooshed”在一起,一个在 lParam
的低字中,另一个在 lParam
的高字中,可以将它们分开分为两个独立的参数。但这还有很多工作要做。
您可能还想寻找 WindowsX.h,一个 header Microsoft 在 SDK 中提供(或至少用于提供)处理映射的方式有点像我上面概述的(后一个版本,其中每个处理程序接收代表它接收的逻辑数据的参数,而不是用于编码该数据的 WPARAM
和 LPARAM
。
您可以像这样动态处理消息:
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;
...
}
}
和add/del句柄:
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);
...
}
使用 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 一样处理整个消息。我不想要处理整个 window 消息并跳过其中一些消息的处理程序。可能这会造成性能或内存泄漏问题。
编辑:来自 的实施示例:
#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();
...
消息 ID 只是一个无符号整数,因此并没有什么特别之处。尽管巨大的 switch 语句是处理消息的一种常见方式,但如果您愿意,您可以做一些完全不同的事情。要支持动态 insertion/deletion 处理程序,一种可能性是使用 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
中存储指向函数的指针,所以添加、查找或删除处理程序都只需使用正常操作来添加、查找或删除 [=13= 中的内容](例如,messageHandlers.erase(WM_CREATE);
从映射中删除 WM_CREATE
处理程序)。
如果您想对此进行更详细的说明,您可以创建特定类型来处理不同的消息,因此(例如)在其 lParam
中没有收到任何有意义的消息的人根本不会收到一个 lParam
,而另一个接收两个东西的“smooshed”在一起,一个在 lParam
的低字中,另一个在 lParam
的高字中,可以将它们分开分为两个独立的参数。但这还有很多工作要做。
您可能还想寻找 WindowsX.h,一个 header Microsoft 在 SDK 中提供(或至少用于提供)处理映射的方式有点像我上面概述的(后一个版本,其中每个处理程序接收代表它接收的逻辑数据的参数,而不是用于编码该数据的 WPARAM
和 LPARAM
。
您可以像这样动态处理消息:
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;
...
}
}
和add/del句柄:
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);
...
}