在 Win32API 中注册一个窗口类
Posted
技术标签:
【中文标题】在 Win32API 中注册一个窗口类【英文标题】:Registering a Window Class in the Win32API 【发布时间】:2012-03-18 20:06:54 【问题描述】:尽管阅读了整个网络上的大量信息,以及 Petzold 的书,Windows API 编程,并且该死的几乎从书中复制了完全相同的方法,以及关于如何操作的 this 文档要初始化 OpenGL 上下文,我无法启动并运行 Window 类。
我尝试在 VC++ 和 MinGW 上进行编译(我正在使用 Qt Creator),看看这是否可行。我试过让我的WNDCLASSEXA
成为一个指针,并将它分配到堆栈上。两者都没有骰子。
因此,我很不确定该怎么做。有时该类根本无法注册,而其他时候从 CreateWindowExA
simply 返回的 HWND
不起作用并返回 NULL。在尝试继续程序后,尽管发生了这些事件,但我最终得到了一个无法绘制窗口的应用程序。
这个想法很简单:我有一个结构用来简单地存储所有使用的数据(DEVMODEA
、WNDCLASSEXA
、HGLRC
等)。
从那里,我使用该结构创建一个窗口,然后将其传递回函数的调用者。
我真正想做的就是使用 GLSL/OpenGL 3.3 在 OpenGL 中编写一个简单的类似乒乓球的游戏。为此,我显然首先需要上下文,但我无法辨别问题是 Qt Creator、Windows 还是其他问题。
那么,我做错了什么?
GameData
结构
typedef struct
HGLRC hrc;
HDC hdc;
HWND hwnd;
HINSTANCE hInstance;
UINT numFormats;
WNDCLASSEXA* winClass;
DWORD dwExStyle;
DWORD dwStyle;
RECT winRect;
DEVMODEA screenSettings;
bool fullscreen;
const char* winClassName;
int pixelFormat;
bool keys[ 256 ];
bool active;
GameData;
initPong()
函数
static GameData* initContextAndWindow( void )
GameData* dat = new GameData;
const int width = 640;
const int height = 480;
const int bitsPerPixel = 32;
dat->winRect.left = ( long )0;
dat->winRect.right = ( long )width;
dat->winRect.top = ( long )0;
dat->winRect.bottom = ( long )height;
dat->fullscreen = false;
dat->hInstance = GetModuleHandleA( NULL );
dat->winClass = ( WNDCLASSEXA* )calloc( sizeof( WNDCLASSEXA ), 1 );
if( !dat->winClass )
MessageBoxA( NULL, "Something wrong!", "ERROR", MB_OK | MB_ICONINFORMATION );
dat->winClass->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
dat->winClass->lpfnWndProc = ( WNDPROC ) eventHandler;
dat->winClass->cbClsExtra = 1;
dat->winClass->cbWndExtra = 1;
dat->winClass->cbSize = sizeof( WNDCLASSEXA );
dat->winClass->hInstance = dat->hInstance;
dat->winClass->hIcon = LoadIcon( NULL, IDI_WINLOGO );
dat->winClass->hCursor = LoadCursor( NULL, IDC_ARROW );
dat->winClass->hbrBackground = ( HBRUSH ) GetStockObject( WHITE_BRUSH );
dat->winClass->lpszMenuName = NULL;
dat->winClass->lpszClassName = "PongDH";
if ( !RegisterClassExA( dat->winClass ) )
MessageBoxA( NULL, "Failed to register class.", "ERROR", MB_OK | MB_ICONINFORMATION );
exit( 1 );
if ( dat->fullscreen )
memset( &dat->screenSettings, 0, sizeof( dat->screenSettings ) );
dat->screenSettings.dmSize = sizeof( dat->screenSettings );
dat->screenSettings.dmPelsWidth = width;
dat->screenSettings.dmPelsHeight = height;
dat->screenSettings.dmBitsPerPel = bitsPerPixel;
dat->screenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if ( ChangeDisplaySettingsA( &dat->screenSettings, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL )
dat->fullscreen = false;
const int continuePlaying = MessageBoxA(
NULL,
"Could not implement fullscreen. Please check your drivers. Do you plan to continue?",
"ERROR",
MB_YESNO | MB_ICONEXCLAMATION
);
if ( continuePlaying == IDYES )
MessageBoxA( NULL, "Will revert back to fullscreen.", "Notifcation", MB_OK );
dat->fullscreen = false;
else
MessageBoxA( NULL, "The program will now close", "Notification", MB_OK );
exit( 1 );
if ( dat->fullscreen )
dat->dwExStyle = WS_EX_APPWINDOW;
dat->dwStyle = WS_POPUP;
ShowCursor( FALSE );
else
dat->dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dat->dwStyle = WS_OVERLAPPEDWINDOW;
AdjustWindowRectEx( &dat->winRect, dat->dwStyle, FALSE, dat->dwExStyle );
dat->hwnd = CreateWindowExA(
dat->dwStyle,
dat->winClass->lpszClassName,
"PongDH",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
dat->winRect.right,
dat->winRect.bottom,
NULL,
NULL,
dat->hInstance,
NULL
);
if ( dat->hwnd == NULL )
MessageBoxA( NULL, "Failed to create window; exiting program.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
exit( 1 );
const int attrList[] =
WGL_DRAW_TO_WINDOW_ARB , GL_TRUE,
WGL_SUPPORT_OPENGL_ARB , GL_TRUE,
WGL_DOUBLE_BUFFER_ARB , GL_TRUE,
WGL_PIXEL_TYPE_ARB , WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB , 32,
WGL_DEPTH_BITS_ARB , 24,
WGL_STENCIL_BITS_ARB , 8,
0,
;
wglChoosePixelFormatARB( dat->hdc, attrList, NULL, 1, &dat->pixelFormat, &dat->numFormats );
const int contextList[] =
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
0,
;
dat->hrc = wglCreateContextAttribsARB( dat->hdc, NULL, contextList );
if( !wglMakeCurrent( dat->hdc, dat->hrc ) )
MessageBoxA( NULL, "Error making OpenGL Rendering Context current.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
exit( 1 );
ShowWindow( dat->hwnd, SW_SHOW );
SetForegroundWindow( dat->hwnd );
SetFocus( dat->hwnd );
resizeScene( width, height );
UpdateWindow( dat->hwnd );
glEnable( GL_DEPTH_TEST );
return dat;
更新
在这里,我将发布我所做的过程:
我首先尝试将cbClsExtra
设置为1,而之前为0。然后我将cbWndExtra
设置为1。之后我尝试将cbSize
设置为sizeof( WNDCLASSEXA )
。
我也尝试在 VC++ 和 MinGW 下编译;在 VC++ 中,该类根本无法注册,而在 MinGW 中,该类将注册,但实际上不会创建所需的hwnd
。
我还尝试通过将WNDCLASSEXA
(即dat->winClass
)设为指针而不是堆栈分配的变量来编辑我的代码。
我还在我的if
检查中注释掉了我的exit
函数,以查看类没有注册,或者hwnd
没有注册 创建。这会在尝试使用 wglChoosePixelFormatARB
渲染 OpenGL 上下文时产生分段错误。
更新 2
这是我的 WndProc:
LRESULT CALLBACK eventHandler( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
return DefWindowProcA( hwnd, uMsg, wParam, lParam );
【问题讨论】:
问题中的代码怎么会失败? Petzold 的代码有效——我已经使用过了。我们能猜到哪里出了问题吗? 好吧,我出去了。下一次,我建议花更多时间以非常精确的术语定义问题。 我建议您将其缩减为可重现的完整程序,然后发布代码。去掉所有无关的东西,只让程序调用RegisterClassEx
、CreateWindowEx
、ShowWindow
、UpdateWindow
和消息循环。
一个好的开始是删除所有 C 样式转换。让它在没有演员表的情况下编译。演员表只是隐藏错误。
【参考方案1】:
我无法启动并运行窗口类。
使用 WinAPI 注册和创建窗口真的没有很多。
以这个简单的 test.cpp 文件为例:
#define STRICT
#include <windows.h>
long PASCAL WndProc (HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
static char szClassName[] = "Hello World";
MSG msg;
WNDCLASS wndclass;
memset(&wndclass, '\0', sizeof(wndclass));
if (!hPrevInstance)
// define the 'Hello World' window class
wndclass.style = CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = 0;
wndclass.lpszClassName = szClassName;
// register the 'Hello World' window class
RegisterClass (&wndclass);
// create a new window that is a 'Hello World' window class
HWND hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
szClassName,
"My Hello World Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow (hwnd, nCmdShow);
while (GetMessage (&msg, NULL, 0, 0))
TranslateMessage (&msg);
DispatchMessage (&msg);
return msg.wParam;
long APIENTRY WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
switch (message)
case WM_DESTROY:
PostQuitMessage (0);
return 0;
return DefWindowProc (hwnd, message, wParam, lParam);
可以从命令行编译和链接:
C:\TEMP>cl test.cpp user32.lib gdi32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.cpp
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
user32.lib
gdi32.lib
然后生成的test.exe可以运行,它会显示一个窗口:
C:\TEMP>test.exe
【讨论】:
以上是关于在 Win32API 中注册一个窗口类的主要内容,如果未能解决你的问题,请参考以下文章