在自定义框架的WndProc函数中,WM_NCCREATE并非作为第一条消息被调用
我正在实现一个自定义的Win32 C++框架,把所有Win32的底层实现封装在一个名为 Framework 的类中。为了满足Win32 C端点的要求,某些核心方法是 static。以下三个静态方法将支持 myRegisterClass / initInstance / mainWndProc 序列,这是 winMain 所期望的。
static ATOM myRegisterClass(HINSTANCE hInstance);
static BOOL initInstance(HINSTANCE hInstance, Framework* framework);
static LRESULT CALLBACK mainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
静态函数 myRegisterClass 将 mainWndProc 指定为
ATOM Framework::myRegisterClass(HINSTANCE hInstance) {
WNDCLASSEXW wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = Framework::mainWndProc;
... //etc.
为了把自定义数据传递给 mainWndProc,initInstance 方法(由一个非静态 winMain 入口点调用)把当前 Framework 对象的指针作为参数(它是从非静态 winMain 入口点通过 this 调用的),并将其传递给 mainWndProc。
BOOL Framework::initInstance(HINSTANCE hInstance, Framework* framework)
{
HWND hWnd = CreateWindow(...,
hInstance, framework/* Last param has user data */);
最后 mainWndProc 需要在 WM_NCCREATE 中获取自定义的 Framework 数据,并将其设置为 SetWindowLongPtr。框架对象包含实现封装消息处理所需的所有细节。
PROBLEM: The WM_NCCREATE is never called. The first message that I get is 36. So I bypass the WM_NCCREATE and don't receive my passedFramework, it's NULL in the else。
LRESULT CALLBACK Framework::mainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
Framework* passedFramework = nullptr;
if (message == WM_NCCREATE) { // NEVER CALLED
// Extract pointer from CREATESTRUCT
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
passedFramework = reinterpret_cast<Framework*>(pCreate->lpCreateParams);
// Store it in the window's user data
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(passedFramework));
// **NOTE: ALSO TRIED THIS, NO DIFFERENCE (it's something related to DefWindowProc()
// return DefWindowProc(hWnd, message, wParam, lParam); // Must return TRUE to continue
}
else {
// Retrieve the pointer for all subsequent messages
passedFramework = reinterpret_cast<Framework*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
// End of WM_NCCREATE If, continuing with regular checks
// -----------------------------------------------------
switch (message)
{
case WM_CREATE:
createMainWindowControls();
break;
case WM_COMMAND:
//etc.
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
EDIT: 这个线程证实,WM_GETMINMAXINFO有时是第一个消息(与官方文档相悖): Windows API: What is the first message a window is guaranteed to receive?
解决方案
解决办法是检查指针是否为NULL。如果是,则直接调用 DefWindowProc。
LRESULT CALLBACK Framework::mainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
Framework* self;
if (message == WM_NCCREATE) {
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
self = reinterpret_cast<Framework*>(pCreate->lpCreateParams);
self->m_hwnd = hWnd;
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self));
}
else {
self = reinterpret_cast<Framework*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if (self) {
return self->HandleMessage(uMsg, wParam, lParam);
}
else {
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
至于为什么 WM_NCCREATE 不是窗口收到的第一条消息,是因为现在修改它会导致严重的兼容性问题。
也就是说,进行空指针检查始终是个好主意,因此这不应该成为太大问题。
我建议你先从改写Raymond Chen的 "The new scratch program" 开始。尽管它有点过时,但基本原则仍然适用。
我建议你阅读Raymond Chen的博客,"The Old New Thing." 它包含了大量关于Windows编程的知识。