backburnt
letters
17.05.2026 | Added blind mode. Enable it by closing your eyes.

How to program windows on Windows - Win32 C++ Practical Example

Introduction

In this article, I will show you a practical example on how to program a window on Windows using the raw Win32 API quickly and efficiently. As this is supposed to be introductory, I will present my entire train of thought while developing a simple application with some buttons. This is by no means a complete tutorial on everything Win32 based, as this guide only scratches the surface of the entire API, and should be more of a reference on how to get started. There are multiple types of windows, I choose to present only one of them for this example, one which is called a "dialog-based window". This type of window allows for easier and faster development and deployment, since its visual structure (buttons, labels, inputs) don't have to be programmed manually, but instead a resource file is supplied that allows for WYSIWYG editing.

Note that I'm not responsible for any trauma caused by following this guide. Also not everything said here is 100% sure to be correct, because the Win32 API is just so good.

Prerequisites

To follow this guide, you need to know these things atleast on a basic level:

  • Computer literacy and terminology
  • C++ language syntax
  • Using the command line

Additionally, you need to acquire these programs:

  • MingW C++ compiler tools, or any other toolchain supporting the Win32 API
  • RisohEditor, or any other Windows resource (.rc) editor (technically you don't need any if you are brave enough)
  • Any code editor, I use Notepad++

Lets Go

As usual, a main file is created, so I make one called "main.cpp" and add standard includes:

#include <windows.h>
#include <stdio.h>
#include <string>

The program needs an entry point, which in C++ is usually int main(), but a Win32 window has a different entry point called wWinMain, so I add that.

INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, INT nCmdShow) {

}

If you read other tutorials on Win32, you may have noticed that I used wWinMain instead of just WinMain, and there is a good reason for it. I aim for most compatibility across the board, so I need to use wide strings instead of normal strings. To explain: there are 2 (relevant for now) types of strings in C++: normal ANSI strings declared with std::string and wide strings, which support more characters and is fit for the UTF-8 text encoding, declared with std::wstring. If I used the normal string type, the application has potential to break in some cases when dealing with special characters. The most obvious place where stuff breaks is in user directory paths, which may contain special characters, like ý,á,í,ó and others. There are ways to deal with special characters using the normal string type, but they are much more complicated than is practical. So for any and all functions that have their respective ANSI and wide variants, I will use the wide variant.

Note that your compiler might provide aliases for these functions and types depending on compiler flags, which might become confusing if something breaks by unintentionally mixing them. I always specify which variant I want, to avoid confusion and potential errors when dealing with both.

Before proceeding any further, it is time to create the dialog using a program called RisohEditor. This editor allows for WYSIWYG editing of resource files, which are files used by the compiler to provide information, such as icons, descriptions, etc, to the target executable. Among this information is the entire dialog layout. This is why I lean toward "dialog-based" windows instead of pure programmed windows, since they require less work and are easily changed when needed. This does not mean that you want to avoid using the pure windows approach, as there are many benefits to be had from that (example: a pure window can recieve and respond to many more system messages than a dialog window), but that is outside the scope of this guide. If the intent is to make a simple window without any fancy stuff going on, dialog-based is the way. If you need more control, search how to create pure windows.

The program is a little complicated to navigate at first, so follow with me to create a dialog. I open up the program, click View on the top bar, and then click on List of Resource IDs. A small window has popped up. Right click on the empty space in the window and select Add ID... . On the popup window, change type of ID to Dialog.ID, then change the name of ID to IDM_MAIN, and set the integer value to 100. After you are done, click OK. These steps created an ID with a name alias to be used for the dialog. To finally create the dialog, right click the empty space on the left pane of the main window and select Add > Add Dialog... . Set the resource name to IDM_MAIN and hit OK. This has created a dialog. To start editing the dialog, click the GUI EDIT button on the top left.

All of this may sound overly complicated, but after you do it several times, it gets easier and easier. It is only a matter of repeating it until you get used to it, there is not much to learn here.

Now I won't show exactly how to edit the dialog, since for me it is usually trial and error until I visually get the result I want. Simply experiment and see what you can cook up with the different controls and styles the editor provides. Click the Test button to preview changes. Remember to save often, since in some cases when I was trying stuff out the program kinda broke, so I had to restart. If you are unsure of what does what, the internet is an endless source of information.

After you are finished with the dialog, export it and include the generated header in your code.

Now that I have my dialog all ready, it's time to go back to the few lines of code I prepared and actually show the window. To show the window, put this in the main function:

HWND hwnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc);
ShowWindow(hwnd, nCmdShow);

This creates a handle to the window that is created with the CreateDialog function. hInstance is the handle to the process, MAKEINTRESOURCE(IDD_MAIN) is a function which gets the resource at the specified ID. DialogProc is a function which is currently undefined, I will define it in a moment. Then I show the window using the ShowWindow function, passing the window handle and the nCmdShow. This is almost enough to show the dialog, but it's not yet ready.

I need to create a message loop for the window. A message loop processes all commands and events of a windows, such as moving, resizing, closing, keyboard input, etc... Without a message loop, the window will not work. To create a message loop, I use this piece of code:

MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { 
	if (bRet == -1) {
		// Error
		return 1;
	}
	else if (!TranslateAccelerator(hwnd, NULL, &msg) && !IsDialogMessage(hwnd, &msg)) { 
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}
return 0;

To summarize: I create a while loop that doesn't stop until the dialog closes or some error happens. The loop processes messages using the TranslateMessage and DispatchMessage functions. TranslateAccelerator is a function which processes keyboard shortcuts, but I won't be using them in my program, so it's set to NULL. IsDialogMessage is there to make handling of navigation via tab stops work properly (you must have the flag WS_TABSTOP in the resource file set for the controls). Note that when programming Win32, if you are dealing with simple applications that don't require complex system integration or low level wizardry, you don't have to know exactly to the point what happens and why, because it's a mess anyway. Instead, it might be more beneficial and productive to learn the ins and outs of becoming an alchemist and brewing some elixir of ultimate wisdom or something.

Now that I have the loop, I need to create the DialogProc function I left undefined earlier. This function will filter the events and respond only to those I want. I insert this code before the main entry point:

INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg) {
		case WM_INITDIALOG: {
			return 1;
			break;
		}
		case WM_COMMAND: {
			switch (LOWORD(wParam)) {
				case 100:
					MessageBoxW(NULL, L"You clicked OK.", L"Information", MB_OK | MB_ICONINFORMATION | MB_TOPMOST | MB_SYSTEMMODAL);
					break;
			}
			return 1;
			break;
		}
		case WM_CLOSE: {
			DestroyWindow(hwnd);
			return 1;
			break;
		}
		case WM_DESTROY: {
			PostQuitMessage(0);
			return 1;
			break;
		}
	}
	return 0;
}

Using the switch, I'm able to capture and respond to different messages. There are many many many messages available, so to find the one which you need, you need to consult the webs.

Note that in Win32: FALSE = 0, TRUE = 1. The function should return TRUE if it processed the message, FALSE if it didn't.

In the WM_COMMAND case, you can see another switch with LOWORD(wParam). According to Microsofts school of illusion magic, this means: The return value is the low/high-order word of the specified value. Wow! Cool! So what I found is that this piece of code responds to certain events from GUI elements, like buttons. In here, I basically ask: "Is the button with ID 100 clicked? If yes, show a message box". So in reality I still have like no clue what is happening, but it works. Basically if you need to do something with the API, find some examples on the internet, that's the best you can realistically do. Need to know how to get text from text input? Search for "win32 get text input cpp". Tada! This kind of thinking and programming is normal for Win32, so get used to it.

After coding all logic and cool stuff, it's time to compile. I execute these commands:

g++ -c src\main.cpp -o src\main.o
windres -i src\resource.rc -o src\resource.o
g++ src\main.o src\resource.o -o ".\build\program.exe" -municode -mwindows -lcomctl32 -static

I tell the compiler to compile the main file to an object file, then compile the resource file to an object file, then link them both in the final step, indicating that I use windows functions, styles, unicode and want it linked statically. This creates a single executable which should run on all modern PCs of the same architecture as yours. If you don't care about icons, file descriptions, monitor DPI or window styling, you are basically done and don't need to do anything more. If you do, continue reading.

To make the generated executable all fancy and cool, I need to create a manifest. A manifest is basically metadata about the executable embedded into the resource file. To create one, go back to RisohEditor, right click the left pane, and choose Add manifest... . In this case, I don't need to create an ID, since I don't need more than one manifest per resource file, so in the resource name field, I just put "1". This creates a manifest. There are many options for the manifest, I use this code:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <asmv3:application>
    <asmv3:windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
      <ws2:longPathAware>true</ws2:longPathAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

This makes the application DPI aware, long path aware, and enables visual styles. There are more options available, look them up on the net.

To add the description you can see if you inspect in the file properties, choose Add Version Info... . This will create a template where you can edit the file version, description, name...

To add an icon, use the same steps as creating a dialog, except select the icon entry. Name it IDI_ICON. To use the icon on the dialog, modify the source code like this:

HWND hwnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAIN), hwnd, DialogProc);
HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
ShowWindow(hwnd, nCmdShow);

Compile the application, and the result should be a normal Win32 window program like any other.

At this point, it might be beneficial to save this as a template for your future Win32 projects.

<
>
Small window with a button
<
>
It is possible to create a main window and a dialog based window as a child. Looks like a base for an installer.
The best way to learn this kind of stuff is to look into the source codes of small programs. I've made one, so take a look if you're interested: FalseControl software for Windows

Published on 27.02.2026

Last updated on 17.04.2026

This page was last updated on: 22.04.2026