在 Windows 上用 C++ 搞一个打开/保存文件对话框
做 Windows 桌面开发,基本绕不开让用户选文件。不管是打开已有文件还是另存为新文件,总得弹个系统对话框。
网上搜一圈,方法不少,但很多要么是 MFC 的,要么是纯 Win32 的,混在一起讲容易乱。我这篇就只聊文件对话框(文件夹的下一期再说),把两种常用路子都过一遍,顺带记几个我踩过的坑。
先上 MFC 的CFileDialog(如果你用 MFC 的话)
如果你的项目本身是 MFC 框架,那直接用CFileDialog最省事,不用自己折腾结构体。
打开文件
#include<afxdlgs.h>voidDoOpenFile(){CFileDialogdlg(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,顺手加上覆盖提示。
voidDoSaveFile(){CFileDialogdlg(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>CStringOpenFileDialog(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))returnCString(szFile);return_T("");// 取消或失败}这里最容易写错的就是lpstrFilter,它用的是双空字符分隔,不是|。如果你复制了 MFC 那种|分隔的字符串过来,对话框直接罢工。
另外szFile缓冲区要足够大,MAX_PATH通常是 260 个字符,一般够用,但长路径的话可以考虑_MAX_PATH或动态分配,不过一般不用太纠结。
保存文件
CStringSaveFileDialog(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))returnCString(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。
版权声明:本文为博主原创,转载注明出处即可。