首先,让我说我是使用WinAPI的新手,我正在尝试学习基础知识。也就是说,我正在尝试创建一些对象,以便我将来使用WinAPI更容易。其中一个是Window Class ...类。另一个当然是Window类。
我正在尝试将消息处理变成简单的事情,比如myClassInst.addHandler(WM_PAINT,PaintFunction)。为此,我想我会使用一个将uints映射到函数指针的映射。这看起来很好。
但是现在,邮件没有被处理。经过一些调试后,我发现每当我尝试在我的类的WndProc处理程序中以任何方式使用地图时,它都会默默地失败。我没有编译错误,没有运行时错误,没有崩溃;该函数只是立即结束,直到下一条消息进来。我不能为我的生活找出原因。也许有人可以帮助我?
下面是我的代码片段,全部在我的WinClass类的私有部分。静态dummyProc消息传递是我发现允许特定于类的WndProc的唯一方法,所以就在那里。这是有效的,因为我的WndProc myHandler的第一部分确实可以正常工作,调试消息的输出就是证明。它只是停止第二次尝试并使用地图,甚至只是为了得到它的大小。
*注意:WindowFunction是指向获取HWND参数的函数的指针的typedef。
std::map<unsigned int, WindowFunction> messageMap;
static LRESULT CALLBACK dummyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
WinClass* context=(WinClass*)GetWindowLong(hwnd, GWL_USERDATA);
return context->myHandler(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK myHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
std::cout<<"Message: "<<msg<<std::endl; // This gets output
std::cout<<" Contains "<<messageMap.size()<<" items"<<std::endl; // This does not
std::cout<<" And me!"<<std::endl; // Nothing works below the map usage.
for (std::map<unsigned int, WindowFunction>::iterator it=messageMap.begin(); it!=messageMap.end(); ++it) {
std::cout<<" Have: "<<it->first<<std::endl;
if (msg==it->first) {
(it->second)(hwnd);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
编辑因此,经过一些进一步的测试后,我发现了更多信息。上下文值始终为0,虽然我不明白这将如何调用myHandler,但这是一个问题。所以我改变了我的代码,尝试从WM_CREATE消息中设置GWL_USERDATA ......我发现我的dummyProc永远不会收到WM_CREATE消息。
下面是新的dummyProc,带有调试输出:
static LRESULT CALLBACK dummyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
std::cout<<"Create would be "<<WM_CREATE<<std::endl;
std::cout<<"The message was "<<msg<<std::endl;
if (msg==WM_CREATE) {
SetLastError(0);
SetWindowLong(hwnd, GWL_USERDATA, lParam);
std::cout<<"ERROR: "<<GetLastError()<<std::endl;
}
long thelong=GetWindowLong(hwnd, GWL_USERDATA);
std::cout<<"Long: "<<thelong<<std::endl;
WinClass* context=(WinClass*)thelong;
std::cout<<"Context: "<<context<<std::endl;
return context->myHandler(hwnd, msg, wParam, lParam);
}
它说的是WM_CREATE的值为1,但是消息从36开始,然后是129和130.我的WM_CREATE消息发生了什么?
以下是创建窗口本身的代码:
hand = CreateWindowEx(WS_EX_CLIENTEDGE, myClass.myName, 标题, WS_OVERLAPPEDWINDOW, x,y, w,h, 父母, 空值, 研究所, &安培; MyClass的);
这些参数中的大多数是传递给Window类的构造函数的参数(此代码所在的位置)。 myClass参数是一个WinClass实例。所有WinClass实例都将dummyProc设置为其WndProc处理程序。测试显示&amp; myClass在调用它时确实是一个有效的非NULL指针。
那是什么阻止了WM_CREATE消息?
答案 0 :(得分:2)
您尚未显示窗口创建或指针存储到GWL_USERDATA
的位置。但是,无论你如何做到这一点,我都保证在第一条消息到达之前无法设置GWL_USERDATA
,因为第一条消息的到达是第一条消息实际知道你的新窗口分配了HWND
的时间。
您的窗口过程无法防止context
尚未设置,然后尝试在无效指针上调用成员函数,该指针以未定义的方式失败。
除此之外,GWL_USERDATA
不足以存储指针的事实;您应该将SetWindowLongPtr
和GetWindowLongPtr
与GWLP_USERDATA
一起使用。
通常,原始窗口程序首先设置GWLP_USERDATA
。看看Raymond Chen's C++ scratch program。
请注意,WM_NCCREATE
是在原始窗口过程中处理的,因为还没有存储的指向调度的指针(它来自WM_NCCREATE
消息参数,而不是来自窗口)。这甚至不是第一条消息(在许多情况下你会先得到WM_GETMINMAXINFO
),虽然这是第一条消息,可以在没有线程本地存储等技巧的情况下获得指针。
答案 1 :(得分:2)
正如@Ben Voigt所说,你没有正确地初始化你的GWLP_USERDATA
,而且你还没有处理它尚未初始化的情况。
请注意,即使修改后的代码也不正确。在WM_CREATE
lParam
值为不您的用户数据指针。它是指向CREATESTRUCT
的指针,其中一个成员是您的用户数据值。
请尝试以下操作来替换dummyProc
功能:
static LRESULT CALLBACK dummyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_NCCREATE)
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>( reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams ));
WinClass* context=(WinClass*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (!context) return DefWindowProc(hwnd, msg, wParam, lParam);
return context->myHandler(hwnd, msg, wParam, lParam);
}