一、简单介绍一下WTL的CShellFileDialog对话框
1、在WTL中用于实现CShellFileDialog模板有
class CShellFileDialogImpl<T>; //基于IFileDialogEvents接口的基础模板,主要封装相关事件,是CShellFileOpenDialogImpl和CShellFileSaveDialogImpl的基础
class CShellFileOpenDialogImpl<T>; //基于IFileOpenDialog接口的基础模板,IFileOpenDialog是IFileDialog的派送类,MFC中是使用IFileDialog作为成员变量的,而后通过传入的参数,实际实例化IFileOpenDialog或者IFileSaveDialog
class CShellFileOpenDialog; //基于IFileOpenDialog接口的基础模板的实现类
class CShellFileSaveDialogImpl<T>; //基于IFileSaveDialog接口的基础模板,IFileSaveDialog是IFileDialog的派送类,MFC中是使用IFileDialog作为成员变量的,而后通过传入的参数,实际实例化IFileOpenDialog或者IFileSaveDialog
class CShellFileSaveDialog; //基于IFileSaveDialog接口的基础模板的实现类
CShellFileDialogImpl<T>已经实现了所有IFileDialogEvents接口,并通过_Advise和_Unadvise来实现对事件的绑定和实际调用
CShellFileOpenDialogImpl<T>是CShellFileDialogImpl<T>派送类,并对集成了IFileOpenDialog接口应用的简单封装,主要完成了构造函数的初始化,其实际功能需要通过GetPtr函数返回的IFileOpenDialog指针来完成
CShellFileOpenDialog则是CShellFileOpenDialogImpl<T>模板的实例,其中_Advise和_Unadvise函数被刻意置空,也就是默认没有实现对自定义事件的实现功能
2、自定义事件处理
要对自定义事件进行处理,就需要自己对CShellFileOpenDialogImpl<T>模板派送一个实例,添加对应的处理函数,但不能添加_Advise和_Unadvise函数,而是让其继承CShellFileDialogImpl< T >中的相关函数用于绑定事件处理
class MyShellFileOpenDialog : public CShellFileOpenDialogImpl<CShellFileOpenDialog>
{
public:
MyShellFileOpenDialog(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U) : CShellFileOpenDialogImpl<MyShellFileOpenDialog>(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount)
{ }
virtual ~MyShellFileOpenDialog()
{ }
//删除原CShellFileOpenDialog中的_Advise和_Unadvise即可以绑定事件
//void _Advise(DWORD& /*dwCookie*/){ }
//void _Unadvise(DWORD /*dwCookie*/){ }
HRESULT OnFileOk()//添加处理函数
{
MessageBox(....);
return S_OK;
}
};
二、实现界面的自定义(IFileDialogCustomize)以及实现自定义事件处理(IFileDialogControlEvents)
1、IFileDialogControlEvents接口是系统自动从实现了IFileDialogEvents接口的对象中调取,不需要用户另外配置或绑定的
class CShellFileOpenDialogCustomizeImpl : public IFileDialogEvents, public IFileDialogControlEvents;
//或者利用WTL已经实现CShellFileDialogImpl,则我们只需要再实现IFileDialogControlEvents
template <class T>
class CShellFileDialogCustomizeImpl : public IFileDialogControlEvents< T >;
template <class T>
class CShellFileOpenDialogCustomizeImpl : public WTL::CShellFileDialogImpl< T >, public CShellFileDialogCustomizeImpl< T >
2、IFileDialogCustomize接口,此接口是用于添加控件,实现界面及控件自定义的关键,这个接口可直接从IFileOpenDialog接口中获取
ATL::CComPtr<IFileOpenDialog> m_spFileDlg;
ATL::CComPtr<IFileDialogCustomize> m_spFileDlgCust;
HRESULT hRet = m_spFileDlg.CoCreateInstance(CLSID_FileOpenDialog);
hRet = m_spFileDlg->QueryInterface(IID_PPV_ARGS(&m_spFileDlgCust));
完成后,只需要调用m_spFileDlgCust(IFileDialogCustomize)中的相关函数的调用,即可完成对对话框的添加控件的任务
保存对话框也是类似,只是接口由IFileOpenDialog变成了IFileSaveDialog
三、全部实现代码如下:
#ifndef _FILEDIALOGCUSTOMIZE_H_
#define _FILEDIALOGCUSTOMIZE_H_
#include <atlwin.h>
#include <atldlgs.h>
namespace WTL {
template <class T>
class ATL_NO_VTABLE CShellFileDialogCustomizeImpl : public IFileDialogControlEvents {
public:
// Implementation - IUnknown interface
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject)
{
if(ppvObject == NULL)
return E_POINTER;
T* pT = static_cast<T*>(this);
if(IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IFileDialogControlEvents))
{
*ppvObject = (IFileDialogControlEvents*)pT;
// AddRef() not needed
return S_OK;
}
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
return 1;
}
virtual ULONG STDMETHODCALLTYPE Release()
{
return 1;
}
virtual HRESULT STDMETHODCALLTYPE OnButtonClicked(IFileDialogCustomize *pfdc, DWORD dwIDCtl)
{
T* pT = static_cast<T*>(this);
ATLASSERT(pT->m_spFileDlgCust.IsEqualObject(pfdc));
(void)pfdc; // avoid level 4 warning
return pT->OnButtonClicked(dwIDCtl);
}
virtual HRESULT STDMETHODCALLTYPE OnCheckButtonToggled(IFileDialogCustomize *pfdc, DWORD dwIDCtl, BOOL bChecked)
{
T* pT = static_cast<T*>(this);
ATLASSERT(pT->m_spFileDlgCust.IsEqualObject(pfdc));
(void)pfdc; // avoid level 4 warning
return pT->OnCheckButtonToggled(dwIDCtl, bChecked);
}
virtual HRESULT STDMETHODCALLTYPE OnControlActivating(IFileDialogCustomize *pfdc, DWORD dwIDCtl)
{
T* pT = static_cast<T*>(this);
ATLASSERT(pT->m_spFileDlgCust.IsEqualObject(pfdc));
(void)pfdc; // avoid level 4 warning
return pT->OnControlActivating(dwIDCtl);
}
virtual HRESULT STDMETHODCALLTYPE OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem)
{
T* pT = static_cast<T*>(this);
ATLASSERT(pT->m_spFileDlgCust.IsEqualObject(pfdc));
(void)pfdc; // avoid level 4 warning
return pT->OnItemSelected(dwIDCtl, dwIDItem);
}
// Overrideables - Event handlers
HRESULT OnButtonClicked(DWORD /*dwIDCtl*/)
{
return E_NOTIMPL;
}
HRESULT OnCheckButtonToggled(DWORD /*dwIDCtl*/, BOOL /*bChecked*/)
{
return E_NOTIMPL;
}
HRESULT OnControlActivating(DWORD /*dwIDCtl*/)
{
return E_NOTIMPL;
}
HRESULT OnItemSelected(DWORD /*dwIDCtl*/, DWORD /*dwIDItem*/)
{
return E_NOTIMPL;
}
};
template <class T>
class ATL_NO_VTABLE CShellFileOpenDialogCustomizeImpl : public CShellFileDialogImpl< T >, public CShellFileDialogCustomizeImpl< T > {
public:
ATL::CComPtr<IFileOpenDialog> m_spFileDlg;
ATL::CComPtr<IFileDialogCustomize> m_spFileDlgCust;
CShellFileOpenDialogCustomizeImpl(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U)
{
HRESULT hRet = m_spFileDlg.CoCreateInstance(CLSID_FileOpenDialog);
if(SUCCEEDED(hRet))
this->_Init(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount);
hRet = m_spFileDlg->QueryInterface(IID_PPV_ARGS(&m_spFileDlgCust));
}
virtual ~CShellFileOpenDialogCustomizeImpl()
{ }
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject)
{
// static const QITAB qit[] =
// {
// QITABENT(CShellFileOpenDialogCustomizeImpl, IID_IFileDialogEvents),
// QITABENT(CShellFileOpenDialogCustomizeImpl, IID_IFileDialogControlEvents),
// { 0 },
// };
// return QISearch(this, qit, riid, ppv);
if(ppvObject == NULL)
return E_POINTER;
T* pT = static_cast<T*>(this);
if(IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IFileDialogEvents))
{
*ppvObject = (IFileDialogEvents*)pT;
// AddRef() not needed
return S_OK;
}
else if (IsEqualGUID(riid, IID_IFileDialogControlEvents))
{
*ppvObject = (IFileDialogControlEvents*)pT;
// AddRef() not needed
return S_OK;
}
return E_NOINTERFACE;
}
IFileOpenDialog* GetPtr()
{
return m_spFileDlg;
}
IFileDialogCustomize* GetCustomizePtr()
{
return m_spFileDlgCust;
}
};
//自定时直接派生CShellFileOpenDialogCustomizeImpl<CShellFileOpenDialogCustomize>,如class CFileDialogCustomize : public CShellFileOpenDialogCustomizeImpl<CFileDialogCustomize>
//如果派生函数不需要响应消息事件是如CShellFileOpenDialogCustomize中直接添加void _Advise(DWORD& /*dwCookie*/){}和void _Unadvise(DWORD /*dwCookie*/){}
//如果需要响应的话,则在新函数中不能添加_Advise和_Unadvise函数
class CShellFileOpenDialogCustomize : public CShellFileOpenDialogCustomizeImpl<CShellFileOpenDialogCustomize> {
public:
CShellFileOpenDialogCustomize(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U) : CShellFileOpenDialogCustomizeImpl<CShellFileOpenDialogCustomize>(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount)
{ }
virtual ~CShellFileOpenDialogCustomize()
{ }
// Implementation (remove _Advise/_Unadvise code using template magic)
void _Advise(DWORD& /*dwCookie*/)
{ }
void _Unadvise(DWORD /*dwCookie*/)
{ }
};
template <class T>
class ATL_NO_VTABLE CShellFileSaveDialogCustomizeImpl : public CShellFileDialogImpl< T >, public CShellFileDialogCustomizeImpl< T > {
public:
ATL::CComPtr<IFileSaveDialog> m_spFileDlg;
ATL::CComPtr<IFileDialogCustomize> m_spFileDlgCust;
CShellFileSaveDialogCustomizeImpl(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U)
{
HRESULT hRet = m_spFileDlg.CoCreateInstance(CLSID_FileSaveDialog);
if(SUCCEEDED(hRet))
this->_Init(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount);
hRet = m_spFileDlg->QueryInterface(IID_PPV_ARGS(&m_spFileDlgCust));
}
virtual ~CShellFileSaveDialogCustomizeImpl()
{ }
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject)
{
if(ppvObject == NULL)
return E_POINTER;
T* pT = static_cast<T*>(this);
if(IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IFileDialogEvents))
{
*ppvObject = (IFileDialogEvents*)pT;
// AddRef() not needed
return S_OK;
}
else if (IsEqualGUID(riid, IID_IFileDialogControlEvents))
{
*ppvObject = (IFileDialogControlEvents*)pT;
// AddRef() not needed
return S_OK;
}
return E_NOINTERFACE;
}
IFileSaveDialog* GetPtr()
{
return m_spFileDlg;
}
IFileDialogCustomize* GetCustomizePtr()
{
return m_spFileDlgCust;
}
};
class CShellFileSaveDialogCustomize : public CShellFileSaveDialogCustomizeImpl<CShellFileSaveDialogCustomize> {
public:
CShellFileSaveDialogCustomize(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U) : CShellFileSaveDialogCustomizeImpl<CShellFileSaveDialogCustomize>(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount)
{ }
virtual ~CShellFileSaveDialogCustomize()
{ }
// Implementation (remove _Advise/_Unadvise code using template magic)
void _Advise(DWORD& /*dwCookie*/)
{ }
void _Unadvise(DWORD /*dwCookie*/)
{ }
};
}
#endif //_FILEDIALOGCUSTOMIZE_H_
四、调用实例,模仿记事本中选取编码的对话框
#define ID_SHELLFILEDIALOG_VISUALGROUP 32807
#define ID_SHELLFILEDIALOG_COMBOBOX 32808
enum{EN_AUTO, EN_UTF_16_LE, EN_UTF_16_BE, EN_UTF_8_BOM, EN_ANSI_UTF_8};
//
COMDLG_FILTERSPEC fileFilter[] = {{_T("文本文件"), _T("*.txt")},
{_T("所有文件"), _T("*.*")}};
CShellFileOpenDialogCustomize shellFileDlg(NULL, FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST, _T("txt"), fileFilter, 2);
//使用我的文档作为默认打开目录
CComPtr<IShellItem> iShellItem;
HRESULT hr = ::SHCreateItemInKnownFolder(FOLDERID_Documents, KF_FLAG_DEFAULT, NULL, IID_PPV_ARGS(&iShellItem));//使用IID_PPV_ARGS宏
if (SUCCEEDED(hr)) {
hr = shellFileDlg.GetPtr()->SetFolder(iShellItem);//设置SetFolder而不是SetDefaultFolder
}
shellFileDlg.GetCustomizePtr()->StartVisualGroup(ID_SHELLFILEDIALOG_VISUALGROUP, _T("编码:"));
shellFileDlg.GetCustomizePtr()->AddComboBox(ID_SHELLFILEDIALOG_COMBOBOX);
shellFileDlg.GetCustomizePtr()->EndVisualGroup();
shellFileDlg.GetCustomizePtr()->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_AUTO, _T("自动监测"));
shellFileDlg.GetCustomizePtr()->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_UTF_16_LE, _T("UTF-16"));
shellFileDlg.GetCustomizePtr()->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_UTF_16_BE, _T("UTF-16 BE"));
shellFileDlg.GetCustomizePtr()->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_UTF_8_BOM, _T("UTF-8带BOM"));
shellFileDlg.GetCustomizePtr()->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_ANSI_UTF_8, _T("ANSI或者UTF-8"));
shellFileDlg.GetCustomizePtr()->SetSelectedControlItem(ID_SHELLFILEDIALOG_COMBOBOX, 0);
if (IDOK == shellFileDlg.DoModal()) {
CString strFile;
HRESULT hr = shellFileDlg.GetFilePath(strFile);
if (SUCCEEDED(hr)) {
UINT nFileTypeIndex = 0;
hr = shellFileDlg.GetPtr()->GetFileTypeIndex(&nFileTypeIndex);
DWORD nEncoded = 0;
shellFileDlg.GetCustomizePtr()->GetSelectedControlItem(ID_SHELLFILEDIALOG_COMBOBOX, &nEncoded);
if (SUCCEEDED(hr)) {
if (0 == nFileTypeIndex) {
LoadTextFile(strFile, nEncoded);
}
else {
LoadUnknownFile(strFile, nEncoded);
}
}
}
}
效果如下:
五、如需要添加事件
#define ID_SHELLFILEDIALOG_VISUALGROUP 32807
#define ID_SHELLFILEDIALOG_COMBOBOX 32808
enum{EN_AUTO, EN_UTF_16_LE, EN_UTF_16_BE, EN_UTF_8_BOM, EN_ANSI_UTF_8};
//
class CFileDialogCustomize : public CShellFileOpenDialogCustomizeImpl<CFileDialogCustomize> {
public:
CFileDialogCustomize(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U) : CShellFileOpenDialogCustomizeImpl<CFileDialogCustomize>(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount)
{
m_Encoded = 0;
m_spFileDlgCust->StartVisualGroup(ID_SHELLFILEDIALOG_VISUALGROUP, _T("编码:"));
m_spFileDlgCust->AddComboBox(ID_SHELLFILEDIALOG_COMBOBOX);
m_spFileDlgCust->EndVisualGroup();
m_spFileDlgCust->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_AUTO, _T("自动监测"));
m_spFileDlgCust->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_UTF_16_LE, _T("UTF-16"));
m_spFileDlgCust->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_UTF_16_BE, _T("UTF-16 BE"));
m_spFileDlgCust->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_UTF_8_BOM, _T("UTF-8带BOM"));
m_spFileDlgCust->AddControlItem(ID_SHELLFILEDIALOG_COMBOBOX, EN_ANSI_UTF_8, _T("ANSI或者UTF-8"));
m_spFileDlgCust->SetSelectedControlItem(ID_SHELLFILEDIALOG_COMBOBOX, 0);
}
virtual ~CFileDialogCustomize()
{ }
DWORD get_encoded()const{return m_Encoded;}
HRESULT OnItemSelected(DWORD dwIDCtl, DWORD dwIDItem)//自定义选取事件
{
if(ID_SHELLFILEDIALOG_COMBOBOX == dwIDCtl) {
m_Encoded = dwIDItem;
}
return S_OK;
}
private:
DWORD m_Encoded;
};
COMDLG_FILTERSPEC fileFilter[] = {{_T("文本文件"), _T("*.txt")},
{_T("所有文件"), _T("*.*")}};
CFileDialogCustomize shellFileDlg(NULL, FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST, _T("txt"), fileFilter, 2);
//使用我的文档作为默认打开目录
CComPtr<IShellItem> iShellItem;
HRESULT hr = ::SHCreateItemInKnownFolder(FOLDERID_Documents, KF_FLAG_DEFAULT, NULL, IID_PPV_ARGS(&iShellItem));//使用IID_PPV_ARGS宏
if (SUCCEEDED(hr)) {
hr = shellFileDlg.GetPtr()->SetFolder(iShellItem);//设置SetFolder而不是SetDefaultFolder
}
if (IDOK == shellFileDlg.DoModal()) {
CString strFile;
HRESULT hr = shellFileDlg.GetFilePath(strFile);
if (SUCCEEDED(hr)) {
UINT nFileTypeIndex = 0;
hr = shellFileDlg.GetPtr()->GetFileTypeIndex(&nFileTypeIndex);
DWORD nEncoded = shellFileDlg.get_encoded();//调用自定义函数获取选取项
if (SUCCEEDED(hr)) {
if (0 == nFileTypeIndex) {
LoadTextFile(strFile, nEncoded);
}
else {
LoadUnknownFile(strFile, nEncoded);
}
}
}
}
六、不太喜欢WTL中关于CShellFileOpenDialog和CShellFileSaveDialog命名
namespace WTL
{
template <bool IsOpenDialog>
class CShellFileDialogCustomize;
template <>
class CShellFileDialogCustomize<true> : public CShellFileOpenDialogCustomizeImpl<CShellFileDialogCustomize<true> > {
public:
CShellFileDialogCustomize(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U) : CShellFileOpenDialogCustomizeImpl<CShellFileDialogCustomize>(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount)
{ }
virtual ~CShellFileDialogCustomize()
{ }
// Implementation (remove _Advise/_Unadvise code using template magic)
void _Advise(DWORD& /*dwCookie*/)
{ }
void _Unadvise(DWORD /*dwCookie*/)
{ }
};
template <>
class CShellFileDialogCustomize<false> : public CShellFileSaveDialogCustomizeImpl<CShellFileDialogCustomize<false> > {
public:
CShellFileDialogCustomize(LPCWSTR lpszFileName = NULL,
DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT,
LPCWSTR lpszDefExt = NULL,
const COMDLG_FILTERSPEC* arrFilterSpec = NULL,
UINT uFilterSpecCount = 0U) : CShellFileSaveDialogCustomizeImpl<CShellFileDialogCustomize>(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount)
{ }
virtual ~CShellFileDialogCustomize()
{ }
// Implementation (remove _Advise/_Unadvise code using template magic)
void _Advise(DWORD& /*dwCookie*/)
{ }
void _Unadvise(DWORD /*dwCookie*/)
{ }
};
}
七、备注
关于获取特定目录作为默认目录,可以使用SHCreateItemFromParsingName函数将路径转换为IShellItem,也可以使用SHGetKnownFolderPath获取特定目录的IShellItem
//使用自定义路径作为默认打开路径
CComPtr<IShellItem> iShellItem;
HRESULT hr = ::SHCreateItemFromParsingName(L"C:\\", NULL, NULL, IID_PPV_ARGS(&iShellItem));//使用IID_PPV_ARGS宏
if (SUCCEEDED(hr)) {
hr = shellFileDlg.GetPtr()->SetFolder(iShellItem);//设置SetFolder而不是SetDefaultFolder
}
或者
//获取特定目录(我的文档)作为默认打开路径
CComPtr<IShellItem> iShellItem;
HRESULT hr = ::SHCreateItemInKnownFolder(FOLDERID_Documents, KF_FLAG_DEFAULT, NULL, IID_PPV_ARGS(&iShellItem));//使用IID_PPV_ARGS宏
if (SUCCEEDED(hr)) {
hr = shellFileDlg.GetPtr()->SetFolder(iShellItem);//设置SetFolder而不是SetDefaultFolder
}