我需要递归的建立一个树形结构只是目录,对于一个给定根/父路径.像"浏览文件夹"对话框.
Delphi FindFirst(FindFirstFileAPI)没有使用faDirectory并且FindNext将获得所有文件(它faAnyFile不管指定使用哪个faDirectory),而不仅仅是目录.这使得构建树的过程 非常缓慢.
是否有一种快速的方法来获取目录列表(树)而不使用FindFirst/ FindNext?
NtQueryDirectoryFileapi ,绝对最快的方式.有了这个,我们可以一次查询单个文件,但查询多个文件.还可以选择要返回的信息(较小的信息 - 更高的速度).示例(完全递归)
// 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), 0, &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, NULL, NULL, &iosb,
pv = buffer, ALLOCSIZE, FileDirectoryInformation, 0, NULL, 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, 0, prefix + MAXUCHAR);
}
Run Code Online (Sandbox Code Playgroud)
但如果你使用的交互树-你不需要全部展开树一次,但只有最顶层,处理TVN_ITEMEXPANDING与TVE_EXPAND和TVN_ITEMEXPANDED与TVE_COLLAPSE用于扩大用户点击/折叠节点并设置cChildren
如果使用FindFirstFileExW和FIND_FIRST_EX_LARGE_FETCH,FindExInfoBasic这给了近似NtQueryDirectoryFile性能,但小一点:
WIN32_FIND_DATA fd;
HANDLE hFindFile = FindFirstFileExW(L"..\\*", FindExInfoBasic, &fd, FindExSearchLimitToDirectories, 0, 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);
}
Run Code Online (Sandbox Code Playgroud)
遗憾的是,FindExSearchLimitToDirectories目前没有实施
Find(First|Next)/File() 是一个可行的解决方案,特别是在Delphi 7中.只需过滤掉您不需要的结果,例如:
if FindFirst(Root, faDirectory, sr) = 0 then
try
repeat
if (sr.Attr and faDirectory <> 0) and (sr.Name <> '.') and (sr.Name <> '..') then
begin
// ...
end;
until FindNext(sr) <> 0;
finally
FindClose(sr);
end;
Run Code Online (Sandbox Code Playgroud)
如果这对您来说不够快,那么其他选项包括:
Win7上+,使用FindFirstFileEx()与FindExInfoBasic和FIND_FIRST_EX_LARGE_FETCH.这将提供速度提升FindFirstFile().
直接访问文件系统元数据.在NTFS上,您可以使用DeviceIoControl()直接枚举主文件表.
| 归档时间: |
|
| 查看次数: |
1360 次 |
| 最近记录: |