有一个文件无法删除,说是被另一个程序使用。怎么办?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有一个文件无法删除,说是被另一个程序使用。怎么办?相关的知识,希望对你有一定的参考价值。

参考技术A 文件被占用,百度搜索金山网盾,下载升级到最新,如果是病毒造成的,点击右侧的一键修复。按提示修复处理然后再删除。
如果是被占用则使用unlocker这个软件,下载安装,右键文件夹选择unlocker即可。或者用冰刃软件来暴力删除。也可以用金山毒霸里的文件粉碎工具。
注意,360会拦截金山网盾安装,如果你有360,请先打开360木马防火墙,点击进程防火墙的已开启按钮暂时关闭
(如发现网盾不能扫描,请在任务栏右键退出360的托盘

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

【中文标题】IOException:该进程无法访问文件“文件路径”,因为它正在被另一个进程使用【英文标题】:IOException: The process cannot access the file 'file path' because it is being used by another process 【发布时间】:2021-12-02 17:25:36 【问题描述】:

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

进程无法访问文件“文件名”,因为它正被 另一个进程

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

【问题讨论】:

查看this question了解如何找到正在使用该文件的其他进程。 【参考方案1】:

是什么原因?

错误消息非常清楚:您正在尝试访问一个文件,但由于另一个进程(甚至是同一个进程)正在处理它(并且它不允许任何共享),所以它无法访问。

调试

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

您的进程是唯一可以访问该文件的进程 您确定 other 进程是您自己的进程。如果您知道您在程序的另一部分中打开了该文件,那么首先您必须在每次使用后检查您是否正确关闭了文件句柄。这是一个带有此错误的代码示例:

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 示例)。

如果一切正常(您确定总是关闭打开的每个文件,即使出现异常情况也是如此)并且您有多个工作线程,那么您有两个选择:重新编写代码以序列化文件访问(并非总是可行并不总是需要)或应用重试模式。这是 I/O 操作的一种非常常见的模式:您尝试做某事并在出现错误时等待并重试(您是否问过自己为什么,例如,Windows Shell 需要一些时间来通知您文件正在使用中并且不能删除?)。在 C# 中它很容易实现(另请参阅关于 disk I/O、networking 和 database 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);
    

请注意我们在 *** 上经常看到的一个常见错误:

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 语句打开文件。如上一段所述,它将积极帮助您避免许多常见错误(请参阅this post,了解有关如何不使用它的示例)。

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

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

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

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

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

高级场景 这并不总是那么容易,因此您可能需要与其他人共享访问权限。例如,如果您是从头读到尾,那么您至少有两个选择。

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

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

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


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

一般 N 个进程可以一起从同一个文件中读取,但只有一个应该写入,在受控场景中,您甚至可以启用并发写入,但这不能在此内部的几个文本段落中概括回答。

是否可以解锁另一个进程使用的文件?这并不总是安全的,也不是那么容易,但是是的,it's possible。

【讨论】:

我不知道我的代码有什么问题,我使用 using 块,但是当我尝试删除文件时仍然出现错误说文件正在使用中。 不,实际上我有一个计时器控件,在计时器旁边,如果我再次调用该函数,它将引发异常。基本上我将一个文件解密为另一个文本文件,然后删除新创建的文件,这会在删除时引发异常! @جمشیدکامران 为什么要使用代码中的内容创建文件,然后将其删除?那么,首先创建文件似乎很奇怪。由于您没有发布代码,我们不知道您在做什么。但是当你创建你的文件时,如果你使用File.Create(path),你应该在写文件之前添加.Close()。除了using 用于写入文件的语句之外,还有类似的陷阱,然后在它们之后删除。您应该在问题中发布有关如何创建和删除文件的代码。但可能符合上述内容。 我的代码使用Directory.SetCreationTimeUTC(),但是当文件资源管理器打开时它失败,声称该目录正在被另一个进程访问。这种情况应该怎么处理? @KyleDelaney 我会说你需要等到文件夹被关闭,如果不是几秒钟的事情,那么一切都会很快变得复杂(保持一个有待处理操作的后台队列?文件系统观察器? 投票?)您可能想发布一个包含更多详细信息的问题,以获得临时答案【参考方案2】:

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

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


【讨论】:

就我而言,这是唯一有效的解决方案。【参考方案3】:

上传图片时出现问题,无法删除,找到了解决方案。高音

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

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

//later...

File.Delete(filePath); //now works

【讨论】:

如果是删除文件,需要先dispose image对象。 很好,我正在寻找这个解决方案。我也遇到了 Image.FromFile 函数的问题。 @thescion :) np 这对我来说非常有效,并解决了我的确切问题。我想处理 Tiff 文件的文件夹,将它们转换为 byte[] 流并发送到服务,然后将 Tiff 文件的文件夹移动到“已处理”存档文件夹。 Image.FromFile 对 Tiff 文件进行了锁定,并且没有及时释放它,所以当我去移动 Tiff 文件和包含文件夹时,我会得到“正在被另一个进程使用”错误,因为锁定仍然存在到位。在获取 Tiff 文件的字节后立即执行 .Release 完全解决了此锁定文件问题。 这对我有用,TYVM。【参考方案4】:

问题

有人想用这种方法打开文件System.IO.File.Open(path, FileMode),并希望对文件进行共享访问,但是

如果您阅读了 System.IO.File.Open(path, FileMode) 的文档,则明确表示它不允许共享

解决方案

使用你必须使用 FileShare 的其他覆盖

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

FileShare.Read

【讨论】:

这个解决方案对我来说是最好的。我最初的代码如上所述,但没有导致异常的 FileShare.Read。添加 FileShare.Read 立即解决了问题。 对不起,我们can't accept images of code, data, documentation or errors。将这些内容发布为 text,以便无需重新输入所有内容即可阅读文本,并且屏幕阅读器可以正确索引或阅读您的帖子。【参考方案5】:

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

【讨论】:

不仅没有文件名。非法(目标)文件名 - 在我的例子中是“...\file.” - 会给出同样的愚蠢错误,并在半天之内将您指向错误的方向!【参考方案6】:

正如该线程中的其他答案所指出的,要解决此错误,您需要仔细检查代码,以了解文件被锁定的位置。

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

所以文件被锁定了几秒钟,直到 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() 操作期间使用时不会出错,因为这只是一个复制操作。如果由于某种原因确实如此,您可以将其复制到临时目录,附加副本,然后删除复制的文件。但我不认为,如果 OP 想要修改一个文件并使用它,这种解决方案(你没有显示代码,只有附件部分)会起作用。 .Dispose() 总是一个好主意,但在这里不相关,除非文件是在先前的操作中打开的。 我也遇到过类似的情况。发送电子邮件后我无法移动文件。就我而言:发送电子邮件后的 mailMessageObj.Attachments.Dispose() 对我有用。【参考方案7】:

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

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

    FileStream 操作中,将其放在using 块中,并使用FileShare.ReadWrite 访问模式。

    例如:

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

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

    当我在使用文件时使用输入流执行File.SaveAs 时遇到了这个问题。在我发现的情况下,我实际上根本不需要将它保存回文件系统,所以我最终只是删除了它,但我可能已经尝试使用 @987654330 在 using 语句中创建 FileStream @,很像上面的代码。

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

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

    下面我的代码中的行,并且可以在 Windows 服务中循环完成,如果你有一个特定的文件要在你想要替换它时定期查看和删除。如果您不总是拥有相同的文件,则可以更新文本文件或数据库表,该服务始终检查文件名,然后执行该检查进程并随后对其执行进程终止和删除,正如我所描述的在下一个选项中。请注意,您当然需要在给定计算机上具有管理员权限的帐户用户名和密码才能执行进程的删除和结束。

    如果您在尝试保存文件时不知道文件是否正在使用,您可以在保存之前关闭所有可能正在使用它的进程,例如 Word,如果它是 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的引用。代码是originally written by 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;
    
        
    
    

【讨论】:

【参考方案8】:

我遇到了导致相同错误的以下情况:

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

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

好不容易找到了,不过解决方法很简单,Waiting“让任务完成执行”:

using (var wc = new WebClient())

   var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
   tskResult.Wait(); 

【讨论】:

【参考方案9】:

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

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

【讨论】:

【参考方案10】:

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

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

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) 如果您需要致电GC.*(),那么您的代码可能还有其他问题。 2) 消息已本地化且脆弱,请改用 HRESULT。 3) 你可能想和Task.Delay() 一起睡觉(在许多情况下,十秒有点过分)。 4)您没有退出条件:此代码可能会永远挂起。 5) 这里绝对不需要goto。 6) 捕获Exception 通常是个坏主意,在这种情况下也是因为... 6) 如果发生其他任何事情,那么您就是在接受错误。 我会说永远不要使用此代码(因为我上面提到的几点)而且我不认为这工作在 prod 系统中,但这只是我的 POV。随意不同意! 另见:docs.microsoft.com/en-us/archive/blogs/ricom/…。我需要使用GC.Collect() 的唯一(我强调唯一)情况是在处理一些 COM 对象时。 4) 不,绝对不是。它可以永远挂起,它可能是你自己的程序来保持它打开然后它永远不会退出。 5)是否可以选择以这种方式处理***ExceptionOutOfMemoryException(吞并重复)?在这些错误之后,您确定您的程序数据仍然正确吗?不,编写可以处理和恢复内存不足情况的代码非常困难。非常难,绝对不会吞下并忽略错误。 对不起,即使您删除了 GC 部分,我仍然认为这个答案完全错误。您基本上建议使用goto 执行while (true) 循环,捕获所有异常,然后使用错误消息进行区分。没有什么好理解的了,即使是一次性脚本也只是次优(恕我直言)。

以上是关于有一个文件无法删除,说是被另一个程序使用。怎么办?的主要内容,如果未能解决你的问题,请参考以下文章

SYSTEM32目录下.dll文件无法删除,文件正在被另一个人或程...怎么删除

truetype造字程序“EUDC正被另一个过程使用,无法保存字符”怎么解决?

桌面上的一个文件删不掉,提示说"文件正被另一个人或程序使用",怎么才能删掉?这是为啥?

win10删除文件提示:操作无法完成,因为程序已经在另外一个程序中打开 怎么解决?

winform c#删除图片提示文件被占用

克服“它正在被另一个人或程序使用”。 [关闭]