我需要以递归方式为给定的根/父路径构建一个树结构.像“浏览文件夹”对话框.
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目前尚未实现