繁体   English   中英

CreateWindowEx 不使用 class 模板的实例创建 HWND

[英]CreateWindowEx does not create HWND with the instance of a class template

我正在关注本教程,如果我将所有定义保存在一个 header 文件中,一切都很好并且可以工作(我的意思是,如果我直接复制此代码)。 但是如果我尝试将定义移动到单独的文件中,它不会创建 HWND。

在调用CreateWindowEx时,它会转到BaseWindow<DERIVED_TYPE>::WindowProc并按顺序一个接一个地发送以下消息:

  • WM_GETMINMAXINFO
  • WM_NCCREATE
  • WM_NCDESTROY

退出后,会导致未创建 HWND。 因此,不会出现 window。 HWND 为 NULL

项目结构:

  • 主文件
  • 视窗/
    • BaseWindow.h
    • BaseWindow.cpp
    • 主窗口.h
    • 主窗口.cpp

这就是我的代码的外观。

主文件

#ifndef UNICODE
#define UNICODE
#endif // !UNICODE

#include <windows.h>
#include <new>

#include "Windows/MainWindow.h"

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    MainWindow win;

    if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
    {
        return 0;
    }

    ShowWindow(win.Window(), nCmdShow);

    // Run the message loop.

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

Windows/BaseWindow.h

#pragma once

#include <windows.h>

template <typename DERIVED_TYPE>
class BaseWindow
{
public:
    BaseWindow() : m_hwnd(NULL) { }
    
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    BOOL Create(
        PCWSTR lpWindowName,
        DWORD dwStyle,
        DWORD dwExStyle = 0,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        int nWidth = CW_USEDEFAULT,
        int nHeight = CW_USEDEFAULT,
        HWND hWndParent = 0,
        HMENU hMenu = 0
    );

    HWND Window() const { return m_hwnd; }

protected:

    virtual PCWSTR ClassName() const = 0;
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;

    HWND m_hwnd;
};

Windows/BaseWindow.cpp

#include "BaseWindow.h"
#include "MainWindow.h"

template <typename DERIVED_TYPE>
LRESULT CALLBACK BaseWindow<DERIVED_TYPE>::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    DERIVED_TYPE* pThis = NULL;

    if (uMsg == WM_NCCREATE)
    {
        CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
        pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
    }
    else
    {
        pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    }

    if (pThis)
    {
        return pThis->HandleMessage(uMsg, wParam, lParam);
    }
    else
    {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

template <typename DERIVED_TYPE>
BOOL BaseWindow<DERIVED_TYPE>::Create(
    PCWSTR lpWindowName,
    DWORD dwStyle,
    DWORD dwExStyle,
    int x,
    int y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu
)
{
    WNDCLASS wc = { 0 };

    wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = ClassName();

    RegisterClass(&wc);

    m_hwnd = CreateWindowEx(
        dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
        nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
    );

    return (m_hwnd ? TRUE : FALSE);
}

// !The definition of the class template
template class BaseWindow<MainWindow>;

Windows/MainWindow.h

#pragma once

#include "BaseWindow.h"

class MainWindow : public BaseWindow<MainWindow>
{
public:
    PCWSTR  ClassName() const { return L"Sample Window Class"; }
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};

Windows/MainWindow.cpp

#include "MainWindow.h"

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(m_hwnd, &ps);
        FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
        EndPaint(m_hwnd, &ps);
    }
    return 0;

    default:
        return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
    }
    return TRUE;
}

我试图在没有模板的情况下仅在类上做它并且它有效。

我还尝试在CreateWindowEx的参数列表末尾删除,它也有效,我的意思是,如果它看起来像这样:它将有效:

m_hwnd = CreateWindowEx(
    dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
    nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), NULL
);

所以也许我在某种程度上滥用模板。

CreateWindowEx返回之前会抽取几条消息。 除了一个 ( WM_NCCREATE ) 之外的所有都将通过 HandleMessage 进行路由,它期望m_hwnd是一个有效的句柄。 他们没有得到它,因为您永远不会保存它,直到CreateWindowEx的最终结果通过返回值获得。 到那时为时已晚。

WM_NCCREATE是顶层 windows(例如你的)接收到的第一个 window 消息,并且应该是用于(a)将 hwnd 参数保存到 m_hwnd,以及(b)将实例指针存储在GWLP_USERDATA空间中的消息。 你在做后者,但你没有做前者。

CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
pThis->m_hwnd = hwnd; // <===== ADD THIS =====
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);

HandleMessage()使用未初始化的m_hwnd 这就是CreateWindowEx()返回 NULL 的原因。

1. CreateWindowEx
2. sends WM_CREATE
3. WM_CREATE is processed by WindowProc
4. WindowProc calls HandleMessage, which uses m_hwnd,
   which is not initialized, so HandleMessage returns
   failure
5. Failure is returned as the result for WM_CREATE,
   cancelling window creation
6. CreateWindowEx fails and returns NULL <--- and right now
   m_hwnd is assigned

简而言之:在CreateWindowEx()返回之前,您不能使用m_hwnd

WM_CREATE 参考

如果应用程序处理此消息,它应该返回零以继续创建 window。 如果应用程序返回 –1,则 window 被销毁,并且 CreateWindowEx 或 CreateWindow function 返回 NULL 句柄。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM