在 Windows 上用 C++ 搞一个打开/保存文件对话框
·
在 Windows 上用 C++ 搞一个打开/保存文件对话框
做 Windows 桌面开发,基本绕不开让用户选文件。不管是打开已有文件还是另存为新文件,总得弹个系统对话框。
网上搜一圈,方法不少,但很多要么是 MFC 的,要么是纯 Win32 的,混在一起讲容易乱。我这篇就只聊文件对话框(文件夹的下一期再说),把两种常用路子都过一遍,顺带记几个我踩过的坑。
先上 MFC 的 CFileDialog(如果你用 MFC 的话)
如果你的项目本身是 MFC 框架,那直接用 CFileDialog 最省事,不用自己折腾结构体。
打开文件
#include <afxdlgs.h>
void DoOpenFile()
{
CFileDialog dlg(TRUE, NULL, NULL,
OFN_HIDEREADONLY | OFN_FILEMUSTEXIST,
_T("文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*||"),
this);
if (dlg.DoModal() == IDOK)
{
CString path = dlg.GetPathName();
// 拿到路径,该读就读
AfxMessageBox(_T("选了: ") + path);
}
}
那个过滤器字符串的格式是固定的:"描述|模式|描述|模式||",最后一定要俩竖线,少一个就弹不出来,别问我怎么知道的。OFN_FILEMUSTEXIST 保证用户不能瞎填一个不存在的文件,省得你后面再校验。
保存文件
保存模式就是把第一个参数改成 FALSE,顺手加上覆盖提示。
void DoSaveFile()
{
CFileDialog dlg(FALSE,
_T("txt"),
_T("默认名字"),
OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST,
_T("文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*||"),
this);
if (dlg.DoModal() == IDOK)
{
CString path = dlg.GetPathName();
// 创建文件写入
}
}
OFN_OVERWRITEPROMPT 必须加,不然用户选了个已存在的文件,你的程序直接覆盖掉,连个招呼都不打,等着被骂吧。
不用 MFC 的话,Win32 老路子 GetOpenFileName / GetSaveFileName
很多朋友的项目是纯 Win32 或者 ATL 之类的,没 MFC 那一套。那就用 commdlg.h 里的这两个 API,它们从 Win95 就存在了,兼容性无敌。
打开文件
#include <commdlg.h>
CString OpenFileDialog(HWND hWnd)
{
OPENFILENAME ofn = { 0 };
TCHAR szFile[MAX_PATH] = { 0 };
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = szFile;
ofn.nMaxFile = MAX_PATH;
// 注意过滤器的写法:用 \0 分隔,最后两个 \0 结尾
ofn.lpstrFilter = _T("文本文件\0*.txt\0所有文件\0*.*\0");
ofn.nFilterIndex = 1;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
if (GetOpenFileName(&ofn))
return CString(szFile);
return _T(""); // 取消或失败
}
这里最容易写错的就是 lpstrFilter,它用的是双空字符分隔,不是 |。如果你复制了 MFC 那种 | 分隔的字符串过来,对话框直接罢工。
另外 szFile 缓冲区要足够大,MAX_PATH 通常是 260 个字符,一般够用,但长路径的话可以考虑 _MAX_PATH 或动态分配,不过一般不用太纠结。
保存文件
CString SaveFileDialog(HWND hWnd)
{
OPENFILENAME ofn = { 0 };
TCHAR szFile[MAX_PATH] = _T("未命名.txt"); // 预填默认名
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = szFile;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = _T("文本文件\0*.txt\0所有文件\0*.*\0");
ofn.nFilterIndex = 1;
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
if (GetSaveFileName(&ofn))
return CString(szFile);
return _T("");
}
跟打开的区别不大,就是 Flags 换了 OFN_OVERWRITEPROMPT,szFile 里给了个初始字符串,用户可以在对话框里改。
| 项目类型 | 推荐接口 | 说明 |
|---|---|---|
| 基于 MFC 框架 | CFileDialog |
无需额外依赖,接口与 MFC 风格一致 |
| 纯 Win32 / ATL / 无 MFC | GetOpenFileName / GetSaveFileName |
仅依赖 commdlg.dll,无框架开销 |
| 需适配 Windows 10/11 界面 | 两者均可 | 底层均调用系统通用对话框,外观一致 |
| 需兼容 Windows XP | 两者均可 | GetOpenFileName 原生支持 XP;CFileDialog 需使用 v141_xp 工具集编译 MFC |
几个容易忽略的细节
- 用户点了取消:两种方式都会返回
FALSE或IDCANCEL,一定要判断,别拿着空路径去读写。 - 默认扩展名:
CFileDialog的第二个参数可以指定默认后缀,如果用户没输后缀,系统会自动补上。Win32 版的话,OPENFILENAME里也有lpstrDefExt可以设。 - 多选文件:这里没讲,因为篇幅长,而且多数场景只要单选。如果需要多选,
CFileDialog可以加OFN_ALLOWMULTISELECT,但处理起来会复杂一些。 - Unicode 和 ANSI:现在基本都用 Unicode 了,代码里的
_T宏和TCHAR能自动适应,如果是 VS 的新项目,默认就是 Unicode。
版权声明:本文为博主原创,转载注明出处即可。
更多推荐

所有评论(0)