IOException:別のプロセスが使用しているため、プロセスはファイル 'file path'にアクセスできません


172

私はいくつかのコードを持っており、それが実行されたとき、それはスローIOExceptionと言って、

別のプロセスによって使用されているため、プロセスはファイル 'filename'にアクセスできません

これはどういう意味ですか。どうすればよいですか。


1
ファイルを使用している他のプロセスを見つける方法については、この質問を参照してください。
ストーマ

回答:


274

原因は何ですか?

エラーメッセージは非常に明確です。ファイルにアクセスしようとしていますが、別のプロセス(または同じプロセス)がファイルで何かを実行しているためにアクセスできません(共有は許可されていませんでした)。

デバッグ中

特定のシナリオによっては、解決が非常に簡単(または理解しにくい)になる場合があります。いくつか見てみましょう。

あなたのプロセスはそのファイルにアクセスする唯一プロセスです他のプロセスはあなた自身のプロセス
だと確信しています。プログラムの別の部分でそのファイルを開いていることがわかっている場合は、最初に、使用するたびにファイルハンドルを適切に閉じることを確認する必要があります。このバグのあるコードの例を次に示します。

var stream = new FileStream(path, FileAccess.Read);
var reader = new StreamReader(stream);
// Read data from this file, when I'm done I don't need it any more
File.Delete(path); // IOException: file is in use

幸いにもをFileStream実装IDisposableしているため、すべてのコードをusingステートメント内に簡単にラップできます。

using (var stream = File.Open("myfile.txt", FileMode.Open)) {
    // Use stream
}

// Here stream is not accessible and it has been closed (also if
// an exception is thrown and stack unrolled

このパターンは、例外が発生した場合にファイルを開いたままにしないことも保証します(ファイルが使用中の理由である可能性があります:何か問題が発生し、誰もファイルを閉じていません。例については、この投稿を参照してください)。

すべてが正常であるように見え(例外の場合でも、開いているすべてのファイルを必ず閉じると確信しています)、複数の作業スレッドがある場合は、2つのオプションがあります:ファイルアクセスをシリアル化するためにコードを再処理します(常に実行できるとは限らない)希望)または再試行パターンを適用します。これは、I / O操作のかなり一般的なパターンです。何かを実行しようとして、エラーが発生した場合は、しばらく待ってから再試行します(たとえば、Windowsシェルがファイルが使用中であることを通知するのにしばらく時間がかかる理由を自問しましたか)削除できませんか?)C#での実装は非常に簡単です(ディスクI / Oネットワークデータベースアクセスに関するより良い例も参照してください)。

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

for (int i=1; i <= NumberOfRetries; ++i) {
    try {
        // Do stuff with file
        break; // When done we can break loop
    }
    catch (IOException e) when (i <= NumberOfRetries) {
        // You may check error code to filter some exceptions, not every error
        // can be recovered.
        Thread.Sleep(DelayOnRetry);
    }
}

StackOverflowで頻繁に発生する一般的なエラーに注意してください。

var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);

この場合ReadAllText()、ファイルは(File.Open()前の行で)使用されているため失敗します。事前にファイルを開くことは不要であるだけでなく、間違っています。同じことが、すべてに適用されますFile返さない関数ハンドルを:あなたがしている作業ファイルにFile.ReadAllText()File.WriteAllText()File.ReadAllLines()File.WriteAllLines()および(のような他File.AppendAllXyz()の機能は)すべてのオープンと自分でファイルを閉じます。

プロセスがそのファイルにアクセスする唯一のプロセスではないプロセスがそのファイルにアクセスする唯一のプロセスではない
場合、相互作用はより困難になる可能性があります。再試行のパターンは(そして、あなたがチェックするプロセスExplorerなどのユーティリティが必要、ファイルは他の誰で開いてはいけませんが、それがある場合に役立ちます誰がやっている何を)。

回避する方法

該当する場合は、必ず使用使用して、開いているファイルへのステートメントを。前の段落で述べたように、多くの一般的なエラーを回避するのに役立ちます(使用しない例については、この投稿を参照してください)。

可能であれば、特定のファイルへのアクセス権を誰が所有するかを決定し、いくつかのよく知られた方法でアクセスを集中化してください。たとえば、プログラムが読み書きするデータファイルがある場合、すべてのI / Oコードを単一のクラス内にボックス化する必要があります。これにより、デバッグが容易になり(いつでもブレークポイントを設定して、誰が何をしているかを確認できるため)、複数アクセスの同期ポイント(必要な場合)にもなります。

I / O操作は常に失敗する可能性があることを忘れないでください。一般的な例は次のとおりです。

if (File.Exists(path))
    File.Delete(path);

の前後で誰かがファイルを削除すると、誤って安全だと思われる場所にがスローされます。File.Exists()File.Delete()IOException

可能な場合は常に再試行パターンを適用し、を使用している場合はFileSystemWatcher、アクションを延期することを検討してください(通知が届きますが、アプリケーションがそのファイルで排他的に機能している可能性があるためです)。

高度なシナリオ
必ずしも簡単ではないため、他の人とアクセスを共有する必要がある場合があります。たとえば、最初から最後まで読んでいる場合、少なくとも2つのオプションがあります。

1)FileStream適切な同期関数と同じものを共有します(スレッドセーフではないため)。例については、これこの投稿を参照してください。

2)FileShare列挙を使用して、他のプロセス(または独自のプロセスの他の部分)が同じファイルに同時にアクセスできるようにOSに指示します。

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}

この例では、書き込み用にファイルを開き、読み取り用に共有する方法を示しました。読み取りと書き込みのオーバーラップは、未定義または無効なデータになることに注意してください。それは、読むときに扱わ​​なければならない状況です。また、これはstreamスレッドセーフにアクセスしないため、アクセスが何らかの方法で同期されない限り、このオブジェクトを複数のスレッドと共有することはできません(前のリンクを参照)。他の共有オプションが利用可能であり、それらはより複雑なシナリオを開きます。詳細については、MSDNを参照してください。

一般的に、Nプロセスは同じファイルからすべてを同時に読み取ることができますが、書き込む必要があるのは1つだけです。制御されたシナリオでは、同時書き込みを有効にすることもできますが、これはこの回答内のいくつかのテキストパラグラフでは一般化できません。

別のプロセスで使用されているファイルのロック解除することはできますか?常に安全で簡単なわけではありませんが、可能です。


コードのどこに問題があるのか​​わかりませんが、ブロックを使用していますが、削除しようとするとファイルが使用中であるというエラーが表示されます。
Jamshaid Kamran

いいえ、実際にタイマーコントロールがあります。タイマーのほかに、関数をもう一度呼び出すと例外がスローされます。基本的に、ファイルを別のテキストファイルに復号化しています。その後、新しく作成されたファイルを削除すると、削除時に例外がスローされます。
Jamshaid Kamran 2016

3
@جمشیدکامرانなぜコードにある内容でファイルを作成し、それを削除するのですか?そもそも、ファイルを最初に作成するのは奇妙に思えます。あなたはコードを投稿しなかったので、あなたが何をしているのかわかりません。ただし、ファイルを作成するときに、で作成する場合は、ファイルに書き込む前に、ファイルの最後にFile.Create(path)追加する必要があります.Close()。そのような落とし穴がありusing、ファイルを書き込み、その後それらを削除するためのステートメントに加えて。ファイルを作成および削除する方法については、質問にコードを投稿してください。しかし、おそらく上記のことと一致します。
vapcguy

私のコードは使用しDirectory.SetCreationTimeUTC()ていますが、ファイルエクスプローラーが開いているときに失敗し、別のプロセスがディレクトリにアクセスしていると主張しています。この状況にどう対処すればよいですか?
カイルデラニー2017

1
@KyleDelaneyフォルダーが閉じるまで待つ必要があると思います。数秒でないと、すべてがすぐに複雑になります(保留中の操作でバックグラウンドキューを保持しますか?ファイルシステムウォッチャー?ポーリング?)アドホック回答の詳細を含む質問を投稿する
Adriano Repetti '

29

FileShareを使用すると、別のプロセスで開いた場合でもファイルを開く問題が修正されました。

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}

9

画像のアップロード中に問題が発生し、画像を削除できず、解決策が見つかりました。GL HF

//C# .NET
var image = Image.FromFile(filePath);

image.Dispose(); // this removes all resources

//later...

File.Delete(filePath); //now works

2
ファイルを削除する場合は、最初に画像オブジェクトを破棄する必要があります。
shazia

1
いいですね、この解決策を探しました。Image.FromFile関数にも問題がありました。
サイオン

1
@thescion :) np
Hudson、

1
これは私にとって見事に機能し、私の正確な問題を解決しました。Tiffファイルのフォルダーを処理し、byte []ストリームに変換してサービスに送信した後、Tiffファイルのフォルダーを「Processed」アーカイブフォルダーに移動したいと考えていました。Image.FromFileはTiffファイルにロックを設定し、タイムリーに解放しないため、Tiffファイルと格納フォルダーを移動しようとすると、「別のプロセスによって使用されています」というエラーが発生します。所定の位置に。Tiffファイルのバイトを取得した直後に.Releaseを実行すると、このロックされたファイルの問題が完全に修正されました。
Developer63

4

File.Moveをファイル名なしでファイルパスに移動していたため、このエラーが発生しました。宛先に完全パスを指定する必要があります。


また、ファイル名がないだけではありません。不正な(宛先)ファイル名-私の場合は "... \ file"。-これと同じ愚かなエラーを出し、間違った方向に半日あなたを向けます!
Nick Westgate、

3

エラーは、別のプロセスがファイルにアクセスしようとしていることを示します。書き込みしようとしているときに、あなたまたは他の誰かが開いている可能性があります。「読み取り」または「コピー」は通常これを引き起こしませんが、書き込みまたは削除を呼び出すと発生します。

他の回答が述べているように、これを回避するいくつかの基本的なものがあります:

  1. FileStream操作にそれを置くusingとブロックFileShare.ReadWriteアクセスのモード。

    例えば:

    using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
    {
    }

    FileAccess.ReadWrite使用する場合は不可能であることに注意してくださいFileMode.Append

  2. 入力ストリームをFile.SaveAs使用してファイルが使用されているときにこの問題に遭遇しました。私が見つけた場合、実際にファイルシステムに保存する必要はまったくなかったので、それを削除するだけでしたが、おそらく次のusingステートメントでFileStreamを作成することができたでしょう。FileAccess.ReadWrite多くのコードのように、上記。

  3. データを別のファイルとして保存し、不要になった古いファイルを削除して元に戻し、正常に保存したファイルの名前を元のファイルの名前に変更することもできます。使用中のファイルをテストする方法は、

    List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);

    以下の私のコードの行で、Windowsサービスのループで実行することができます。特定のファイルを監視したい場合、定期的に削除したい場合は、ファイルを置き換える必要があります。常に同じファイルがあるとは限らない場合は、テキストファイルまたはデータベーステーブルを更新して、サービスが常にファイル名をチェックし、プロセスのチェックを実行してから、プロセスの強制終了と削除を実行します。次のオプションで。もちろん、プロセスの削除と終了を実行するには、特定のコンピューターに対する管理者権限を持つアカウントのユーザー名とパスワードが必要です。

  4. 保存しようとしているときにファイルが使用されるかどうかわからない場合は、保存する前に、Wordなどのファイルを使用している可能性のあるすべてのプロセスを閉じることができます。

    ローカルの場合、これを行うことができます。

    ProcessHandler.localProcessKill("winword.exe");

    リモートの場合、これを行うことができます。

    ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");

    はのtxtUserName形式ですDOMAIN\user

  5. ファイルをロックしているプロセス名がわからないとしましょう。次に、これを行うことができます:

    List<Process> lstProcs = new List<Process>();
    lstProcs = ProcessHandler.WhoIsLocking(file);
    
    foreach (Process p in lstProcs)
    {
        if (p.MachineName == ".")
            ProcessHandler.localProcessKill(p.ProcessName);
        else
            ProcessHandler.remoteProcessKill(p.MachineName, txtUserName, txtPassword, p.ProcessName);
    }

    file:UNCパスでなければならない \\computer\share\yourdoc.docxためにProcess、それは上で何コンピュータを把握し、p.MachineName有効であると。

    以下は、これらの関数が使用するクラスであり、への参照を追加する必要がありSystem.Managementます。コードは元々Eric Jによって書かれました

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Management;
    
    namespace MyProject
    {
        public static class ProcessHandler
        {
            [StructLayout(LayoutKind.Sequential)]
            struct RM_UNIQUE_PROCESS
            {
                public int dwProcessId;
                public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
            }
    
            const int RmRebootReasonNone = 0;
            const int CCH_RM_MAX_APP_NAME = 255;
            const int CCH_RM_MAX_SVC_NAME = 63;
    
            enum RM_APP_TYPE
            {
                RmUnknownApp = 0,
                RmMainWindow = 1,
                RmOtherWindow = 2,
                RmService = 3,
                RmExplorer = 4,
                RmConsole = 5,
                RmCritical = 1000
            }
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            struct RM_PROCESS_INFO
            {
                public RM_UNIQUE_PROCESS Process;
    
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
                public string strAppName;
    
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
                public string strServiceShortName;
    
                public RM_APP_TYPE ApplicationType;
                public uint AppStatus;
                public uint TSSessionId;
                [MarshalAs(UnmanagedType.Bool)]
                public bool bRestartable;
            }
    
            [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
            static extern int RmRegisterResources(uint pSessionHandle,
                                                UInt32 nFiles,
                                                string[] rgsFilenames,
                                                UInt32 nApplications,
                                                [In] RM_UNIQUE_PROCESS[] rgApplications,
                                                UInt32 nServices,
                                                string[] rgsServiceNames);
    
            [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
            static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
    
            [DllImport("rstrtmgr.dll")]
            static extern int RmEndSession(uint pSessionHandle);
    
            [DllImport("rstrtmgr.dll")]
            static extern int RmGetList(uint dwSessionHandle,
                                        out uint pnProcInfoNeeded,
                                        ref uint pnProcInfo,
                                        [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
                                        ref uint lpdwRebootReasons);
    
            /// <summary>
            /// Find out what process(es) have a lock on the specified file.
            /// </summary>
            /// <param name="path">Path of the file.</param>
            /// <returns>Processes locking the file</returns>
            /// <remarks>See also:
            /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
            /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
            /// 
            /// </remarks>
            static public List<Process> WhoIsLocking(string path)
            {
                uint handle;
                string key = Guid.NewGuid().ToString();
                List<Process> processes = new List<Process>();
    
                int res = RmStartSession(out handle, 0, key);
                if (res != 0) throw new Exception("Could not begin restart session.  Unable to determine file locker.");
    
                try
                {
                    const int ERROR_MORE_DATA = 234;
                    uint pnProcInfoNeeded = 0,
                        pnProcInfo = 0,
                        lpdwRebootReasons = RmRebootReasonNone;
    
                    string[] resources = new string[] { path }; // Just checking on one resource.
    
                    res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
    
                    if (res != 0) throw new Exception("Could not register resource.");
    
                    //Note: there's a race condition here -- the first call to RmGetList() returns
                    //      the total number of process. However, when we call RmGetList() again to get
                    //      the actual processes this number may have increased.
                    res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
    
                    if (res == ERROR_MORE_DATA)
                    {
                        // Create an array to store the process results
                        RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                        pnProcInfo = pnProcInfoNeeded;
    
                        // Get the list
                        res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                        if (res == 0)
                        {
                            processes = new List<Process>((int)pnProcInfo);
    
                            // Enumerate all of the results and add them to the 
                            // list to be returned
                            for (int i = 0; i < pnProcInfo; i++)
                            {
                                try
                                {
                                    processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                                }
                                // catch the error -- in case the process is no longer running
                                catch (ArgumentException) { }
                            }
                        }
                        else throw new Exception("Could not list processes locking resource.");
                    }
                    else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
                }
                finally
                {
                    RmEndSession(handle);
                }
    
                return processes;
            }
    
            public static void remoteProcessKill(string computerName, string userName, string pword, string processName)
            {
                var connectoptions = new ConnectionOptions();
                connectoptions.Username = userName;
                connectoptions.Password = pword;
    
                ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions);
    
                // WMI query
                var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'");
    
                using (var searcher = new ManagementObjectSearcher(scope, query))
                {
                    foreach (ManagementObject process in searcher.Get()) 
                    {
                        process.InvokeMethod("Terminate", null);
                        process.Dispose();
                    }
                }            
            }
    
            public static void localProcessKill(string processName)
            {
                foreach (Process p in Process.GetProcessesByName(processName))
                {
                    p.Kill();
                }
            }
    
            [DllImport("kernel32.dll")]
            public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags);
    
            public const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4;
    
        }
    }

2

このスレッドの他の回答が指摘したように、このエラーを解決するには、コードを注意深く調べて、ファイルがロックされている場所を理解する必要があります。

私の場合、移動操作を実行する前に、ファイルを電子メールの添付ファイルとして送信していました。

そのため、SMTPクライアントが電子メールの送信を完了するまで、ファイルは数秒間ロックされました。

私が採用した解決策は 、最初にファイル移動し、次にメールを送信することでした。これで問題は解決しました。

ハドソンが以前に指摘したように、別の可能な解決策は、使用後にオブジェクトを処分することでした。

public static SendEmail()
{
           MailMessage mMailMessage = new MailMessage();
           //setup other email stuff

            if (File.Exists(attachmentPath))
            {
                Attachment attachment = new Attachment(attachmentPath);
                mMailMessage.Attachments.Add(attachment);
                attachment.Dispose(); //disposing the Attachment object
            }
} 

ファイルが使用中の場合File.Move()は機能せず、同じエラーが発生します。電子メールにファイルを追加するだけの場合Attachments.Add()、これは単なるコピー操作であるため、操作中に使用してもエラーにはならないと思います。何らかの理由でそれが一時ディレクトリにコピーできた場合は、コピーを添付し、後でコピーしたファイルを削除します。しかし、OPがファイルを変更してそれを使用したい場合、この種のソリューション(コードを示していない、添付部分のみ)が機能するとは思いません。.Dispose()常に良い考えですが、前の操作でファイルが開かれていない限り、ここでは関係ありません。
vapcguy

1

同じエラーを引き起こしている次のシナリオがありました:

  • サーバーにファイルをアップロードする
  • 次に、アップロード後に古いファイルを削除します

ほとんどのファイルのサイズは小さいですが、いくつかのファイルは大きいため、それらを削除しようとすると、ファイルにアクセスできなくなりましたエラーが発生した。

見つけるのは簡単ではありませんでしたが、解決策は「タスクの実行が完了するのを待つ」のと同じくらい簡単でした。

using (var wc = new WebClient())
{
   var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
   tskResult.Wait(); 
}

-1

私の以下のコードはこの問題を解決しますが、私は最初にこの問題の原因を理解し、コードを変更して見つけることができる解決策を試す必要があることをお勧めします

私はこの問題を解決する別の方法を提供できますが、より良い解決策は、コーディング構造を確認し、これが発生する原因を分析することです。解決策が見つからない場合は、以下のこのコードを使用できます

try{
Start:
///Put your file access code here


}catch (Exception ex)
 {
//by anyway you need to handle this error with below code
   if (ex.Message.StartsWith("The process cannot access the file"))
    {
         //Wait for 5 seconds to free that file and then start execution again
         Thread.Sleep(5000);
         goto Start;
    }
 }

1
このコードに関するいくつかの問題:1)呼び出す必要がある場合GC.*()は、コードに他の問題がある可能性があります。2)メッセージはローカライズされ、壊れやすいため、代わりにHRESULTを使用してください。3)で眠りたいかもしれませんTask.Delay()(多くの場合、10秒はどういうわけか過度です)。4)終了条件がありません。このコードはハングする可能性があります。5)あなたは間違いなくgotoここを必要としません。6)キャッチすることExceptionは通常悪い考えです。この場合も次の理由によります。6)他に何かが起こった場合、エラーを飲み込んでいます。
Adriano Repetti

@AdrianoRepetti私の最初のコメントは、このコードを使用しないで他の解決策を見つけようとすると言ったこれは最後のオプションであり、このエラーが発生した場合コードは停止しますが、このエラーを処理している場合、システムはファイルを解放するのを待ってから開始します動作していて、このコードを使用している間、GC。*で他の問題に直面していません。私はすでにこのコードで動作するシステムを持っているので、投稿した人が誰かに役立つかもしれません。
アッシュ

私はこのコードを使用していないと言うだろう、これまで(なぜなら私は上記の点で)、私はこれを検討していないと思います作業を PRODシステムではなく、それはちょうど私のPOVです。どうぞご遠慮なくどうぞ!
Adriano Repetti

1)私はこれを消費し、GCを使用したWindowsサービスを使用しました*これまで私はこれで問題に直面していません2)ええHRESULTはコーディング標準の点でより良いオプションになることができます3)私が編集した時間を与えることができる状況に依存します5秒まで4)とにかく、システムをエラーでシステムを停止するのではなく、システムがしばらくの間リソースを解放するのを待つ方が良い5)はい、例外処理に同意することは常に良い考えとは限らないが、コードスニペットが小さく、ここで私は例外を処理できるので、この解決策はあなたのためのオプションです。
Ash

参照:docs.microsoft.com/en-us/archive/blogs/ricom/...GC.Collect()一部のCOMオブジェクトを処理する場合、私が使用する必要がある唯一の(そして私が強調するだけの)ケースがありました。
Adriano Repetti
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.