Introduction

Welcome back to System Events and Integration. We are now in Lesson 4, so we already have a solid base in timers, mouse input, and keyboard input. In this lesson, we take the next practical step: saving data to disk.

Our program will remember the most recent mouse click, then write those coordinates to a local file when we press S. This is a great example of turning event handling into something lasting, because the data survives beyond the moment the click happened.

Why Native File Output Matters

As you may recall from the previous two lessons, WinAPI programs react to messages. A mouse event can collect information, and a keyboard event can trigger an action. Native file output follows the same pattern, except the action now reaches the file system.

At a high level, our program works like this:

  1. We capture the click position.
  2. We store that position in memory.
  3. We press S to save it through CreateFileW and WriteFile.

This approach matters because we are not using higher-level C++ file streams here. We are talking directly to Windows, which helps us understand how handles, byte counts, and system resources really work.

Preparing Unicode And Shared State

Before we can save anything, we need the usual Windows headers and a place to remember the latest click. These global variables let one message store data and another message use it later. This is a common pattern in event-driven programs.

We once again start by defining the g_lastX and g_lastY variables, and set them to 0, so the program always has a valid pair of numbers, even before the first click. Later, WM_LBUTTONDOWN will update them with real coordinates.

Opening A Windows File Handle

With the shared state ready, we can build the save function. The first job is to ask Windows for a file handle. This handle represents an open connection to the file system, and we must check it before doing any write operation.

CreateFileW is a very flexible API, but here we use a simple setup. GENERIC_WRITE means write access; CREATE_ALWAYS means Windows creates the file if needed, or replaces the old contents if it already exists. That also means this version stores only the latest click, even though the name sounds like a history log. If opening fails, INVALID_HANDLE_VALUE tells us the handle is not usable, so we show an error dialog and exit the function right away.

Formatting And Writing The Coordinate Text

Once the file is open, we still need data to write. We first format a readable sentence into a wide-character buffer, then pass that buffer to WriteFile. Notice the important conversion step: WriteFile expects a byte count, not a character count. We must also verify that the system actually wrote the number of bytes we expected.

This part does several useful things at once. swprintf inserts the x and y values into the text and returns the number of wide characters written. We multiply that count by sizeof(wchar_t) because Windows needs the size in bytes.

When calling WriteFile, we check both the BOOL return value (which tells us if the operation itself failed) and the bytesWritten count. In low-level programming, it is possible for a write to "succeed" partially—for example, if the disk runs out of space mid-write. Only if can we be certain the file is complete. Finally, we call to ensure the resource is released regardless of whether the write succeeded or failed.

Connecting Mouse And Keyboard Events

Now we connect the save routine to the window procedure. This is where the lesson joins ideas from the previous units: mouse input provides the data, and keyboard input decides when to persist it.

As in the mouse lesson, GET_X_LPARAM(lParam) and GET_Y_LPARAM(lParam) pull out the click coordinates. As in the keyboard lesson, WM_KEYDOWN uses wParam to tell us which key was pressed. When that key is S, we call SaveDataToFile with the most recent stored coordinates. InvalidateRect asks Windows to repaint the window, which keeps the normal draw cycle active and leaves room for future visual feedback if we want to add it later.

Painting Instructions And Handling Shutdown

The window should tell the user what to do, so we draw a short instruction during WM_PAINT. We also keep the standard shutdown path with WM_DESTROY, and we still let DefWindowProcW handle anything we do not process ourselves.

This section keeps the interface simple and clear. BeginPaint and EndPaint mark the proper drawing region, and TextOutW displays the instruction in the client area. PostQuitMessage(0) ends the application loop when the window is destroyed. Finally, DefWindowProcW remains important because it preserves normal Windows behavior for all messages we do not customize.

Viewing The File Across Platforms

If you open click_history.txt in an editor that expects UTF-8 text (common on Linux or macOS), the file might be reported as a binary file. This happens because we are writing the wchar_t buffer directly.

On Windows, wchar_t is a 16-bit character type, and these strings are stored as UTF-16LE (Little-Endian). Because every character uses two bytes, a simple word like "User" looks different on disk compared to standard web or Linux text:

  • UTF-16LE (Our file): 55 00 73 00 65 00 72 00
  • UTF-8 (Standard): 55 73 65 72

Many editors see those 00 bytes and assume the file contains binary data. While tools like cat might still display the text by ignoring the null bytes, a production application would typically use WideCharToMultiByte to convert the string to UTF-8 before saving to ensure cross-platform compatibility.

For this lesson, however, writing the buffer directly is useful because it demonstrates exactly how Windows represents wide-character strings in memory and how those raw bytes are persisted to disk.

Conclusion And Next Steps

In this lesson, we combined several skills from earlier units into one practical WinAPI program. We stored click coordinates with WM_LBUTTONDOWN, listened for the S key with WM_KEYDOWN, opened a file through CreateFileW, wrote text with WriteFile, and released the system resource with CloseHandle. We also learned to verify our write operations by comparing requested bytes against actual bytes written.

The key idea is simple and powerful: events collect data, and native Windows APIs let us persist that data directly. In the practice section coming up, we will turn this flow into hands-on work so we can build real confidence with native file output, one focused step at a time.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal