Welcome to Drawing with Windows GDI. Since this is lesson 1, we are at the very start of the course, which makes this a great time to build strong habits. In this lesson, we will learn how Windows lets us draw inside a window using the Graphics Device Interface (GDI). GDI is the engine Windows uses to render graphics by giving us a Device Context, often called an HDC.
Then, we will use that drawing surface to render a rectangle and an ellipse in the client area. By the end, we will be able to read and recreate the painting flow with confidence.
Before we touch the code, it helps to know when drawing should happen. In WinAPI, Windows sends a WM_PAINT message when part of a window needs to be redrawn; for example, after being uncovered or resized. Inside that message, we ask for a Device Context, draw using GDI functions, and then give it back.
- BeginPaint: Prepares the window for painting and gives us an
HDC. - Use the
HDC: Call GDI drawing functions such asRectangleandEllipse. - EndPaint: Finishes the paint cycle and tells Windows we are done.
The client area is the inside of the window where our content appears, not the title bar or borders. In GDI, the coordinate (0,0) starts at the top-left corner of the client area. As you move to the right, X increases; as you move down towards the bottom, Y increases.
The lesson code starts by bringing in the WinAPI tools, defining a few control IDs, and opening the window procedure. Even though our main goal is drawing, this structure is what lets Windows deliver messages to our program in an organized way.
A few pieces matter here:
#include <windows.h>gives us access toWinAPItypes and GDI functions.- The
#definevalues create stable IDs for child controls. WindowProcis the function Windows calls for messages such asWM_CREATEandWM_PAINT.
So, before we draw anything, we first need this message-driven frame in place.
The first handled message is WM_CREATE. This runs when the window is being set up. In this lesson, the controls are not the main topic, but they give useful context by showing that GDI drawing can live beside regular interface elements.
GetWindowLongPtr(hwnd, GWLP_HINSTANCE) retrieves the application instance handle needed when creating child controls. Then, CreateWindowW builds a label, an edit box, and a button.
Notice their positions: they sit on the left side of the window, leaving room for our shapes on the right. The return 0; tells Windows that we handled WM_CREATE ourselves.
Now we arrive at the key message for this lesson: WM_PAINT. This is where drawing belongs. The first job is to prepare a PAINTSTRUCT and ask Windows for a valid drawing context.
PAINTSTRUCT stores information about the paint request, while BeginPaint does two important jobs at once: it prepares the client area for repainting and it returns an HDC we can draw with. We should think of hdc as our connection to the window's surface. Without it, GDI functions have nowhere to send their output.
With a valid HDC, we can start rendering geometry. The first shape is a Rectangle. This is a simple but very useful starting point because its coordinates are direct and easy to picture.
The four numbers define the rectangle's bounding edges inside the client area. In this case:
Next, we draw an Ellipse below the rectangle. The important idea is that Ellipse also uses a bounding box. In other words, we still provide left, top, right, and bottom coordinates, and Windows fits the oval inside that region using its GDI rendering engine.
This box is also 200 pixels wide, but its height is:
Because the width and height are different, the result is an ellipse, not a perfect circle. If both values matched, the shape would appear as a circle. This is a useful rule to remember whenever we want precise control over curved shapes.
After the shapes are drawn, we must close the painting operation properly. This step is small in code but very important in practice.
EndPaint tells Windows that painting is complete and releases the HDC obtained by BeginPaint. These two functions belong together, so we should always treat them as a pair inside WM_PAINT. The return 0; again signals that the message was fully handled. At this point, the client area has been redrawn with both geometric shapes.
The remaining cases handle shutdown and pass any unhandled messages back to Windows. This part may not draw anything, but it completes the window procedure and keeps the application behavior correct.
When the user closes the window, WM_CLOSE calls DestroyWindow(hwnd). That leads to WM_DESTROY, where PostQuitMessage(0) tells the application message loop to stop. Finally, DefWindowProcW handles any messages we did not process ourselves. This default behavior is essential because Windows still expects many standard actions to work normally.
In this lesson, we built the core painting path step by step: we received messages through WindowProc, created a few standard controls, entered WM_PAINT, obtained an HDC with BeginPaint, drew a Rectangle and an Ellipse using GDI functions, then finished cleanly with EndPaint. That is the basic WinAPI drawing cycle, and it is the foundation for all later GDI work.
Next, we will put this knowledge to use in hands-on practice, where we will place shapes with purpose and make the painting flow feel natural through repetition.
