从用户定义的类/指向成员函数的子类化编辑控件

时间:2013-01-06 22:53:14

标签: c++ c winapi subclass editbox

我认为我已经陷入了与我之前相同的陷阱,我试图在win32 API编程上强加一个好的OO方法。没有MFC,没有AFX,我甚至没有使用VC ++,我正在使用C :: B和gcc。

我认为我想做的事情是不可能的,但由于MFC存在(虽然我没有使用它),但必须有某种方式。

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

它非常适用于按钮,静态控件,甚至是轻量级GDI方法,但是当我尝试子类化编辑控件时它都会崩溃。

真的,我只想捕获“输入”键,但是任何在此之前已经走过的人都会证明,当编辑控件有焦点时,父窗口不会收到WM_KEYDOWN或WM_COMMAND,我们留给实现我们自己的过程。超级跛脚。

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

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

之前尝试过这种方法的读者要么已经放弃并使用了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以获取返回值,但是直到我越过这一点,没有必要担心这一点。

提前感谢您阅读!

1 个答案:

答案 0 :(得分: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绝对是这样做的方法,除非你想要与XP之前兼容。它自动为您处理子类化过程,允许您将自动传递给子类过程的userdata指针(例如this)关联起来,并安全地处理最后删除子类。