首页 > 解决方案 > 在运行时创建事件处理程序而不使用 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 一样处理整个消息。我不想要一个处理整个窗口消息并跳过其中一些消息的处理程序。可能这会造成性能或内存泄漏问题。

编辑:来自答案的实施示例:

#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();
    ...

标签: c++winapiwndprocsetwindowshookex

解决方案


消息 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);
}

由于我们只是在 an 中存储指向函数的指针std::unordered_map,因此添加、查找或删除处理程序都只需使用正常操作来添加、查找或删除 an 中的某些内容std::unodered_map(例如,从映射messageHandlers.erase(WM_CREATE);中删除处理程序)。WM_CREATE

如果您想对此进行更详细的说明,您可以创建特定类型来处理不同的消息,因此(例如)一个没有收到任何有意义的消息的消息lParam根本不会收到lParam,而另一个接收两个消息“smooshed”在一起,一个在 的低位词中lParam,另一个在 的高位词中lParam可以将它们分成两个单独的参数。但这还有很多工作要做。

您可能还想查找 WindowsX.h,这是 Microsoft 在 SDK 中提供(或至少用于提供)的头文件,它处理映射有点像我上面概述的(后一个版本,其中每个处理程序接收代表它接收的逻辑数据,而不是用于对数据进行编码的WPARAM和。LPARAM


推荐阅读