1

我想我和之前的许多人一样落入了同样的陷阱,我试图在 win32 API 编程上强加一个很好的 OO 方法。没有 MFC,没有 AFX,我什至没有使用 VC++,我使用 C::B 和 gcc。

我认为我正在尝试做的事情是不可能的,但是由于 MFC 存在(尽管我没有使用它),所以必须有某种方法。

我创建了一个包含多个窗口控件的类。它实现了 WM_CREATE 和 WM_COMMAND 的处理程序,并跟踪我的一小组控件(ID 代码和 HWND)周围的所有相关数据。

它适用于按钮、静态控件,甚至是轻量级 GDI 方法,但是当我尝试对编辑控件进行子类化时,这一切都崩溃了。

真的,我只是想捕获“回车”键,但正如任何曾经走过这条路的人都会证明的那样,当编辑控件获得焦点时,父窗口不会收到 WM_KEYDOWN 或 WM_COMMAND,我们只能自己实现过程。超级跛脚。

好的,如果 editProc 是全局或静态的,那么子类化编辑控件就可以了。我知道这是因为 SetWindowLongPtr 需要一个函数地址,而这个概念对于成员函数来说是模糊的。

所以我的类的对象在父 WndProc 中被声明为“静态”。但是该函数不是“静态的”,因为这样我就无法访问非静态数据成员(完全违背了本练习的目的)。我希望因为对象本身是静态的,所以我应该能够正确定义其成员函数之一的地址。

以前尝试过的读者要么已经放弃并使用 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);

要从静态函数访问您的类数据,您可以将其保存在编辑控件的 userdata 字段中,例如:

// 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的那样,除非您想要与 pre-XP 兼容,否则绝对是这样做的方法。它自动为您处理子类化过程,让您关联一个this自动传递给子类过程的用户数据指针(例如 ),并在最后安全地处理删除子类。

于 2013-01-06T23:28:58.133 回答