1

私は、win32 API プログラミングに優れたオブジェクト指向の方法論を押し付けようとする多くの人と同じ罠に陥ったと思います。MFC も AFX もありません。VC++ も使用していません。gcc で C::B を使用しています。

私がやろうとしていることは不可能だと思いますが、MFC は存在するので (私は使用していませんが)、何らかの方法があるに違いありません。

いくつかのウィンドウ コントロールを含むクラスを作成しました。WM_CREATE と WM_COMMAND のハンドラーを実装し、小さなコントロール グループ (ID コードと HWND) に関連するすべてのデータを追跡します。

ボタン、静的コントロール、軽い GDI メソッドでさえうまく機能しますが、エディット コントロールをサブクラス化しようとすると、すべてが機能しなくなります。

本当に、私は「Enter」キーをキャプチャしたいだけですが、以前にその道を進んだことのある人なら誰でも証明するように、エディット コントロールにフォーカスがある場合、親ウィンドウは WM_KEYDOWN または WM_COMMAND を受け取りません。 proc。スーパーラメ。

editProc がグローバルまたは静的であれば、エディット コントロールのサブクラス化は問題ありません。これは、SetWindowLongPtr が関数アドレスを必要とするためであり、その概念はメンバー関数にとって曖昧であるためです。

したがって、私のクラスのオブジェクトは、親 WndProc 内で「静的」として宣言されます。しかし、関数は「静的」ではありません。なぜなら、非静的データ メンバーにアクセスできないからです (この演習の目的を完全に無効にします)。オブジェクト自体が静的であるため、そのメンバー関数の 1 つのアドレスを適切に定義できるはずです。

以前にこれを試したことがある読者は、あきらめて MFC などを使用したか、巧妙な回避策を見つけた可能性があります。

残りの部分はこのサンプル コードに任せます: (簡略化 - そのままではコンパイルされません)

/**** myprogram.c ****/
#include "MyControlGroup.h"

int winMain(){ // etc... }

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // object is static becuse it only needs to be initialized once
    static MyControlGroup myControl; 

    if (msg == WM_CREATE)
        myControl.onWMCreate(hWnd);

    else if (msg == WM_COMMAND)
        myControl.onWMCommand( wParam, lParam );

    else if (msg == WM_DESTROY) 
        PostQuitMessage(0);

    return DefWindowProcW(l_hWnd, l_msg, l_wParam, l_lParam);
}

私のクラスのヘッダーファイル:

/**** MyControlGroup.h ****/
class MyControlGroup
{
private:
    HWND m_hWndParent;
    HWND m_hWndEditBox;
    int  m_editBoxID;
public:
    MyControlGroup();
    void onWMCreate(HWND);
    void onWMCommand(WPARAM, LPARAM);

    // want to find a way to pass the address of this function to SetWindowLongPtr
    LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};

...そして実装:

/**** MyControlGroup.cpp ****/
static int staticID = 1;
MyControlGroup::MyControlGroup()
{
    m_editBoxID = staticID++;
}

void MyControlGroup::onWMCreate(HWND hWnd)
{
    // My control group has buttons, static controls, and other stuff which are created here with CreateWindowW.  It also has an edit control:
    m_hWndEditBox = CreateWindowW(L"EDIT", L"initial text", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 150, 20, hWnd, (HMENU)m_editBoxID, NULL, NULL);

    /* 
    To subclass the edit control, I need a pointer to my customized proc.  That means I 
    need a pointer-to-member-function, but SetWindowLongPtr needs a pointer to global or 
    static function (__stdcall or CALLBACK, but not __thiscall).
    */

    // I'd like to do something like this, adapted from a great write-up at
    // http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

    LERSULT (MyControlGroup::*myEditProcPtr)(HWND, UINT, WPARAM, LPARAM);
    myEditProcPtr = &MyControlGroup::myEditProc;

    // Up to now it compiles ok, but then when I try to pass it to SetWindowLongPtr, I get 
    // an "invalid cast" error.  Any ideas?
    SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProcPtr);
}

void MyControlGroup::onWMCommand(WPARAM wParam, LPARAM lParam){ /* process parent window messages.  Editboxes don't generate WM_COMMAND or WM_KEYDOWN in the parent :''( */}

LRESULT MyControlGroup::myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // process messages like IDOK, WM_KEYDOWN and so on in the edit control
}

これが完了したとしても、親の WndProc のアドレスを戻り値として myEditProc に渡す方法を見つける必要がありますが、これを乗り越えるまでは心配する必要はありません。

読んでくれてありがとう!

4

1 に答える 1

1

myEditProc静的関数である必要があります。これが完了したら、中間変数を経由せずに関数のアドレスを直接渡すことができます。

static LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
...
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProc);

静的関数からクラス データにアクセスするには、編集コントロールのユーザーデータ フィールドに保存します。

// before sub-classing the control
SetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA, (LPARAM)this);

// in the sub-class procedure
MyControlGroup* pThis = (MyControlGroup*)GetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA);

しかし、@ K-balloが示唆しSetWindowSubclassたように、XP以前との互換性が必要でない限り、間違いなくこれを行う方法です。サブクラス化手順を自動的に処理し、サブクラス手順に自動的に渡されるユーザーデータ ポインター (例: this) を関連付け、最後にサブクラスの削除を安全に処理します。

于 2013-01-06T23:28:58.133 に答える