加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – 获取目录列表的最快方法是什么

发布时间:2020-12-15 04:19:05 所属栏目:大数据 来源:网络整理
导读:我需要以递归方式为给定的根/父路径构建一个树结构.像“浏览文件夹”对话框. Delphi的FindFirst( FindFirstFile API)不能与faDirectory一起使用,FindNext将获取所有文件(它使用faAnyFile而不管指定的faDirectory),而不仅仅是目录.这使得构建树的过程非常缓慢
我需要以递归方式为给定的根/父路径构建一个树结构.像“浏览文件夹”对话框.

Delphi的FindFirst(FindFirstFile API)不能与faDirectory一起使用,FindNext将获取所有文件(它使用faAnyFile而不管指定的faDirectory),而不仅仅是目录.这使得构建树的过程非常缓慢.

有没有快速的方法来获取目录列表(树)而不使用FindFirst / FindNext?

解决方法

绝对最快的方式,使用 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 > MAXUCHARn");
        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 %wZn",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("%Sn",fd.cFileName);
        }
    } while (FindNextFile(hFindFile,&fd));

    FindClose(hFindFile);
}

遗憾的是,FindExSearchLimitToDirectories目前尚未实现

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读