如何使用C#同步并一致地删除NTFS上的文件夹
这个:
Directory.Delete(dir, true);
是不同步的。
在紧接着的行中,您仍然可以操作/读取目录。
例如,这个:
Directory.Delete(destinationDir, true); Directory.CreateDirectory(destinationDir); Thread.Sleep(1000);
导致文件夹不存在。 删除运行异步, CreateDirectory
不创建,因为它已经存在,然后删除实际触发并删除目录。
是否有一个IO API可以提供一致性?
涉及Thread.Sleep
答案将调用Zalgo。 我想要一个真正的解决方案。
在C ++中进行一些测试后,似乎用于删除文件/目录的本机Windows函数会阻塞。 当删除函数未被阻止时,似乎问题出现在.NET端,因为Directory.CreateDirectory()
似乎在Directory.Delete()
完成之前被调用。
这是我在Win32控制台应用程序中尝试的:
printf("Press enter to begin..."); while(getchar() != '\n'); LPCSTR DeletePath = "C:\\test\\DeleteMe"; //The directory to delete. _SHFILEOPSTRUCTA* fileopt = new _SHFILEOPSTRUCTA(); fileopt->hwnd = NULL; //No window handle. fileopt->wFunc = FO_DELETE; //Delete mode. fileopt->pFrom = DeletePath; //The directory to delete. fileopt->pTo = NULL; //No target directory (this is only used when moving, copying, etc.). fileopt->fFlags = FOF_NO_UI; //Display no UI dialogs. int Success = SHFileOperationA(fileopt); //Remove the entire directory and all it's contents. bool Success2 = CreateDirectoryA(DeletePath, NULL); //Create a new directory. LPCSTR ReturnedValue = "False"; //I'm no C++ guru, so please don't hate. :) LPCSTR ReturnedValue2 = "False"; if(Success == 0) { ReturnedValue = "True"; } //The SHFileOperation() returns 0 if it succeeds. if(Success2 == true) { ReturnedValue2 = "True"; } //Print the result of SHFileOperation(). printf("Returned value: "); printf(ReturnedValue); printf("\n"); //Print the result of CreateDirectory(). printf("Returned value 2: "); printf(ReturnedValue2); printf("\n"); //Continue. printf("Press enter to exit..."); while(getchar() != '\n');
在第一次按下ENTER之后,在显示结果之前有一个小的延迟,然后在查看文件夹时,它是空的,带有新的创建和最后修改的日期 – 这意味着它已被删除并以正确的顺序重新创建。
所以为了实现你想要的,我想你可以尝试创建自己的方法来调用SHFileOperation()
,因为问题似乎是Directory.Delete()
方法在.NET代码中执行迭代(参见参考资料来源 )。
—编辑—
在C#中测试后,这似乎有效! 唯一的问题是,第一次(自应用程序启动以来)调用P / Invoked SHFileOperation()
函数,它将返回值2,这相当于ERROR_FILE_NOT_FOUND
。 但如果再次执行它将返回0(成功)。
NativeMethods.cs:
需要import:
using System; using System.Runtime.InteropServices;
其余代码:
[DllImport("shell32.dll", CharSet = CharSet.Unicode)] public static extern int SHFileOperation([In] ref SHFILEOPSTRUCT lpFileOp); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SHFILEOPSTRUCT { public IntPtr hwnd; public FileFuncFlags wFunc; [MarshalAs(UnmanagedType.LPWStr)] public string pFrom; [MarshalAs(UnmanagedType.LPWStr)] public string pTo; public FILEOP_FLAGS fFlags; [MarshalAs(UnmanagedType.Bool)] public bool fAnyOperationsAborted; public IntPtr hNameMappings; [MarshalAs(UnmanagedType.LPWStr)] public string lpszProgressTitle; } public enum FileFuncFlags : uint { FO_MOVE = 0x1, FO_COPY = 0x2, FO_DELETE = 0x3, FO_RENAME = 0x4 } [Flags] public enum FILEOP_FLAGS : ushort { FOF_MULTIDESTFILES = 0x1, FOF_CONFIRMMOUSE = 0x2, /// /// Don't create progress/report /// FOF_SILENT = 0x4, FOF_RENAMEONCOLLISION = 0x8, /// /// Don't prompt the user. /// FOF_NOCONFIRMATION = 0x10, /// /// Fill in SHFILEOPSTRUCT.hNameMappings. /// Must be freed using SHFreeNameMappings /// FOF_WANTMAPPINGHANDLE = 0x20, FOF_ALLOWUNDO = 0x40, /// /// On *.*, do only files /// FOF_FILESONLY = 0x80, /// /// Don't show names of files /// FOF_SIMPLEPROGRESS = 0x100, /// /// Don't confirm making any needed dirs /// FOF_NOCONFIRMMKDIR = 0x200, /// /// Don't put up error UI /// FOF_NOERRORUI = 0x400, /// /// Dont copy NT file Security Attributes /// FOF_NOCOPYSECURITYATTRIBS = 0x800, /// /// Don't recurse into directories. /// FOF_NORECURSION = 0x1000, /// /// Don't operate on connected elements. /// FOF_NO_CONNECTED_ELEMENTS = 0x2000, /// /// During delete operation, /// warn if nuking instead of recycling (partially overrides FOF_NOCONFIRMATION) /// FOF_WANTNUKEWARNING = 0x4000, /// /// Treat reparse points as objects, not containers /// FOF_NORECURSEREPARSE = 0x8000 }
其他地方:
string DeletePath = "C:\\test\\DeleteMe"; NativeMethods.SHFILEOPSTRUCT fileopt = new NativeMethods.SHFILEOPSTRUCT(); fileopt.hwnd = IntPtr.Zero; fileopt.wFunc = NativeMethods.FileFuncFlags.FO_DELETE; fileopt.pFrom = DeletePath; fileopt.pTo = null; fileopt.fFlags = NativeMethods.FILEOP_FLAGS.FOF_SILENT | NativeMethods.FILEOP_FLAGS.FOF_NOCONFIRMATION | NativeMethods.FILEOP_FLAGS.FOF_NOERRORUI | NativeMethods.FILEOP_FLAGS.FOF_NOCONFIRMMKDIR; //Equivalent of FOF_NO_UI. int Success = NativeMethods.SHFileOperation(ref fileopt); Directory.CreateDirectory(DeletePath); MessageBox.Show("Operation returned value: " + Success.ToString(), "Test", MessageBoxButtons.OK, MessageBoxIcon.Information);
希望这可以帮助!
这是一个有相同问题的人的链接 ,首先重命名/移动目录的解决方案可能适合您。
否则你可以使用FileWatcher来对目录删除作出反应,但这感觉就像是矫枉过正。
正如其他人所提到的那样,.net Framework似乎并没有同步运行它。 在PowerShell中测试它表明.Net调用不会等待,所以这样的事情会产生类似的结果:
Remove-Item -Recurse -Force "C:\tempfolder" New-Item -ItemType Directory "C:\tempfolder"
使用文件观察器(前面也提到过)将确保在创建完成之前完成目录删除:
var path = @"C:\tempfolder"; var watcher = new FileSystemWatcher(path); watcher.Deleted += (sender, args) => Directory.CreateDirectory(args.FullPath); Directory.Delete(path, true);
没有真正意外,但至少它是一个有效的解决方案,不涉及从托管代码调用C ++ API。