IOException:该进程无法访问文件“文件路径”,因为它正在被另一个进程使用


Answers:


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

这种模式还可以确保在出现异常的情况下不会打开文件(这可能是文件正在使用的原因:出了点问题,没有人关闭它;请参阅示例)。

如果一切看起来都很好(即使有例外,您肯定会始终关闭打开的每个文件),并且您有多个工作线程,那么您有两种选择:重新编写代码以序列化文件访问权限(并非始终可行,而且并非总是如此)想要)或应用重试模式。这是一种非常常见的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()函数)将所有打开和关闭自己的文件。

您的进程不是唯一访问该文件
的进程如果您的进程不是唯一访问该文件的进程,则交互可能会更加困难。一个重试模式将帮助(如果该文件不应该是开放的其他任何人,但它是,那么你需要像Process Explorer的一个实用程序,检查在做什么)。

避免的方法

如果适用,请始终使用using语句打开文件。如前一段所述,它将积极帮助您避免许多常见错误(有关如何不使用它的示例,请参阅此帖子)。

如果可能,请尝试确定谁拥有对特定文件的访问权,并通过一些众所周知的方法集中访问。例如,如果您有一个程序可以在其中读写的数据文件,则应将所有I / O代码装在一个类中。这将使调试更加容易(因为您始终可以在此处放置一个断点,并查看谁在做什么),而且它将成为多路访问的同步点(如果需要)。

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

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

如果有人在之后File.Exists(),之前删除了文件File.Delete(),则会IOException在您可能会误以为安全的地方抛出一个文件。

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

高级方案
并非总是那么容易,因此您可能需要与其他人共享访问权限。例如,如果您从头开始阅读,然后从头开始写作,则至少有两个选择。

1)FileStream与适当的同步功能共享相同的内容(因为它不是线程安全的)。见这个这个职位的一个例子。

2)使用FileShare枚举指示OS允许其他进程(或您自己进程的其他部分)同时访问同一文件。

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

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

通常,N个进程可以一起读取同一文件,但只能写入一个文件,在受控的情况下,您甚至可以启用并发写入,但这不能在此答案中的几个文本段落中进行概括。

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


我不知道我的代码有什么问题,我使用using块,但是当我尝试删除该文件时仍然出现错误,表明该文件正在使用中。
Jamshaid Kamran

不,实际上我在计时器旁边有一个计时器控件,如果我再次调用该函数,它将引发异常。基本上,我将一个文件解密为另一个文本文件,然后删除新创建的文件,这将导致删除时发生异常!
Jamshaid Kamran

3
@جمشیدکامران为什么要创建一个包含代码中内容的文件,然后将其删除?首先,似乎无法创建文件。由于您没有发布代码,因此我们不知道您在做什么。但是,当您创建文件时,如果使用进行操作File.Create(path),则应.Close()在文件末尾添加内容,然后再进行写入。除了using编写文件的语句之外,还有类似的陷阱,然后在它们之后删除。您应该在问题中张贴有关如何创建和删除文件的代码。但可能与上述内容相符。
vapcguy

我的代码使用了,Directory.SetCreationTimeUTC()但是在打开文件资源管理器时失败,声称该目录正在被另一个进程访问。我应该如何处理这种情况?
凯尔·德莱尼

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文件的文件夹移动到“已处理”的存档文件夹中。Image.FromFile在Tiff文件上放置了一个锁,并且没有及时释放它,因此当我移动Tiff文件和包含文件夹时,由于锁仍然存在,因此我会收到“被另一个进程使用”错误。到位。在获取Tiff文件的字节之后立即执行.Release可以完全解决此锁定文件的问题。
Developer63

4

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


而且也不只是没有文件名。非法的(目标)文件名-在我的情况下为“ ... \ file”。-会出现同样的愚蠢错误,并在半天内将您指向错误的方向!
尼克·韦斯特盖特

3

该错误表明另一个进程正在尝试访问该文件。尝试写入时,可能您或其他人将其打开。“读取”或“复制”通常不会导致这种情况,但是会对其进行写入或调用delete来导致。

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

  1. FileStream操作中,将其放在具有访问模式的usingFileShare.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(如果是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客户端完成发送电子邮件为止。

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

正如哈德森(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
            }
} 

如果文件正在使用File.Move()中将无法正常工作并给出相同的错误。如果仅将文件添加到电子邮件中,我认为在Attachments.Add()操作过程中使用该文件不会出错,因为这只是复制操作。如果由于某种原因它可以将其复制到Temp目录,请附加副本,然后再删除复制的文件。但是我不认为,如果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()(在许多情况下,十秒钟有点过长)。4)您没有退出条件:此代码可能会永远挂起。5)您绝对不需要goto这里。6)捕获Exception通常不是一个好主意,在这种情况下,还因为... 6)如果发生任何其他事情,那么您会误入歧途。
阿德里亚诺·雷佩蒂

@AdrianoRepetti我的第一句话说不要使用此代码,尝试寻找其他解决方案,这将是最后的选择,无论如何,如果此错误发生,代码将停止,但是如果您正在处理此错误,系统将等待释放文件,然后它将启动工作,我在使用此代码时没有遇到其他与GC。*有关的问题。我已经有使用此代码的系统,因此我发布的内容可能对某人有所帮助。
灰烬

我说永远不要使用此代码(由于我在上面提到的要点),并且我不认为这可以在产品系统中工作,但这只是我的POV。随时不同意!
阿德里亚诺·雷佩蒂

1)我使用Windows服务时使用了它,而我使用了GC。*到目前为止,我对此没有任何问题2)是的HRESULT在编码标准方面可能是更好的选择3)根据情况,您可以给我刚刚编辑的时间到5秒4)最好让系统等待一段时间以释放资源,而不要停止系统并出现错误5)是的,我同意您的观点,异常处理并不总是一个好主意,但是如果您的代码段很小并且您知道我可以在这里处理异常,那么此解决方案是您的选择。
灰烬

另请参阅:docs.microsoft.com/en-us/archive/blogs/ricom/…GC.Collect()处理某些COM对象时,我需要使用的唯一一种情况(并且我只强调)。
阿德里亚诺·雷佩蒂
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.