[WinAPI] 使用新风格的 IFileDialog 来展示文件(夹)选择对话框
以往在选择打开文件/保存文件的时候用的是 GetOpenFileName 和 GetSaveFileName 这两个函数,选择文件夹用的是 SHBrowseForFolder 这个函数,这几个函数都特别老了。
自从 Windows Vista 开始,Windows SDK 的 Common Item Dialog 中提供了另外几个 COM 接口用来执行类似的操作,相关的接口有:IFileDialog、IFileOpenDialog、IFileSaveDialog,MFC 中有另外的完整的可直接使用的封装类 CFolderPickerDialog。前面几个是跟普通系统函数一样,使用起来比较简单。后面几个基于接口的用起来稍微要复杂点,需要掌握 COM 的基本用法。
至于后者有哪些优点我就不做介绍了,想要具体了解的可以参考文后的参考链接。不过,在文件夹选择的表现上有个很大的不同,如下图所示:


我想多数人就是因为如上图示的不同才准备使用这个新版本的接口函数的吧?
下面我以简单的示例代码举例说明了怎么使用新版的接口函数(非 MFC)。IFileDialog 是 IFileOpenDialog 和 IFileSaveDialog 的直接基类接口,直接单独使用后面两个就好了。
#include <cstdio>
#include <string>
#include <atlbase.h>
#include <shobjidl.h>
#include <windows.h>
static std::wstring folder_open_dialog()
{
std::wstring ret;
CComPtr<IFileOpenDialog> spFileOpenDialog;
if(SUCCEEDED(spFileOpenDialog.CoCreateInstance(__uuidof(FileOpenDialog)))) {
FILEOPENDIALOGOPTIONS options;
if(SUCCEEDED(spFileOpenDialog->GetOptions(&options))) {
spFileOpenDialog->SetOptions(options | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM);
if(SUCCEEDED(spFileOpenDialog->Show(nullptr))) {
CComPtr<IShellItem> spResult;
if(SUCCEEDED(spFileOpenDialog->GetResult(&spResult))) {
wchar_t* name;
if(SUCCEEDED(spResult->GetDisplayName(SIGDN_FILESYSPATH, &name))) {
ret = name;
CoTaskMemFree(name);
}
}
}
}
}
return std::move(ret);
}
int main()
{
CoInitialize(nullptr);
::MessageBox(nullptr, folder_open_dialog().c_str(), L"", MB_OK);
CoUninitialize();
}
参考: