簡體   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