IOException:进程无法访问文件'因为它正在被另一个进程使用

我有一些代码,当它执行时,它抛出一个IOException,说

进程不能访问文件'filename',因为它正在被 另一个进程< / p >

这意味着什么?我能做些什么?

794651 次浏览

原因是什么?

错误消息非常清楚:您试图访问一个文件,但它无法访问,因为另一个进程(甚至是相同的进程)正在对它做一些事情(并且它不允许任何共享)。

调试

它可能很容易解决(也可能很难理解),这取决于您的具体场景。让我们看看。

< p > 您的进程是唯一访问该文件的进程
你确定其他进程是你自己的进程。如果您知道您在程序的另一部分打开了该文件,那么首先您必须检查在每次使用后是否正确地关闭了文件句柄。下面是这个错误的代码示例:

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

此模式还将确保在异常情况下文件不会保持打开状态(这可能是文件正在使用的原因:出错了,没有人关闭它;有关示例,请参见这篇文章)。

如果一切看起来都很好(你确定你总是关闭你打开的每个文件,即使在异常的情况下),你有多个工作线程,那么你有两个选择:重做你的代码来序列化文件访问(不总是可行的,也不总是想要的)或应用重试模式。这是I/O操作的一个非常常见的模式:你尝试做一些事情,如果出现错误,你会等待并再次尝试(例如,你有没有问过自己,为什么Windows Shell需要一些时间来通知你一个文件正在使用并且不能删除?)在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()函数)都将自行打开和关闭文件。

< p > 您的进程并不是唯一访问该文件的进程
如果您的进程不是唯一访问该文件的进程,那么交互就会更加困难。重试模式将有所帮助(如果文件不应该被其他人打开,但它却被打开了,那么你需要一个像进程资源管理器这样的实用程序来检查正在执行什么)

避免的方法

如果适用,总是使用使用语句打开文件。如前一段所述,它将积极地帮助你避免许多常见错误(有关如何不使用它的示例,请参阅这篇文章)。

如果可能的话,尝试决定谁拥有对特定文件的访问权,并通过一些众所周知的方法集中访问权。例如,如果您有一个用于程序读取和写入的数据文件,那么您应该将所有I/O代码装入单个类中。这将使调试更容易(因为您总是可以在那里放置一个断点,并查看谁在做什么),而且它将成为多个访问的同步点(如果需要)。

不要忘记I/O操作总是会失败,一个常见的例子是:

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

如果一个人File.Exists()之后但在File.Delete()之前删除文件,那么它将在一个你可能错误地认为安全的地方抛出IOException

只要可能,应用重试模式,如果你使用FileSystemWatcher,考虑延迟操作(因为你会收到通知,但应用程序可能仍然只使用该文件)。

< p > 先进的场景
这并不总是那么容易,所以您可能需要与其他人共享访问权限。例如,如果你要从头读到尾写,你至少有两个选项

1)与适当的同步函数共享相同的FileStream(因为它不是线程安全的)。有关示例,请参阅帖子。

2)使用FileShare枚举来指示操作系统允许其他进程(或你自己进程的其他部分)并发访问同一个文件。

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

在这个例子中,我展示了如何打开一个文件进行写入和共享以进行读取;请注意,当读写重叠时,会导致未定义或无效的数据。这是阅读时必须处理的情况。还要注意,这并不能使对stream的访问是线程安全的,所以这个对象不能与多个线程共享,除非以某种方式同步访问(参见前面的链接)。还有其他可用的共享选项,它们打开了更复杂的场景。详情请参考MSDN

一般来说,N进程可以一起从同一个文件中读取,但只应该写入一个文件,在受控的情况下,你甚至可以启用并发写入,但这不能在这个答案中的几个文本段落中泛化。

是否可以解锁一个文件被另一个进程使用?它并不总是安全的,不那么容易,但是的,这是有可能的

我有下面的场景会导致同样的错误:

  • 上传文件到服务器
  • 然后在旧文件上传后删除它们

大多数文件都很小,但也有少数文件很大,因此试图删除这些文件会导致无法访问文件错误。

然而,这并不容易找到,解决方案就像等待“for the task to complete execution”一样简单:

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

在上传图像时出现问题,无法删除并找到解决方案。gl高频

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


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


//later...


File.Delete(filePath); //now works

使用文件共享修复了我打开文件的问题,即使它是由另一个进程打开的。

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

我得到这个错误是因为我在执行File。移动到没有文件名的文件路径时,需要在目标中指定完整路径。

正如本文中的其他回答所指出的,要解决这个错误,您需要仔细检查代码,以了解文件被锁定的位置。

在我的例子中,我在执行移动操作之前将文件作为电子邮件附件发送出去。

所以文件被锁定了几秒钟,直到SMTP客户端完成发送电子邮件。

我采用的解决方案是先移动文件,然后再发送邮件。这解决了我的问题。

另一个可能的解决方案,正如Hudson之前指出的,应该是在使用后处理对象。

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

该错误表示另一个进程正在试图访问该文件。可能您或其他人在尝试向其写入时打开了它。“Read”或“Copy”通常不会导致这种情况,但对其写入或调用delete则会。

正如其他回答所提到的,有一些基本的事情可以避免这种情况:

  1. FileStream操作中,将其放在具有FileShare.ReadWrite访问模式的using块中。

    例如:

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

    注意,如果你使用FileMode.AppendFileAccess.ReadWrite是不可能的

  2. 我在使用文件时使用输入流执行File.SaveAs时遇到了这个问题。在我的例子中,我发现,我实际上根本不需要将它保存回文件系统,所以我最终只是删除了它,但我可能已经尝试过用FileAccess.ReadWriteusing语句中创建FileStream,就像上面的代码一样。

  3. 将您的数据保存为不同的文件,并在发现不再使用时返回删除旧文件,然后将成功保存的文件重命名为原始文件的名称是一种选项。如何测试正在使用的文件是通过

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

    如果你有一个特定的文件,你想要定期查看并在你想要替换它的时候删除它,那么可以在Windows服务中循环执行。如果你不总是有相同的文件,一个文本文件或数据库表可以被更新,服务总是检查文件名,然后执行检查进程&然后执行进程kill和delete,正如我在下一个选项中描述的那样。当然,要执行删除和结束进程,您需要一个在给定计算机上具有Admin权限的帐户用户名和密码

  4. 当你试图保存一个文件时,你不知道它是否会被使用,你可以在保存之前关闭所有可能正在使用它的进程,比如Word文档。

    如果它是本地的,你可以这样做:

    ProcessHandler.localProcessKill("winword.exe");
    

    如果它是远程的,你可以这样做:

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

    其中txtUserName的形式为DOMAIN\user。< / p >

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

我下面的代码解决了这个问题,但我建议 首先,您需要了解导致此问题的原因,并尝试通过更改code

来找到解决方案

我可以给出另一种方法来解决这个问题,但更好的解决方案是检查你的编码结构,并尝试分析是什么导致这种情况发生,如果你没有找到任何解决方案,那么你可以使用下面的代码

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

问题

一个是绑定打开文件System.IO.File.Open(path, FileMode)使用此方法,并希望共享访问文件,但是

如果你阅读System.IO.File。打开(路径,FileMode)的文档,它会明确地说它不是允许共享

enter image description here

解决方案

使用你必须使用其他重写文件共享 enter image description here < / p >

using FileStream fs = System.IO.File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

FileShare.Read

我遇到了这个问题,通过下面的代码解决了这个问题

var _path=MyFile.FileName;
using (var stream = new FileStream
(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// Your Code! ;
}

我有一个非常具体的情况,我得到了一个IOException:进程无法访问文件“文件路径”;在行

File.Delete(fileName);

在一个NUnit测试中,看起来像这样:

Assert.Throws<IOException>(() =>
{
using (var sr = File.OpenText(fileName) {
var line = sr.ReadLine();
}
});
File.Delete(fileName);

结果NUnit 3使用了一种他们称之为“孤立上下文”的东西;用于异常断言。这可能运行在一个单独的线程上。

我的修复是把File.Delete 放在相同的上下文中。

Assert.Throws<IOException>(() =>
{
try
{
using (var sr = File.OpenText(fileName) {
var line = sr.ReadLine();
}
}
catch
{
File.Delete(fileName);
throw;
}
});
在我的情况下,这个问题通过打开共享写作/阅读的文件来解决。共享读写的代码示例如下:— 流作家< / >强

using(FileStream fs = new FileStream("D:\\test.txt",
FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine("any thing which you want to write");
}

流的读者

using (FileStream fs = new FileStream("D:\\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader rr=new StreamReader(fs))
{
rr.ReadLine())
}