33

thisで使用するポインタを格納するための最良の/一般的な方法を知りたいですWndProc。私はいくつかのアプローチを知っていますが、私が理解しているように、それぞれに独自の欠点があります。私の質問は次のとおりです。

この種のコードを作成するには、どのような方法がありますか。

CWindow::WndProc(UINT msg, WPARAM wParam, LPARAM)
{
  this->DoSomething();
}

サンク、ハッシュマップ、スレッドローカルストレージ、ウィンドウユーザーデータ構造体について考えることができます。

これらの各アプローチの長所/短所は何ですか?

コード例と推奨事項に対して付与されるポイント。

これは純粋に好奇心のためです。MFCを使用した後、私はそれがどのように機能するのか疑問に思い、ATLなどについて考えるようになりました。

編集:HWNDウィンドウプロシージャで有効に使用できる最も早い場所はどこですか?これは-として文書化されてWM_NCCREATEいますが、実際に実験すると、ウィンドウに送信される最初のメッセージではありません。

編集: ATLは、このポインターにアクセスするためにサンクを使用します。MFCは、sのハッシュテーブルルックアップを使用しますHWND

4

11 に答える 11

14

コンストラクターで、lpParam引数として「this」を指定してCreateWindowExを呼び出します。

次に、WM_NCCREATEで、次のコードを呼び出します。

SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) ((CREATESTRUCT*)lParam)->lpCreateParams);
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);

次に、ウィンドウプロシージャの上部で、次の操作を実行できます。

MyWindowClass *wndptr = (MyWindowClass*) GetWindowLongPtr(hwnd, GWL_USERDATA);

これにより、これを行うことができます。

wndptr->DoSomething();

もちろん、同じ手法を使用して、上記の関数のようなものを呼び出すこともできます。

wndptr->WndProc(msg, wparam, lparam);

...これにより、「this」ポインタを期待どおりに使用できます。

于 2008-09-22T21:52:02.087 に答える
14

SetWindowLongPtrGetWindowLongPtrを使用してGWL_USERDATAにアクセスするのは良い考えのように聞こえるかもしれませんが、この方法を使用しないことを強くお勧めします。

これはまさにZeusエディターが使用するアプローチであり、近年では苦痛以外の何物でもありません。

サードパーティの Windows メッセージがZeusに送信され、GWL_USERDATA値も設定されていると思います。特に 1 つのアプリケーションは、任意の Windows アプリケーション (つまり、ある種のソフトウェア キーボード ユーティリティ) でアジア文字を入力する別の方法を提供する Microsoft ツールでした。

問題は、 Zeusが常にGWL_USERDATAデータが設定されていると想定し、そのデータをthis ポインターとして使用しようとするため、クラッシュが発生することです。

私が今知っていることでそれをすべてやり直すとしたら、ウィンドウハンドルがキーとして使用されるキャッシュされたハッシュルックアップアプローチに行きます。

于 2008-09-23T00:29:07.277 に答える
9

GetWindowLongPtr()/ SetWindowLongPtr()(または非推奨のGetWindowLong()/ )を使用する必要がありますSetWindowLong()。彼らは速く、あなたがやりたいことを正確に行います。唯一の注意が必要な部分は、いつ呼び出すかを判断することです。SetWindowLongPtr()これは、最初のウィンドウ メッセージが送信されたときに行う必要がありますWM_NCCREATE。サンプル コードと詳細な説明については、この記事
を 参照してください。

1 つのスレッドで複数のウィンドウを実行している可能性があるため、スレッド ローカル ストレージはお勧めできません。

ハッシュ マップも機能しますが、すべてのウィンドウ メッセージ (およびLOTがある) のハッシュ関数を計算すると、コストが高くなる可能性があります。

サンクをどのように使用するつもりなのかわかりません。どうやってサンクを回っていますか?

于 2008-09-22T21:42:59.737 に答える
6

SetProp/GetProp を使用して、データへのポインターをウィンドウ自体に格納しました。あなたが言及した他のアイテムとどのように重なり合うのかわかりません。

于 2008-09-22T21:41:53.967 に答える
4

GetWindowLongPtrSetWindowLongPtr;を使用できます。GWLP_USERDATAウィンドウにポインターをアタッチするために使用します。ただし、カスタム コントロールを作成している場合は、追加のウィンドウ バイトを使用して作業を完了することをお勧めします。ウィンドウクラスを登録しながら、WNDCLASS::cbWndExtraこのようにデータのサイズを設定しますwc.cbWndExtra = sizeof(Ctrl*);.

パラメータを に設定してGetWindowLongPtrおよびSetWindowLongPtrを使用して、値を取得および設定できます。このメソッドは、他の目的のために保存できます。nIndex0GWLP_USERDATA

GetPropとの欠点はSetProp、プロパティを取得/設定するための文字列比較です。

于 2011-09-12T10:13:21.267 に答える
3

Microsoft によると、SetWindowLong() / GetWindowLong() セキュリティに関して:

hWnd パラメータで指定されたウィンドウが呼び出しスレッドと同じプロセスに属していない場合、SetWindowLong 関数は失敗します。

残念ながら、 2004 年 10 月 12 日にセキュリティ アップデートがリリースされるまで、Windowsはこのルールを適用せず、アプリケーションが他のアプリケーションの GWL_USERDATA を設定することを許可していませんでした。したがって、パッチが適用されていないシステムで実行されているアプリケーションは、SetWindowLong() の呼び出しによる攻撃に対して脆弱です。

于 2008-09-24T06:22:37.437 に答える
2

thread_localを呼び出す直前に変数を設定し、CreateWindowそれを読んで変数WindowProcを見つけることをお勧めしthisます(あなたが を制御できると思いますWindowProc)。

このようにして、ウィンドウに送信される最初のメッセージにthis/関連付けが作成されます。HWND

WM_CREATEここで提案されている他のアプローチでは、 / WM_NCCREATE/の前に送信されたメッセージを見逃す可能性がありますWM_GETMINMAXINFO

class Window
{
    // ...
    static thread_local Window* _windowBeingCreated;
    static thread_local std::unordered_map<HWND, Window*> _hwndMap;
    // ...
    HWND _hwnd;
    // ...
    // all error checking omitted
    // ...
    void Create (HWND parentHWnd, UINT nID, HINSTANCE hinstance)
    {
        // ...
        _windowBeingCreated = this;
        ::CreateWindow (YourWndClassName, L"", WS_CHILD | WS_VISIBLE, x, y, w, h, parentHWnd, (HMENU) nID, hinstance, NULL);
    }

    static LRESULT CALLBACK Window::WindowProcStatic (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
    {
        Window* _this;
        if (_windowBeingCreated != nullptr)
        {
            _hwndMap[hwnd] = _windowBeingCreated;
            _windowBeingCreated->_hwnd = hwnd;
            _this = _windowBeingCreated;
            windowBeingCreated = NULL;
        }
        else
        {
            auto existing = _hwndMap.find (hwnd);
            _this = existing->second;
        }

        return _this->WindowProc (msg, wparam, lparam);
    }

    LRESULT Window::WindowProc (UINT msg, WPARAM wparam, LPARAM lparam)
    {
        switch (msg)
        {
            // ....
于 2015-10-02T21:51:46.763 に答える