在 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 成为一个指针,并将它分配到堆栈上。两者都没有骰子。

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

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

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

我真正想做的就是使用 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 的代码有效——我已经使用过了。我们能猜到哪里出了问题吗? 好吧,我出去了。下一次,我建议花更多时间以非常精确的术语定义问题。 我建议您将其缩减为可重现的完整程序,然后发布代码。去掉所有无关的东西,只让程序调用RegisterClassExCreateWindowExShowWindowUpdateWindow 和消息循环。 一个好的开始是删除所有 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 中注册一个窗口类的主要内容,如果未能解决你的问题,请参考以下文章

Win32API 窗口程序的创建7大步骤

如何在 Win32 控制台应用程序中注册不可见的窗口类?

VC++ win32 application里如何建一个子窗口

4.win32注册窗口类和创建窗口

Win32API的小问题

WIN32 SDK API的基础问题(窗口显示)