CreateWindowEx 不使用 class 模板的实例创建 HWND
CreateWindowEx does not create HWND with the instance of a class template
我正在关注 this tutorial and if I keep all the definitions in a single header file, it's all fine and works (I mean, if I directly copy this code)。但是如果我尝试将定义移动到单独的文件中,它不会创建 HWND。
在调用 CreateWindowEx 时,它转到 BaseWindow::WindowProc 并发送以下消息,按顺序一个接一个:
- WM_GETMINMAXINFO
- WM_NCCREATE
- WM_NCDESTROY
它跳出后,导致HWND没有被创建。因此,不会出现 window。
项目结构:
- main.cpp
- Windows/
- BaseWindow.h
- BaseWindow.cpp
- MainWindow.h
- MainWindow.cpp
这是我的代码的样子。
main.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 参数列表末尾的 this 并且它也有效!我的意思是,如果它看起来像那样,它就会起作用:
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), NULL
);
所以我可能在某种程度上滥用了模板。
HandleMessage()
使用m_hwnd
,未初始化。这就是为什么 CreateWindowEx()
returns 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
简而言之:您不能使用 m_hwnd
直到 CreateWindowEx()
returns。
If an application processes this message, it should return zero to continue creation of the window. If the application returns –1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle.
在 CreateWindowEx
return 之前将发送几条消息。除了一个 (WM_NCCREATE
) 之外,所有的都将通过 HandleMessage 路由,它期望 m_hwnd
是一个有效的句柄。他们没有得到它,因为你永远不会保存它,直到 CreateWindowEx
的最终结果被 return 值获得。到时候就晚了。
WM_NCCREATE
是顶级 windows(例如你的)收到的第一条 window 消息,应该是用于 (a) 将 hwnd 参数保存到 m_hwnd,以及 (b) 存储实例指针 int GWLP_USERDATA
space。你做的是后者,而你没有做前者。
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
pThis->m_hwnd = hwnd; // <===== ADD THIS =====
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
我正在关注 this tutorial and if I keep all the definitions in a single header file, it's all fine and works (I mean, if I directly copy this code)。但是如果我尝试将定义移动到单独的文件中,它不会创建 HWND。
在调用 CreateWindowEx 时,它转到 BaseWindow
- WM_GETMINMAXINFO
- WM_NCCREATE
- WM_NCDESTROY
它跳出后,导致HWND没有被创建。因此,不会出现 window。
项目结构:
- main.cpp
- Windows/
- BaseWindow.h
- BaseWindow.cpp
- MainWindow.h
- MainWindow.cpp
这是我的代码的样子。
main.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 参数列表末尾的 this 并且它也有效!我的意思是,如果它看起来像那样,它就会起作用:
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), NULL
);
所以我可能在某种程度上滥用了模板。
HandleMessage()
使用m_hwnd
,未初始化。这就是为什么 CreateWindowEx()
returns 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
简而言之:您不能使用 m_hwnd
直到 CreateWindowEx()
returns。
If an application processes this message, it should return zero to continue creation of the window. If the application returns –1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle.
在 CreateWindowEx
return 之前将发送几条消息。除了一个 (WM_NCCREATE
) 之外,所有的都将通过 HandleMessage 路由,它期望 m_hwnd
是一个有效的句柄。他们没有得到它,因为你永远不会保存它,直到 CreateWindowEx
的最终结果被 return 值获得。到时候就晚了。
WM_NCCREATE
是顶级 windows(例如你的)收到的第一条 window 消息,应该是用于 (a) 将 hwnd 参数保存到 m_hwnd,以及 (b) 存储实例指针 int GWLP_USERDATA
space。你做的是后者,而你没有做前者。
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
pThis->m_hwnd = hwnd; // <===== ADD THIS =====
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);