Something about programming

WinAPI Hello World

To create native classic Windows programs you need to use WinAPI - Windows API. WinAPI itself is written in C language. It's just a collection of functions, structures, and constants. It's declared in a bunch of header files and implemented in static (.lib) and dynamic (.dll) libraries.

First, let's create a new project. Choose File -> New -> Project

In the opened window on left pane choose Other, then Empty Project. It is also available Windows Desktop Application template, but we'll create a window from scratch as default template is too complicated for us yet. In the bottom part choose the name of the project, it's location and if you want to create a solution for it. The folder can be anywhere you want, for me, it's C:\prog\cpp\

In Solution Explorer click with the right mouse button and choose Add -> New Item.

In opened window choose C++ File (.cpp) and type the name of the file at the bottom part - main.cpp.

Before we start exploring the code, let's talk about data types and calling conventions.

WinAPI Data Types

WinAPI redefines many data types. Some of redefinitions depend on target platform. For example, for type LRESULT, if you compile your code for x86 platform, LRESULT will be long, but if you compile for x64, LRESULT will be __int64. Here is how LRESULT defined internally (it depends on LONG_PTR, and LONG_PTR itself is either __int64 or long):

typedef LONG_PTR LRESULT; #if defined(_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR; #endif

Calling Conventions

In the code I used __stdcall before names of all functions. It's one of calling conventions. Calling conventions define in what order arguments are pushed on the stack. For __stdcall arguments are pushed in reverse order - from right to left. Also, __stdcall tells that after the function is finished, it will pop its arguments itself (not the caller function). All WinAPI functions use __stdcall convention.

WinAPI redefines __stdcall to WINAPI or CALLBACK or APIENTRY. So in MSDN examples, you will not see __stdcall, but it used behind the curtains anyway.

WinAPI types are written in uppercase.

Handles in WinAPI

Handle is a reference to a resource in memory. For example, you create a window. This window is stored in memory. And this window has an entry in a table that stores pointers to all system resources that you created (windows, fonts, files, bitmaps). The pointer to your window in this table is called the handle of the window.

Internally any handle is just a redefinition of type void*. Examples of handle types in WinAPI: HWND, HINSTANCE, HBITMAP, HCURSOR, HFILE, HMENU.

So, we use handles to get access to some system resources.

First WinAPI Program - Empty Window

Let's first check the whole code for simplest WinAPI program.

#pragma comment( lib, "user32.lib" ) #include <windows.h> LRESULT __stdcall WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) { WNDCLASS windowClass = { 0 }; windowClass.lpfnWndProc = WindowProc; windowClass.hInstance = hInstance; windowClass.lpszClassName = "HELLO_WORLD"; RegisterClass(&windowClass); HWND hwnd = CreateWindow( windowClass.lpszClassName, "WinAPI Empty Window - Hello World", WS_OVERLAPPEDWINDOW, 100, 50, 1280, 720, nullptr, nullptr, hInstance, nullptr); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); MSG msg = {}; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0; } __int64 __stdcall WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); { switch (message) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, message, wParam, lParam); }
#pragma comment( lib, "user32.lib" ) #include <windows.h>

First, we need to add WinAPI: link library that contains the implementation of different functions, and include header files with declarations of these functions, structures, and constants. user32.lib contains main Windows capabilities: everything related to windows and event handling..

On next line we declare callback functions that will be called when our app receives some message from the operating system. We'll return to it a bit later.

WinMain Function

WinMain is called an entry-point function - that's the function that will be executed when your program will be run by operating system.

The main function for Windows app is a bit different than for console app. It returns integer - and it always will be 0. __sdtcall tells that arguments pushed to stack in reverse order and WinMain itself will pop them from the stack when it finished. WinMain accept 4 arguments:

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)

- hInstance - handle to an instance of your application. You can think of it as a representation of your application in memory. It's used during windows creation.

- The second argument is a legacy of 16-bit Windows. Not used anymore.

- The third argument represents command line arguments. We'll not use it.

- nCmdShow is a special flag that can be used during application window creation. It tells the state of it: should it be shown normally or minimized/maximized

Now let's discuss creation of an actual window.

Window Classes

To create the window we need to define and register its class first. Windows uses the same process for its classes. All standard elements that you see in Windows are classes: buttons, edit boxes, scroll bars and so on. Windows maintains the list of all registered classes. It's mandatory to define only three fields of a class: class name, instance handle, and window procedure.

First, we need to fill a WNDCLASS structure. Don't let word class in WNDCLASS confuse you. It's not a C++ class. It's just a term that used in WinAPI.

WNDCLASS windowClass = { 0 }; windowClass.lpfnWndProc = WindowProc; windowClass.hInstance = hInstance; windowClass.lpszClassName = "HELLO_WORLD"; RegisterClass(&windowClass);

Here, we initialize WNDCLASS structure with zeroes, define mandatory fields and register the class.

- lpfnWndProc has type WNDPROC. It's a pointer to WindowProc function that we declared in the beginning. Every window class must have its own window procedure, but we'll talk about it more in a bit.

- hInstance - handle to our application instance. All window classes must tell what application registered them. We use first argument of our WinMain function.

- lpszClassName - name of a class. You define the name yourself. Windows itself using uppercase for class names (examples are: BUTTON, EDIT, LISTBOX) so will do I in all tutorials..

There are more fields in WNDCLASS: style, icon, background, menu name, but we can omit them. Some of them we'll examine in the next tutorials. You can check all of them in WinAPI documentation.

At last, we register our class with RegisterClass function. We pass the address of WNDCLASS structure to it. Now we ready to create the window.

Creating First WinAPI Window

WinAPI provides function CreateWindow to create new window of specific class:

HWND hwnd = CreateWindow( windowClass.lpszClassName, "WinAPI Empty Window - Hello World", WS_OVERLAPPEDWINDOW, 100, 50, 1280, 720, nullptr, nullptr, hInstance, nullptr);

First parameter - class name. Second is the name of the window. This string user will see at the window header. Next one is the style. WS_OVERLAPPEDWINDOW tells that window will have a caption, maximize/minimize buttons, system menu, and a border.

Four numbers tell the position of the top left corner of the window and width and height.

Then there are two null pointers. First stands for a handler of a parent window - in our case the window doesn't have a parent. Second is for menu handler - our window doesn't

hInstance - instance handler of the app to which the window is bound.

In last argument we pass nullptr. This argument is used for special user interfaces - MDI (Multiple Document Interface ) - window in window.

CreateWindow returns window handler. We can use it to refer to the created window in the code. Now we can show window and call update function:

ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);

ShowWindow uses nCmdShow argument of WinMain function to control the initial state: minimized, maximized and so on. UpdateWindow we'll discuss in next tutorials.

WinAPI main loop

After we've created the window we need to run an infinite loop. In this loop, we'll react to different events that will happen during the interaction of the user with our app.

MSG msg = {}; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } }

First we declare structure variable MSG - in this structure, Windows encodes events like when a user clicked a mouse button or pressed a button on a keyboard. If the message is WM_QUIT, then we finish app loop.

There is a different kind of messages generated by the system. They generated when something happens: a window is resized, mouse clicked, a button on keyboard pushed.

Message queue in WinAPI

Comments:

No comments yet