我需要以递归方式为给定的根/父路径构建一个树结构.像“浏览文件夹”对话框.
Delphi的FindFirst(FindFirstFile
API)不能与faDirectory一起使用,FindNext将获取所有文件(它使用faAnyFile而不管指定的faDirectory),而不仅仅是目录.这使得构建树的过程非常缓慢.
解决方法
绝对最快的方式,使用
NtQueryDirectoryFile
api.有了这个我们可以一次查询不是单个文件而是多个文件.还可以选择要返回的信息(较小的信息 – 更高的速度).示例(完全递归)
// int nLevel,PSTR prefix for debug only void ntTraverse(POBJECT_ATTRIBUTES poa,int nLevel,PSTR prefix) { enum { ALLOCSIZE = 0x10000 };//64kb if (nLevel > MAXUCHAR) { DbgPrint("nLevel > MAXUCHAR\n"); return ; } NTSTATUS status; IO_STATUS_BLOCK iosb; UNICODE_STRING ObjectName; OBJECT_ATTRIBUTES oa = { sizeof(oa),&ObjectName }; DbgPrint("%s[<%wZ>]\n",prefix,poa->ObjectName); if (0 <= (status = NtOpenFile(&oa.RootDirectory,FILE_GENERIC_READ,poa,&iosb,FILE_SHARE_VALID_FLAGS,FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT))) { if (PVOID buffer = new UCHAR[ALLOCSIZE]) { union { PVOID pv; PBYTE pb; PFILE_DIRECTORY_INFORMATION DirInfo; }; while (0 <= (status = NtQueryDirectoryFile(oa.RootDirectory,NULL,pv = buffer,ALLOCSIZE,FileDirectoryInformation,FALSE))) { ULONG NextEntryOffset = 0; do { pb += NextEntryOffset; ObjectName.Buffer = DirInfo->FileName; switch (ObjectName.Length = (USHORT)DirInfo->FileNameLength) { case 2*sizeof(WCHAR): if (ObjectName.Buffer[1] != '.') break; case sizeof(WCHAR): if (ObjectName.Buffer[0] == '.') continue; } ObjectName.MaximumLength = ObjectName.Length; if (DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { ntTraverse(&oa,nLevel + 1,prefix - 1); } } while (NextEntryOffset = DirInfo->NextEntryOffset); if (ALLOCSIZE - iosb.Information > (ULONG)FIELD_OFFSET(FILE_DIRECTORY_INFORMATION,FileName[256])) { break;//NO_MORE_FILES } } delete [] buffer; if (status == STATUS_NO_MORE_FILES) { status = STATUS_SUCCESS; } } NtClose(oa.RootDirectory); } if (0 > status) { DbgPrint("---- %x %wZ\n",status,poa->ObjectName); } } void ntTraverse() { BOOLEAN b; RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE,TRUE,FALSE,&b); char prefix[MAXUCHAR + 1]; memset(prefix,'\t',MAXUCHAR); prefix[MAXUCHAR] = 0; STATIC_OBJECT_ATTRIBUTES(oa,"\\systemroot"); ntTraverse(&oa,prefix + MAXUCHAR); }
但是如果您使用交互式树 – 您不需要一次扩展所有树,但只需要顶层,使用TVE_EXPAND处理TVN_ITEMEXPANDING,使用TVE_COLLAPSE处理TVN_ITEMEXPANDED,以便在用户点击时设置扩展/折叠节点并设置cChildren
如果将FindFirstFileExW与FIND_FIRST_EX_LARGE_FETCH和FindExInfoBasic一起使用,这会给N4QueryDirectoryFile提供近似性能,但要小一点:
WIN32_FIND_DATA fd; HANDLE hFindFile = FindFirstFileExW(L"..\\*",FindExInfoBasic,&fd,FindExSearchLimitToDirectories,FIND_FIRST_EX_LARGE_FETCH); if (hFindFile != INVALID_HANDLE_VALUE) { do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (fd.cFileName[0] == '.') { switch (fd.cFileName[1]) { case 0: continue; case '.': if (fd.cFileName[2] == 0) continue; break; } } DbgPrint("%S\n",fd.cFileName); } } while (FindNextFile(hFindFile,&fd)); FindClose(hFindFile); }
遗憾的是,FindExSearchLimitToDirectories目前尚未实现