私はいくつかのコードを持っており、それが実行されたとき、それはスローIOException
と言って、
別のプロセスによって使用されているため、プロセスはファイル 'filename'にアクセスできません
これはどういう意味ですか。どうすればよいですか。
私はいくつかのコードを持っており、それが実行されたとき、それはスローIOException
と言って、
別のプロセスによって使用されているため、プロセスはファイル 'filename'にアクセスできません
これはどういう意味ですか。どうすればよいですか。
回答:
エラーメッセージは非常に明確です。ファイルにアクセスしようとしていますが、別のプロセス(または同じプロセス)がファイルで何かを実行しているためにアクセスできません(共有は許可されていませんでした)。
特定のシナリオによっては、解決が非常に簡単(または理解しにくい)になる場合があります。いくつか見てみましょう。
あなたのプロセスはそのファイルにアクセスする唯一のプロセスです他のプロセスはあなた自身のプロセス
だと確信しています。プログラムの別の部分でそのファイルを開いていることがわかっている場合は、最初に、使用するたびにファイルハンドルを適切に閉じることを確認する必要があります。このバグのあるコードの例を次に示します。
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つだけです。制御されたシナリオでは、同時書き込みを有効にすることもできますが、これはこの回答内のいくつかのテキストパラグラフでは一般化できません。
別のプロセスで使用されているファイルのロックを解除することはできますか?常に安全で簡単なわけではありませんが、可能です。
File.Create(path)
追加する必要があります.Close()
。そのような落とし穴がありusing
、ファイルを書き込み、その後それらを削除するためのステートメントに加えて。ファイルを作成および削除する方法については、質問にコードを投稿してください。しかし、おそらく上記のことと一致します。
Directory.SetCreationTimeUTC()
ていますが、ファイルエクスプローラーが開いているときに失敗し、別のプロセスがディレクトリにアクセスしていると主張しています。この状況にどう対処すればよいですか?
画像のアップロード中に問題が発生し、画像を削除できず、解決策が見つかりました。GL HF
//C# .NET
var image = Image.FromFile(filePath);
image.Dispose(); // this removes all resources
//later...
File.Delete(filePath); //now works
File.Moveをファイル名なしでファイルパスに移動していたため、このエラーが発生しました。宛先に完全パスを指定する必要があります。
エラーは、別のプロセスがファイルにアクセスしようとしていることを示します。書き込みしようとしているときに、あなたまたは他の誰かが開いている可能性があります。「読み取り」または「コピー」は通常これを引き起こしませんが、書き込みまたは削除を呼び出すと発生します。
他の回答が述べているように、これを回避するいくつかの基本的なものがあります:
FileStream
操作にそれを置くusing
とブロックFileShare.ReadWrite
アクセスのモード。
例えば:
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
FileAccess.ReadWrite
使用する場合は不可能であることに注意してくださいFileMode.Append
。
入力ストリームをFile.SaveAs
使用してファイルが使用されているときにこの問題に遭遇しました。私が見つけた場合、実際にファイルシステムに保存する必要はまったくなかったので、それを削除するだけでしたが、おそらく次のusing
ステートメントでFileStreamを作成することができたでしょう。FileAccess.ReadWrite
多くのコードのように、上記。
データを別のファイルとして保存し、不要になった古いファイルを削除して元に戻し、正常に保存したファイルの名前を元のファイルの名前に変更することもできます。使用中のファイルをテストする方法は、
List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);
以下の私のコードの行で、Windowsサービスのループで実行することができます。特定のファイルを監視したい場合、定期的に削除したい場合は、ファイルを置き換える必要があります。常に同じファイルがあるとは限らない場合は、テキストファイルまたはデータベーステーブルを更新して、サービスが常にファイル名をチェックし、プロセスのチェックを実行してから、プロセスの強制終了と削除を実行します。次のオプションで。もちろん、プロセスの削除と終了を実行するには、特定のコンピューターに対する管理者権限を持つアカウントのユーザー名とパスワードが必要です。
保存しようとしているときにファイルが使用されるかどうかわからない場合は、保存する前に、Wordなどのファイルを使用している可能性のあるすべてのプロセスを閉じることができます。
ローカルの場合、これを行うことができます。
ProcessHandler.localProcessKill("winword.exe");
リモートの場合、これを行うことができます。
ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");
はのtxtUserName
形式ですDOMAIN\user
。
ファイルをロックしているプロセス名がわからないとしましょう。次に、これを行うことができます:
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;
}
}
このスレッドの他の回答が指摘したように、このエラーを解決するには、コードを注意深く調べて、ファイルがロックされている場所を理解する必要があります。
私の場合、移動操作を実行する前に、ファイルを電子メールの添付ファイルとして送信していました。
そのため、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()
常に良い考えですが、前の操作でファイルが開かれていない限り、ここでは関係ありません。
同じエラーを引き起こしている次のシナリオがありました:
ほとんどのファイルのサイズは小さいですが、いくつかのファイルは大きいため、それらを削除しようとすると、ファイルにアクセスできなくなりましたエラーが発生した。
見つけるのは簡単ではありませんでしたが、解決策は「タスクの実行が完了するのを待つ」のと同じくらい簡単でした。
using (var wc = new WebClient())
{
var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
tskResult.Wait();
}
私の以下のコードはこの問題を解決しますが、私は最初にこの問題の原因を理解し、コードを変更して見つけることができる解決策を試す必要があることをお勧めします
私はこの問題を解決する別の方法を提供できますが、より良い解決策は、コーディング構造を確認し、これが発生する原因を分析することです。解決策が見つからない場合は、以下のこのコードを使用できます
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;
}
}
GC.*()
は、コードに他の問題がある可能性があります。2)メッセージはローカライズされ、壊れやすいため、代わりにHRESULTを使用してください。3)で眠りたいかもしれませんTask.Delay()
(多くの場合、10秒はどういうわけか過度です)。4)終了条件がありません。このコードはハングする可能性があります。5)あなたは間違いなくgoto
ここを必要としません。6)キャッチすることException
は通常悪い考えです。この場合も次の理由によります。6)他に何かが起こった場合、エラーを飲み込んでいます。
GC.Collect()
一部のCOMオブジェクトを処理する場合、私が使用する必要がある唯一の(そして私が強調するだけの)ケースがありました。