在Win32API中注册窗口类

Registering a Window Class in the Win32API

本文关键字:窗口 注册 Win32API      更新时间:2023-10-16

尽管在网上阅读了大量信息,以及Petzold的书《Windows API编程》,并且几乎从书中复制了完全相同的方法,以及关于如何初始化OpenGL上下文的文档,但我还没能建立并运行Window class。

我试过在VC++和MinGW上编译(我使用的是QtCreator),看看这是否可行。我已经尝试将WNDCLASSEXA作为指针,并在堆栈上分配它。两者都没有骰子。

因此,我很不确定该怎么办。有时,类只是无法注册,而其他时候,从CreateWindowExA返回的HWND根本不起作用,并返回NULL。在尝试继续这个程序之后,尽管发生了这些事件,但我最终得到了一个无法绘制窗口的应用程序。

想法很简单:我有一个结构,用来简单地存储所有使用的数据(DEVMODEAWNDCLASSEXAHGLRC等)。

从那里,我使用该结构创建一个Window,然后将其传递回函数的调用方。

我真正想做的就是使用GLSL/ONGGL3.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 );
}

我还没能找到一个正在运行的Window Class。

使用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, '', 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