在自定义框架的WndProc函数中,WM_NCCREATE并非作为第一条消息被调用

后端开发 2026-07-09

我正在实现一个自定义的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);

静态函数 myRegisterClassmainWndProc 指定为

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.

为了把自定义数据传递给 mainWndProcinitInstance 方法(由一个非静态 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编程的知识。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章