为什么 winapi 在 class 构造函数中用于 window class 时坚持使用 PCSTR 而不是 PCWSTR?
Why does winapi insist on using PCSTR instead of PCWSTR when used for window class from within class constructor?
我正在使用 WINAPI 编写一个简单的 UI。尽管我有 UNICODE 定义,但我在编译时遇到了一个问题,坚持使用函数的 A 版本。我有两个版本的代码,一个可以工作但不符合我的要求,另一个打包到 class 的构造函数中。提供的代码存在编译器错误,无法构建。
很抱歉,如果问题很长而且充满了代码,不知道如何在没有示例的情况下进行解释,但我希望太多总比太少好。
默认编译 Visual Studio 2017 编译器。
MainWindow class 继承自 BaseWindow 模板 class。
默认构造函数的工作版本如下:
MainWindow mainWindow;
if (!mainWindow.Create(L"Awesome Erpidzi", WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0, CW_USEDEFAULT, CW_USEDEFAULT, g_WindowWidth, g_WindowHeight))
{
return 0;
}
ShowWindow(mainWindow.Window(), ncmdShow);
并从父级创建函数 class:
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
)
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DERIVED_WINDOW_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);
}
ClassName函数很简单:
PCWSTR MainWindow::ClassName() const
{
return L"MainWindow";
}
然而,当我将函数打包到 MainWindow class 的构造函数中时,如下所示:
MainWindow::MainWindow(PCWSTR windowName, DWORD style, int width, int height)
{
Create(windowName, style, 0, CW_USEDEFAULT, CW_USEDEFAULT, width, height);
}
我收到一个编译错误
CreateWindowExA(DWORD,LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID)':
cannot convert argument 2 from 'PCWSTR' to 'LPCSTR'
通过调试我发现 wc.lpszClassName
处的 Create 函数需要 LPCSTR 类型。
我的问题是为什么当我将函数放入构造函数时它突然需要 ASCII 类型的字符串而不是 UNICODE ?我怎样才能修复它以使用宽字符串?
这是main、basewindow和mainwindow的完整cpp文件代码。
#ifndef UNICODE
#define UNICODE
#endif // !UNICODE
#include <Windows.h>
#include "IncludeAll.h"
const int g_WindowWidth = 1024;
const int g_WindowHeight = 768;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR args, int ncmdShow)
{
MainWindow mainWindow;
if (!mainWindow.Create(L"Awesome Erpidzi", WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0, CW_USEDEFAULT, CW_USEDEFAULT, g_WindowWidth, g_WindowHeight))
{
return 0;
}
ShowWindow(mainWindow.Window(), ncmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
#ifndef WIN_UI_BASE_INCLUDE
#define WIN_UI_BASE_INCLUDE
#include <Windows.h>
template <class DERIVED_WINDOW_TYPE>
class BaseWindow
{
public:
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_WINDOW_TYPE *pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_WINDOW_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
pThis->m_hwnd = hwnd;
}
else
{
pThis = (DERIVED_WINDOW_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
BaseWindow() : m_hwnd(NULL) { }
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
)
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DERIVED_WINDOW_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);
}
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;
};
#endif
#ifndef WIN_UI_MAINWINDOW_INCLUDE
#define WIN_UI_MAINWINDOW_INCLUDE
#include "WIN_UI_Base.h"
class MainWindow : public BaseWindow<MainWindow>
{
public:
MainWindow();
MainWindow(PCWSTR windowName,DWORD style, int width, int height);
PCWSTR ClassName() const;
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
#endif
#include "../IncludeAll.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);
ColourPicker& rColourPicker = UIHelpers::GetColourPicker();
const HBRUSH brush = rColourPicker.GetBrushWithColour(eColour::eWhite);
FillRect(hdc, &ps.rcPaint, brush);
EndPaint(m_hwnd, &ps);
}
return 0;
default:
return DefWindowProcW(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
MainWindow::MainWindow()
{
}
MainWindow::MainWindow(PCWSTR windowName, DWORD style, int width, int height)
{
Create(windowName, style, 0, CW_USEDEFAULT, CW_USEDEFAULT, width, height);
}
PCWSTR MainWindow::ClassName() const
{
return L"MainWindow";
}
看来主要问题是您只在一个源文件而不是所有源文件中定义了 UNICODE
。在 Visual Studio 中避免此问题的一种方法是在项目设置中将 UNICODE
定义为预处理器定义。
设置为
Project Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions
确保为所有配置添加此设置,而不仅仅是当前配置。
我正在使用 WINAPI 编写一个简单的 UI。尽管我有 UNICODE 定义,但我在编译时遇到了一个问题,坚持使用函数的 A 版本。我有两个版本的代码,一个可以工作但不符合我的要求,另一个打包到 class 的构造函数中。提供的代码存在编译器错误,无法构建。
很抱歉,如果问题很长而且充满了代码,不知道如何在没有示例的情况下进行解释,但我希望太多总比太少好。
默认编译 Visual Studio 2017 编译器。
MainWindow class 继承自 BaseWindow 模板 class。 默认构造函数的工作版本如下:
MainWindow mainWindow;
if (!mainWindow.Create(L"Awesome Erpidzi", WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0, CW_USEDEFAULT, CW_USEDEFAULT, g_WindowWidth, g_WindowHeight))
{
return 0;
}
ShowWindow(mainWindow.Window(), ncmdShow);
并从父级创建函数 class:
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
)
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DERIVED_WINDOW_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);
}
ClassName函数很简单:
PCWSTR MainWindow::ClassName() const
{
return L"MainWindow";
}
然而,当我将函数打包到 MainWindow class 的构造函数中时,如下所示:
MainWindow::MainWindow(PCWSTR windowName, DWORD style, int width, int height)
{
Create(windowName, style, 0, CW_USEDEFAULT, CW_USEDEFAULT, width, height);
}
我收到一个编译错误
CreateWindowExA(DWORD,LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID)': cannot convert argument 2 from 'PCWSTR' to 'LPCSTR'
通过调试我发现 wc.lpszClassName
处的 Create 函数需要 LPCSTR 类型。
我的问题是为什么当我将函数放入构造函数时它突然需要 ASCII 类型的字符串而不是 UNICODE ?我怎样才能修复它以使用宽字符串?
这是main、basewindow和mainwindow的完整cpp文件代码。
#ifndef UNICODE
#define UNICODE
#endif // !UNICODE
#include <Windows.h>
#include "IncludeAll.h"
const int g_WindowWidth = 1024;
const int g_WindowHeight = 768;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR args, int ncmdShow)
{
MainWindow mainWindow;
if (!mainWindow.Create(L"Awesome Erpidzi", WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0, CW_USEDEFAULT, CW_USEDEFAULT, g_WindowWidth, g_WindowHeight))
{
return 0;
}
ShowWindow(mainWindow.Window(), ncmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
#ifndef WIN_UI_BASE_INCLUDE
#define WIN_UI_BASE_INCLUDE
#include <Windows.h>
template <class DERIVED_WINDOW_TYPE>
class BaseWindow
{
public:
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_WINDOW_TYPE *pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_WINDOW_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
pThis->m_hwnd = hwnd;
}
else
{
pThis = (DERIVED_WINDOW_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
BaseWindow() : m_hwnd(NULL) { }
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
)
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DERIVED_WINDOW_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);
}
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;
};
#endif
#ifndef WIN_UI_MAINWINDOW_INCLUDE
#define WIN_UI_MAINWINDOW_INCLUDE
#include "WIN_UI_Base.h"
class MainWindow : public BaseWindow<MainWindow>
{
public:
MainWindow();
MainWindow(PCWSTR windowName,DWORD style, int width, int height);
PCWSTR ClassName() const;
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
#endif
#include "../IncludeAll.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);
ColourPicker& rColourPicker = UIHelpers::GetColourPicker();
const HBRUSH brush = rColourPicker.GetBrushWithColour(eColour::eWhite);
FillRect(hdc, &ps.rcPaint, brush);
EndPaint(m_hwnd, &ps);
}
return 0;
default:
return DefWindowProcW(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
MainWindow::MainWindow()
{
}
MainWindow::MainWindow(PCWSTR windowName, DWORD style, int width, int height)
{
Create(windowName, style, 0, CW_USEDEFAULT, CW_USEDEFAULT, width, height);
}
PCWSTR MainWindow::ClassName() const
{
return L"MainWindow";
}
看来主要问题是您只在一个源文件而不是所有源文件中定义了 UNICODE
。在 Visual Studio 中避免此问题的一种方法是在项目设置中将 UNICODE
定义为预处理器定义。
设置为
Project Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions
确保为所有配置添加此设置,而不仅仅是当前配置。