檔案開啟/儲存對話框提供兩種實作:傳統 GetOpenFileName (C 風格) 與 CLR 整合 System.Windows.Forms.OpenFileDialog。本文完整對比兩種方案實作細節與避坑指南。
OPENFILENAME ofn = {0};
wchar_t filename[MAX_PATH] = {0};
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.lpstrFilter = L"所有檔案\0*.*\0文字檔\0*.txt\0MP3\0*.mp3\0"; // 雙\0結尾
ofn.nFilterIndex = 1; // 預設篩選器 (1-based)
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; // 檢查存在性
ofn.lpstrTitle = L"選擇要開啟的檔案";
描述1\0模式1\0描述2\0模式2\0\0\0 分隔,**結尾雙重 \0\0**\0,故程式碼中寫一個 \0 即可形成雙重結尾
if (GetOpenFileName(&ofn)) {
HWND hEdit = GetDlgItem(hDlg, IDC_EDIT_FILE);
SetWindowText(hEdit, filename);
}
/clrSystem.Windows.Forms.dllusing namespace System::Windows::Forms;
if (LOWORD(wParam) == IDC_BTN_OPEN_CLR) {
OpenFileDialog^ dlg = gcnew OpenFileDialog();
dlg->Filter = L"所有檔案|*.*|圖片檔|*.jpg"; // '|' 分隔,.NET 風格
dlg->FilterIndex = 1;
dlg->CheckFileExists = true;
if (dlg->ShowDialog() == DialogResult::OK) {
// 轉換 System::String^ → const wchar_t*
pin_ptr<const wchar_t> nativePath = PtrToStringChars(dlg->FileName);
HWND hEdit = GetDlgItem(hDlg, IDC_EDIT_FILE);
SendMessage(hEdit, WM_SETTEXT, 0, (LPARAM)nativePath);
}
}
OpenFileDialog 需單一執行緒單元 (STA)STA (/CLRTHREADATTRIBUTE:STA)ShowDialog() 拋出 System.Threading.ThreadStateException



| 維度 | GetOpenFileName | OpenFileDialog (CLR) |
|---|---|---|
| 相依性 | 純 Win32 (comdlg32.dll) | 需 /clr、.NET Framework |
| 程式碼風格 | C 結構體、手動記憶體 | Managed 物件、GC 自動管理 |
| 篩選器語法 | \0 分隔、雙重結尾 | | 分隔、.NET 慣用 |
| Unicode 支援 | 原生 wchar_t | 原生 String^ (UTF-16) |
| 自訂擴充 | Hook 程序 OFNHOOKPROC | 事件、繼承、自訂控制項 |
| 部署大小 | 極小 | 需 .NET Runtime |
| 適用專案 | 純原生、輕量、嵌入式 | 混合模式、既有 .NET 專案 |
IFileDialog* pfd;
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
pfd->SetFileTypes(3, (COMDLG_FILTERSPEC[]){
{L"所有檔案", L"*.*"}, {L"文字檔", L"*.txt"}, {L"MP3", L*.mp3}
});
pfd->SetOptions(FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST);
if (SUCCEEDED(pfd->Show(hWnd))) {
IShellItem* psi; pfd->GetResult(&psi);
PWSTR path; psi->GetDisplayName(SIGDN_FILESYSPATH, &path);
// 使用 path... CoTaskMemFree(path);
}
pfd->Release();
優點:原生 COM、無 .NET 相依、支援自訂位置、非同步、高 DPI 友善。