[英]CreateWindowEx does not create HWND with the instance of a class template
我正在关注本教程,如果我将所有定义保存在一个 header 文件中,一切都很好并且可以工作(我的意思是,如果我直接复制此代码)。 但是如果我尝试将定义移动到单独的文件中,它不会创建 HWND。
在调用CreateWindowEx时,它会转到BaseWindow<DERIVED_TYPE>::WindowProc并按顺序一个接一个地发送以下消息:
退出后,会导致未创建 HWND。 因此,不会出现 window。
项目结构:
这就是我的代码的外观。
主文件
#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
。
如果应用程序处理此消息,它应该返回零以继续创建 window。 如果应用程序返回 –1,则 window 被销毁,并且 CreateWindowEx 或 CreateWindow function 返回 NULL 句柄。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.