快速健全性检查:是否可以使用仿函数对窗口进行子类化?我遇到了想要在win proc中获得一些数据的情况,但是GWLP_USERDATA已经被使用了。仿函数似乎是一个很好的选择,但是我无法让它工作。
以下是基础知识:
class MyWinProc { // Win Proc Functor
public:
MyWinProc(ExternalClass* obj, HWND window) :
obj(obj), window(window) {
oldWinProc = SubclassWindow(window, this); // Apply Subclass
}
virtual ~MyWinProc() {
SubclassWindow(window, oldWinProc); // Remove Subclass
}
LRESULT CALLBACK operator()(HWND, UINT, WPARAM, LPARAM) {
switch( uMsg ) {
case WM_MOUSEMOVE: {
obj->onMouseMove(/*etc*/);
break;
}
}
return CallWindowProc(oldWinProc, hWnd, uMsg, wParam, lParam);
}
private:
ExternalClass* obj;
HWND window;
WNDPROC oldWinProc;
};
似乎一切都很好,但当我在消息泵中点击DispatchMessage()时,我“访问违规写入位置0x00000000”,显然不是一个好兆头。删除对上述代码的调用,生活再次开心。 :(所以这甚至是可能的,还是我完全以错误的方式去做?
答案 0 :(得分:8)
CALLBACK函数必须是静态成员函数或其他直接C风格函数。 Windows API对C ++对象一无所知。
有些事情应该有效:
class MyWinProc {
public:
MyWinProc(ExternalClass* obj, HWND window) :
obj(obj), window(window) {
pContext = this;
oldWinProc = SubclassWindow(window, &MyWinProc::wndproc); // Apply Subclass
}
virtual ~MyWinProc() {
SubclassWindow(window, oldWinProc); // Remove Subclass
}
private:
static MyWinProc* pContext;
static
LRESULT CALLBACK wndproc( HWND, UINT, WPARAM, LPARAM) {
MyWndProc& me = *pContext;
// do your WndProc work...
}
ExternalClass* obj;
HWND window;
WNDPROC oldWinProc;
};
答案 1 :(得分:5)
使用仿函数的问题是调用约定:Windows期望地址是静态函数的地址,并且将使用/调用该地址;而你传递的'this'不是静态函数的地址。
Windows将使用这样的地址(伪编码程序集):
; push the necessary parameters
push [hWnd]
push etc...
; invoke the specified address (of the static function)
call [callback]
要调用仿函数,Windows代码必须像这样
; push the necessary parameters
push [hWnd]
push etc...
; invoke the specified address (of the functor object)
; ... first, put the 'this' pointer as a hidden parameter into the ecx register
mov ecx,[callback]
; ... next, invoke the address (where is it?) of the class' functor method
call MyWinProc::operator()
...或者代替最后两个语句,如果运算符是虚拟的,则使用以下语句...
; ... first, put the 'this' pointer as a hidden parameter into the ecx register
mov ecx,[callback]
; ... next, invoke the address of the operator via an (which?) entry
; in the class' vtable
call [ecx+8]
这些都不可能,因为O / S不知道非静态C ++方法的调用约定,尤其包括:
答案 2 :(得分:3)
GWLP_USERDATA已被使用
我不知道您的SubclassWindow函数是什么,但是CWnd::SubclassWindow说,“调用此函数时,窗口不得已经附加到MFC对象。”
我遇到了想要在win proc
中获得一些数据的情况
通常的(非MFC)实现方法是拥有一个全局/静态字典,其键/索引是子类窗口的HWND值,其数据是您要与该窗口关联的数据:该数据通常是您的C ++类的this
指针。
使用你的静态回调函数对窗口过程进行子类化:静态回调函数然后,当它被调用时,使用它传递的HWND来查找静态字典中的数据。
答案 3 :(得分:3)
GWLP_USERDATA不是存储与窗口关联的数据的唯一方法,您也可以使用SetProp()。
至少在x86上,你可以进行ATL样式的thunking(一小段asm代码,将你的类指针放在ecx中然后跳转到你的wndproc)你可以在我发布的答案中找到一些关于它的链接{{ 3}}
答案 4 :(得分:-1)
您仍然可以使用存储在GWLP_USERDATA ...
中的值class MyWinProc { // Win Proc Functor
public:
MyWinProc(ExternalClass* obj, HWND window) :
obj(obj), window(window) {
oldUserData = GetWindowLongPtr(GWLP_USERDATA);
oldWinProc = SubclassWindow(window, this); // Apply Subclass
}
virtual ~MyWinProc() {
SubclassWindow(window, oldWinProc); // Remove Subclass
}
LRESULT CALLBACK operator()(HWND, UINT, WPARAM, LPARAM) {
switch( uMsg ) {
case WM_MOUSEMOVE: {
obj->onMouseMove(/*etc*/);
break;
}
}
LONG userDataToRestore = SetWindowLongPtr(GWLP_USERDATA, oldUserData);
LRESULT lRet = CallWindowProc(oldWinProc, hWnd, uMsg, wParam, lParam);
SetWindowLongPtr(GWLP_USERDATA, userDataToRestore);
}
private:
ExternalClass* obj;
HWND window;
LONG oldUserData;
WNDPROC oldWinProc;
};