Gijahara
Gijahara

Reputation: 71

Win32 API C++ Hwnd Returning Null

I've got a simple WIN3API application, It was working completely at first, when I decided to do a code clean-up by adding comments and spaces to make the code more readable due for myself so I could easily add more functionality, next thing I know the code does not work. When the main function runs the 'Mainhwnd' returns 'NULL' and closes, I assumed that it might've been the class registering although it does not seem to be the problem, any help would be utmost appreciated! Thanks, Here's a code snippet:

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;
    }

Full Code

// 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
    );
}

Upvotes: 1

Views: 1693

Answers (2)

Gijahara
Gijahara

Reputation: 71

Fixed Working Code:

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
    );
}

Upvotes: 0

IInspectable
IInspectable

Reputation: 51511

The outcome of calling CreateWindowExW falls into one of three categories: "Failure", "cancellation", or "success". The following table lists those categories based on the function's return value, and the return value of 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

Since it isn't relevant to the discussion here, let's get the final row out of the way: The function call returned a non-NULL value, singalling successful window creation. The return value of GetLastError is meaningless in this case, and can literally be any value.

The first row marks a true error condition. The window creation request couldn't be satisfied, either due to a programming error (e.g. passing in an invalid window class, window handle, ...) or an environmental issue like resource exhaustion.

The second row in the table above is the one that frequently causes confusion. Window creation didn't happen, but there's no error reported either. Wait, what? How can this fail without an error!?

It is failing on request!

Unintuitive as it may appear, the window procedure responsible for the behavior of an instance of a window class is already involved in the process of creating that instance. There are two1 window messages sent to the window procedure associated with a window class during window construction. Either one can abandon the request prematurely2: WM_NCCREATE and WM_CREATE.

The semantics of WM_CREATE's return value include the following:

If the application returns -1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle.

Prior to that, a WM_NCCREATE message is sent. The semantics of its return value are documented as well:

If the application returns FALSE, the CreateWindow or CreateWindowEx function will return a NULL handle.

That's all the information needed to answer the question:

default:
    return 0;

This returns 0 (which is interpreted as FALSE) for any unhandled message, including WM_NCCREATE. Since there is no explicit message handler for the WM_NCCREATE message that's what the window procedure returns, thereby terminating the request to create the window.

To fix this3 you would either need to add default processing to the default: label, i.e.

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

and remove the trailing return statement4, or remove the default label altogether.


1 Future visitors may find this to be inaccurate. Sorry, I don't have access to a time machine.

2 Literally.

3 Your compiler probably warned you about this.

4 Make sure to fix the (possibly unintended) WM_PAINT fall-through into default along the way.

Upvotes: 4

Related Questions