Home > Net >  What is the bare minimum I need to write in Win32 to get a Window to open?
What is the bare minimum I need to write in Win32 to get a Window to open?

Time:02-04

Question:

I am trying to set up the Winapi with C to display a simple window with the bare minimum of code, how do I do that in the way that my source code is formatted?

Issue:

The following does not open a window, it simply closes, why is this?


/* window_s.h */
typedef struct {
        WNDCLASS wc;                                                                     
        HINSTANCE hInstance;
        HWND hwnd;
} WINDOW; 

/* setUpWinProc.h */
LRESULT CALLBACK WindowProc( HWND hwnd,
        UINT uMsg, WPARAM wParam,
        LPARAM lParam) { }

/* registerWindow.c */
void registerWindow(WINDOW *window) {
        const char CLASS_NAME[]
                = "Window Class Name";
        window->wc.lpfnWndProc  = WindowProc;
        window->wc.hInstance    = window->wc.hInstance;
        window->wc.lpszClassName        = CLASS_NAME;
        RegisterClass(&(window->wc));
}


/* createWindow.c */
int_fast64_t CreateWindow_(WINDOW *window) {
        window->hwnd = CreateWindowEx(
                0,              // Optional window styles
                window->wc.lpszClassName,       // Window class
                "Window",       // Window text
                WS_OVERLAPPEDWINDOW,    //Window style

                // Size and position
                CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

                NULL,   // Parent window
                NULL,   // Menu
                window->hInstance,      // Instance handle
                NULL            // Additional application data
                );
        if (window->hwnd == NULL)
                return 0;
        return window->hwnd;
}


/* Window_Main.c */
#include <windows.h>
#include "window_s.h"
#include "setUpWinProc.h"
#include "registerWindow.c"
#include "createWindow.c"
#include <stdio.h>
int WINAPI WinMain ( HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR pCmdLine,
                        int nCmdShow ) {
        WINDOW window = {{}, hInstance};
        registerWindow(&window);
        CreateWindow_(&window);
        ShowWindow(window.hwnd, nCmdShow);
}

CodePudding user response:

This is part of the issue:

int WINAPI WinMain ( HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR pCmdLine,
                        int nCmdShow ) {
        WINDOW window = {{}, hInstance};
        registerWindow(&window);
        CreateWindow_(&window);
        ShowWindow(window.hwnd, nCmdShow);
}

What do you think happens after ShowWindow returns and WinMain itself returns? (Hint: the program exits).

Extend your WinMain to pump messages with TranslateMessage DispatchMessage.

int WINAPI WinMain ( HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR pCmdLine,
                        int nCmdShow ) {

        MSG msg;

        WINDOW window = {{}, hInstance};
        registerWindow(&window);
        CreateWindow_(&window);
        ShowWindow(window.hwnd, nCmdShow);

        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
}

Then your message proc needs to handle WM_CLOSE and WM_PAINT as a minimum and be able to forward to the default window proc for messages it doesn't handle.

LRESULT CALLBACK WindowProc( HWND hwnd,
        UINT uMsg, WPARAM wParam,
        LPARAM lParam) {

    switch (uMsg) {
        case WM_PAINT: {
    
             PAINTSTRUCT ps;
             BeginPaint(hwnd, &ps);
             EndPaint(hwnd, &ps);
             break;
        }

        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }

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

    return 0;
}

Your RegisterClass call looks suspicous as well. Let's initialize like this:


    WNDCLASSEXW wcex = {0};
    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WindowProc;
    wcex.hInstance      = hInstance;
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW 1);
    wcex.lpszClassName  = szWindowClass;

    RegisterClassExW(&wcex);

If you have Visual Studio (and edition), there's a default Win32 application that generates the most minimal of stub applications that does exactly what you are trying to achieve. Look for the C project for "Default Windows Application" or similar.

CodePudding user response:

The core issue is here:

LRESULT CALLBACK WindowProc( HWND hwnd,
        UINT uMsg, WPARAM wParam,
        LPARAM lParam) { }

As the compiler warned, this function needs to return a value (but isn't). The behavior of registering this as a window procedure is undefined. It will probably fail to create a window; CreateWindowEx() calls into the window procedure with WM_NCCREATE and WM_CREATE messages before it returns. Either message handler must return a particular value to continue window creation.

There's a similar issue with the window class name: It's using a local variable, but passes a pointer to it out. As registerWindow() returns, CLASS_NAME is gone. Class registration succeeds, but when it comes time to create the window, the call to CreateWindowEx() uses garbage as the window class name.

The first fix is thus:

LRESULT CALLBACK WindowProc( HWND hwnd,
        UINT uMsg, WPARAM wParam,
        LPARAM lParam) {
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

and

void registerWindow(WINDOW *window) {
    static const char CLASS_NAME[] = "Window Class Name";
    // ...
}

That solves the window creation, though you won't see the window for long (if at all) because the code immediately falls out of WinMain() (which also needs to return a value), causing the process to terminate.

To fix that, you'll have to dispatch messages on the thread that created the window. The following will address both of these issues:

int WINAPI WinMain ( HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR pCmdLine,
                        int nCmdShow ) {
    WINDOW window = {{}, hInstance};
    registerWindow(&window);
    CreateWindow_(&window);
    ShowWindow(window.hwnd, nCmdShow);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0)) {
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

That's the bare minimum required (unless you count MessageBox() as "getting a window to open"). But it won't allow you to exit the application. To add that functionality, you'll want to add a WM_DESTROY handler that will signal the message loop to end, like so:

LRESULT CALLBACK WindowProc( HWND hwnd,
        UINT uMsg, WPARAM wParam,
        LPARAM lParam) {
    switch (uMsg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}
  • Related