2

クイックサニティチェック:ファンクターを使用してウィンドウをサブクラス化することは可能ですか?win procでデータを利用できるようにしたいという状況に遭遇していますが、GWLP_USERDATAはすでに使用されています。ファンクターは良い代替手段のように思えますが、私はそれを機能させるのに苦労しています。

基本は次のとおりです。

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()を押すと、「違反書き込み場所0x00000000にアクセス」します。これは明らかに良い兆候ではありません。上記のコードへの呼び出しを削除すると、人生は再び幸せになります。:(それで、これは可能でさえありますか、それとも私はそれを完全に間違った方法で行っていますか?

4

5 に答える 5

8

CALLBACK関数は、静的メンバー関数またはそれ以外の場合はストレートCスタイルの関数である必要があります。Windows APIは、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;
};
于 2009-08-30T05:16:17.827 に答える
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()

... または最後の 2 つのステートメントの代わりに、演算子が仮想の場合は次のステートメント ...

; ... 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++ メソッドの呼び出し規則を認識していないため、これらのいずれも可能ではありません。特に、次のものが含まれます。

  • 暗黙の「this」パラメーターが渡される方法
  • クラスの非仮想メソッドのアドレス
  • クラスの仮想メソッドの vtable エントリ
于 2009-08-30T16:44:42.880 に答える
3

GWLP_USERDATAはすでに使用されています

SubclassWindow関数が何であるかはわかりませんが、CWnd :: SubclassWindowは、「この関数が呼び出されたときに、ウィンドウがMFCオブジェクトにアタッチされていてはなりません」と言っています。

winprocでいくつかのデータを利用できるようにしたい状況に遭遇しています

これを実装する通常の(非MFC)方法は、グローバル/静的ディクショナリを使用することです。そのキー/インデックスはサブクラス化されたウィンドウのHWND値であり、そのデータはそのウィンドウに関連付けるデータです。そのデータ多くの場合、thisあなたのC++クラスのポインタです。

ウィンドウプロシージャを自分の静的コールバック関数でサブクラス化します。静的コールバック関数は、呼び出されると、渡されたHWNDを使用して静的ディクショナリでデータを検索します。

于 2009-08-30T05:31:43.817 に答える
3

ウィンドウに関連付けられたデータを格納する方法は GWLP_USERDATA だけではありません。 SetProp()を使用することもできます。

そして、少なくとも x86 では、ATL スタイルのサンク (クラス ポインターを ecx に配置してから wndproc にジャンプする小さな asm コード) を実行できます

于 2009-08-30T15:02:36.077 に答える