我认为我已经陷入了与我之前相同的陷阱,我试图在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以获取返回值,但是直到我越过这一点,没有必要担心这一点。
提前感谢您阅读!
答案 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
)关联起来,并安全地处理最后删除子类。