繁体   English   中英

WndProc的类方法

[英]Class method for WndProc

本文精彩地解释了调用类成员WndProc的选项。 在stackoverflow中看到了这个响应,但是在CreateWindow之后关联类成员WndProc的主要问题是某些消息将丢失(包括重要的WM_CREATE), 如上所述

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

简要介绍文章中公开的两个最终解决方案(假设它存在一个带WndProc方法的Window类):

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

     // 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钩子程序(从这里提取):

     // 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挂钩远远超过顶部。 虽然它指向了正确的方向。

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

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

Raymond Chen在这里详细说明: 如何让WNDPROC或DLGPROC成为我的C ++类的成员?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM