如何使用C#同步并一致地删除NTFS上的文件夹 [英] How to synchronously and consistently delete a folder on NTFS with C#

查看:146
本文介绍了如何使用C#同步并一致地删除NTFS上的文件夹的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个:

Directory.Delete(dir, true);

不同步。

关于立即行,你仍然可以操纵/读取目录。

On the immediately proceeding line you can still manipulate/read the directory.

例如,这个:

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

导致文件夹不存在。删除运行async, CreateDirectory 没有创建,因为它已经存在,然后删除实际触发并删除目录。

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.

是否有一个IO API可以保证一致性?

Is there an IO API that will give me consistency?

涉及 Thread.Sleep 的答案将调用Zalgo。我想要一个真正的解决方案。

Answer involving Thread.Sleep will invoke Zalgo. I want a real solution please.

推荐答案

在C ++中进行一些测试之后,似乎本机Windows函数用于删除文件/目录阻止。在删除函数没有被阻止时,似乎问题出在.NET端,因为 Directory.CreateDirectory()似乎在 Directory.Delete()已完成。

After doing some testing in C++ it seems that the native Windows functions for removing files/directories does block. 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.

这是我在Win32控制台应用程序中尝试的:

This is what I tried in a Win32 Console Application:

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键之后,以及查看结果时该文件夹之后它是空的,带有新的创建和最后修改日期 - 意味着它已被删除并以正确的顺序重新创建。

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.

所以为了达到你想要的目的我想你可以尝试创建自己的方法来调用 SHFileOperation(),因为问题似乎是 Directory.Delete()方法执行在.NET代码迭代本身(参见参考资料来源

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

在C#中测试后,这似乎有效!唯一的问题是,第一次(自应用程序启动)你调用P / Invoked SHFileOperation()函数它将返回值2,这相当于 ERROR_FILE_NOT_FOUND 。但如果再次执行它将返回0(成功)。

After testing in C#, this seems to work! 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. But if you execute it again it will return 0 (success).

NativeMethods.cs:

所需进口商品:

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,
    /// <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
}

其他地方:

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

希望这有帮助!

这篇关于如何使用C#同步并一致地删除NTFS上的文件夹的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆