gpt4 book ai didi

c++ - WndProc 的类方法

转载 作者:太空狗 更新时间:2023-10-29 20:41:23 30 4
gpt4 key购买 nike

article出色地解释了调用类成员 WndProc 的选项。我看过this response in stackoverflow但是在 CreateWindow 之后关联类成员 WndProc 的主要问题是某些消息将丢失(包括重要的 WM_CREATE),如 the mentioned article 中所述。 .

我的问题:我想听听专家的意见,看看下面公开的哪种方法或新方法是创建类的最佳方法(性能、可维护性……)成员 WndProc。

简述文章中暴露的两个最终解决方案(假设它存在一个带有WndProc方法的Window类):

  1. 具有 this 全局指针存储的每个窗口数据,使用 CRITICAL_SECTION 保护它以使其线程安全(从 here 中提取):

    // The helper window procedure
    // It is called by Windows, and thus it's a non-member function
    // This message handler will only be called after successful SetWindowLong call
    // We can assume that pointer returned by GetWindowLong is valid
    // It will route messages to our member message handler
    LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    // Get a window pointer associated with this window
    Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);
    // It should be valid, assert so
    _ASSERT(w);
    // Redirect messages to the window procedure of the associated window
    return w->WndProc(hwnd, msg, wp, lp);
    }
    // The temporary global this pointer
    // It will be used only between CreateWindow is called and the first message is processed by WndProc
    // WARNING: it is not thread-safe.
    Window *g_pWindow;

    // Critical section protecting the global Window pointer
    CRITICAL_SECTION g_WindowCS;

    // The helper window procedure
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    // Stash global Window pointer into per-window data area
    SetWindowLong(hwnd, GWL_USERDATA, (long) g_pWindow);
    // Unlock global critical section
    g_pWindow->HaveCSLock = false;
    LeaveCriticalSection(&g_WindowCS);
    // Reset the window message handler
    SetWindowLong(hwnd, GWL_WNDPROC, (long) WndProc2);
    // Dispatch first message to the member message handler
    return WndProc2(hwnd, msg, wp, lp);
    }

    现在我们可以创建窗口了:

    InitializeCriticalSection(&g_WindowCS);
    // Enter the critical section before you write to protected data
    EnterCriticalSection(&g_WindowCS);

    // Set global Window pointer to our Window instance
    // Moved the assignment here, where we have exclusive access to the pointer
    g_pWindow = &w;

    // Set a flag indicating that the window has the critical section lock
    // Note: this must be executed after the above assignment
    g_pWindow->HaveCSLock = true;

    // Create window
    // Note: lpParam is not used
    HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, 0);

    // Leave critical section if window creation failed and our window procedure hasn't released it
    if (g_pWindow->HaveCSLock)
    LeaveCriticalSection(&g_WindowCS);
    // Destroy critical section
    // In production code, you'd do this when application terminates, not immediately after CreateWindow call
    DeleteCriticalSection(&g_WindowCS);
  2. 使用 CBT Hook 程序(摘自 here):

    // The helper window procedure
    // It is called by Windows, and thus it's a non-member function
    // This message handler will only be called after successful SetWindowLong call from the hook
    // We can assume that pointer returned by GetWindowLong is valid
    // It will route messages to our member message handler
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
    {
    // Get a window pointer associated with this window
    Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);
    // It should be valid, assert so
    _ASSERT(w);
    // Redirect messages to the window procedure of the associated window
    return w->WndProc(hwnd, msg, wp, lp);
    }

    // The CBT hook procedure
    // It is called during CreateWindow call before WndProc receives any messages
    // Its job is to set per-window Window pointer to the one passed through lpParam to CreateWindow
    LRESULT CALLBACK CBTProc(int code, WPARAM wp, LPARAM lp)
    {
    if (code != HCBT_CREATEWND) {
    // Ignore everything but create window requests
    // Note: generally, HCBT_CREATEWND is the only notification we will get,
    // assuming the thread is hooked only for the duration of CreateWindow call.
    // However, we may receive other notifications, in which case they will not be passed to other CBT hooks.
    return 0;
    }
    // Grab a pointer passed to CreateWindow as lpParam
    std::pair<Window *, HHOOK> *p = (std::pair<Window *, HHOOK> *) LPCBT_CREATEWND(lp)->lpcs->lpCreateParams;
    // Only handle this window if it wasn't handled before, to prevent rehooking windows when CreateWindow is called recursively
    // ie, when you create windows from a WM_CREATE handler
    if (p->first) {
    // Stash the associated Window pointer, which is the first member of the pair, into per-window data area
    SetWindowLong((HWND) wp, GWL_USERDATA, (long) p->first);
    // Mark this window as handled
    p->first = 0;
    }
    // Call the next hook in chain, using the second member of the pair
    return CallNextHookEx(p->second, code, wp, lp);
    }

    现在我们可以创建窗口了:

    // Install the CBT hook
    // Note: hook the thread immediately before, and unhook it immediately after CreateWindow call.
    // The hook procedure can only process window creation nofitications, and it shouldn't be called for other types of notifications
    // Additionally, calling hook for other events is wasteful since it won't do anything useful anyway
    HHOOK hook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
    _ASSERT(hook);

    // Create window
    // Pass a pair consisting of window object pointer and hook as lpParam
    std::pair<Window *, HHOOK> p(&w, hook);
    HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, &p);

    // Unhook first
    UnhookWindowsHookEx(hook);

最佳答案

我个人不会使用这两种方法中的任何一种。全局变量方法有效,但感觉很脏。特别是带锁。而 CBT 钩子(Hook),远远超出了顶部。尽管它指向了正确的方向。

在创建期间将状态信息传递给窗口过程的标准方法是通过CreateWindowCreateWindowExlpParam 参数。所以技术如下:

  1. CreateWindowCreateWindowExlpParam 参数中传递您的实例指针。
  2. 在您的 WM_NCCREATE 处理程序中读取此值。该消息将信息作为 CREATESTRUCT 结构的一部分提供。
  3. 还是在WM_NCCREATE调用SetWindowLongPtr将窗口的用户数据设置为实例指针。
  4. 所有将来对窗口过程的调用现在都可以通过调用 GetWindowLongPtr 来获取实例指针。

Raymond Chen 在此处说明了详细信息:How can I make a WNDPROC or DLGPROC a member of my C++ class?

关于c++ - WndProc 的类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21727015/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com