简体   繁体   English

如何使用C#同步和一致地删除NTFS上的文件夹

[英]How to synchronously and consistently delete a folder on NTFS with C#

this: 这个:

Directory.Delete(dir, true);

is not synchronous. 不是同步的。

On the immediately proceeding line you can still manipulate/read the directory. 在紧接着的行中,您仍然可以操作/读取目录。

For example, this: 例如,这个:

Directory.Delete(destinationDir, true);
Directory.CreateDirectory(destinationDir);
Thread.Sleep(1000);

results in the folder not existing. 导致文件夹不存在。 The delete runs async, CreateDirectory doesn't create because it already exists, then delete actually fires and removes the directory. 删除运行异步, CreateDirectory不创建,因为它已经存在,然后删除实际触发并删除目录。

Is there an IO API that will give me consistency? 是否有一个IO API可以提供一致性?

Answer involving Thread.Sleep will invoke Zalgo. 涉及Thread.Sleep答案将调用Zalgo。 I want a real solution please. 我想要一个真正的解决方案。

As mentioned by others it appears that the .net Framework doesn't appear to run this synchronously. 正如其他人所提到的那样,.net Framework似乎并没有同步运行它。 Testing it in PowerShell shows that the .Net calls do no wait so something like this would create a similar outcome: 在PowerShell中测试它表明.Net调用不会等待,所以这样的事情会产生类似的结果:

Remove-Item -Recurse -Force "C:\tempfolder"
New-Item -ItemType Directory "C:\tempfolder"

Using a file watcher (also mentioned previously) will ensure that the directory deletion is completed before the creation is done: 使用文件观察器(前面也提到过)将确保在创建完成之前完成目录删除:

var path = @"C:\tempfolder";
var watcher = new FileSystemWatcher(path);
watcher.Deleted += (sender, args) => Directory.CreateDirectory(args.FullPath);
Directory.Delete(path, true);

No real surprises there but at least it's a working solution that doesn't involve calling the C++ API's from Managed Code. 没有真正意外,但至少它是一个有效的解决方案,不涉及从托管代码调用C ++ API。

Here is a link of someone with the same issue, there solution of first renaming /moving the directory may work for you. 这是一个有相同问题的人的链接 ,首先重命名/移动目录的解决方案可能适合您。

Otherwise you could use the a FileWatcher to react to the directory deletion, but that feels like overkill. 否则你可以使用FileWatcher来对目录删除作出反应,但这感觉就像是矫枉过正。

After doing some testing in C++ it seems that the native Windows functions for removing files/directories does block. 在C ++中进行一些测试后,似乎用于删除文件/目录的本机Windows功能会阻塞。 It seems the problem is on the .NET side when it comes to the deletion function not being blocked, as Directory.CreateDirectory() appears to be called before Directory.Delete() is finished. 当删除函数未被阻止时,似乎问题出现在.NET端,因为Directory.CreateDirectory()似乎在Directory.Delete()完成之前被调用。

This is what I tried in a Win32 Console Application: 这是我在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');

After pressing ENTER the first time there is a small delay before the result is shown, and when looking at the folder afterwards it's empty with a new creation and last modified date - meaning that it has been deleted and recreated in the correct order. 在第一次按下ENTER之后,在显示结果之前会有一个小的延迟,然后在查看文件夹后,它会为空,带有新的创建和最后修改日期 - 这意味着它已被删除并以正确的顺序重新创建。

So in order to achieve what you want I guess you could try to create your own method which invokes SHFileOperation() instead, as the problem seems to be that the Directory.Delete() method performs the iteration itself in .NET code (see the Reference Source ). 所以为了实现你想要的,我想你可以尝试创建自己的方法来调用SHFileOperation() ,因为问题似乎是Directory.Delete()方法在.NET代码中执行迭代(参见参考资料来源 )。


--- EDIT --- ---编辑---

After testing in C#, this seems to work! 在C#中测试后,这似乎有效! The only problem is that the very first time (since the application started) you call the P/Invoked SHFileOperation() function it will return a value of 2, which is equivalent to ERROR_FILE_NOT_FOUND . 唯一的问题是,第一次(自应用程序启动以来)调用P / Invoked SHFileOperation()函数,它将返回值2,这相当于ERROR_FILE_NOT_FOUND But if you execute it again it will return 0 (success). 但如果再次执行它将返回0(成功)。

NativeMethods.cs: NativeMethods.cs:

Imports required: 需要进口:

using System;
using System.Runtime.InteropServices;

Rest of the code: 其余代码:

[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,
    /// <summary>
    /// Don't create progress/report
    /// </summary>
    FOF_SILENT = 0x4,
    FOF_RENAMEONCOLLISION = 0x8,
    /// <summary>
    /// Don't prompt the user.
    /// </summary>
    FOF_NOCONFIRMATION = 0x10,
    /// <summary>
    /// Fill in SHFILEOPSTRUCT.hNameMappings.
    /// Must be freed using SHFreeNameMappings
    /// </summary>
    FOF_WANTMAPPINGHANDLE = 0x20,
    FOF_ALLOWUNDO = 0x40,
    /// <summary>
    /// On *.*, do only files
    /// </summary>
    FOF_FILESONLY = 0x80,
    /// <summary>
    /// Don't show names of files
    /// </summary>
    FOF_SIMPLEPROGRESS = 0x100,
    /// <summary>
    /// Don't confirm making any needed dirs
    /// </summary>
    FOF_NOCONFIRMMKDIR = 0x200,
    /// <summary>
    /// Don't put up error UI
    /// </summary>
    FOF_NOERRORUI = 0x400,
    /// <summary>
    /// Dont copy NT file Security Attributes
    /// </summary>
    FOF_NOCOPYSECURITYATTRIBS = 0x800,
    /// <summary>
    /// Don't recurse into directories.
    /// </summary>
    FOF_NORECURSION = 0x1000,
    /// <summary>
    /// Don't operate on connected elements.
    /// </summary>
    FOF_NO_CONNECTED_ELEMENTS = 0x2000,
    /// <summary>
    /// During delete operation, 
    /// warn if nuking instead of recycling (partially overrides FOF_NOCONFIRMATION)
    /// </summary>
    FOF_WANTNUKEWARNING = 0x4000,
    /// <summary>
    /// Treat reparse points as objects, not containers
    /// </summary>
    FOF_NORECURSEREPARSE = 0x8000
}

Some place else: 其他地方:

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);

Hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM