File Dialogs

Windows

Includes and libraries

#include <windows.h>
#include <shobjidl.h>
#pragma comment (lib, "shell32.lib")
#pragma comment (lib, "advapi32.lib")
#pragma comment (lib, "comctl32.lib")
#pragma comment (lib, "ole32.lib")
#pragma comment (lib, "Comdlg32.lib")

Save

OPENFILENAME config = {};
config.lStructSize = sizeof(OPENFILENAME);
config.hwndOwner = mainWindow; // Put the owner window handle here.
config.lpstrFilter = L"Text files\0*.txt\0"; // Put the file extension here.
wchar_t path[MAX_PATH];
path[0] = 0;
config.lpstrFile = path;
config.lpstrDefExt = FILE_EXT + 1;
config.nMaxFile = sizeof(path) / sizeof(path[0]);
config.Flags = OFN_OVERWRITEPROMPT;
config.Flags |= OFN_NOCHANGEDIR;//To prevent GetSaveFileName() from changing the working directory

if (GetSaveFileName(&config)) {
    // `path` contains the file.
}

Open

OPENFILENAME config = {};
config.lStructSize = sizeof(OPENFILENAME);
config.hwndOwner = mainWindow; // Put the owner window handle here.
config.lpstrFilter = L"Text files\0*.txt\0"; // Put the file extension here.
wchar_t path[MAX_PATH];
path[0] = 0;
config.lpstrFile = path;
config.lpstrDefExt = FILE_EXT + 1;
config.nMaxFile = sizeof(path) / sizeof(path[0]);
config.Flags = OFN_FILEMUSTEXIST;
config.Flags |= OFN_NOCHANGEDIR;//To prevent GetOpenFileName() from changing the working directory

if (GetOpenFileName(&config)) {
    // `path` contains the file
}

Open directory

CoInitialize(NULL); // Initialise COM if you haven't already.

IFileOpenDialog *dialog;
IShellItem *item;
wchar_t *path;
HRESULT result = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void **) &dialog);
if (!SUCCEEDED(result)) { goto error; }
result = dialog->SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR);
if (!SUCCEEDED(result)) { dialog->Release(); goto error; }
result = dialog->Show(mainWindow); // Put the owner window handle here.
if (!SUCCEEDED(result)) { dialog->Release(); goto error; }
result = dialog->GetResult(&item);
if (!SUCCEEDED(result)) { dialog->Release(); goto error; }
result = item->GetDisplayName(SIGDN_FILESYSPATH, &path);
if (!SUCCEEDED(result)) { item->Release(); dialog->Release(); goto error; }

// `path` contains the directory.

CoTaskMemFree(path);
error:

Edited by Ben Visness on Reason: rerendering code blocks