2009-08-30 52 views
2

快速完整性檢查:是否可以使用仿函數子類化一個窗口?我遇到了一種情況,我希望在win proc中有一些可用的數據,但是GWLP_USERDATA已經被使用。一個仿函數看起來是一個很好的選擇,但是我很難讓它工作。使用仿函數子類化一個窗口(Win32)

這裏的基礎知識:

class MyWinProc { // Win Proc Functor 
public: 
    MyWinProc(ExternalClass* obj, HWND window) : 
       obj(obj), window(window) { 
       oldWinProc = SubclassWindow(window, this); // Apply Subclass 
      } 

    virtual ~MyWinProc() { 
       SubclassWindow(window, oldWinProc); // Remove Subclass 
      } 

    LRESULT CALLBACK operator()(HWND, UINT, WPARAM, LPARAM) { 
       switch(uMsg) { 
     case WM_MOUSEMOVE: { 
      obj->onMouseMove(/*etc*/); 
      break; 
     } 
       } 
       return CallWindowProc(oldWinProc, hWnd, uMsg, wParam, lParam); 
      } 

private: 
    ExternalClass* obj; 
    HWND window; 
    WNDPROC oldWinProc; 
}; 

似乎一切都很好,但是當我打在DispatchMessage()在我的消息泵,I「訪問衝突寫入位置00000000」,顯然不是一個好兆頭。取消對上述代碼的呼叫,生活再次開心。 :(所以這甚至是可能的,還是我完全錯誤的方式?

+0

的重複http://stackoverflow.com/questions/23083/whats-an-alternative-to-gwluserdata-for-storing-an-對象指針? – Anders 2009-08-30 15:06:17

+0

我不一定稱這是重複的,因爲如果在這種情況下可以使用仿函數,所要問的問題就非常明確。最後,答案與你關聯的問題是一樣的,但重複的答案並不是重複的問題。 :)(我欣賞它,但!) – Toji 2009-08-30 15:37:10

回答

3

GWLP_USERDATA不是存儲與窗口關聯數據的唯一方法,您也可以使用SetProp()

至少在x86上,你可以做ATL風格thunking(一小段asm代碼,將你的類指針放入ecx,然後跳轉到你的wndproc)你可以在我發佈的here的答案中找到一些關於這個的鏈接

+0

呵呵。並認爲,儘可能多地在Win32中完成,我從來沒有聽說過SetProp!這是一個好主意,我想我可以使用它! – Toji 2009-08-30 15:30:52

8

一個CALLBACK函數必須是一個靜態成員函數或其他直C風格的函數。關於C++對象

沿此線的東西應該工作:已被使用

class MyWinProc { 
public: 
     MyWinProc(ExternalClass* obj, HWND window) : 
       obj(obj), window(window) { 
       pContext = this; 

       oldWinProc = SubclassWindow(window, &MyWinProc::wndproc); // Apply Subclass 
      } 

     virtual ~MyWinProc() { 
       SubclassWindow(window, oldWinProc); // Remove Subclass 
      } 


private: 
     static MyWinProc* pContext; 

     static 
     LRESULT CALLBACK wndproc(HWND, UINT, WPARAM, LPARAM) { 
      MyWndProc& me = *pContext; 

      // do your WndProc work... 
     } 

     ExternalClass* obj; 
     HWND window; 
     WNDPROC oldWinProc; 
}; 
+1

如果您只是對該窗口的一個實例進行子類化,則靜態pContext是可以的。 – ChrisW 2009-08-30 05:33:06

+0

@ChrisW:你說得對 - 這個類應該是一個單例,或者需要更復雜的方法來獲得上下文。這僅僅是示例代碼之上的一個簡單例子。 – 2009-08-30 09:58:26

+0

是的,雖然你的代碼示例在技術上是正確的(我會upvote它使新手更容易找到)我確實需要一個更強大的方式來獲取上下文,因爲我可能是幾個窗口的子類。 看來我的錯誤在這裏假設一個仿函數可以代替任何回調,顯然不是這種情況。這真的太糟糕了,因爲這會打開一個全新的可能性世界! – Toji 2009-08-30 15:34:36

3

GWLP_USERDATA

我不知道你的SubclassWindow函數是什麼,但是CWnd::SubclassWindow說:「調用這個函數時窗口不能被附加到MFC對象上」。

我遇到,我想有一些在win PROC

一個平常(非MFC)的方式來實現,現有的數據的情況是有一個全局/靜態字典,其鍵/索引是子類窗口的HWND值,其數據是要與該窗口關聯的數據:該數據通常是您C++類的this指針。

您使用您的靜態回調函數對窗口過程進行子類化:您的靜態回調函數然後在調用時使用傳遞的HWND查找靜態字典中的數據。

+0

對不起,我應該指出我沒有使用MFC。 SubclassWindow是我在windowsx.h中定義的一個宏,而不是MFC函數。此外,在MFC之外,可以多次對窗口進行子類化,只需要注意WinProc的鏈接和安裝/刪除的順序。 – Toji 2009-08-30 15:26:14

5

使用函數的問題是調用約定:Windows期望地址是靜態函數的地址,並且會使用/調用該地址;而你傳遞的'this'不是靜態函數的地址。

的Windows將使用的地址是這樣的(僞編碼的組件):

; push the necessary parameters 
push [hWnd] 
push etc... 
; invoke the specified address (of the static function) 
call [callback] 

要調用一個函數對象,在Windows代碼將需要這樣

; push the necessary parameters 
push [hWnd] 
push etc... 
; invoke the specified address (of the functor object) 
; ... first, put the 'this' pointer as a hidden parameter into the ecx register 
mov ecx,[callback] 
; ... next, invoke the address (where is it?) of the class' functor method 
call MyWinProc::operator() 

..或者代替最後兩條語句,如果操作符是虛擬的,則使用以下語句...

; ... first, put the 'this' pointer as a hidden parameter into the ecx register 
mov ecx,[callback] 
; ... next, invoke the address of the operator via an (which?) entry 
;  in the class' vtable 
call [ecx+8] 

這些都不是可能的,因爲O/S是不知道用於非靜態C++方法的調用約定的,尤其是包括:

  • 的方式,其中所述隱式「這個」參數傳遞
  • 的類的非虛擬方法
  • 類的V表項的地址的虛方法
+0

非常感謝您的深入介紹!我開始意識到,在我開始獲得答案後,​​它一定是這樣的,很高興有詳細信息。我希望我可以將兩個答案標記爲「正確」,因爲這回答了我所問的具體問題,但另一個答案是我標記了最終解決方案的點,這更可能是類似問題的搜索者所期待的。儘管如此,你還是會得到我的讚賞,我希望還有更多!再次感謝你! – Toji 2009-08-31 04:24:46

-1

您仍然可以使用存儲在GWLP_USERDATA值...

class MyWinProc { // Win Proc Functor 
public: 
MyWinProc(ExternalClass* obj, HWND window) : 
    obj(obj), window(window) { 
     oldUserData = GetWindowLongPtr(GWLP_USERDATA); 
     oldWinProc = SubclassWindow(window, this); // Apply Subclass 
    } 

    virtual ~MyWinProc() { 
     SubclassWindow(window, oldWinProc); // Remove Subclass 
    } 

    LRESULT CALLBACK operator()(HWND, UINT, WPARAM, LPARAM) {  
     switch(uMsg) { 
      case WM_MOUSEMOVE: { 
       obj->onMouseMove(/*etc*/); 
       break; 
       } 
     } 
     LONG userDataToRestore = SetWindowLongPtr(GWLP_USERDATA, oldUserData); 
     LRESULT lRet = CallWindowProc(oldWinProc, hWnd, uMsg, wParam, lParam); 
     SetWindowLongPtr(GWLP_USERDATA, userDataToRestore); 
    } 

private: 
ExternalClass* obj; 
HWND window; 

LONG oldUserData; 
WNDPROC oldWinProc; 
};