【问题标题】:How do I delete a file which is locked by another process in C#?如何删除被 C# 中的另一个进程锁定的文件?
【发布时间】:2010-09-05 06:55:12
【问题描述】:

我正在寻找一种方法来使用 C# 删除被另一个进程锁定的文件。我怀疑该方法必须能够找到哪个进程正在锁定文件(也许通过跟踪句柄,尽管我不确定如何在 C# 中执行此操作)然后关闭该进程,然后才能使用 @987654322 完成文件删除@。

【问题讨论】:

标签: c# file-locking


【解决方案1】:

杀死其他进程并不是一件健康的事情。如果您的场景涉及卸载等操作,您可以使用MoveFileEx API function 将文件标记为在下次重新启动时删除。

如果您确实需要删除另一个进程正在使用的文件,我建议您在考虑任何解决方案之前重新考虑实际问题。

【讨论】:

  • 请注意此 MSDN 备注:MOVEFILE_DELAY_UNTIL_REBOOT - "...仅当进程位于属于管理员组或 LocalSystem 帐户的用户的上下文中时,才能使用此值。. 。”
【解决方案2】:

典型的方法如下。你说过你想用 C# 来做这件事,所以这里......

  1. 如果您不知道哪个进程锁定了文件,则需要检查每个进程的句柄列表,并查询每个句柄以确定它是否标识了锁定的文件。在 C# 中执行此操作可能需要 P/Invoke 或中间 C++/CLI 来调用您需要的本机 API。
  2. 一旦您确定了哪些进程锁定了文件,您需要安全地将一个小的本机 DLL 注入该进程(您也可以注入一个托管 DLL,但这比较麻烦,因为必须启动或附加到 .NET 运行时)。
  3. 该引导 DLL 然后使用 CloseHandle 等关闭句柄。

本质上:解锁“锁定”文件的方法是将 DLL 文件注入违规进程的地址空间并自行关闭。您可以使用本机或托管代码执行此操作。无论如何,您将需要少量的本机代码或至少 P/Invoke 到相同的代码中。

有用的链接:

祝你好运!

【讨论】:

    【解决方案3】:

    如果您想以编程方式进行。我不确定......我真的建议不要这样做。 如果您只是在自己的机器上进行故障排除,SysInternals Process Explorer 可以帮助您

    运行它,使用查找句柄命令(我认为它在查找或句柄菜单中),然后搜索文件的名称。找到句柄后,您可以强制关闭它们。

    然后您可以删除文件等等。

    当心,这样做可能会导致拥有句柄的程序行为异常,因为您刚刚从它下面拉出了众所周知的地毯,但是当您自己调试时它工作得很好错误的代码,或者当 Visual Studio/Windows 资源管理器被破坏并且没有释放文件句柄时,即使你在很久以前告诉他们关闭文件......叹息 :-)

    【讨论】:

      【解决方案4】:

      您可以使用此程序Handle 来查找哪个进程锁定了您的文件。这是一个命令行工具,所以我猜你会使用它的输出。我不确定以编程方式找到它。

      如果删除文件可以等待,您可以指定在您的计算机下次启动时删除它:

      1. 启动 REGEDT32 (W2K)REGEDIT (WXP) 并导航至:

        HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
        
      2. W2K 和 WXP

        • W2K:
          编辑
          添加值...
          数据类型:REG_MULTI_SZ
          值名称:PendingFileRenameOperations
          好的

        • WXP:
          编辑
          新建
          多字符串值
          输入
          PendingFileRenameOperations

      3. 在数据区域,输入"\??\" + filename要删除。 LFN 可能 输入时不嵌入引号。要删除C:\Long Directory Name\Long File Name.exe,请输入以下数据:

        \??\C:\Long Directory Name\Long File Name.exe
        

        然后按确定

      4. “目标文件名”是一个空(零)字符串。它被输入 如下:

        • W2K:
          编辑
          二进制
          选择数据格式:十六进制
          单击十六进制字符串的末尾
          输入 0000(四个零)
          好的

        • WXP:
          右键单击该值
          选择“修改二进制数据”
          单击十六进制字符串的末尾
          输入 0000(四个零)
          确定

      5. 关闭REGEDT32/REGEDIT并重启​​以删除文件。

      (为了子孙后代,从some random forum无耻地窃取。)

      【讨论】:

        【解决方案5】:

        使用 Orion Edwards 的建议,我下载了 Sysinternals Process Explorer,这反过来又让我发现我难以删除的文件实际上不是由我认为的 Excel.Applications 对象持有,而是我的C# 代码发送邮件代码创建了一个附件对象,该对象使该文件的句柄处于打开状态。

        看到这个,我很简单的调用了Attachment对象的dispose方法,句柄就被释放了。

        Sysinternals 浏览器让我发现它与 Visual Studio 2005 调试器结合使用。

        我强烈推荐这个工具!

        【讨论】:

        • 这也正是我的问题。谢谢+1
        【解决方案6】:

        哦,我多年前使用的一个大技巧是,Windows 不允许您删除文件,但它允许您移动它们。

        伪代码:

        mv %WINDIR%\System32\mfc42.dll %WINDIR\System32\mfc42.dll.old
        Install new mfc42.dll
        Tell user to save work and restart applications
        

        当应用程序重新启动时(注意我们不需要重新启动机器),它们加载了新的mfc42.dll,一切都很好。再加上PendingFileOperations 在下次整个系统重新启动时删除旧的,效果很好。

        【讨论】:

          【解决方案7】:

          这看起来很有希望。一种杀死文件句柄的方法....

          http://www.timstall.com/2009/02/killing-file-handles-but-not-process.html

          【讨论】:

            【解决方案8】:

            您可以使用提供完整文件路径的代码,它会返回一个List<Processes> 锁定该文件的任何内容:

            using System.Runtime.InteropServices;
            using System.Diagnostics;
            
            static public class FileUtil
            {
                [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;
                }
            }
            

            然后,迭代进程列表并关闭它们并删除文件:

                string[] files = Directory.GetFiles(target_dir);
                List<Process> lstProcs = new List<Process>();
            
                foreach (string file in files)
                {
                    lstProcs = ProcessHandler.WhoIsLocking(file);
                    if (lstProcs.Count > 0) // deal with the file lock
                    {
                        foreach (Process p in lstProcs)
                        {
                            if (p.MachineName == ".")
                                ProcessHandler.localProcessKill(p.ProcessName);
                            else
                                ProcessHandler.remoteProcessKill(p.MachineName, txtUserName.Text, txtPassword.Password, p.ProcessName);
                        }
                        File.Delete(file);
                    }
                    else
                        File.Delete(file);
                }
            

            并且取决于文件是否在本地计算机上:

            public static void localProcessKill(string processName)
            {
                foreach (Process p in Process.GetProcessesByName(processName))
                {
                    p.Kill();
                }
            }
            

            或网络计算机:

            public static void remoteProcessKill(string computerName, string fullUserName, string pword, string processName)
            {
                var connectoptions = new ConnectionOptions();
                connectoptions.Username = fullUserName;  // @"YourDomainName\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();
                    }
                }
            }
            

            参考资料:
            How do I find out which process is locking a file using .NET?

            Delete a directory where someone has opened a file

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-11-11
              • 1970-01-01
              • 1970-01-01
              • 2012-04-16
              相关资源
              最近更新 更多