web-dev-qa-db-ja.com

Directory.Delete(path、true)ではディレクトリを削除できません

私は.NET 3.5を使用していて、再帰的にディレクトリを削除しようとしています:

Directory.Delete(myPath, true);

私の理解するところでは、ファイルが使用されているかパーミッションの問題がある場合はこれがスローされますが、そうでなければディレクトリとその内容のすべてが削除されるはずです。

しかし、私は時々これを得ます:

System.IO.IOException: The directory is not empty.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
    at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
    ...

このメソッドが時々スローされることに驚いてはいませんが、再帰が真のときにこの特定のメッセージが表示されるのに驚いています。 (I 知っているディレクトリは空ではありません。)

AccessViolationExceptionの代わりにこれが表示される理由はありますか?

359
Jason Anderson

編集者注:この回答には役に立つ情報がいくつか含まれていますが、実際のところDirectory.Deleteの働きについては正しくありません。この回答に対するコメント、およびこの質問に対する他の回答をお読みください。


私は前にこの問題に遭遇しました。

問題の根本は、この関数はディレクトリ構造内のファイルを削除しないことです。そのため、ディレクトリ構造を削除する前に、ディレクトリ構造内のすべてのファイルを削除してからすべてのディレクトリを削除する関数を作成する必要があります。私はこれが2番目のパラメータに反することを知っていますが、それははるかに安全なアプローチです。さらに、削除する直前に、ファイルから読み取り専用アクセス属性を削除することをお勧めします。それ以外の場合は例外が発生します。

このコードをプロジェクトに追加するだけです。

public static void DeleteDirectory(string target_dir)
{
    string[] files = Directory.GetFiles(target_dir);
    string[] dirs = Directory.GetDirectories(target_dir);

    foreach (string file in files)
    {
        File.SetAttributes(file, FileAttributes.Normal);
        File.Delete(file);
    }

    foreach (string dir in dirs)
    {
        DeleteDirectory(dir);
    }

    Directory.Delete(target_dir, false);
}

また、私は個人的にC:\WINDOWS (%WinDir%)またはC:\で誰かにこの関数を呼び出させたいので、私は個人的に削除を許可されるマシンの領域に制限を加えます。

220
Jeremy Edwards

ディレクトリaを再帰的に削除しようとしており、ディレクトリa\bがエクスプローラで開いている場合、bは削除されますが、移動して見てもaのエラー 'directory is not empty'が発生します。任意のアプリケーションの現在のディレクトリ(エクスプローラを含む) ディレクトリへのハンドルを保持Directory.Delete(true)を呼び出すと、ボトムアップから削除します:b、次にabがエクスプローラで開いている場合、エクスプローラはbの削除を検出し、ディレクトリを上方向にcd ..に変更し、開いているハンドルをクリーンアップします。ファイルシステムは非同期的に動作するため、Directory.Delete操作はExplorerとの競合のために失敗します。

不完全な解決策

私はもともと次の解決策を投稿しました。エクスプローラがディレクトリハンドルを解放できるように現在のスレッドを中断するという考えです。

// incomplete!
try
{
    Directory.Delete(path, true);
}
catch (IOException)
{
    Thread.Sleep(0);
    Directory.Delete(path, true);
}

しかし、これはオープンディレクトリがあなたが削除しようとしているディレクトリのimmediate子である場合にのみ機能します。 a\b\c\dがExplorerで開いていて、これをaで使用している場合、dおよびcを削除した後でこの手法は失敗します。

やや良い解決策

このメソッドは、下位レベルのディレクトリの1つがエクスプローラで開いている場合でも、深いディレクトリ構造の削除を処理します。

/// <summary>
/// Depth-first recursive delete, with handling for descendant 
/// directories open in Windows Explorer.
/// </summary>
public static void DeleteDirectory(string path)
{
    foreach (string directory in Directory.GetDirectories(path))
    {
        DeleteDirectory(directory);
    }

    try
    {
        Directory.Delete(path, true);
    }
    catch (IOException) 
    {
        Directory.Delete(path, true);
    }
    catch (UnauthorizedAccessException)
    {
        Directory.Delete(path, true);
    }
}

私たち自身の再帰的な追加作業にもかかわらず、我々はstillその過程で起こり得るUnauthorizedAccessExceptionを処理しなければなりません。最初の削除が2番目の成功への道を切り開いているのか、それとも単にファイルシステムが追いつくことを可能にする例外をスロー/キャッチすることによってもたらされたタイミング遅延であるのかは明らかではありません。

tryブロックの先頭にThread.Sleep(0)を追加することで、通常の条件下でスローされてキャッチされる例外の数を減らすことができます。さらに、システム負荷が高い場合は、両方のDirectory.Deleteの試行を飛ばして失敗する可能性があります。この解決策を、より堅牢な再帰的削除の出発点と考えてください。

一般的な答え

この解決策は、Windowsエクスプローラとのやりとりの特殊性のみを扱います。強固な削除操作が必要な場合、留意すべきことの1つは、何でも(ウィルススキャナなど)、いつでも削除しようとしているものに対してオープンなハンドルを持つことができるということです。だから、後でもう一度やり直す必要があります。どれくらい後に、そして何回試したかは、オブジェクトが削除されることがどれほど重要かによって異なります。として MSDNは示しています

堅牢なファイル反復コードでは、ファイルシステムの複雑さを考慮する必要があります。

NTFSリファレンスドキュメントへのリンクのみが付属しているこの無実の声明は、あなたの髪の毛を立ち上がらせるべきです。

編集:たくさん。この答えには最初の、不完全な解決策しかありませんでした。)

170
ryascl

先に進む前に、自分の管理下にある以下の理由を確認してください。

  • フォルダはプロセスの現在のディレクトリとして設定されていますか?もしそうなら、最初に何か他のものに変更してください。
  • そのフォルダからファイルを開いた(またはDLLをロードした)ことがありますか。 (そしてそれを閉じる/アンロードするのを忘れた)

それ以外の場合は、自分の管理外の次の正当な理由を確認してください。

  • そのフォルダに読み取り専用としてマークされたファイルがあります。
  • これらのファイルのいくつかに削除権限がありません。
  • ファイルまたはサブフォルダがエクスプローラまたは他のアプリで開かれています。

上記のいずれかが問題である場合、あなたはそれがあなたの削除コードを改善しようとする前になぜ起こるのか理解するべきです。 必要なあなたのアプリは読み取り専用またはアクセスできないファイルを削除していますか?誰がそのようにそれらをマークしましたか、そして、なぜですか?

あなたが上記の理由を排除したら、それでも偽の失敗の可能性があります。削除されているファイルまたはフォルダのいずれかへのハンドルを誰かが持っていると、削除は失敗します。その理由は、誰かがフォルダを列挙したりそのファイルを読み取っている可能性があるためです。

  • 検索インデクサー
  • ウイルス対策
  • バックアップソフトウェア

誤った失敗に対処するための一般的なアプローチは、試行の間に一時停止して複数回試行することです。あなたは明らかに永遠に挑戦し続けることを望まないので、あなたは一定回数の試みの後にあきらめて、例外を投げるかエラーを無視するべきです。このような:

private static void DeleteRecursivelyWithMagicDust(string destinationDir) {
    const int magicDust = 10;
    for (var gnomes = 1; gnomes <= magicDust; gnomes++) {
        try {
            Directory.Delete(destinationDir, true);
        } catch (DirectoryNotFoundException) {
            return;  // good!
        } catch (IOException) { // System.IO.IOException: The directory is not empty
            System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes);

            // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic
            Thread.Sleep(50);
            continue;
        }
        return;
    }
    // depending on your use case, consider throwing an exception here
}

私の意見では、偽の失敗は常に可能であるため、このようなヘルパーはすべての削除に使用されるべきです。ただし、盲目的にコピーするのではなく、このコードを使用例に合わせて変更する必要があります。

私は自分のアプリによって生成された、%LocalAppData%の下にある内部データフォルダーに対して誤った失敗をしたので、私の分析は次のようになります。

  1. フォルダは私のアプリケーションによってのみ制御されており、ユーザはそのフォルダ内で読み取り専用またはアクセスできないとマークを付ける正当な理由がないので、そのような場合を処理しようとはしません。

  2. ユーザーが作成した貴重なものはそこにはありません。そのため、誤って何かを強制的に削除する危険性はありません。

  3. 内部データフォルダなので、エクスプローラで開くことを期待していません。少なくとも、ケースを特別に処理する必要性を感じません(つまり、サポートを介してケースを処理します)。

  4. すべての試みが失敗した場合、私はエラーを無視することにしました。最悪の場合、アプリはいくつかの新しいリソースをアンパックするのに失敗し、クラッシュしてサポートに連絡するようにユーザーに促します。あるいは、アプリがクラッシュしない場合は、古いデータが残りますが、これも私にとっては許容できる範囲です。

  5. 再試行回数を500ミリ秒(50×10)に制限します。これは実際に機能する任意のしきい値です。応答が停止したと考えて、ユーザーがアプリを強制終了しないように、しきい値を十分に短くすることを望みました。一方、犯罪者が自分のフォルダの処理を完了するのに十分な時間は0.5秒です。他のSO回答から判断するとSleep(0)でさえ許容できると判断する場合があるため、1回以上の再試行を経験するユーザーはごくわずかです。

  6. 私は50msごとに再試行します。これは別の任意の数値です。ファイルを削除しようとしたときにファイルが処理(インデックス作成、チェック)されている場合、50ミリ秒が私の場合は処理が完了すると予想するのに適した時間です。また、50msは目立った減速を引き起こさないほど十分に小さいです。繰り返しますが、Sleep(0)は多くの場合十分であるように思われるので、あまり遅らせたくはありません。

  7. コードは、任意のIO例外を再試行します。通常、%LocalAppData%にアクセスする例外は想定されていません。そのため、単純さを選択し、正当な例外が発生した場合には500ミリ秒の遅延のリスクを受け入れました。また、再試行したい正確な例外を検出する方法も見つけたくありませんでした。

40

言及すべき重要なことの1つ(コメントとして追加しましたが許可されていません)は、オーバーロードの動作が.NET 3.5から.NET 4.0に変更されたことです。

Directory.Delete(myPath, true);

.NET 4.0以降、フォルダ内のファイルは削除されますが、3.5では削除されません。これはMSDNのドキュメントでも見られます。

。NET 4.

指定されたディレクトリを削除し、指定されている場合は、ディレクトリ内のサブディレクトリとファイルを削除します。

。NET 3.5

空のディレクトリを削除し、指定されている場合は、そのディレクトリ内のサブディレクトリとファイルを削除します。

15
jettatore

私はDelphiでも同じ問題を抱えていました。そして最終結果は、私自身のアプリケーションが削除したいディレクトリをロックしていたことです。どういうわけか私がそれを書いていたときディレクトリがロックされました(いくつかの一時ファイル)。

キャッチ22は、削除する前に、単純なディレクトリを変更を親にしました。

13
Drejc

読み取り専用ファイルを含むディレクトリを削除することができ、それぞれの読み取り専用属性を変更する必要なしに、この単純で非再帰的な方法を誰も考えていなかったことに驚きました。

Process.Start("cmd.exe", "/c " + @"rmdir /s/q C:\Test\TestDirectoryContainingReadOnlyFiles"); 

(少しの間、cmdウィンドウを一瞬起動しないように変更してください。これはインターネット上で利用可能です)

11
Piyush Soni

現代の非同期回答

ディスクからファイルを取得するのにかかる時間がファイルをロックしていたものは何でも解放するので、受け入れられた答えは単なる間違ったものです。実際のところ、これはファイルが他のプロセス/ストリーム/アクションによってロックされるために起こります。他の答えはしばらくしてからディレクトリの削除を再試行するためにThread.Sleep(Yuck)を使用します。この質問はもっと現代的な答えで再考する必要があります。

public static async Task<bool> TryDeleteDirectory(
   string directoryPath,
   int maxRetries = 10,
   int millisecondsDelay = 30)
{
    if (directoryPath == null)
        throw new ArgumentNullException(directoryPath);
    if (maxRetries < 1)
        throw new ArgumentOutOfRangeException(nameof(maxRetries));
    if (millisecondsDelay < 1)
        throw new ArgumentOutOfRangeException(nameof(millisecondsDelay));

    for (int i = 0; i < maxRetries; ++i)
    {
        try
        {
            if (Directory.Exists(directoryPath))
            {
                Directory.Delete(directoryPath, true);
            }

            return true;
        }
        catch (IOException)
        {
            await Task.Delay(millisecondsDelay);
        }
        catch (UnauthorizedAccessException)
        {
            await Task.Delay(millisecondsDelay);
        }
    }

    return false;
}

単体テスト

これらのテストは、ロックされたファイルがDirectory.Deleteを失敗させる方法と、上記のTryDeleteDirectoryメソッドがどのように問題を解決するかの例を示します。

[Fact]
public async Task TryDeleteDirectory_FileLocked_DirectoryNotDeletedReturnsFalse()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            var result = await TryDeleteDirectory(directoryPath, 3, 30);
            Assert.False(result);
            Assert.True(Directory.Exists(directoryPath));
        }
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}

[Fact]
public async Task TryDeleteDirectory_FileLockedThenReleased_DirectoryDeletedReturnsTrue()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        Task<bool> task;
        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            task = TryDeleteDirectory(directoryPath, 3, 30);
            await Task.Delay(30);
            Assert.True(Directory.Exists(directoryPath));
        }

        var result = await task;
        Assert.True(result);
        Assert.False(Directory.Exists(directoryPath));
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}
11

次のコマンドを実行してエラーを再現できます。

Directory.CreateDirectory(@"C:\Temp\a\b\c\");
Process.Start(@"C:\Temp\a\b\c\");
Thread.Sleep(1000);
Directory.Delete(@"C:\Temp\a\b\c");
Directory.Delete(@"C:\Temp\a\b");
Directory.Delete(@"C:\Temp\a");

ディレクトリ 'b'を削除しようとすると、「ディレクトリが空ではありません」というIOExceptionがスローされます。ディレクトリ 'c'を削除したばかりなので、これはばかげています。

私の理解から、説明はディレクトリ 'c'が削除されたものとしてスタンプされているということです。しかし、削除はまだシステム内でコミットされていません。システムはジョブが完了したと応答しましたが、実際にはまだ処理中です。システムはおそらくファイルエクスプローラが削除をコミットするために親ディレクトリにフォーカスを持っているのを待つ。

Delete関数のソースコード( http://referencesource.Microsoft.com/#mscorlib/system/io/directory.cs )を見ると、ネイティブのWin32Native.RemoveDirectoryが使用されていることがわかります。関数。この待機禁止の振る舞いは、ここに記載されています。

RemoveDirectory関数は、クローズ時にディレクトリを削除対象としてマークします。したがって、ディレクトリへの最後のハンドルが閉じられるまで、ディレクトリは削除されていません。

http://msdn.Microsoft.com/ja-jp/library/windows/desktop/aa365488(v = vs85).aspx

スリープ状態にして再試行することが解決策です。 ryasclの解決策を参照してください。

10

私はエクスプローラーシェルでそうすることができるにもかかわらず(C:\ Documents and Settingsに)User Profileディレクトリを削除するそれらの奇妙な許可問題を抱えていました。

File.SetAttributes(target_dir, FileAttributes.Normal);
Directory.Delete(target_dir, false);

ディレクトリに対する "ファイル"操作が何をするのか私には意味がありませんが、それが機能することはわかっていますし、それで十分です。

7
Nick

この答えはに基づいています。 https://stackoverflow.com/a/1703799/184528 。私のコードとの違いは、必要に応じて多くのdeleteサブディレクトリとファイルを再帰するだけであることです(Directory Explorerがディレクトリを見ているために起こることがあります)。

    public static void DeleteDirectory(string dir, bool secondAttempt = false)
    {
        // If this is a second try, we are going to manually 
        // delete the files and sub-directories. 
        if (secondAttempt)
        {
            // Interrupt the current thread to allow Explorer time to release a directory handle
            Thread.Sleep(0);

            // Delete any files in the directory 
            foreach (var f in Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly))
                File.Delete(f);

            // Try manually recursing and deleting sub-directories 
            foreach (var d in Directory.GetDirectories(dir))
                DeleteDirectory(d);

            // Now we try to delete the current directory
            Directory.Delete(dir, false);
            return;
        }

        try
        {
            // First attempt: use the standard MSDN approach.
            // This will throw an exception a directory is open in Explorer
            Directory.Delete(dir, true);
        }
        catch (IOException)
        {
            // Try again to delete the directory manually recursing. 
            DeleteDirectory(dir, true);
        }
        catch (UnauthorizedAccessException)
        {
            // Try again to delete the directory manually recursing. 
            DeleteDirectory(dir, true);
        } 
    }
3
cdiggins

ファイルを削除しない再帰的なディレクトリ削除は確かに予想外です。それに対する私の修正:

public class IOUtils
{
    public static void DeleteDirectory(string directory)
    {
        Directory.GetFiles(directory, "*", SearchOption.AllDirectories).ForEach(File.Delete);
        Directory.Delete(directory, true);
    }
}

これが助けになるケースを経験しましたが、一般的に、Directory.Deleteは再帰的な削除の際にディレクトリ内のファイルを削除します、 msdnに文書化されています

時々私はWindowsエクスプローラのユーザとしてもこの不規則な振る舞いに遭遇します:時々私はフォルダを削除することができません(それは無意味なメッセージは "アクセス拒否"だと思います)アイテムも。だから私は上のコードはOSの異常を扱っていると思う - 基本クラスライブラリの問題ではない。

2
citykid

私はこの問題とディレクトリの削除に関する他の例外を解決するために数時間を費やしました。これが私の解決策です

 public static void DeleteDirectory(string target_dir)
    {
        DeleteDirectoryFiles(target_dir);
        while (Directory.Exists(target_dir))
        {
            lock (_lock)
            {
                DeleteDirectoryDirs(target_dir);
            }
        }
    }

    private static void DeleteDirectoryDirs(string target_dir)
    {
        System.Threading.Thread.Sleep(100);

        if (Directory.Exists(target_dir))
        {

            string[] dirs = Directory.GetDirectories(target_dir);

            if (dirs.Length == 0)
                Directory.Delete(target_dir, false);
            else
                foreach (string dir in dirs)
                    DeleteDirectoryDirs(dir);
        }
    }

    private static void DeleteDirectoryFiles(string target_dir)
    {
        string[] files = Directory.GetFiles(target_dir);
        string[] dirs = Directory.GetDirectories(target_dir);

        foreach (string file in files)
        {
            File.SetAttributes(file, FileAttributes.Normal);
            File.Delete(file);
        }

        foreach (string dir in dirs)
        {
            DeleteDirectoryFiles(dir);
        }
    }

このコードにはわずかな遅延がありますが、これは私のアプリケーションにとっては重要ではありません。ただし、削除したいディレクトリ内に多数のサブディレクトリがある場合は、遅延が問題になる可能性があります。

2
Demid

上記の解決策のどれも私にとってはうまくいきませんでした。私は以下のように@ryasclソリューションの編集版を使用することになりました。

    /// <summary>
    /// Depth-first recursive delete, with handling for descendant 
    /// directories open in Windows Explorer.
    /// </summary>
    public static void DeleteDirectory(string path)
    {
        foreach (string directory in Directory.GetDirectories(path))
        {
            Thread.Sleep(1);
            DeleteDir(directory);
        }
        DeleteDir(path);
    }

    private static void DeleteDir(string dir)
    {
        try
        {
            Thread.Sleep(1);
            Directory.Delete(dir, true);
        }
        catch (IOException)
        {
            DeleteDir(dir);
        }
        catch (UnauthorizedAccessException)
        {
            DeleteDir(dir);
        }
    }
2
cahit beyaz

私は今日この問題を抱えていました。これは、削除しようとしていたディレクトリに対してWindowsエクスプローラを開いているため、再帰呼び出しが失敗し、IOExceptionが発生したためです。ディレクトリに対して開いているハンドルがないことを確認してください。

また、MSDNは、あなたがあなた自身の妄想を書く必要がないことを明らかにしています。 http://msdn.Microsoft.com/en-us/library/fxeahc5f.aspx

1
GrokSrc

Windowsエクスプローラでパスまたはサブフォルダを選択するだけで、Directory.Delete(path、true)の1回の実行がブロックされ、上記のようにIOExceptionがスローされ、Windowsエクスプローラを親フォルダから起動して次のように処理される代わりに停止します。期待されています。

1
David Alpert

この問題は、パス長が260シンボルを超えるディレクトリ(または任意のサブディレクトリ)にファイルがある場合にWindowsで発生する可能性があります。

そのような場合は、\\\\?\C:\mydirの代わりにC:\mydirを削除する必要があります。 260シンボル制限についてあなたが読むことができる ここ

1
HostageBrain

TFS2012のビルドサーバー上のWindows Workflow Foundationでも同じ問題がありました。内部的に、ワークフローは再帰フラグをtrueに設定してDirectory.Delete()を呼び出しました。それは私達の場合ネットワークに関連しているようです。

ネットワーク共有上のバイナリドロップフォルダを削除してから最新のバイナリを再作成しました。他のすべてのビルドは失敗します。ビルドが失敗した後にドロップフォルダを開くと、フォルダは空でした。これは、Directory.Delete()呼び出しのあらゆる側面が実際のディレクトリの削除を除いて成功したことを示します。

この問題は、ネットワークファイル通信の非同期性によって引き起こされているようです。ビルドサーバーはファイルサーバーにすべてのファイルを削除するように指示し、ファイルサーバーはそれが完全に終了していなくてもそれがあると報告しました。その後、ビルドサーバーはディレクトリの削除を要求し、ファイルサーバーはファイルの削除が完全に終了していないため、要求を拒否しました。

私たちの場合、2つの解決策があります。

  • 各ステップ間で遅延と検証を行いながら、独自のコードで再帰的削除を構築する
  • IOExceptionの後で最大X回再試行し、再試行の前に遅延を与えます。

後者の方法は早くて汚いですが、うまくいくようです。

1
Shaun

別のスレッドまたはプロセスがファイルをディレクトリに追加しているという競合状態が発生する可能性はありますか。

シーケンスは次のようになります。

削除者プロセスA:

  1. ディレクトリを空にする
  2. (今は空の)ディレクトリを削除します。

他の誰かが1と2の間にファイルを追加した場合、おそらく2はリストされている例外をスローしますか?

1
Douglas Leeder

ディレクトリまたはその中のファイルはロックされているため削除できません。それをロックしている犯人を見つけて、あなたがそれを排除できるかどうかを確かめてください。

1
Vilx-

これは、 FileChangesNotifications。 が原因です。

ASP.NET 2.0以降で発生します。アプリ内のフォルダを削除すると、再起動されますASP.NET Health Monitoring を使用すると、自分で確認できます。

このコードをweb.config/configuration/system.webに追加するだけです。

<healthMonitoring enabled="true">
  <rules>
    <add name="MyAppLogEvents" eventName="Application Lifetime Events" provider="EventLogProvider" profile="Critical"/>
  </rules>
</healthMonitoring>


その後、Windows Log -> Applicationをチェックしてください。何が起こっている:

フォルダを削除するとき、サブフォルダがある場合は、Delete(path, true)が最初にサブフォルダを削除します。 FileChangesMonitorが削除について知っていて、アプリをシャットダウンすれば十分です。その間、あなたのメインディレクトリはまだ削除されていません。これはLogからのイベントです:


enter image description here


Delete()はその仕事を終えていませんでした、そして、アプリはシャットダウンしているので、それは例外を上げます:

enter image description here

あなたがあなたが削除しているフォルダの中にサブフォルダを持っていないとき、Delete()は単にすべてのファイルとそのフォルダを削除します、アプリも再起動されています、しかし、アプリの再起動は何も中断しないため、あなたは例外を受け取ることはありません。しかし、それでも、すべてのインプロセスセッションが失われ、再起動時にアプリがリクエストに応答しなくなります。

今何ですか?

この動作を無効にするためのいくつかの回避策と調整があります。 Directory JunctionレジストリでFCNをオフにする公開されたメソッドがないため)Reflectionを使用したFileChangesMonitorの停止 しかし、FCNが理由でそこにあるので、それらはすべて正しいとは思えません。それはあなたのアプリの構造の構造ですが、データの構造ではありません。簡単な答えは次のとおりです。削除するフォルダをアプリの外に置きます。 FileChangesMonitorは通知を受け取らず、アプリは毎回再起動されません。例外はありません。 Webからそれらを見えるようにするには、2つの方法があります。

  1. 着信呼び出しを処理し、アプリの外側のフォルダー(wwwrootの外側)から読み取ることによってファイルを処理するコントローラーを作成します。

  2. プロジェクトが大きく、パフォーマンスが最も重要な場合は、静的コンテンツを配信するための小型で高速なWebサーバーを別に設定してください。したがって、あなたは彼の特定の仕事をIISに任せるでしょう。同じマシン(Windowsの場合はmongoose)または別のマシン(Linuxの場合はnginx)上に存在する可能性があります。良いニュースは、Linux上で静的コンテンツサーバーをセットアップするために追加のMicrosoftライセンスを支払う必要がないことです。

お役に立てれば。

1
Roman

上で述べたように、 "受け入れられた"解決策は再解析ポイントで失敗します - それでも人々はそれをマークアップします(???)。機能を適切に複製するためのもっと短い解決策があります。

public static void rmdir(string target, bool recursive)
{
    string tfilename = Path.GetDirectoryName(target) +
        (target.Contains(Path.DirectorySeparatorChar.ToString()) ? Path.DirectorySeparatorChar.ToString() : string.Empty) +
        Path.GetRandomFileName();
    Directory.Move(target, tfilename);
    Directory.Delete(tfilename, recursive);
}

私は知っていますが、後で述べるパーミッションのケースを扱っていませんが、すべての意図と目的のためにFAR BETTERは 期待される機能 /元の/ Stock Directory.Delete()を提供します。あまりにも

古いファイルディレクトリは邪魔にならないので処理を続行することができます...e(ファイルシステムがまだ追いついていないからといって消えていなくても)(またはMSが壊れた機能を提供した理由はなんでも)

利点として、あなたのターゲットディレクトリが大きい/深いことを知っていて、待ちたくない(あるいは例外を気にしたくない)なら、最後の行を次のように置き換えることができます。

    ThreadPool.QueueUserWorkItem((o) => { Directory.Delete(tfilename, recursive); });

あなたはまだ仕事を続けても安全です。

1
Rob

私はあなたが私が同じ問題を抱えていたことに気づいていないことを気づいていないいくつかのストリームによって開かれたファイルがあると思います。

0
Ahmad Moussa

このエラーは、ファイルまたはディレクトリが使用中と見なされると発生します。それは誤解を招くエラーです。ツリー内の任意のディレクトリ、またはそのツリー内のファイルを使用しているプログラムに対して開いているエクスプローラウィンドウまたはコマンドラインウィンドウがあるかどうかを確認します。

0
allen1

ネットワークファイルの場合、Directory.DeleteHelper(recursive:= true)を実行すると、ファイル削除の遅延によりIOExceptionが発生することがあります。

0
crowdy

あなたのアプリケーション(あるいは他のアプリケーション)のカレントディレクトリがあなたが削除しようとしているものであれば、それはアクセス違反エラーにはなりませんが、ディレクトリは空ではありません。現在のディレクトリを変更して、自分のアプリケーションではないことを確認してください。また、ディレクトリが他のプログラム(Word、Excel、Total Commanderなど)で開かれていないことを確認してください。ほとんどのプログラムは最後に開かれたファイルのディレクトリにcdするでしょう、そしてそれはそれを引き起こすでしょう。

0
configurator

メソッドが非同期で次のようにコーディングされている場合、私は上記の問題の1つの可能な例を解決しました。

// delete any existing update content folder for this update
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
       await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);

これとともに:

bool exists = false;                
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
    exists = true;

// delete any existing update content folder for this update
if (exists)
    await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);

結論?マイクロソフトが話すことができなかった存在をチェックするのに使用されるハンドルを取り除くことのいくつかの非同期の局面があります。 if文の内部の非同期メソッドに、if文がusing文のように機能しているかのようです。

0
Pat Pattillo

私はこの千年ごとのテクニックを使って解決しました(あなたはThread.Sleepをキャッチの中に置いておくことができます)

bool deleted = false;
        do
        {
            try
            {
                Directory.Delete(rutaFinal, true);                    
                deleted = true;
            }
            catch (Exception e)
            {
                string mensaje = e.Message;
                if( mensaje == "The directory is not empty.")
                Thread.Sleep(50);
            }
        } while (deleted == false);
0
Mauricio Rdz