Welcome back to Application Menus and Dialogs. We are now in Lesson 3, and we already have a working menu and a confirmation step to close the app. This progress provides a strong foundation for something much more useful: allowing the user to browse for a file with the standard Windows Open dialog.
In this lesson, we will transform the Open... menu item from a placeholder into a functional action. By the end, we will configure an OPENFILENAMEW structure, call GetOpenFileNameW, and read the path that the user selected. This is a practical step toward making our app feel like a professional desktop program.
Before we write code, let us build the correct mental model. The Common Dialog Box Library provides ready-made Windows dialogs for common tasks, such as opening a file. Instead of drawing our own browser window, we fill in a structure that describes what we want and then call a function that shows the dialog for us.
The flow is simple:
- The user clicks
File, thenOpen.... - We prepare an
OPENFILENAMEWstructure. - We call
GetOpenFileNameW. - If the user chooses a file, Windows writes the full path into our buffer.
This pattern is useful because it provides a familiar interface and saves a significant amount of custom UI work.
To use the file selection dialog, we need access to the declarations in the Common Dialog API, and we also need the library that contains the dialog function. As we may recall from previous units, windows.h already includes the core WinAPI types. Here, we add one more header so the open dialog functions are available.
The beneficial part of this lesson is that the menu itself does not require a redesign. As we have already set up in earlier lessons, the File menu contains both Open... and Exit. This means our interface is already prepared; we only need to give the Open... command actual behavior.
This section still functions as it did before: it creates the menu bar, builds the File menu, and attaches it to the window. The important point for this lesson is the ID_FILE_OPEN entry, because that is the command that will now launch the file browser.
Now we move to the place where menu clicks are handled. In the WinAPI, a menu selection arrives as WM_COMMAND, and we use LOWORD(wParam) to identify which item was chosen. The Open... case now receives its own block because we need local variables that belong specifically to the file dialog logic.
There are two key ideas here. First, OPENFILENAMEW is the structure that defines how the dialog should behave. Second, szFile is a writable buffer where Windows will place the chosen file path. The braces after case ID_FILE_OPEN are important because they create a scope for these local variables.
Once the variables exist, we must initialize the structure carefully. This is one of the most important parts of the lesson because GetOpenFileNameW depends on the fields in OPENFILENAMEW being set correctly. We start by clearing the structure and then fill in the fields that inform Windows which window owns the dialog and where the result should be stored.
Each assignment serves the following purpose:
ZeroMemoryclears every field so that nothing contains leftover data.lStructSizeinforms Windows which version and size of the structure we are using.hwndOwner = hwndmakes the dialog belong to our main window.lpstrFilepoints to the buffer that will receive the file path.nMaxFileinforms Windows how much space that buffer has.
Because we use OPENFILENAMEW, the buffer is a wchar_t array, which matches the wide-character WinAPI functions.
With the basic fields in place, we can refine the dialog further. This is where we tell Windows which file types to show and which basic rules to enforce. These settings help the dialog feel focused and secure, even though the code remains concise.
This snippet performs several useful functions:
lpstrFilteruses a specific format of null-terminated pairs ("Label\0Pattern\0"). The entire string must end with an extra\0(which the compiler adds automatically to the end of the string literal).- "All Files": The text the user sees in the dropdown.
- ".": The actual filter applied to the file system.
- "\0": Separates the label from the pattern and each pair from the next.
nFilterIndex = 1selects the first filter ("All Files") when the dialog opens.OFN_PATHMUSTEXISTrequires a valid folder path.OFN_FILEMUSTEXISTprevents selecting a file that does not actually exist.
These flags are appropriate for an Open dialog because we want the user to choose an existing file rather than inventing a new one.
Now, the actual dialog appears. Once OPENFILENAMEW is ready, we pass its address to GetOpenFileNameW. If the function succeeds, Windows will have already copied the selected path into szFile, allowing us to use it immediately. In this lesson, we display that path with a message box so we can clearly see the result.
This is the key behavior of the lesson:
GetOpenFileNameW(&ofn)opens the native file picker and waits for the user.- If the user selects a file and confirms, the function returns a nonzero value.
- At that point,
szFilecontains the full path, and we display it usingMessageBoxW.
If the user cancels the dialog, the if body is skipped, and the app simply continues running.
Adding file browsing does not alter the rest of the app's structure. The exit command still asks for confirmation, but we now process the shutdown through two distinct messages: WM_CLOSE and WM_DESTROY. This separation allows us to distinguish between the intent to close and the actual destruction of the window.
The flow remains logical:
ID_FILE_EXITperforms the confirmation and sendsWM_CLOSE.WM_DESTROYcallsPostQuitMessage(0)to end the message loop.DefWindowProcWensures that any unhandled messages, including system commands, are processed correctly.
In this lesson, we maintained the menu from earlier units, added access to the Common Dialog API, prepared an OPENFILENAMEW structure, and used GetOpenFileNameW to show the standard Windows file picker. We also refined the shutdown process by using both WM_CLOSE and WM_DESTROY.
This is an important step forward because our program now interacts with the file system through a familiar Windows interface instead of a placeholder action. In the practice section ahead, we will reinforce this workflow so that we can build file selection features with more confidence and less guesswork.
