NewLife/XCoder

优化文件夹大小统计,捕获多线程异常
大石头 authored at 2022-09-13 14:20:24
3f28115
Tree
1 Parent(s) 33f0994
Summary: 1 changed files with 215 additions and 243 deletions.
Modified +215 -243
Modified +215 -243
diff --git a/XCoder/FolderInfo/FrmMain.cs b/XCoder/FolderInfo/FrmMain.cs
index 428ab28..2dad258 100644
--- a/XCoder/FolderInfo/FrmMain.cs
+++ b/XCoder/FolderInfo/FrmMain.cs
@@ -1,168 +1,152 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
 using System.ComponentModel;
 using System.Diagnostics;
-using System.Drawing;
-using System.IO;
 using System.Text;
-using System.Windows.Forms;
 using NewLife.Log;
-using NewLife.Threading;
 
-namespace XCoder.FolderInfo
+namespace XCoder.FolderInfo;
+
+[DisplayName("文件夹大小统计")]
+public partial class FrmMain : Form, IXForm
 {
-    [DisplayName("文件夹大小统计")]
-    public partial class FrmMain : Form, IXForm
+    /// <summary>业务日志输出</summary>
+    private ILog BizLog;
+
+    #region 初始化
+    public FrmMain()
     {
-        /// <summary>业务日志输出</summary>
-        ILog BizLog;
+        InitializeComponent();
 
-        #region 初始化
-        public FrmMain()
-        {
-            InitializeComponent();
+        // 动态调节宽度高度,兼容高DPI
+        this.FixDpi();
 
-            // 动态调节宽度高度,兼容高DPI
-            this.FixDpi();
+        //Icon = IcoHelper.GetIcon("文件");
+    }
 
-            //Icon = IcoHelper.GetIcon("文件");
-        }
+    private void Form1_Load(Object sender, EventArgs e)
+    {
+        //var log = TextFileLog.Create(null, "Folder_{0:yyyy_MM_dd}.log");
+        BizLog = txtLog.Combine(XTrace.Log);
+        txtLog.UseWinFormControl();
 
-        private void Form1_Load(Object sender, EventArgs e)
+        foreach (var item in DriveInfo.GetDrives())
         {
-            var log = TextFileLog.Create(null, "Folder_{0:yyyy_MM_dd}.log");
-            BizLog = txtLog.Combine(log);
-            txtLog.UseWinFormControl();
-
-            foreach (var item in DriveInfo.GetDrives())
+            if (item.DriveType == DriveType.Fixed)
             {
-                if (item.DriveType == DriveType.Fixed)
-                {
-                    var str = String.Format("{0,-10} ({1})", item.Name, FormatSize(item.TotalSize));
-                    var tn = treeView1.Nodes.Add(str);
-                    tn.Tag = item.RootDirectory.ToString();
-                    tn.Nodes.Add("no");
-                }
+                var str = $"{item.Name,-10} ({FormatSize(item.TotalSize)})";
+                var tn = treeView1.Nodes.Add(str);
+                tn.Tag = item.RootDirectory;
+                tn.Nodes.Add("no");
             }
         }
-        #endregion
+    }
+    #endregion
 
-        #region 构造目录树
-        void MakeTree(String path, TreeNode Node)
-        {
-            BizLog.Info("展开目录 {0}", path);
+    #region 构造目录树
+    private void MakeTree(FileSystemInfo fsi, TreeNode Node)
+    {
+        BizLog.Info("展开目录 {0}", fsi.FullName);
+
+        Node.Nodes.Clear();
 
-            Node.Nodes.Clear();
+        ////修正大小
+        //if (Node.Text.EndsWith("-1 Byte"))
+        //{
+        //    Node.Text = Node.Text.Substring(0, Node.Text.Length - 8) + FormatSize(FolderSize(Node.Tag.ToString()));
+        //}
 
-            ////修正大小
-            //if (Node.Text.EndsWith("-1 Byte"))
-            //{
-            //    Node.Text = Node.Text.Substring(0, Node.Text.Length - 8) + FormatSize(FolderSize(Node.Tag.ToString()));
-            //}
+        var di = fsi as DirectoryInfo;
 
-            var di = new DirectoryInfo(path);
-            var dis = di.GetDirectories();
-            var fis = di.GetFiles();
+        var list = new List<FileSystemInfo>();
+        list.AddRange(di.GetDirectories());
+        list.AddRange(di.GetFiles());
 
-            var list = new List<FileSystemInfo>();
-            list.AddRange(dis);
-            list.AddRange(fis);
+        var max = 0;
+        foreach (var item in list)
+        {
+            max = Math.Max(max, StrLen(item.Name));
+        }
+        max++;
+        foreach (var item in list)
+        {
+            var len = max;
+            len -= (StrLen(item.Name) - item.Name.Length);
+            Int64 size = 0;
 
-            var max = 0;
-            foreach (var item in list)
-            {
-                max = Math.Max(max, StrLen(item.Name));
-            }
-            max++;
-            foreach (var item in list)
+            if (item is FileInfo)
+                size = (item as FileInfo).Length;
+            else//默认不统计大小,加快显示速度
+                size = -1;
+
+            var str = String.Format("{0,-" + len.ToString() + "} {1,10}", item.Name, FormatSize(size));
+            var tn = Node.Nodes.Add(str);
+            tn.Tag = item;
+            tn.BackColor = GetColor(size);
+            tn.ContextMenuStrip = contextMenuStrip1;
+            if (item is DirectoryInfo)
             {
-                var len = max;
-                len -= (StrLen(item.Name) - item.Name.Length);
-                Int64 size = 0;
-
-                if (item is FileInfo)
-                    size = (item as FileInfo).Length;
-                else//默认不统计大小,加快显示速度
-                    size = -1;
-
-                var str = String.Format("{0,-" + len.ToString() + "} {1,10}", item.Name, FormatSize(size));
-                var tn = Node.Nodes.Add(str);
-                tn.Tag = item;
-                tn.BackColor = GetColor(size);
-                tn.ContextMenuStrip = contextMenuStrip1;
-                if (item is DirectoryInfo)
-                {
-                    tn.Nodes.Add("no");
-                    //使用后台线程统计大小信息
-                    ThreadPool.QueueUserWorkItem(s => TongJi(tn));
-                }
+                tn.Nodes.Add("no");
+
+                // 使用后台线程统计大小信息
+                //ThreadPool.QueueUserWorkItem(s => TongJi(tn));
+                Task.Run(() => TongJi(tn));
             }
         }
+    }
 
-        String FormatSize(Int64 size)
-        {
-            if (size < 1024) return size.ToString() + " Byte";
-            var ds = (Double)size / 1024;
-            if (ds < 1024) return ds.ToString("N2") + " K";
-            ds = ds / 1024;
-            if (ds < 1024) return ds.ToString("N2") + " M";
-            ds = ds / 1024;
-            if (ds < 1024) return ds.ToString("N2") + " G";
-            ds = ds / 1024;
-            if (ds < 1024) return ds.ToString("N2") + " T";
-            throw new Exception("Too Large");
-        }
+    private String FormatSize(Int64 size)
+    {
+        if (size < 1024) return size.ToString() + " Byte";
+        var ds = (Double)size / 1024;
+        if (ds < 1024) return ds.ToString("N2") + " K";
+        ds /= 1024;
+        if (ds < 1024) return ds.ToString("N2") + " M";
+        ds /= 1024;
+        if (ds < 1024) return ds.ToString("N2") + " G";
+        ds /= 1024;
+        if (ds < 1024) return ds.ToString("N2") + " T";
+        throw new Exception("Too Large");
+    }
 
-        Int32 StrLen(String str)
-        {
-            return Encoding.UTF8.GetBytes(str).Length;
-        }
-        #endregion
+    private Int32 StrLen(String str) => (str.Length + Encoding.UTF8.GetByteCount(str)) / 2;
+    #endregion
 
-        #region 统计文件夹大小
-        //long FolderSize(String path)
-        //{
-        //    return FolderSize(new DirectoryInfo(path));
-        //}
+    #region 统计文件夹大小
+    private readonly ConcurrentDictionary<String, Int64> cache = new();
 
-        //long FolderSize(FileSystemInfo si)
-        //{
-        //    return FolderSize(si as DirectoryInfo);
-        //}
+    private Int64 FolderSize(DirectoryInfo di)
+    {
+        Int64 size = 0;
+        if (cache.TryGetValue(di.FullName, out var v)) return v;
 
-        Dictionary<String, Int64> cache = new Dictionary<String, Int64>();
-        Int64 FolderSize(DirectoryInfo di)
+        try
         {
-            Int64 size = 0;
-            if (cache.ContainsKey(di.FullName)) return cache[di.FullName];
-            lock (di.FullName)
+            foreach (var item in di.GetFiles())
+            {
+                size += item.Length;
+            }
+            foreach (var item in di.GetDirectories())
             {
-                if (cache.ContainsKey(di.FullName)) return cache[di.FullName];
-                try
-                {
-                    foreach (var item in di.GetFiles())
-                    {
-                        size += item.Length;
-                    }
-                    foreach (var item in di.GetDirectories())
-                    {
-                        size += FolderSize(item);
-                    }
-                }
-                catch { }
-                if (!cache.ContainsKey(di.FullName)) cache.Add(di.FullName, size);
+                size += FolderSize(item);
             }
-            return size;
         }
-        #endregion
+        catch { }
+
+        cache.TryAdd(di.FullName, size);
+
+        return size;
+    }
+    #endregion
+
+    #region 统计目录并设置大小
+    private void TongJi(Object obj)
+    {
+        if (obj is not TreeNode node || node.Tag is not DirectoryInfo di) return;
 
-        #region 统计目录并设置大小
-        void TongJi(Object obj)
+        try
         {
-            var node = obj as TreeNode;
-            if (node == null || node.Tag == null) return;
-            var size = FolderSize(node.Tag as DirectoryInfo);
-            var str = node.Text.Substring(0, node.Text.Length - 10) + String.Format("{0,10}", FormatSize(size));
+            var size = FolderSize(di);
+            var str = node.Text[..^10] + $"{FormatSize(size),10}";
             //SetNodeText(node, str, GetColor(size));
             Invoke(() =>
             {
@@ -170,153 +154,141 @@ namespace XCoder.FolderInfo
                 node.BackColor = GetColor(size);
             });
         }
-
-        Color GetColor(Int64 size)
+        catch (Exception ex)
         {
-            var color = Color.White;
-            if (size > 1024) color = Color.MistyRose;
-            if (size > 1024 * 1024) color = Color.LightBlue;
-            if (size > 100 * 1024 * 1024) color = Color.LawnGreen;
-            if (size > 1024 * 1024 * 1024) color = Color.Yellow;
-            return color;
+            BizLog.Info(ex.ToString());
         }
+    }
 
-        //delegate void SetNodeTextDelegate(TreeNode node, String txt, Color color);
-        //void SetNodeText(TreeNode node, String txt, Color color)
-        //{
-        //    if (treeView1.InvokeRequired)
-        //    {
-        //        var d = new SetNodeTextDelegate(SetNodeText);
-        //        treeView1.Invoke(d, new object[] { node, txt, color });
-        //    }
-        //    else
-        //    {
-        //        node.Text = txt;
-        //        node.BackColor = color;
-        //    }
-        //}
-        #endregion
+    private Color GetColor(Int64 size)
+    {
+        var color = Color.White;
+        if (size > 1024) color = Color.MistyRose;
+        if (size > 1024 * 1024) color = Color.LightBlue;
+        if (size > 100 * 1024 * 1024) color = Color.LawnGreen;
+        if (size > 1024 * 1024 * 1024) color = Color.Yellow;
+        return color;
+    }
+    #endregion
 
-        #region 展开折叠目录树
-        private void treeView1_BeforeExpand(Object sender, TreeViewCancelEventArgs e)
+    #region 展开折叠目录树
+    private void treeView1_BeforeExpand(Object sender, TreeViewCancelEventArgs e)
+    {
+        var node = e.Node;
+        if (node.Nodes != null && node.Nodes.Count > 0 && node.Nodes[0].Text == "no")
         {
-            var node = e.Node;
-            if (node.Nodes != null && node.Nodes.Count > 0 && node.Nodes[0].Text == "no")
+            try
             {
-                try
-                {
-                    var fi = node.Tag as FileSystemInfo;
-                    if (fi != null)
-                        MakeTree(fi.FullName, node);
-                    else if (node.Tag is String)
-                        MakeTree(node.Tag + "", node);
-                }
-                catch { }
+                if (node.Tag is FileSystemInfo fi)
+                    MakeTree(fi, node);
+                //else if (node.Tag is String str)
+                //    MakeTree(str + "", node);
             }
+            catch { }
         }
+    }
 
-        private void treeView1_AfterCollapse(Object sender, TreeViewEventArgs e)
+    private void treeView1_AfterCollapse(Object sender, TreeViewEventArgs e)
+    {
+        // 折叠后清空,使得再次展开时重新计算
+        var nodes = e.Node?.Nodes;
+        if (nodes != null)
         {
-            // 折叠后清空,使得再次展开时重新计算
-            if (e.Node != null && e.Node.Nodes != null)
-            {
-                e.Node.Nodes.Clear();
-                e.Node.Nodes.Add("no");
-            }
+            nodes.Clear();
+            nodes.Add("no");
         }
-        #endregion
+    }
+    #endregion
 
-        #region 右键菜单
-        String GetSelectedPath()
-        {
-            var node = treeView1.SelectedNode;
-            if (node == null || node.Tag == null) return null;
+    #region 右键菜单
+    private String GetSelectedPath()
+    {
+        var node = treeView1.SelectedNode;
+        if (node == null) return null;
 
-            var fi = node.Tag as FileSystemInfo;
-            if (fi != null) return fi.FullName;
+        if (node.Tag is FileSystemInfo fi) return fi.FullName;
 
-            return node.Tag.ToString();
-        }
+        return null;
+    }
 
-        private void 打开目录ToolStripMenuItem_Click(Object sender, EventArgs e)
-        {
-            var path = GetSelectedPath();
-            if (String.IsNullOrEmpty(path)) return;
+    private void 打开目录ToolStripMenuItem_Click(Object sender, EventArgs e)
+    {
+        var path = GetSelectedPath();
+        if (String.IsNullOrEmpty(path)) return;
 
-            if (path.Contains(" ")) path = "\"" + path + "\"";
+        if (path.Contains(" ")) path = "\"" + path + "\"";
 
-            try
-            {
-                var exp = Environment.SystemDirectory.CombinePath("../explorer.exe").GetFullPath();
-                if (File.Exists(path))
-                    Process.Start(exp, "/select," + path);
-                else
-                    Process.Start(exp, path);
-            }
-            catch (Exception ex)
-            {
-                MessageBox.Show(ex.Message, "打开出错");
-            }
+        try
+        {
+            var exp = Environment.SystemDirectory.CombinePath("../explorer.exe").GetFullPath();
+            if (File.Exists(path))
+                Process.Start(exp, "/select," + path);
+            else
+                Process.Start(exp, path);
         }
-
-        private void 删除ToolStripMenuItem_Click(Object sender, EventArgs e)
+        catch (Exception ex)
         {
-            var path = GetSelectedPath();
-            if (String.IsNullOrEmpty(path)) return;
+            MessageBox.Show(ex.Message, "打开出错");
+        }
+    }
+
+    private void 删除ToolStripMenuItem_Click(Object sender, EventArgs e)
+    {
+        var path = GetSelectedPath();
+        if (String.IsNullOrEmpty(path)) return;
 
-            if (MessageBox.Show("准备删除" + path + Environment.NewLine + "删除将不可恢复,是否删除?", "确认删除", MessageBoxButtons.YesNo) == DialogResult.No) return;
+        if (MessageBox.Show($"准备删除{path}{Environment.NewLine}删除将不可恢复,是否删除?", "确认删除", MessageBoxButtons.YesNo) == DialogResult.No) return;
 
 
-            //if (path.Contains(" ")) path = "\"" + path + "\"";
+        //if (path.Contains(" ")) path = "\"" + path + "\"";
 
-            try
-            {
-                if (File.Exists(path))
-                    File.Delete(path);
-                else
-                    //Directory.Delete(path, true);
-                    DeleteRecursive(new DirectoryInfo(path));
+        try
+        {
+            if (File.Exists(path))
+                File.Delete(path);
+            else
+                //Directory.Delete(path, true);
+                DeleteRecursive(new DirectoryInfo(path));
 
-                treeView1.SelectedNode.Remove();
-            }
-            catch (Exception ex)
-            {
-                MessageBox.Show(ex.Message, "删除出错");
-            }
+            treeView1.SelectedNode.Remove();
+        }
+        catch (Exception ex)
+        {
+            MessageBox.Show(ex.Message, "删除出错");
         }
+    }
 
-        /// <summary>递归删除</summary>
-        /// <param name="di"></param>
-        void DeleteRecursive(DirectoryInfo di)
+    /// <summary>递归删除</summary>
+    /// <param name="di"></param>
+    private void DeleteRecursive(DirectoryInfo di)
+    {
+        // 删除本目录文件
+        foreach (var item in di.GetFiles())
         {
-            // 删除本目录文件
-            foreach (var item in di.GetFiles())
-            {
-                BizLog.Info("删除 {0}", item.FullName);
-                try
-                {
-                    item.Delete();
-                }
-                catch (Exception ex)
-                {
-                    BizLog.Error(ex.Message);
-                }
-            }
-            // 递归子目录
-            foreach (var item in di.GetDirectories())
-            {
-                DeleteRecursive(item);
-            }
+            BizLog.Info("删除 {0}", item.FullName);
             try
             {
-                // 删除本目录
-                di.Delete(true);
+                item.Delete();
             }
             catch (Exception ex)
             {
                 BizLog.Error(ex.Message);
             }
         }
-        #endregion
+        // 递归子目录
+        foreach (var item in di.GetDirectories())
+        {
+            DeleteRecursive(item);
+        }
+        try
+        {
+            // 删除本目录
+            di.Delete(true);
+        }
+        catch (Exception ex)
+        {
+            BizLog.Error(ex.Message);
+        }
     }
+    #endregion
 }
\ No newline at end of file