共享代码:解决OpenDialog打开文件数量有限制的问题

cczlp 2012-05-23 01:18:20
加精
调用TOpenPictureDialog选择大量图片时候发现,返回的Files里面只有前面的一千多个文件,实际只有我选择文件的一半左右。开始以为是CB的BUG,于是把OpenDialog源码中的MultiSelectBufferSize改大,测试没效果。网上有人说,GetOpenFileName的Ansi版本有32K内存限制,Unicode版本没有限制。实际我的程序已经是Unicode的了。Ansi的API只是进行的字符转换,之后还是调用Unicode版本的API,所以这个方法行不通。

在一个博客里面介绍,可以通过Shell接口自己获取选择的文件,有参考代码,这就好办了,有了解决问题的方向。

那怎么加入CB中呢,直接调用GetOpenFileName? 重写TOpenDialog?都太麻烦了,打开源码,发现有TOpenDialog有个函数void __fastcall GetFileNames(tagOFNW &OpenFileName)是用来取得所选文件的,正好就改它了。这里给出改好的代码,加入工程源码中就可以。

#include <shlobj.h>
#ifndef WM_GETISHELLBROWSER
#define WM_GETISHELLBROWSER (WM_USER+7)
#endif
#define GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])

BOOL ILIsFile(LPCITEMIDLIST pidl)
{
BOOL bRet = FALSE;
LPCITEMIDLIST pidlChild = NULL;
IShellFolder* psf = NULL;
HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID*) & psf, &pidlChild);
if (SUCCEEDED(hr) && psf)
{
SFGAOF rgfInOut = SFGAO_FOLDER | SFGAO_FILESYSTEM;
hr = psf->GetAttributesOf(1, &pidlChild, &rgfInOut);
if (SUCCEEDED(hr))
{
if ((~rgfInOut & SFGAO_FOLDER) && (rgfInOut & SFGAO_FILESYSTEM))
{
bRet = TRUE;
}
}
psf->Release();
}
return bRet;
}

void __fastcall TOpenDialog::GetFileNames(tagOFNW &OpenFileName)
{
static TStringList *lst = new TStringList;
FORMATETC fmte;
STGMEDIUM stgmedium;
LPITEMIDLIST pidlFull = NULL;
IShellView * pIShellView = NULL;
LPMALLOC pMalloc = NULL;
IDataObject* pIDataObject = NULL;
IShellBrowser* pSB = (IShellBrowser*)SendMessage(GetParent(Handle), WM_GETISHELLBROWSER, 0, 0);
TCHAR szPath[_MAX_PATH];

if (pSB == NULL) //GetFileNames被调用两次,第二次pSB为空, 返回第一次结果
{
if (lst)
{
FFileName = lst->Count > 0 ? lst->Strings[0] : String("");
FFiles->Assign(lst);
delete lst;
lst = NULL;
}
return;
}

if (lst == NULL)
lst = new TStringList;

ZeroMemory((LPVOID) & fmte, sizeof(STGMEDIUM));
ZeroMemory((LPVOID) & fmte, sizeof(FORMATETC));
fmte.tymed = TYMED_HGLOBAL;
fmte.lindex = -1;
fmte.dwAspect = DVASPECT_CONTENT;
fmte.cfFormat = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
do
{
HRESULT hr = pSB->QueryActiveShellView(&pIShellView);
if (FAILED(hr))
break;
hr = ::SHGetMalloc(&pMalloc);
if (FAILED(hr))
break;
hr = pIShellView->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (LPVOID*) & pIDataObject);
if (FAILED(hr))
break;
if (pIDataObject == NULL)
break;
hr = pIDataObject->GetData(&fmte, &stgmedium);
if (FAILED(hr))
break;
LPIDA pida = (LPIDA)GlobalLock(stgmedium.hGlobal);
if (pida)
{
LPCITEMIDLIST pidlFolder = GetPIDLFolder(pida);
for (UINT i = 0; i < pida->cidl; i++)
{
LPCITEMIDLIST pidl = GetPIDLItem(pida, i);
pidlFull = ILCombine(pidlFolder, pidl);
if (ILIsFile(pidlFull))
{
ZeroMemory(szPath, sizeof(TCHAR) * _MAX_PATH);
hr = SHGetPathFromIDList(pidlFull, szPath);
if (SUCCEEDED(hr))
{
lst->Add((szPath));
}
}
pMalloc->Free(pidlFull);
pidlFull = NULL;
}
}
GlobalUnlock(stgmedium.hGlobal);
ReleaseStgMedium(&stgmedium);
}
while (FALSE);

if (pIDataObject) pIDataObject->Release();
if (pIShellView) pIShellView->Release();
if (pMalloc)
{
if (pidlFull) pMalloc->Free(pidlFull);
pMalloc->Release();
}
}

加入代码后,不管TOpenDialog还是TOpenPictureDialog,打开文件数量都不再受限制(当然受内存限制,也不是无限的)。

在此BS一下微软的程序员,同时也接受BS,欢迎拍砖。
...全文
4551 48 打赏 收藏 转发到动态 举报
写回复
用AI写文章
48 条回复
切换为时间正序
请发表友善的回复…
发表回复
clever101 2015-01-21
  • 打赏
  • 举报
回复
多谢楼主!你解决了我的一大难题啊!
  • 打赏
  • 举报
回复
43楼的解释很好。
ltioaf001 2012-06-12
  • 打赏
  • 举报
回复
版主都推荐了啊,学习一下
LAONINGA098 2012-05-31
  • 打赏
  • 举报
回复
写的还不错!
cczlp 2012-05-31
  • 打赏
  • 举报
回复
前面说的不太明确,补充下,打开文件数量限制不是固定的,跟路径长度有关,看看API声明就知道了.
BOOL GetOpenFileName(LPOPENFILENAME lpofn); 
typedef struct tagOFN { // ofn
DWORD lStructSize;
HWND hwndOwner;
HINSTANCE hInstance;
LPCTSTR lpstrFilter;
LPTSTR lpstrCustomFilter;
DWORD nMaxCustFilter;
DWORD nFilterIndex;
LPTSTR lpstrFile;
DWORD nMaxFile;
LPTSTR lpstrFileTitle;
DWORD nMaxFileTitle;
LPCTSTR lpstrInitialDir;
LPCTSTR lpstrTitle;
DWORD Flags;
WORD nFileOffset;
WORD nFileExtension;
LPCTSTR lpstrDefExt;
DWORD lCustData;
LPOFNHOOKPROC lpfnHook;
LPCTSTR lpTemplateName;
} OPENFILENAME;

其中 nMaxFile是存放所有选择文件的路径的内存大小.
TOpenFile调用了这个函数, 在文件多选时候, 给出的nMaxFile 是65519字节.所以在XP以以前的系统中,一定有数量限制的. 在Vista及以后系统, 虽然也调用了这个函数, 但已经不使用lpstrFile了,也是使用直接从Shell查找文件的方法.
bigfog 2012-05-31
  • 打赏
  • 举报
回复
不错,不错
LAONINGA098 2012-05-31
  • 打赏
  • 举报
回复
写的还不错!
LAONINGA098 2012-05-31
  • 打赏
  • 举报
回复
写的还不错
LAONINGA098 2012-05-31
  • 打赏
  • 举报
回复
写的还不错
panyun920 2012-05-29
  • 打赏
  • 举报
回复
好,不错。
ssc317 2012-05-28
  • 打赏
  • 举报
回复
谢谢!!!!
  • 打赏
  • 举报
回复
CBXE+XP sp2/sp3测试都正常,用TOpenDialog添加2300多个文件到TMemo。
c13701306174 2012-05-28
  • 打赏
  • 举报
回复
不错,好好学习
Bigwinner 2012-05-25
  • 打赏
  • 举报
回复
牛啊!
lmc158 2012-05-24
  • 打赏
  • 举报
回复
小论坛地址:http://rayyu.5d6d.com/thread-9444-1-1.html

lnmichaelliu 2012-05-24
  • 打赏
  • 举报
回复
非常好!
maqiang_316 2012-05-24
  • 打赏
  • 举报
回复
都好牛啊!!!
sczyq 2012-05-23
  • 打赏
  • 举报
回复
汗! 只好 BS 自己了
_C嘎 2012-05-23
  • 打赏
  • 举报
回复
给力啊
tradeiswear 2012-05-23
  • 打赏
  • 举报
回复
好强哦
加载更多回复(12)

604

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder VCL组件使用和开发
社区管理员
  • VCL组件使用和开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧