简体   繁体   English

WndProc的类方法

[英]Class method for WndProc

This article explains brilliantly the options to call a class member WndProc. 本文精彩地解释了调用类成员WndProc的选项。 I've seen this response in stackoverflow but the main problem associating class member WndProc after CreateWindow is that some messages will be lost (including the important WM_CREATE) as explained in the mentioned article . 在stackoverflow中看到了这个响应,但是在CreateWindow之后关联类成员WndProc的主要问题是某些消息将丢失(包括重要的WM_CREATE), 如上所述

My question : I would like to hear the opinion from an expert on which of the methods exposed below or new one is the best one (performance, maintanability, ...) to create a class member WndProc. 我的问题 :我想听听专家的意见,即下面公开的方法或新方法是创建类成员WndProc的最佳方法(性能,可维护性......)。

Briefing the two final solutions exposed in the article (suposing that it exists a Window class with WndProc method): 简要介绍文章中公开的两个最终解决方案(假设它存在一个带WndProc方法的Window类):

  1. Per-window data with this global pointer storage, protecting it with CRITICAL_SECTION to make it thread safe (extracted from here ): 具有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); } 

    And now we can create the window: 现在我们可以创建窗口:

     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. Using CBT hook procedure (extracted from here ): 使用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); } 

    And now we can create the window: 现在我们可以创建窗口:

     // 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); 

I personally would not use either of these methods. 我个人不会使用这两种方法。 The global variable approach works, but feels dirty. 全局变量方法有效,但感觉很脏。 Especially with the lock. 特别是带锁。 And the CBT hook is, well over the top. CBT挂钩远远超过顶部。 Although it points in the right direction. 虽然它指向了正确的方向。

The standard way to pass state information to your window procedure during creation is through lpParam parameter of CreateWindow or CreateWindowEx . 在创建过程lpParam状态信息传递给窗口过程的标准方法是通过CreateWindowCreateWindowEx lpParam参数。 So the technique is as follows: 所以技术如下:

  1. Pass your instance pointer in the lpParam parameter of CreateWindow or CreateWindowEx . 将实例指针传递给CreateWindowCreateWindowExlpParam参数。
  2. Read this value in your WM_NCCREATE handler. WM_NCCREATE处理程序中读取此值。 That message supplies the information as part of the CREATESTRUCT struct. 该消息提供信息作为CREATESTRUCT结构的一部分。
  3. Still in WM_NCCREATE call SetWindowLongPtr to set the user data of the window to the instance pointer. 仍然在WM_NCCREATE调用SetWindowLongPtr将窗口的用户数据设置为实例指针。
  4. All future calls to the window procedure can now obtain the instance pointer by calling GetWindowLongPtr . 以后对窗口过程的所有调用现在都可以通过调用GetWindowLongPtr来获取实例指针。

Raymond Chen illustrates the details here: How can I make a WNDPROC or DLGPROC a member of my C++ class? Raymond Chen在这里详细说明: 如何让WNDPROC或DLGPROC成为我的C ++类的成员?

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

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