Win32 API programming with C - Creating a window

Ciprian FusaCiprian Fusa
5 min read

The Win32 API, also known as the Windows API, is the foundational platform for creating native Windows applications in C/C++ that require direct interaction with the operating system and hardware. It offers a premier development experience without the need for a managed runtime environment like .NET, positioning the Win32 API as the preferred choice for applications demanding optimal performance and direct hardware access.

Prepare Your Development Environment

To write a Windows program in C or C++, you must install the Microsoft Windows Software Development Kit (SDK) or Microsoft Visual Studio. The Windows SDK contains the headers and libraries necessary to compile and link your application. The Windows SDK also contains command-line tools for building Windows applications, including the Visual C++ compiler and linker.

What Is a Window

This kind of window, often referred to as an application or main window, typically features a title bar, Minimize and Maximize buttons, along with other standard interface components. The outer frame is known as the non-client area, while the interior, managed by your program, is called the client area.

This is another type of window:

screen shot of a control window

In user interfaces, elements like buttons and text fields are essentially specialized windows. Unlike standalone application windows, these UI components are designed to be integrated within an application window, moving in unison with it and allowing for interaction between the component and the application window itself.

Think of a window as a programming construct that:

  • Occupies a certain portion of the screen.

  • May or may not be visible at a given moment.

  • Knows how to draw itself.

  • Responds to events from the user or the operating system.

Window Handles

Windows in an operating system are treated as objects with their unique code and data, though not in the form of C++ classes. They are identified and referenced through a specific value known as a handle, which is essentially a unique identifier or number assigned by the operating system for object identification. This concept allows the OS to manage and access windows efficiently by referencing them in a comprehensive list or table based on their handles.

The WinMain application entry point

Every Windows program includes an entry-point function named either WinMain or wWinMain. The following code shows the signature for WinMain:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);

The four WinMain parameters are as follows:

  • hInstance is the handle to an instance or handle to a module. The operating system uses this value to identify the executable or EXE when it's loaded in memory. Certain Windows functions need the instance handle, for example to load icons or bitmaps.

  • hPrevInstance has no meaning. It was used in 16-bit Windows, but is now always zero.

  • pCmdLine contains the command-line arguments as a Unicode string.

  • nCmdShow is a flag that indicates whether the main application window is minimized, maximized, or shown normally.

A calling convention, such as WINAPI, defines how a function receives parameters from the caller. Make sure to declare your WinMain function as shown in the preceding example.

Create a window

Every window must be associated with a window class. A window class isn't a class in the C++ sense. Rather, it's a data structure used internally by the operating system. Window classes are registered with the system at run time. To register a new window class, fill in a WNDCLASSEX structure:

char g_szClassName[] = "myWindowClass";
WNDCLASSEX wc;
wc.cbSize         = sizeof(WNDCLASSEX);                // The size, in bytes, of this structure
wc.style         = 0;                                 // The class style(s)
wc.lpfnWndProc     = WndProc;                           // A pointer to the window procedure
wc.cbClsExtra     = 0;                                 // The number of extra bytes to allocate following the window-class structure.
wc.cbWndExtra     = 0;                                 // The number of extra bytes to allocate following the window instance.
wc.hInstance     = hInstance;                         // A handle to the instance that contains the window procedure for the class.
wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);   // A handle to the class icon. This member must be a handle to an icon resource. If this member is NULL, the system provides a default icon.
wc.hCursor         = LoadCursor(NULL, IDC_ARROW);       // A handle to the class cursor. This member must be a handle to a cursor resource. If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window.
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);          // A handle to the class background brush.
wc.lpszMenuName  = NULL;                              // Pointer to a null-terminated character string that specifies the resource name of the class menu.
wc.lpszClassName = g_szClassName;                     // A string that identifies the window class.
wc.hIconSm         = LoadIcon(NULL, IDI_APPLICATION);   // A handle to a small icon that is associated with the window class.Create the window

Next, pass the address of the WNDCLASSEX structure to the RegisterClassEx function. This function registers the window class with the operating system.

if(!RegisterClassEx(&wc))
{
    MessageBox(NULL, "Window Registration Failed!", "Error!",
        MB_ICONEXCLAMATION | MB_OK);
    return 0;
}

To create a new instance of a window, call the CreateWindowEx function:

HWND hwnd;
hwnd = CreateWindowEx(
    0,                      // Optional window styles.
    g_szClassName,          // Window class
    "My window",            // Window text
    WS_OVERLAPPEDWINDOW,    // Window style
    CW_USEDEFAULT,          // Position X
    CW_USEDEFAULT,          // Position Y
    800,                    // Width
    600,                    // Height
    NULL,                   // Parent window
    NULL,                   // Menu
    hInstance,              // Instance handle
    NULL                    // Additional application data
);

if(hwnd == NULL)
{
    MessageBox(NULL, "Create Window Failed!", "Error!",
        MB_ICONEXCLAMATION | MB_OK);
    return 0;
}

CreateWindowEx returns a handle to the new window, or zero if the function fails. To show the window, that is, make the window visible, pass the window handle to the ShowWindow function:

ShowWindow(hwnd, nCmdShow);

Currently, the window is inactive, lacking both content and user interaction. In a typical graphical user interface (GUI) application, the window would engage with user and system events. The next article will cover how window messages facilitate this kind of interactivity.

https://learn.microsoft.com/en-us/windows/win32/learnwin32/your-first-windows-program

1
Subscribe to my newsletter

Read articles from Ciprian Fusa directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ciprian Fusa
Ciprian Fusa

I am a .NET Developer and Consultant with over 15 years of experience, I specialize in crafting scalable, high-performance applications that drive business growth and innovation.