首页 文章

IOException:进程无法访问文件'file path',因为它正由另一个进程使用

提问于
浏览
126

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

进程无法访问文件'filename',因为它正由另一个进程使用

这是什么意思,我能做些什么呢?

7 回答

  • 218

    原因是什么?

    错误消息非常清楚:您正在尝试访问文件,并且它无法访问,因为另一个进程(甚至同一进程)正在使用它(并且它不允许任何共享) .

    调试

    它可能很容易解决(或很难理解),具体取决于您的具体情况 . 我们来看看吧 .

    Your process is the only one to access that file
    您确定其他流程是您自己的流程 . 如果您知道在程序的另一部分中打开该文件,那么首先必须检查每次使用后是否正确关闭文件句柄 . 以下是此错误的代码示例:

    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
    

    此模式还将确保在异常情况下文件不会保持打开状态(可能是文件正在使用的原因:出现问题,没有人关闭它;有关示例,请参阅this post) .

    如果一切看起来都很好(你确定你总是关闭你打开的每个文件,即使是例外情况)并且你有多个工作线程,那么你有两个选择:重新编写你的代码以序列化文件访问(并不总是可行但并不总是想要)或应用重试模式 . 它很容易实现(另请参阅有关disk I/Onetworkingdatabase access的更好示例) .

    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() 函数)将自行打开和关闭文件 .

    Your process is not the only one to access that file
    如果您的进程不是唯一访问该文件的进程,那么交互可能会更难 . 重试模式将有所帮助(如果文件不应由其他任何人打开,但它是,那么您需要像Process Explorer这样的实用程序来检查谁在做什么) .

    要避免的方法

    如果适用,请始终使用using语句打开文件 . 如前一段所述,它会主动帮助您避免许多常见错误(请参阅this post以获取 how not to use it 上的示例) .

    如果可能,尝试确定谁拥有对特定文件的访问权限,并通过一些众所周知的方法集中访问 . 例如,如果您有一个程序读写的数据文件,那么您应该将所有I / O代码放在一个类中 . 它将使调试变得更容易(因为你总是可以在那里放置一个断点,看看谁在做什么),并且它也是多重访问的同步点(如果需要) .

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

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

    如果有人在 File.Exists() 之后但在 File.Delete() 之前删除了该文件,那么它会在您可能错误安全的地方抛出 IOException .

    只要有可能,应用重试模式,如果您正在使用 FileSystemWatcher ,请考虑推迟操作(因为您将收到通知,但应用程序可能仍在使用该文件) .

    Advanced scenarios
    它从头开始读到写到最后,你至少有两个选择 .

    1)与适当的同步功能共享相同的 FileStream (因为 it is not thread-safe ) . 有关示例,请参阅thisthis个帖子 .

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

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

    在这个例子中,我展示了如何打开文件进行编写和共享以供阅读;请注意,在读取和写入重叠时,会导致数据未定义或无效 . 它's a situation that must be handled when reading. Also note that this doesn' t访问 stream 线程安全,因此除非以某种方式同步访问,否则不能与多个线程共享此对象(请参阅前面的链接) . 其他共享选项可用,它们开辟了更复杂的场景 . 有关详细信息,请参阅MSDN .

    一般来说,N个进程可以从同一个文件中一起读取,但只有一个应该写入,在受控方案中,您甚至可以启用并发写入,但这不能在本答案中的几个文本段落中概括 .

    是否有可能 unlock 另一个进程使用的文件?它并不总是安全而且不那么容易,但是,it's possible .

  • 0

    我收到此错误是因为我正在执行File.Move到没有文件名的文件路径,需要在目标中指定完整路径 .

  • 5

    正如本主题中的其他答案所指出的,要解决此错误,您需要仔细检查代码,了解文件被锁定的位置 .

    在我的情况下,我在执行移动操作之前将文件作为电子邮件附件发送 .

    因此,文件被锁定几秒钟,直到SMTP客户端完成发送电子邮件 .

    我采用的解决方案是 move the file first, and then send the email. 这解决了我的问题 .

    正如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
                }
    }
    
  • 1

    我有以下情况导致相同的错误:

    • 将文件上传到服务器

    • 然后在上传后删除旧文件

    大多数文件都很小,但是有些文件很大,因此尝试删除这些文件导致无法访问文件错误 .

    这并不容易找到,但解决方案就像Waiting "for the task to complete execution"一样简单:

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

    上传图片时出现问题但无法删除并找到解决方案 . gl hf

    //C# .NET
    var image = Image.FromFile(filePath);
    
    image.Dispose(); // this removes all resources
    
    //later...
    
    File.Delete(filePath); //now works
    
  • 2

    该错误表示另一个进程正在尝试访问该文件 . 也许你或其他人在你试图写信时打开它 . “读取”或“复制”通常不会导致这种情况,但是写入或调用删除就可以了 .

    有一些基本的东西要避免这种情况,正如其他答案所提到的:

    • FileStream 操作中,将其置于具有 FileShare.ReadWrite 访问模式的 using 块中 .

    防爆 .

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

    请注意,如果您使用 FileMode.Append ,则无法使用 FileAccess.ReadWrite .

    • 当我使用输入流在文件使用时执行 File.SaveAs 时,我遇到了这个问题 . 在我的情况下,我发现,我没有尝试使用 FileAccess.ReadWriteusing 语句中创建一个FileStream,就像上面的代码一样 .

    • 将数据保存为另一个文件,当发现不再使用时,返回删除旧数据,然后将成功保存的文件重命名为原始文件的名称是一个选项 . 如何测试正在使用的文件是通过我下面的代码中的 List<Process> lstProcs = ProcessHandler.WhoIsLocking(file); 行完成的,并且可以在Windows服务中,在循环中完成,如果您有想要观看的特定文件并在需要时定期删除代替它 . 如果您不需要在给定计算机上具有管理员权限的帐户用户名和密码,当然,要执行删除和结束进程 .

    4A . 当您在尝试保存文件时不知道文件是否正在使用时,您可以在保存之前关闭可能正在使用它的所有进程,如Word,如果它是Word文档 .

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

    ProcessHandler.localProcessKill("winword.exe");
    

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

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

    其中 txtUserName 的形式为 DOMAIN\user .

    4B . 假设您不知道锁定文件的进程名称...您可以这样做:

    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 的引用,并且来自https://stackoverflow.com/a/20623311

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

    使用 FileShare 解决了打开文件的问题,即使它是由另一个进程打开的 .

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

相关问题