Browse Source

关于ntfs磁盘的usn读取问题,继续读取确实有不可预知的遗漏问题

yuzhengyang 8 years ago
parent
commit
c719f71626

+ 11 - 8
Fork.Net/Fork.Net.SpeciallyTools/Y.FileQueryEngine/UsnOperation/UsnOperator.cs

@@ -143,16 +143,15 @@ namespace Y.FileQueryEngine.UsnOperation
 
             return result;
         }
-        public void GetEntries(long usn, GetEntriesHandler handler, int count)
+        public void GetEntries(long usn, ulong fileNumber, GetEntriesHandler handler, int count)
         {
-            bool usnjump = usn > 0;
             List<UsnEntry> result = new List<UsnEntry>();
             UsnErrorCode usnErrorCode = this.QueryUSNJournal();
             if (usnErrorCode == UsnErrorCode.SUCCESS)
             {
                 MFT_ENUM_DATA mftEnumData = new MFT_ENUM_DATA();
-                mftEnumData.StartFileReferenceNumber = 0;
-                mftEnumData.LowUsn = usn;
+                mftEnumData.StartFileReferenceNumber = fileNumber;
+                mftEnumData.LowUsn = 0;
                 mftEnumData.HighUsn = this.ntfsUsnJournalData.NextUsn;
                 int sizeMftEnumData = Marshal.SizeOf(mftEnumData);
                 IntPtr ptrMftEnumData = GetHeapGlobalPtr(sizeMftEnumData);
@@ -171,16 +170,20 @@ namespace Y.FileQueryEngine.UsnOperation
                     out outBytesCount,
                     IntPtr.Zero))
                 {
-
-                    IntPtr ptrUsnRecord = new IntPtr(ptrData.ToInt32() + sizeof(Int64));
+                    long purvalue = ptrData.ToInt64() + sizeof(long);
+                    IntPtr ptrUsnRecord = new IntPtr(purvalue);
 
                     while (outBytesCount > 60)
                     {
                         var usnRecord = new USN_RECORD_V2(ptrUsnRecord);
 
-                        if (usnjump) usnjump = false; else result.Add(new UsnEntry(usnRecord));
+                        UsnEntry rec = new UsnEntry(usnRecord);
+                        if (rec.FileReferenceNumber > fileNumber || rec.Usn > usn)
+                        {
+                            result.Add(rec);
+                        }
 
-                        ptrUsnRecord = new IntPtr(ptrUsnRecord.ToInt32() + usnRecord.RecordLength);
+                        ptrUsnRecord = new IntPtr(ptrUsnRecord.ToInt64() + usnRecord.RecordLength);
                         outBytesCount -= usnRecord.RecordLength;
 
                         if (result.Count >= count)

+ 1 - 1
Fork.Net/Fork.Net.SpeciallyTools/Y.FileQueryEngine/Win32/Structures/USN_RECORD_V2.cs

@@ -42,7 +42,7 @@ namespace Y.FileQueryEngine.Win32.Structures
             this.FileAttributes = (UInt32)Marshal.ReadInt32(usnRecordPtr, FA_OFFSET);
             this.FileNameLength = Marshal.ReadInt16(usnRecordPtr, FNL_OFFSET);
             this.FileNameOffset = Marshal.ReadInt16(usnRecordPtr, FN_OFFSET);
-            this.FileName = Marshal.PtrToStringUni(new IntPtr(usnRecordPtr.ToInt32() + this.FileNameOffset), this.FileNameLength / sizeof(char));
+            this.FileName = Marshal.PtrToStringUni(new IntPtr(usnRecordPtr.ToInt64() + this.FileNameOffset), this.FileNameLength / sizeof(char));
         }
     }
 }

+ 1 - 1
Fork.Net/Fork.Net.SpeciallyTools/Y.FileQueryEngine/Y.FileQueryEngine.csproj

@@ -9,7 +9,7 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>Y.FileQueryEngine</RootNamespace>
     <AssemblyName>Y.FileQueryEngine</AssemblyName>
-    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <TargetFrameworkProfile />
   </PropertyGroup>

+ 2 - 2
Fork.Net/Oreo.Plugins/Oreo.FileMan/App.config

@@ -5,8 +5,8 @@
     <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
   </configSections>
   <startup>
-    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
-  </startup>
+    
+  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup>
   <connectionStrings>
     <add name="DefaultConnection" connectionString="data source=|DataDirectory|\FileMan.db" providerName="System.Data.SQLite"/>
   </connectionStrings>

+ 4 - 0
Fork.Net/Oreo.Plugins/Oreo.FileMan/Models/BackupFiles.cs

@@ -24,5 +24,9 @@ namespace Oreo.FileMan.Models
         /// 备份时间
         /// </summary>
         public string BackupTime { get; set; }
+        /// <summary>
+        /// 最后修改时间
+        /// </summary>
+        public string LastWriteTime { get; set; }
     }
 }

+ 3 - 2
Fork.Net/Oreo.Plugins/Oreo.FileMan/Oreo.FileMan.csproj

@@ -9,12 +9,13 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>Oreo.FileMan</RootNamespace>
     <AssemblyName>Oreo.FileMan</AssemblyName>
-    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
     <NuGetPackageImportStamp>
     </NuGetPackageImportStamp>
-    <TargetFrameworkProfile />
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <PlatformTarget>AnyCPU</PlatformTarget>

+ 1 - 0
Fork.Net/Oreo.Plugins/Oreo.FileMan/Partials/FileBackupPartial.Designer.cs

@@ -154,6 +154,7 @@
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.Color.White;
             this.Controls.Add(this.BtDelPath);
             this.Controls.Add(this.DgvFile);
             this.Controls.Add(this.BtAddPath);

+ 3 - 3
Fork.Net/Oreo.Plugins/Oreo.FileMan/Partials/FileBackupPartial.cs

@@ -32,11 +32,11 @@ namespace Oreo.FileMan.Partials
         {
             if (ListTool.HasElements(R.Services.FBS.Paths))
             {
-                foreach(var p in R.Services.FBS.Paths)
+                foreach (var p in R.Services.FBS.Paths)
                 {
                     UIDgvPathAdd(DirTool.GetPathName(p.Path));
                 }
-            } 
+            }
         }
 
         private void BtAddPath_Click(object sender, EventArgs e)
@@ -71,7 +71,7 @@ namespace Oreo.FileMan.Partials
                                 if (db.Add(bp) > 0)
                                 {
                                     R.Services.FBS.Paths.Add(bp);//添加到列表
-                                    R.Services.FBS.Watcher.AddPath(bp.Path);//添加到监听
+                                    R.Services.FBS.AddToWatcherPath(bp.Path);//添加到监听
                                     UIDgvPathAdd(name);//添加到列表UI
 
                                     long size = 0;//目录下的文件大小

+ 19 - 87
Fork.Net/Oreo.Plugins/Oreo.FileMan/Partials/FileTypePartial.cs

@@ -16,6 +16,7 @@ using System.Data.SqlClient;
 using System.Data.Entity.Infrastructure;
 using Y.Utils.AppUtils;
 using Y.Utils.DataUtils.DateTimeUtils;
+using Y.Utils.DataUtils.StringUtils;
 
 namespace Oreo.FileMan.Partials
 {
@@ -61,93 +62,15 @@ namespace Oreo.FileMan.Partials
                 }
             });
         }
-        [Obsolete]
-        private void GetFileToDatabase()
-        {
-            var drives = FileQueryEngine.GetReadyNtfsDrives().OrderByDescending(x => x.Name);
-            if (ListTool.HasElements(drives))
-            {
-                foreach (var drive in drives)
-                {
-                    var usnList = FileQueryEngine.GetAllFiles(drive);
-                    if (ListTool.HasElements(usnList))
-                    {
-                        using (var db = new Muse())
-                        {
-                            //检测磁盘是否格式化,如果格式化则清空USN记录
-                            DateTime dt1 = DriveTool.GetLastFormatTime(drive.Name);
-                            var ds = db.Get<UsnDrives>(x => x.Name == drive.Name, null);
-                            if ((ds == null) || (ds != null && ds.LastFormatTime != dt1.ToString()))
-                            {
-                                var fs = db.Gets<UsnFiles>(x => x.Drive == drive.Name, null);
-                                if (ListTool.HasElements(fs)) db.Dels(fs);
-
-                                if (ds == null)
-                                {
-                                    db.Add(new UsnDrives() { Name = drive.Name, LastFormatTime = dt1.ToString() });
-                                }
-                                else
-                                {
-                                    ds.LastFormatTime = dt1.ToString();
-                                    db.Update(ds, true);
-                                }
-                            }
-
-                            //查询上次读取到的位置并读取USN
-                            if (db.Any<UsnFiles>(x => x.Drive == drive.Name, null))
-                            {
-                                long currentUsn = db.Do<UsnFiles>().Where(x => x.Drive == drive.Name).Max(x => x.Usn);
-                                usnList = usnList.Where(x => x.Usn > currentUsn).ToList();
-                            }
-                            //将记录存储到数据库中
-                            if (ListTool.HasElements(usnList))
-                            {
-                                //List<Files> temp = new List<Files>();
-                                for (int i = 0; i < usnList.Count; i++)
-                                {
-                                    UISetFileCount(i + 1, usnList.Count());
-
-                                    //temp.Add(new Files()
-                                    //{
-                                    //    Name = usnList[i].FileName,
-                                    //    IsFolder = usnList[i].IsFolder,
-                                    //    Number = usnList[i].FileReferenceNumber.ToString(),
-                                    //    ParentNumber = usnList[i].ParentFileReferenceNumber.ToString(),
-                                    //    Drive = drive.Name,
-                                    //    Usn = usnList[i].Usn,
-                                    //});
-                                    //if (temp.Count > 100)
-                                    //{
-                                    //    db.Adds(temp);
-                                    //    temp = new List<Files>();
-                                    //    Thread.Sleep(100);
-                                    //}
-                                    db.Add(new UsnFiles()
-                                    {
-                                        Name = usnList[i].FileName,
-                                        IsFolder = usnList[i].IsFolder,
-                                        Number = usnList[i].FileReferenceNumber.ToString(),
-                                        ParentNumber = usnList[i].ParentFileReferenceNumber.ToString(),
-                                        Drive = drive.Name,
-                                        Usn = usnList[i].Usn,
-                                    });
-                                }
-                                //db.Adds(temp);
-                            }
-                        }
-                    }
-                }
-            }
-        }
         private void GetFileToDatabase2()
         {
-            var drives = FileQueryEngine.GetReadyNtfsDrives().OrderByDescending(x => x.Name);
+            var drives = FileQueryEngine.GetReadyNtfsDrives().OrderBy(x => x.Name);
             if (ListTool.HasElements(drives))
             {
                 foreach (var drive in drives)
                 {
                     NewFileCount = 0;
-                    if (drive.Name.Contains("C")) continue;//测试时跳过C
+                    if (!drive.Name.Contains("E")) continue;//测试只读取D盘
                     //if (drive.Name.Contains("D")) continue;//测试时跳过D盘
                     //if (drive.Name.Contains("F")) continue;//测试时跳过F盘
 
@@ -172,14 +95,23 @@ namespace Oreo.FileMan.Partials
                         }
 
                         //查询上次读取到的位置
-                        long currentUsn = 0;
+                        ulong filenumber = 0;
+                        long usn = 0;
                         if (db.Any<UsnFiles>(x => x.Drive == drive.Name, null))
                         {
-                            currentUsn = db.Do<UsnFiles>().Where(x => x.Drive == drive.Name).Max(x => x.Usn);
+                            int lastId = db.Do<UsnFiles>().Where(x => x.Drive == drive.Name).Max(x => x.Id);
+                            UsnFiles lastRec = db.Get<UsnFiles>(x => x.Id == lastId, null);
+
+                            usn = lastRec.Usn;
+                            filenumber = NumberStringTool.ToUlong(lastRec.Number);
+
+                            //usn = db.Do<UsnFiles>().Where(x => x.Drive == drive.Name).Max(x => x.Usn);
+                            //string filenumberstr = db.Do<UsnFiles>().Where(x => x.Drive == drive.Name).Max(x => x.Number);
+                            //filenumber = NumberStringTool.ToUlong(filenumberstr);
                         }
-                        //从上次Usn记录开始读取
+                        //从上次FileNumber记录开始读取
                         var usnOperator = new UsnOperator(drive);
-                        usnOperator.GetEntries(currentUsn, GetFileToDatabaseEvent, 1000);
+                        usnOperator.GetEntries(usn, filenumber, GetFileToDatabaseEvent, 1000);
                     }
                 }
             }
@@ -195,11 +127,11 @@ namespace Oreo.FileMan.Partials
                     {
                         Name = data[i].FileName,
                         IsFolder = data[i].IsFolder,
-                        Number = data[i].FileReferenceNumber.ToString(),
-                        ParentNumber = data[i].ParentFileReferenceNumber.ToString(),
+                        Number = NumberStringTool.ToString(data[i].FileReferenceNumber),
+                        ParentNumber = NumberStringTool.ToString(data[i].ParentFileReferenceNumber),
                         Drive = drive.Name,
                         Usn = data[i].Usn,
-                        CreateTime = DateTimeConvert.StandardString(DateTime.Now)
+                        CreateTime = DateTimeConvert.DetailString(DateTime.Now)
                     });
                     NewFileCount++;
                 }

+ 69 - 18
Fork.Net/Oreo.Plugins/Oreo.FileMan/Services/FileBackupService.cs

@@ -16,8 +16,8 @@ namespace Oreo.FileMan.Services
 {
     public class FileBackupService
     {
-        public FileWatcher Watcher = new FileWatcher(null);
-        public string FileManBackup = @"G:\FileManBackup\";
+        private FileWatcher Watcher = new FileWatcher(null);
+        public string FileManBackup = @"D:\temp\FileManBackup\";
         public List<BackupPaths> Paths = new List<BackupPaths>();
 
         List<string> BackupFiles = new List<string>();
@@ -31,13 +31,21 @@ namespace Oreo.FileMan.Services
             {
                 IsStart = true;
 
-                Watcher.eventHandler += WatcherChangedEvent;
+                Watcher.EventHandler += WatcherChangedEvent;
                 Watcher.Start();//启动文件变动监听
 
                 Task.Factory.StartNew(() =>
                 {
                     ReadBackupPaths();//读取备份文件夹列表
-                    //常规检查备份
+
+                    if (ListTool.HasElements(Paths))
+                    {
+                        foreach (var p in Paths)
+                        {
+                            DefaultBackupFile(p.Path);//常规检查备份
+                        }
+                    }
+
                     BackupFileTask();//开始定时备份任务
                 });
             }
@@ -49,19 +57,19 @@ namespace Oreo.FileMan.Services
                 IsStart = false;
             }
         }
+
+        /// <summary>
+        /// 文件发生变动事件
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
         private void WatcherChangedEvent(object sender, FileWatcherEventArgs e)
         {
-            if (Paths.Any(x => e.FullPath.Contains(x.Path)))
-            {
-                //变动的是文件且文件存在
-                if (FileTool.IsFile(e.FullPath))
-                {
-                    //添加到备份列表
-                    if (!BackupFiles.Contains(e.FullPath)) BackupFiles.Add(e.FullPath);
-                    //UIDgvFileAdd(e.Name, e.FullPath, e.ChangeType.ToString());
-                }
-            }
+            AddToBackupFiles(e.FullPath);
         }
+        /// <summary>
+        /// 定时处理要备份的文件任务
+        /// </summary>
         private void BackupFileTask()
         {
             while (IsStart)
@@ -115,22 +123,63 @@ namespace Oreo.FileMan.Services
                 {
                     foreach (var p in Paths)
                     {
-                        Watcher.AddPath(p.Path);
+                        AddToWatcherPath(p.Path);
                     }
                 }
             }
         }
-        public void DefaultBackupFileTask()
+        /// <summary>
+        /// 初始读取文件并备份
+        /// </summary>
+        public void DefaultBackupFile(string path)
         {
-            if (ListTool.HasElements(Paths))
+            //读取本地文件夹中的所有文件列表
+            List<string> files = FileTool.GetAllFile(path);
+            if (ListTool.HasElements(files))
             {
-                foreach (var p in Paths)
+                foreach (var file in files)
                 {
+                    try
+                    {
+                        string lastwritetime = DateTimeConvert.StandardString(File.GetLastWriteTime(file));
+                        using (var db = new Muse())
+                        {
+                            BackupFiles backfile = db.Get<BackupFiles>(x => x.FullPath == file && x.LastWriteTime == lastwritetime, null);
+                            if (backfile == null) AddToBackupFiles(file);
 
+                        }
+                    }
+                    catch (Exception e) { }
+                }
+            }
+        }
+        /// <summary>
+        /// 添加要备份的文件到备份计划列表
+        /// </summary>
+        /// <param name="fullpath"></param>
+        private void AddToBackupFiles(string fullpath)
+        {
+            if (Paths.Any(x => fullpath.Contains(x.Path)))
+            {
+                //变动的是文件且文件存在
+                if (FileTool.IsFile(fullpath))
+                {
+                    //添加到备份列表
+                    if (!BackupFiles.Contains(fullpath)) BackupFiles.Add(fullpath);
+                    //UIDgvFileAdd(e.Name, e.FullPath, e.ChangeType.ToString());
                 }
             }
         }
         /// <summary>
+        /// 添加要备份的文件夹
+        /// </summary>
+        /// <param name="path"></param>
+        public void AddToWatcherPath(string path)
+        {
+            Watcher.AddPath(path);//添加要备份的文件夹
+            DefaultBackupFile(path);//常规检查备份文件夹
+        }
+        /// <summary>
         /// 删除超过备份最大次数的项
         /// </summary>
         private void DeleteExcess(string path)
@@ -169,6 +218,7 @@ namespace Oreo.FileMan.Services
                 {
                     if (DirTool.Create(DirTool.GetFilePath(newpath)))
                     {
+                        string lastwritetime = DateTimeConvert.StandardString(File.GetLastWriteTime(path));
                         File.Copy(path, newpath, true);
                         db.Add(new BackupFiles()
                         {
@@ -176,6 +226,7 @@ namespace Oreo.FileMan.Services
                             BackupFullPath = newpath,
                             Size = FileTool.Size(path),
                             BackupTime = DateTimeConvert.StandardString(DateTime.Now),
+                            LastWriteTime = lastwritetime,
                         });
                     }
                 }

+ 6 - 6
Fork.Net/Oreo.Plugins/Oreo.FileMan/packages.config

@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="EntityFramework" version="6.1.3" targetFramework="net40" />
-  <package id="EntityFramework.zh-Hans" version="6.1.3" targetFramework="net40" />
-  <package id="SQLite.CodeFirst" version="1.3.1.18" targetFramework="net40" />
+  <package id="EntityFramework" version="6.1.3" targetFramework="net40" requireReinstallation="true" />
+  <package id="EntityFramework.zh-Hans" version="6.1.3" targetFramework="net40" requireReinstallation="true" />
+  <package id="SQLite.CodeFirst" version="1.3.1.18" targetFramework="net40" requireReinstallation="true" />
   <package id="System.Data.SQLite" version="1.0.105.2" targetFramework="net40" />
-  <package id="System.Data.SQLite.Core" version="1.0.105.2" targetFramework="net40" />
-  <package id="System.Data.SQLite.EF6" version="1.0.105.2" targetFramework="net40" />
+  <package id="System.Data.SQLite.Core" version="1.0.105.2" targetFramework="net40" requireReinstallation="true" />
+  <package id="System.Data.SQLite.EF6" version="1.0.105.2" targetFramework="net40" requireReinstallation="true" />
   <package id="System.Data.SQLite.EF6.Migrations" version="1.0.104" targetFramework="net40" />
-  <package id="System.Data.SQLite.Linq" version="1.0.105.2" targetFramework="net40" />
+  <package id="System.Data.SQLite.Linq" version="1.0.105.2" targetFramework="net40" requireReinstallation="true" />
 </packages>

+ 9 - 0
Fork.Net/Y.Utils/DataUtils/DateTimeUtils/DateTimeConvert.cs

@@ -36,5 +36,14 @@ namespace Y.Utils.DataUtils.DateTimeUtils
         {
             return dt.ToString("yyyyMMddHHmmss");
         }
+        /// <summary>
+        /// yyyy-MM-dd HH:mm:ss.fff
+        /// </summary>
+        /// <param name="dt"></param>
+        /// <returns></returns>
+        public static string DetailString(DateTime dt)
+        {
+            return dt.ToString("yyyy-MM-dd HH:mm:ss.fff");
+        }
     }
 }

+ 30 - 0
Fork.Net/Y.Utils/DataUtils/StringUtils/NumberStringTool.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Y.Utils.DataUtils.StringUtils
+{
+    public class NumberStringTool
+    {
+        public static string ToString(ulong value, int digit = 32)
+        {
+            string basenumber = value.ToString();
+            if (digit > basenumber.Length)
+            {
+                string digitnumber = new string('0', digit - basenumber.Length);
+                return digitnumber + basenumber;
+            }
+            return basenumber;
+        }
+        public static ulong ToUlong(string value)
+        {
+            ulong number = 0;
+            if (ulong.TryParse(value, out number))
+            {
+
+            }
+            return number;
+        }
+    }
+}

+ 5 - 5
Fork.Net/Y.Utils/IOUtils/FileManUtils/FileWatcher.cs

@@ -32,7 +32,7 @@ namespace Y.Utils.IOUtils.FileManUtils
         /// <summary>
         /// 获取文件监控信息
         /// </summary>
-        public FileWatcherEventHandler eventHandler;
+        public FileWatcherEventHandler EventHandler;
 
         private int Interval = 10 * 1000;
         private bool _IsWatching = false;
@@ -155,19 +155,19 @@ namespace Y.Utils.IOUtils.FileManUtils
         }
         private void CreatedEvent(object sender, FileSystemEventArgs e)
         {
-            eventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), null, null));
+            EventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), null, null));
         }
         private void ChangedEvent(object sender, FileSystemEventArgs e)
         {
-            eventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), null, null));
+            EventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), null, null));
         }
         private void DeletedEvent(object sender, FileSystemEventArgs e)
         {
-            eventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), null, null));
+            EventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), null, null));
         }
         private void RenamedEvent(object sender, RenamedEventArgs e)
         {
-            eventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), e.OldFullPath, e.OldName));
+            EventHandler?.Invoke(sender, new FileWatcherEventArgs(e.ChangeType, e.FullPath, Path.GetFileName(e.FullPath), e.OldFullPath, e.OldName));
         }
         private void ErrorEvent(object sender, ErrorEventArgs e)
         { }

+ 1 - 0
Fork.Net/Y.Utils/Y.Utils.csproj

@@ -52,6 +52,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AppUtils\AppUnique.cs" />
+    <Compile Include="DataUtils\StringUtils\NumberStringTool.cs" />
     <Compile Include="DelegateUtils\ProgressDelegate.cs" />
     <Compile Include="DelegateUtils\ProgressEventArgs.cs" />
     <Compile Include="IOUtils\DriveUtils\DriveTool.cs" />