子类化 winapi Window
Subclassing a winapi Window
我正在尝试在 Windows (Winapi) 上子 class 一个 Window。请原谅我的知识匮乏,Windows 样式 subclassing 与我从 Qt 中了解到的完全不同。
所以我有一个像这样的基础class Window(代码摘自deimos1877),这是最重要的部分:
class BorderlessWindow
{
enum class Style : DWORD
{
windowed = ( WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN | WS_SYSMENU ),
aero_borderless = ( WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN )
};
public:
HWND hWnd;
HINSTANCE hInstance;
BorderlessWindow( HBRUSH windowBackground, const int x, const int y, const int width, const int height );
~BorderlessWindow();
static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
};
源文件部分:
HWND winId = 0;
BorderlessWindow::BorderlessWindow( HBRUSH windowBackground, const int x, const int y, const int width, const int height )
: hWnd( 0 ),
hInstance( GetModuleHandle( NULL ) ),
borderless( false ),
borderlessResizeable( true ),
aeroShadow( false ),
closed( false ),
visible( false )
{
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof( WNDCLASSEX );
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.hInstance = hInstance;
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.lpszClassName = L"WindowClass";
wcx.hbrBackground = windowBackground;
wcx.hCursor = LoadCursor( hInstance, IDC_ARROW );
RegisterClassEx( &wcx );
if ( FAILED( RegisterClassEx( &wcx ) ) ) {
throw std::runtime_error( "Couldn't register window class" );
}
hWnd = CreateWindow( L"WindowClass", L"Updater", static_cast<DWORD>( Style::windowed ), x, y, width, height, 0, 0, hInstance, nullptr );
if ( !hWnd ){
throw std::runtime_error( "Couldn't create window because of reasons" );
}
SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>( this ) );
show();
}
LRESULT CALLBACK BorderlessWindow::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
std::cout<<"BorderlessWindow::WndProc"<<std::endl;
BorderlessWindow *window = reinterpret_cast<BorderlessWindow*>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
if ( !window ) return DefWindowProc( hWnd, message, wParam, lParam );
switch ( message ) {
// ...
}
return DefWindowProc(hWnd, message, wParam, lParam);
一开始,subclass-to-be的所有代码都在BorderlessWindowclass中首发。本质上,我将一些图像和文本绘制到 Window 中。在我的第一次 subclass 尝试(此处未显示)中,我没有处理 WndProc,我认为这就是图像和文本不再绘制的原因。所以这是我最后一次尝试:
class Dialog : public BorderlessWindow
{
public:
explicit Dialog();
~Dialog() = default;
void updateTitlebar(HWND hwndParent);
void updateImage(HWND hwndParent);
void updateText(HWND hwndParent);
void setupContent(HWND hwndParent);
protected:
static LRESULT DialogWndProc(HWND hWnd, UINT msg, WPARAM w, LPARAM l, UINT_PTR uid, DWORD_PTR RefData){
std::cout << "Dialog::SubclassWndProc" << std::endl;
Dialog *dlg = reinterpret_cast<Dialog*>(RefData);
if ( !dlg ){
return DefSubclassProc(hWnd, msg, w, l);
}
switch (msg) {
case WM_PAINT:{
dlg->updateTitlebar(hWnd);
dlg->updateText(hWnd);
dlg->updateImage(hWnd);
break;
}
default:
return DefSubclassProc(hWnd, msg, w, l);
}
}
};
请注意,我更改了新 WndProc 的签名以包含参数 UINT_PTR uid
,因为最初我收到此错误:
invalid conversion from
'LRESULT (*)(HWND, UINT, WPARAM, LPARAM, DWORD_PTR)
{aka long int (*)
(HWND__*, unsigned int, unsigned int, long int, long unsigned int)}' to
'SUBCLASSPROC
{aka long int (__attribute__((__stdcall__)) *)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' [-fpermissive]
SetWindowSubclass(hWnd,DialogWndProc,0,(DWORD_PTR)this);
添加参数后,根据headercommctrl.h
typedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT_PTR uIdSubclass,DWORD_PTR dwRefData);
WINBOOL WINAPI SetWindowSubclass(HWND hWnd,SUBCLASSPROC pfnSubclass,UINT_PTR uIdSubclass,DWORD_PTR dwRefData);
它仍然没有编译,显示以下错误:
invalid conversion from
'LRESULT (*)(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR)
{aka long int (*)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' to '
SUBCLASSPROC
{aka long int (__attribute__((__stdcall__)) *)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' [-fpermissive]
SetWindowSubclass(hWnd,DialogWndProc,NULL,(DWORD_PTR)this);
现在签名匹配了,不是吗?我没有正确理解错误。我至少在正确的轨道上吗?
它们并不完全相同。您需要将声明更改为:
static LRESULT __stdcall DialogWndProc(HWND hWnd, UINT msg,
WPARAM w, LPARAM l, UINT_PTR uid, DWORD_PTR RefData)
注意 __stdcall
我正在尝试在 Windows (Winapi) 上子 class 一个 Window。请原谅我的知识匮乏,Windows 样式 subclassing 与我从 Qt 中了解到的完全不同。
所以我有一个像这样的基础class Window(代码摘自deimos1877),这是最重要的部分:
class BorderlessWindow
{
enum class Style : DWORD
{
windowed = ( WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN | WS_SYSMENU ),
aero_borderless = ( WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN )
};
public:
HWND hWnd;
HINSTANCE hInstance;
BorderlessWindow( HBRUSH windowBackground, const int x, const int y, const int width, const int height );
~BorderlessWindow();
static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
};
源文件部分:
HWND winId = 0;
BorderlessWindow::BorderlessWindow( HBRUSH windowBackground, const int x, const int y, const int width, const int height )
: hWnd( 0 ),
hInstance( GetModuleHandle( NULL ) ),
borderless( false ),
borderlessResizeable( true ),
aeroShadow( false ),
closed( false ),
visible( false )
{
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof( WNDCLASSEX );
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.hInstance = hInstance;
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.lpszClassName = L"WindowClass";
wcx.hbrBackground = windowBackground;
wcx.hCursor = LoadCursor( hInstance, IDC_ARROW );
RegisterClassEx( &wcx );
if ( FAILED( RegisterClassEx( &wcx ) ) ) {
throw std::runtime_error( "Couldn't register window class" );
}
hWnd = CreateWindow( L"WindowClass", L"Updater", static_cast<DWORD>( Style::windowed ), x, y, width, height, 0, 0, hInstance, nullptr );
if ( !hWnd ){
throw std::runtime_error( "Couldn't create window because of reasons" );
}
SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>( this ) );
show();
}
LRESULT CALLBACK BorderlessWindow::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
std::cout<<"BorderlessWindow::WndProc"<<std::endl;
BorderlessWindow *window = reinterpret_cast<BorderlessWindow*>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
if ( !window ) return DefWindowProc( hWnd, message, wParam, lParam );
switch ( message ) {
// ...
}
return DefWindowProc(hWnd, message, wParam, lParam);
一开始,subclass-to-be的所有代码都在BorderlessWindowclass中首发。本质上,我将一些图像和文本绘制到 Window 中。在我的第一次 subclass 尝试(此处未显示)中,我没有处理 WndProc,我认为这就是图像和文本不再绘制的原因。所以这是我最后一次尝试:
class Dialog : public BorderlessWindow
{
public:
explicit Dialog();
~Dialog() = default;
void updateTitlebar(HWND hwndParent);
void updateImage(HWND hwndParent);
void updateText(HWND hwndParent);
void setupContent(HWND hwndParent);
protected:
static LRESULT DialogWndProc(HWND hWnd, UINT msg, WPARAM w, LPARAM l, UINT_PTR uid, DWORD_PTR RefData){
std::cout << "Dialog::SubclassWndProc" << std::endl;
Dialog *dlg = reinterpret_cast<Dialog*>(RefData);
if ( !dlg ){
return DefSubclassProc(hWnd, msg, w, l);
}
switch (msg) {
case WM_PAINT:{
dlg->updateTitlebar(hWnd);
dlg->updateText(hWnd);
dlg->updateImage(hWnd);
break;
}
default:
return DefSubclassProc(hWnd, msg, w, l);
}
}
};
请注意,我更改了新 WndProc 的签名以包含参数 UINT_PTR uid
,因为最初我收到此错误:
invalid conversion from
'LRESULT (*)(HWND, UINT, WPARAM, LPARAM, DWORD_PTR)
{aka long int (*)
(HWND__*, unsigned int, unsigned int, long int, long unsigned int)}' to
'SUBCLASSPROC
{aka long int (__attribute__((__stdcall__)) *)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' [-fpermissive]
SetWindowSubclass(hWnd,DialogWndProc,0,(DWORD_PTR)this);
添加参数后,根据headercommctrl.h
typedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT_PTR uIdSubclass,DWORD_PTR dwRefData);
WINBOOL WINAPI SetWindowSubclass(HWND hWnd,SUBCLASSPROC pfnSubclass,UINT_PTR uIdSubclass,DWORD_PTR dwRefData);
它仍然没有编译,显示以下错误:
invalid conversion from
'LRESULT (*)(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR)
{aka long int (*)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' to '
SUBCLASSPROC
{aka long int (__attribute__((__stdcall__)) *)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' [-fpermissive]
SetWindowSubclass(hWnd,DialogWndProc,NULL,(DWORD_PTR)this);
现在签名匹配了,不是吗?我没有正确理解错误。我至少在正确的轨道上吗?
它们并不完全相同。您需要将声明更改为:
static LRESULT __stdcall DialogWndProc(HWND hWnd, UINT msg,
WPARAM w, LPARAM l, UINT_PTR uid, DWORD_PTR RefData)
注意 __stdcall