在我们的程序中有时候需要一次选择多个文件,例如InstallShield的安装程序向导中的一个步骤就是为feature选择文件;但是在选择的文件过多(超过30个)的时候,这样的选择没有效果。我不得不在InstallShield的文件打开对话框中切换到小图标模式,并且一次选择两列的文件(很久之前就不得不用Linked Directory来动态链接整个目录中的文件了,不知道新的InstallShield版本改进没有)。
这种情况是因为默认的缓冲区只有两百多个字符。如果缓冲区不够长,那么调用对话框可能会失败,CommDlgExtendedError()会返回FNERR_BUFFERTOOSMALL。为了解决这个问题,你可以处理CDN_SELCHANGE来在选择项数目变化的时候动态重新分配缓冲区(参见http://support.microsoft.com/kb/131462/)或者在创建对话框的时候把缓冲区设大一点:
但是尽管提供了这么长的缓冲区(我是使用ANSI配置编译的,所以缓冲区有26M,足足可以容纳10万个文件名),但是结果还是没有获得全部文件名,这是因为GetOpenFileName限制了复制到缓冲区中的文件名的总长度(http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/userinput/commondialogboxlibrary/commondialogboxreference/commondialogboxfunctions/getopenfilename.asp)
OK,在文档中我看到了解决方案之一是使用UNICODE版本的函数GetOpenFileNameW,但是在Windows 9x上似乎只能用ANSI版本的函数。难道我要编写Windows版本检查代码来调用不同版本的函数?
这是推荐的作法。但是程序员是很懒的,所以有时候我会使用一些非官方方法。在我以前的一篇BLOG(http://blog.joycode.com/jiangsheng/archive/2004/09/17/33756.aspx)中我提到了如何访问文件打开对话框的IShellBrowser接口。通过这个接口我们可以访问文件打开对话框的IShellView接口,进而获得选中的文件(为节省篇幅,ILIsFile、GetPIDLFolder、GetPIDLItem和WM_GETISHELLBROWSER的定义就不重复了):
然后如下使用
调试输出是
Selected 2415 Files(和资源管理器的报告相符)
GetOpenFileName returned 2378 Files(……)