Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/256.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 列举100000+;文件夹层次结构需要几个小时_C#_Pinvoke - Fatal编程技术网

C# 列举100000+;文件夹层次结构需要几个小时

C# 列举100000+;文件夹层次结构需要几个小时,c#,pinvoke,C#,Pinvoke,为什么此代码需要几个小时才能完成: public void SetRoot(string path) { Tag = path; BeginUpdate(); AddFolderRecursive(Nodes, path); EndUpdate(); } private void AddFolderRecursive(TreeNodeCollection nodes, string path) { try { var dirs =

为什么此代码需要几个小时才能完成:

public void SetRoot(string path)
{
    Tag = path;
    BeginUpdate();
    AddFolderRecursive(Nodes, path);
    EndUpdate();
}

private void AddFolderRecursive(TreeNodeCollection nodes, string path)
{
    try
    {
        var dirs = Directory.EnumerateDirectories(path).OrderBy(d => d).Select(d => d.Split(Path.DirectorySeparatorChar).Last());
        TreeNode node;
        ShellFileGetInfo.FolderIcons fi;
        foreach (var d in dirs)
        {
            node = nodes.Add(Path.Combine(path, d), d, ImageList.Images.Count);
            node.Tag = Path.Combine(path, d);
            node.SelectedImageIndex = ImageList.Images.Count + 1;
            fi = ShellFileGetInfo.GetFolderIcon((string)node.Tag, false);
//                  ImageList.Images.Add(fi.closed);
//                  ImageList.Images.Add(fi.open);
            AddFolderRecursive(node.Nodes, (string)node.Tag);
        }
    }
    catch (UnauthorizedAccessException)
    {

    }
}
我已经让这段代码运行了14个小时了,但在像
SetRoot(@“c:\”)那样调用它时,它仍然没有完成获取所有文件夹的列表。代码正在运行,它正在添加到树中,但这太荒谬了

基本上,我想用我驱动器上的所有文件夹来流行treeview(因此treeview是可搜索的),并使用实际的文件夹图标(我使用的是
SHGetFileInfo
p/invoke和必要的参数)。即使没有得到图标(我遇到的另一个问题是,尽管图标图像本身可能是相同的,但获取文件夹的图标在每个文件夹中都是唯一的。我找不到一种方法来快速确定,如果我已经将该图像保存在我的树视图的
ImageList
-aka中,“c:\windows”的文件夹图标是相同的但是,作为“c:\windows\system32”,句柄等都返回不同的信息,因此似乎没有任何东西可以对它们进行唯一索引。)

我的代码中有什么可以调整以使这个过程更快,同时仍然保留系统中的文件夹图标?请记住,我还希望所有文件夹都不跳过空文件夹

(我甚至无法显示TreeView的图片,因为在它完成显示之前14小时,我停止了循环的运行)

为了测试TreeView控件的速度,我编写了以下代码:

DateTime start = DateTime.UtcNow;
treeView1.BeginUpdate();
await Task.Run(() =>
{
    int x = 0;
    while (x < 100000)
    {
        x++;
        if (treeView1.InvokeRequired)
        {
            treeView1.Invoke((MethodInvoker)delegate { treeView1.Nodes.Add("Node - " + x); } );
        }
    }
});
treeView1.EndUpdate();
Text = start.ToLongTimeString() + " - " + DateTime.UtcNow.ToLongTimeString();
DateTime start=DateTime.UtcNow;
treeView1.BeginUpdate();
等待任务。运行(()=>
{
int x=0;
而(x<100000)
{
x++;
if(treeView1.invokererequired)
{
Invoke((MethodInvoker)委托{treeView1.Nodes.Add(“Node-”+x);});
}
}
});
treeView1.EndUpdate();
Text=start.ToLongTimeString()+“-”+DateTime.UtcNow.ToLongTimeString();
下面是结果的屏幕截图:


如您所见,如果您使用
BeginUpdate
EndUpdate
来防止控件在每个项目上绘制或刷新,则在大约2分钟的时间内用100000个项目填充TreeView会非常快。这也表明,绝对不是TreeView控件在拖我的后腿——14个小时是必要的过度-即使是1996年的驱动器,14小时枚举100000个文件夹也太长了。

我敢打赌代码不是原因,它可能是两件事之一:

  • 你的硬盘乱七八糟,你可以试试碎片分割法

  • (我也遇到了这种情况)windows没有为您的文件夹编制索引(索引-)若要修复此问题,您需要转到您正在处理的主文件夹,让windows为文件夹及其所有子文件夹(文件夹框上的某个位置)编制索引,此过程大约需要一天时间,但在此之后,您的程序应该可以正常(快速)运行


  • 我敢打赌代码不是原因,它可能是两件事之一:

  • 你的硬盘乱七八糟,你可以试试碎片分割法

  • (我也遇到了这种情况)windows没有为您的文件夹编制索引(索引-)若要修复此问题,您需要转到您正在处理的主文件夹,让windows为文件夹及其所有子文件夹(文件夹框上的某个位置)编制索引,此过程大约需要一天时间,但在此之后,您的程序应该可以正常(快速)运行


  • Windows树视图控件的设计根本不适合容纳这么多节点。您根本不可能在实际时间内用数千个节点填充此控件。此外,即使在短时间内枚举所有这些项,也肯定是不现实的。更糟糕的是,试图提取树中每个对象的图标时间的广告


    前进的方向是,您不必尝试用所有项填充控件。只需填充父节点。然后,当它们打开时,枚举并添加子节点。这是所有shell程序的操作方式。

    Windows树视图控件的设计不是为了容纳这么多节点。您根本不可能填充此控件ol在实际时间内有数千个节点。此外,即使在短时间内枚举所有这些项目也肯定是不现实的。更尴尬的是,试图提前提取树中每个对象的图标


    前进的方向是,您不必尝试用所有项填充控件。只需填充父节点。然后当它们打开时,枚举并添加子节点。这就是所有shell程序的操作方式。

    进一步研究后,我发现问题在于,可用于枚举文件和文件夹的方法非常慢,并且t当文件夹不可访问时,会抛出一个
    UnauthorizedAccessException
    ,每次事件的固有延迟约为200ms。这些异常会叠加并导致较大的延迟

    此外,关于向TreeView添加项时的二次指数的Davids陈述也会导致进一步的延迟,但是在这种情况下,额外的延迟仅与TreeView节点添加的比例不成比例

    为了解决这个问题,我能够将其缩小到3个问题,其中两个问题我已经完全解决,以便在合理的时间范围内控制功能的这些部分。分解它,下面是导致OP问题延迟的3个问题:

    • 树越深,添加的节点越多,添加的TreeView节点的速度就越慢
    • 文件系统访问不会访问NTFS可用的本机日志系统,因此每次调用都会单独获取每个文件或目录。此外,如果文件夹被标记为受限,则每次遇到
      UnauthorizedAccessException
      会造成约200ms的人为延迟
    • 检索自定义文件夹图标
      #region TreeViewFast
      private readonly Dictionary<ulong, TreeNode> _treeNodes = new Dictionary<ulong, TreeNode>();
      
      /// <summary>
      /// Load the TreeView with items.
      /// </summary>
      /// <typeparam name="T">Item type</typeparam>
      /// <param name="items">Collection of items</param>
      /// <param name="getId">Function to parse Id value from item object</param>
      /// <param name="getParentId">Function to parse parentId value from item object</param>
      /// <param name="getDisplayName">Function to parse display name value from item object. This is used as node text.</param>
      public void LoadItems<T>(IEnumerable<T> items, Func<T, ulong> getId, Func<T, ulong?> getParentId, Func<T, string> getDisplayName)
      {
          // Clear view and internal dictionary
          Nodes.Clear();
          _treeNodes.Clear();
      
          // Load internal dictionary with nodes
          foreach (var item in items)
          {
              var id = getId(item);
              var displayName = getDisplayName(item);
              var node = new TreeNode { Name = id.ToString(), Text = displayName, Tag = item };
              _treeNodes.Add(getId(item), node);
          }
      
          // Create hierarchy and load into view
          foreach (var id in _treeNodes.Keys)
          {
              var node = GetNode(id);
              var obj = (T)node.Tag;
              var parentId = getParentId(obj);
      
              if (parentId.HasValue)
              {
                  var parentNode = GetNode(parentId.Value);
                  if(parentNode == null)
                  {
                      Nodes.Add(node);
                  } else
                  {
                      parentNode.Nodes.Add(node);
                  }
              }
              else
              {
                  Nodes.Add(node);
              }
          }
      }
      
      /// <summary>
      /// Get a handle to the object collection.
      /// This is convenient if you want to search the object collection.
      /// </summary>
      public IQueryable<T> GetItems<T>()
      {
          return _treeNodes.Values.Select(x => (T)x.Tag).AsQueryable();
      }
      
      /// <summary>
      /// Retrieve TreeNode by Id.
      /// Useful when you want to select a specific node.
      /// </summary>
      /// <param name="id">Item id</param>
      public TreeNode GetNode(ulong id)
      {
          try
          {
              return _treeNodes[id];
          } catch (KeyNotFoundException)
          {
              return null;
          }
      }
      
      /// <summary>
      /// Retrieve item object by Id.
      /// Useful when you want to get hold of object for reading or further manipulating.
      /// </summary>
      /// <typeparam name="T">Item type</typeparam>
      /// <param name="id">Item id</param>
      /// <returns>Item object</returns>
      public T GetItem<T>(ulong id)
      {
          return (T)GetNode(id).Tag;
      }
      
      
      /// <summary>
      /// Get parent item.
      /// Will return NULL if item is at top level.
      /// </summary>
      /// <typeparam name="T">Item type</typeparam>
      /// <param name="id">Item id</param>
      /// <returns>Item object</returns>
      public T GetParent<T>(ulong id) where T : class
      {
          var parentNode = GetNode(id).Parent;
          return parentNode == null ? null : (T)Parent.Tag;
      }
      
      /// <summary>
      /// Retrieve descendants to specified item.
      /// </summary>
      /// <typeparam name="T">Item type</typeparam>
      /// <param name="id">Item id</param>
      /// <param name="deepLimit">Number of generations to traverse down. 1 means only direct children. Null means no limit.</param>
      /// <returns>List of item objects</returns>
      public List<T> GetDescendants<T>(ulong id, int? deepLimit = null)
      {
          var node = GetNode(id);
          var enumerator = node.Nodes.GetEnumerator();
          var items = new List<T>();
      
          if (deepLimit.HasValue && deepLimit.Value <= 0)
              return items;
      
          while (enumerator.MoveNext())
          {
              // Add child
              var childNode = (TreeNode)enumerator.Current;
              var childItem = (T)childNode.Tag;
              items.Add(childItem);
      
              // If requested add grandchildren recursively
              var childDeepLimit = deepLimit.HasValue ? deepLimit.Value - 1 : (int?)null;
              if (!deepLimit.HasValue || childDeepLimit > 0)
              {
                  var childId = ulong.Parse(childNode.Name);
                  var descendants = GetDescendants<T>(childId, childDeepLimit);
                  items.AddRange(descendants);
              }
          }
          return items;
      }
      #endregion
      
      public void PopulateTree(string path)
      {
          Tag = path;
          using (NtfsUsnJournal ntfs = new NtfsUsnJournal(new DriveInfo(path)))
          {
              List<NtfsUsnJournal.UsnEntry> folders;
              ntfs.GetNtfsVolumeFolders(out folders);
      
      
              Func<NtfsUsnJournal.UsnEntry, ulong> getId = (x => x.FileReferenceNumber);
              Func<NtfsUsnJournal.UsnEntry, ulong?> getParentId = (x => x.ParentFileReferenceNumber);
              Func<NtfsUsnJournal.UsnEntry, string> getDisplayName = (x => x.Name);
      
              LoadItems(folders, getId, getParentId, getDisplayName);
          }
      }
      
      private List<string> _expandedCache;
      
      protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
      {
          if (!_expandedCache.Contains(e.Node.FullPath))
          {
              BeginUpdate();
              ShellFileGetInfo.FolderIcons fi;
              _expandedCache.Add(e.Node.FullPath);
              string curPath;
              foreach(TreeNode n in e.Node.Nodes)
              {
                  curPath = Path.Combine((string)Tag, n.FullPath.Replace('/', Path.DirectorySeparatorChar));
                  if (File.Exists(Path.Combine(curPath, "desktop.ini")) == true)
                  {
                      fi = ShellFileGetInfo.GetFolderIcon(curPath, false);
                      if(fi.closed != null || fi.open != null)
                      {
                          ImageList.Images.Add(fi.closed);
                          ImageList.Images.Add(fi.open);
                          n.SelectedImageIndex = ImageList.Images.Count - 1;
                          n.ImageIndex = ImageList.Images.Count - 2;
                      }
                  }
              }
              EndUpdate();
          }
          base.OnBeforeExpand(e);
      }