IFileDialog使用指南

IFileDialog的使用方法
作者:收割者
Prerequisite:
C++编程者,了解或是熟悉COM(组件对象模型)。
打开文件对话框或是另存为对话框,大家都很熟悉。在过去(Vista之前),在win32中是使用GetOpenFileName或是GetSaveFileName API来实现,在MFC中使用CFileDialog类来实现。现在在Vista之后的系统,有了一种新的实现方法,就是今天要讲的,通过IFileDialog接口,这是一个COM接口,从它继承之后有两个接口,一个是IFileOpenDialog,另外一个IFileSaveDialog。这两个接口分别实现了打开文件对话框和另存为文件对话框。如下图:



这个接口提供了我们强大的功能来控制打开文件或是另存为文件对话框的外观和行为。下面一一介绍。
1基本实现
基本实现是最基本的使用,也就是实现基本的打开文件或是另存为文件对话框的功能。下面,一步一步的实现。IFileDialog基本的实现中,我们只需要这个接口就可以实现打开文件对话框或是另存为对话框。
获取IFileDialog接口:
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
这个获取方式使COM中标准的对象创建方式。使用CoCreateInstance函数,第一个参数是组件类标示符,第二个参数主要是针对创建的对象是聚集还是不是聚集对象,如果是NULL,就表明获取的接口不是通过聚集的方式实现的,如果是非NULL表明需要一个指向对象IUnknown指针,第三个接口表明了实现该接口的组件类的类别,这是一个枚举值,第四个参数是请求的接口的标示符,最后一个参数用来接收函数成功之后返回的对象指针。这里只有四个参数,主要是因为最后两个参数被这个IID_PPV_ARGS宏代替了。这个函数的原型可以参考msdn。这个宏的作用就是自动根据你传递的接口类型来判断接口的标示符,简化了操作。
设置对话框选项:
hr = pfd->GetOptions(&dwFlags);
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
首先获取选项,目的是为了设置新选项的时候,保留原有的选项。选项的设置可以控制对话框的行为。具体设置什么值,可以实现什么样的效果,参考msdn。
设置文件类型:
设置文件类型就是对对话框的要打开的文件类型做过滤。如*.jpg,*.bmp。在这里它使用的是一个结构体来实现。
typedef struct _COMDLG_FILTERSPEC {
LPCWSTR pszName;
LPCWSTR pszSpec;
} COMDLG_FILTERSPEC;
第一个成员是文件类型,第二个成员是文件的扩展名。例如:
COMDLG_FILTERSPEC rgSpec[] =
{
{ L"JPG", L"*.jpg;*.jpeg" },
{ L"BMP", L"*.bmp" },
{ L"All", L"*.*" },
};
设置好了之后,调用IFileDialog的方法:
HRESULT SetFileTypes(UINT cFileTypes, constCOMDLG_FILTERSPEC *rgFilterSpec);
这个方法的第一个参数后面那个参数中元素的个数,如在这里,是3个,因此就是3:
hr=pfd->SetFileTypes(3,rgSpec);
设置文件类型索引:
hr = pfd->SetFileTypeIndex(1);
通过设置这个,就可以设置默认选中的文件类型项目上面。
设置默认扩展名:
这个功能主要是用于在另存为对话框中,将这里设置的默认扩展名附到将要保存的文件的后面。
hr=pfd->SetDefaultExtension(L"jpg");
显示对话框:
如果设置完毕之后,就可以显示对话框,调用Show方法,这个方法是IModalWindow接口的方法,IFileDialog继承于它,因此可以调用。
hr=pfd->Show(this->m_hWnd);///显示打开文件对话框
这个时候,对话框显示出来,根据返回值hr就可以判断用户是点击去了取消还是选择了一个文件打开,根据不同的用户选择做出反应。如果用户点击了取消,那么就释放所有的接口指针并返回。如果用户点击了打开,就可以继续调用GetResult方法,获取用户选择的项目:
hr=pfd->GetResult(&pShellItem);获取用户选择对象(文件)
这个方法直接就可以获取文件的IShellItem接口指针。通过这个接口指针,就可以很方便的实现如获取上一级目录的IShellItem指针,实现枚举,获取文件名,还是获取完整路径(相对于桌面路径)等等,因为在windows中要对文件夹,或是文件做各种操作,是通过获取它们的IShellItem接口指针实现的,过去我们只能获取打开文件的路径,而要获取被选中文件的IShellItem还需要我们自己编写代码实现。现在就可以直接获取了。如果是设置了可以选择多个文件,可以使用QueryInterface获取IFileOpenDialog接口,然后调用GetResults获取IShellItemArray指针,通过它就可以获取各个被选中文件的IShellItem的接口指针。
获取文件名或路径:

LPWSTR filePath;
LPWSTR fileName;
//
hr=pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&filePath);//获取文件的完整路径
hr=pShellItem->GetDisplayName(SIGDN_NORMALDISPLAY,&fileName);///获取文件名
调用IShellItem接口的GetDisplayName方法就可以获取你想要的信息,第一个参数是一个枚举值,通过设置不同的值,返回的接口就不同,这里我分别设置获取所选择文件的完整路径和文件名。另外,需要注意的是,这里的输入参数是一个指针,但是不用我们分配内存,由COM对象为我们分配,这样很方便了,但是需要我们自己来释放,而且必须使用COM的内存管理方式来管理COM分配的内存,在COM中,分配内存使用CoTaskMemAlloc,释放类存使用CoTaskMemFree。另外补充一下,对于初学COM的同学来说,不明白为什么随处可见参数是双指针的情况,例如,这里也是,因为要实现传递指针的地址,我们知道,传递一个变量的地址用指针,现在要传递指针的地址,就需要用双指针。
CoTaskMemFree(filePath);//销毁com分配的内存
CoTaskMemFree(fileName);//
释放所有的接口指针:
SafeRelease(&pfd);
SafeRelease(&pShellItem);
///
这样就完成了IFileDialog接口指针的基本实现。
2获取IFileDialog对话框上的事件
在过去的使用打开文件对话框或是另存为对话框是没有办法实现的。例如:用户选择的项目发生变化,选择的对话框发生变化等等,用户的每个操作都可以捕捉到。要实现这个功能,需要我们重写IFileDialogEvents接口,重写这个接口和我们在编写COM组件时实现接口的过程是一样一样的。至于如何实现COM接口,这里不做叙述。我这里做的,可以到我的源码中查看。实现完了接口之后,我需要一个函数来初始化这个一个对象,因此我在实现的接口类中定义了一个静态的函数CreateInstance来初始化对象并获取指定的接口指针。
static HRESULT CreateInstance(REFIID iid,void**ppObject);
///建立对象
HRESULT CFileDialogEvent::CreateInstance(REFIID iid,void**ppObject)
{
if(ppObject==NULL)
{
return E_POINTER;
}
CFileDialogEvent *fdf=new (std::nothrow)CFileDialogEvent;
if(fdf==NULL)
{
return E_FAIL;
}
fdf->QueryInterface(iid,ppObject);
fdf->Release();
return S_OK;
}
接着,调用IFileDialog接口的Advise方法将这个初始化的对象指针与其绑定:
hr=CFileDialogEvent::CreateInstance(IID_IFileDialogEvents,(void**)&pfde);///创建事件处理对象
hr=pfd->Advise(pfde,&dwCookie);///将事件处理对象与IFileDialog绑定
dwCookie是返回回来的一个标示符,当IFileDialog接口销毁之前,调用UnAdvise方法解除绑定,就用这个标识符来解除之前的绑定。
pfd->Unadvise(dwCookie);
这样之后,就可以完成对对话框事件的捕捉功能
3自定义对话框的外观
过去要自定义打开文件对话框模板,需要使用内存模板,这是一个很费力的工作。现在就很容易了,通过使用IFileDialogCustomize接口,通过在IFileDialog接口上调用QueryInterface方法,就可以获取这个接口,通过这个接口,可以添加按钮,下拉列表,编辑框等等窗口元素到打开文件或是文件另存为对话框中。例如,我在这里添加了两个按钮:
hr=pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
if(FAILED(hr))
{
goto End;
}
hr=pfdc->StartVisualGroup(ID_GROUP,L"收割者");
if(FAILED(hr))
{
goto End;
}
hr=pfdc->AddPushButton(ID_BUTTONA,L"收割者的按钮A");
if(FAILED(hr))
{
goto End;
}
hr=pfdc->AddPushButton(ID_BUTTONB,L"收割者的按钮B");
if(FAILED(hr))
{
goto End;
}
hr=pfdc->EndVisualGroup();
为了能够响应添加到对话框上按钮的事件,我们还需要实现一个接口IFileDialogControlEvents,实现这个接口和实现IFileDialogEvents一样,而且是在同一个对象上。但是IFileDialogControlEvents接口不需要我们传递给IFileDialog,因为我们之前调用Advise将IFileDialogEvents接口指针与IFileDialog的对象绑定,由于两个事件接口指针都在同一个对象上实现的,在内部,当我们点击按钮或是别的控件的时候,就会自动调用实现这两个事件接口的类的QueryInterface方法,获取IFileDialogControlEvents指针,然后在内部调用相关的方法,例如,在这里我在添加了两个按钮,我响应这两个按钮的点击事件:
HRESULT CFileDialogEvent::OnButtonClicked(IFileDialogCustomize *pfdc,DWORD dwIDCtl)
{
if(dwIDCtl==1)
{
AfxMessageBox(L"我是收割者A");
}
else if(dwIDCtl==2)
{
AfxMessageBox(L"我是收割者B");
}
return S_OK;
}
下面是效果图:


这里是以打开文件对话框为例,如果要实现另外为对话框,使用CLSID_FileSaveDialog来获取另存为对话框的IFileDialog接口指针,来实现另存为对话框。



本文代码 http://download.csdn.net/detail/xinzhiyounizhiyouni/7114295

其他推荐阅读地址:http://www.panshy.com/article/Sort_Desktop/UI/2014-03-23/2470.php


  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值