【问题标题】:Win32 API C++ Hwnd Returning NullWin32 API C++ Hwnd 返回 Null
【发布时间】:2021-08-09 16:22:33
【问题描述】:

我有一个简单的 WIN3API 应用程序, 起初它完全可以工作,当我决定通过添加 cmets 和空格来进行代码清理以使代码对自己更具可读性时,我可以轻松地添加更多功能,接下来我知道代码不起作用。当主函数运行'Mainhwnd'返回'NULL'并关闭时,我认为它可能是类注册,虽然它似乎不是问题,任何帮助将不胜感激!谢谢, 这是一个代码sn-p:

int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    const wchar_t MainWndClass[] = L"MainWndClass";       // Creating And Registering Main Class
    WNDCLASS MainClass = { };
    MainClass.lpfnWndProc = WindowProc;
    MainClass.hInstance = hInstance;
    MainClass.lpszClassName = MainWndClass;
    if (!RegisterClass(&MainClass))
    {
        MessageBox(NULL, L"Error Registering Window Class", L"Error Registering Window", MB_OK | MB_ICONEXCLAMATION);
        return 0;
    }
Mainhwnd = CreateWindowEx
    (
        0,                              // Optional window styles.
        MainWndClass,                   // Window class
        L"Main Wnd",                    // Window text
        WS_VISIBLE,                     // Window style
        CW_USEDEFAULT,                  // XPos
        CW_USEDEFAULT,                  // YPos
        MainWindWidth,                  // Width
        MainWindHeight,                 // Height
        NULL,                           // Parent window    
        NULL,                           // Menu
        hInstance,                      // Instance handle
        NULL                            // Additional application data
    );
    if (Mainhwnd == NULL)               // Quit If Error
    {
        MessageBox(NULL, L"Error Creating Window; Window Handle Is NULL", L"Error Creating Window", MB_OK | MB_ICONEXCLAMATION);
        return 0;
    }

完整代码

// Includes

#include <iostream>
#include <windows.h>

// Definitions

#define BUTTON_ONE_ID 0001
#define BUTTON_TWO_ID 0002
#define BUTTON_THREE_ID 0003
#define BUTTON_FOUR_ID 0004
#ifndef UNICODE
#define UNICODE
#endif

// Global Variables

HWND Mainhwnd;
HMENU hMenu;
HWND ButtonOne;
HWND ButtonTwo;
HWND ButtonThree;
HWND ButtonFour;
INT MainWindWidth = 800;
INT MainWindHeight = 600;
INT ButtonWidth = MainWindWidth/4;
INT ButtonHeight = 100;

// Prototypes

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void AddMenus(HWND);
void AddControls(HWND);

// Entry Point

int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    const wchar_t MainWndClass[] = L"MainWndClass";       // Creating And Registering Main Class
    WNDCLASS MainClass = { };
    MainClass.lpfnWndProc = WindowProc;
    MainClass.hInstance = hInstance;
    MainClass.lpszClassName = MainWndClass;
    if (!RegisterClass(&MainClass))
    {
        MessageBox(NULL, L"Error Registering Window Class", L"Error Registering Window", MB_OK | MB_ICONEXCLAMATION);
        return 0;
    }
    Mainhwnd = CreateWindowEx
    (
        0,                              // Optional window styles.
        MainWndClass,                   // Window class
        L"Main Wnd",                    // Window text
        WS_VISIBLE,                     // Window style
        CW_USEDEFAULT,                  // XPos
        CW_USEDEFAULT,                  // YPos
        MainWindWidth,                  // Width
        MainWindHeight,                 // Height
        NULL,                           // Parent window    
        NULL,                           // Menu
        hInstance,                      // Instance handle
        NULL                            // Additional application data
    );
    if (Mainhwnd == NULL)               // Quit If Error
    {
        MessageBox(NULL, L"Error Creating Window; Window Handle Is NULL", L"Error Creating Window", MB_OK | MB_ICONEXCLAMATION);
        return 0;
    }
    ShowWindow(Mainhwnd, nCmdShow);     // Show Window
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

// Create Window Procedure

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_COMMAND:
        switch (wParam)
        {
        case BUTTON_ONE_ID:
            std::cout << "B1" << std::endl;
            return 0;
        case BUTTON_TWO_ID:
            std::cout << "B2" << std::endl;
            return 0;
        case BUTTON_THREE_ID:
            std::cout << "B3" << std::endl;
            return 0;
        case BUTTON_FOUR_ID:
            std::cout << "B4" << std::endl;
            return 0;
        }
        return 0;

    case WM_CREATE:
        AddMenus(hwnd);
        AddControls(hwnd);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 2));
        EndPaint(hwnd, &ps);
    }
    default:
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void AddMenus(HWND hwnd)                    // Add Menus Function
{
    hMenu = CreateMenu();
    HMENU hFileMenu = CreateMenu();
    SetMenu(hwnd, hMenu);
}
void AddControls(HWND hwnd)                 // Add Controls Function
{
    ButtonOne = CreateWindowW
    (
        L"Button", L"Button1", WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_ONE_ID, NULL, NULL
    );
    ButtonTwo = CreateWindowW
    (
        L"Button", L"Button2", WS_CHILD | WS_VISIBLE | WS_BORDER, ButtonWidth, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_TWO_ID, NULL, NULL
    );
    ButtonThree = CreateWindowW
    (
        L"Button", L"Button3", WS_CHILD | WS_VISIBLE | WS_BORDER, ButtonWidth * 2, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_THREE_ID, NULL, NULL
    );
    ButtonFour = CreateWindowW
    (
        L"Button", L"Button4", WS_CHILD | WS_VISIBLE | WS_BORDER, ButtonWidth * 3, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_FOUR_ID, NULL, NULL
    );
}

【问题讨论】:

  • 您没有收到任何编译器警告吗?
  • 首先要做的是阅读documentation。它说“如果函数失败,则返回值为 NULL。要获取扩展的错误信息,请调用 GetLastError。”
  • default: return 0; 是错误。它可以防止 DefWindowProc() 被调用。所以 WM_NCCREATE 没有得到处理。也没有错误,您明确表示您自己处理。删掉就好了。
  • 这能回答你的问题吗? Why CreateWindowEx() return NULL
  • FWIW 在#define &lt;windows.h&gt; 之后更改UNICODE 的定义是一个非常糟糕的主意...

标签: c++ visual-studio winapi


【解决方案1】:

调用CreateWindowExW 的结果属于以下三个类别之一:“失败”、“取消”或“成功”。下表根据函数的返回值和GetLastError的返回值列出了这些类别:

Return value GetLastError Description
NULL non-zero Window creation failed due to an error
NULL zero Window creation was cancelled by client code
non-NULL <indeterminate> Window creation succeeded

由于它与这里的讨论无关,让我们把最后一行移开:函数调用返回一个非NULL 值,表明窗口创建成功。 GetLastError 的返回值在这种情况下是没有意义的,实际上可以是任何值。

第一行标记一个真正的错误条件。由于编程错误(例如传入无效的窗口类、窗口句柄等)或资源耗尽等环境问题,无法满足窗口创建请求。

上表中的第二行是经常引起混淆的行。没有创建窗口,但也没有报告错误。等等,什么?这怎么会失败而没有错误!?

请求失败

可能看起来不直观,负责窗口类实例行为的窗口过程已经参与到创建该实例的过程中。在窗口构造期间,有两个1 窗口消息发送到与窗口类关联的窗口过程。任何一个都可以提前放弃请求2WM_NCCREATEWM_CREATE

WM_CREATE的返回值的语义包括:

如果应用程序返回 -1,则窗口被销毁,CreateWindowExCreateWindow 函数返回一个 NULL 句柄。

在此之前,会发送一条WM_NCCREATE 消息。它的返回值的语义也被记录在案:

如果应用程序返回 FALSECreateWindowCreateWindowEx 函数将返回 NULL 句柄。

这就是回答问题所需的全部信息:

default:
    return 0;

对于任何未处理的消息,包括 WM_NCCREATE,这将返回 0(解释为 FALSE)。由于窗口过程返回的WM_NCCREATE 消息没有显式消息处理程序,因此终止创建窗口的请求。

要解决此问题3,您需要将默认处理添加到default: 标签,即

default:
    return DefWindowProc(hwnd, uMsg, wParam, lParam);

并删除尾随的 return 语句4,或完全删除 default 标签。


1未来的访问者可能会发现这不准确。抱歉,我没有时间机器。

2字面意思。

3Your compiler probably warned you about this.

4确保一路修复(可能是无意的)WM_PAINT 掉入default

【讨论】:

    【解决方案2】:

    固定工作代码:

    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {   
        switch (uMsg)
        {
        case WM_COMMAND:
            switch (wParam)
            {
            case BUTTON_ONE_ID:
                std::cout << "B1" << std::endl;
                break;
            case BUTTON_TWO_ID:
                std::cout << "B2" << std::endl;
                break;
            case BUTTON_THREE_ID:
                std::cout << "B3" << std::endl;
                break;
            case BUTTON_FOUR_ID:
                std::cout << "B4" << std::endl;
                break;
            }
            break;
    
        case WM_CREATE:
            AddMenus(hwnd);
            AddControls(hwnd);
            break;
    
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    
        case WM_PAINT:
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 2));
            EndPaint(hwnd, &ps);
        }
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    void AddMenus(HWND hwnd)                    // Add Menus Function
    {
        hMenu = CreateMenu();
        HMENU hFileMenu = CreateMenu();
        SetMenu(hwnd, hMenu);
    }
    void AddControls(HWND hwnd)                 // Add Controls Function
    {
        
        ButtonOne = CreateWindowW
        (
            L"Button", L"Button1", WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_ONE_ID, NULL, NULL
        );
        ButtonTwo = CreateWindowW
        (
            L"Button", L"Button2", WS_CHILD | WS_VISIBLE | WS_BORDER, ButtonWidth, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_TWO_ID, NULL, NULL
        );
        ButtonThree = CreateWindowW
        (
            L"Button", L"Button3", WS_CHILD | WS_VISIBLE | WS_BORDER, ButtonWidth * 2, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_THREE_ID, NULL, NULL
        );
        ButtonFour = CreateWindowW
        (
            L"Button", L"Button4", WS_CHILD | WS_VISIBLE | WS_BORDER, ButtonWidth * 3, 475, ButtonWidth, 100, hwnd, (HMENU)BUTTON_FOUR_ID, NULL, NULL
        );
    }
    

    【讨论】:

    • WM_PAINT 处理程序仍有缺陷。您应该返回 0 。你的编译器也应该警告你这一点。
    • 我明白了,Visual Studio 2019 没有在运行时或编译时引发任何类型的问题,谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    相关资源
    最近更新 更多