如何删除由C#中的另一个进程锁定的文件?

如何删除由C#中的另一个进程锁定的文件?

How do I delete a file which is locked by another process in C#?

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


杀死其他进程不是健康的事情。如果您的方案涉及卸载,则可以使用MoveFileEx API函数将文件标记为在下次重新引导时删除。

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


典型的方法如下。您已经说过要在C#中执行此操作,所以这里...

  • 如果您不知道哪个进程已锁定文件,则需要检查每个进程的句柄列表,并查询每个句柄以确定它是否标识了锁定的文件。在C#中执行此操作可能需要P / Invoke或中介C ++ / CLI来调用所需的本机API。
  • 确定哪个进程已锁定文件后,您需要安全地将一个小的本机DLL注入到该进程中(也可以注入托管DLL,但这比较麻烦,因为您必须先启动或附加到.NET运行时)。
  • 然后,该Bootstrap DLL使用CloseHandle等关闭该句柄。
  • 本质上:解锁"锁定"文件的方法是将DLL文件注入有问题的进程的地址空间,然后自行关闭它。您可以使用本机或托管代码来执行此操作。无论如何,您将需要少量的本机代码,或者至少需要P / Invoke。

    有用的网址:

    • 将代码注入另一个进程的三种方法
    • .NET代码注入

    祝好运!


    如果要以编程方式执行此操作。我不确定...我真的建议反对。
    如果您只是在自己的计算机上进行故障排除,SysInternals Process Explorer可以为您提供帮助

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

    然后,您可以删除文件,依此类推。

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


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

    如果删除文件可以等待,可以在下次启动计算机时将其指定为删除:

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

    1
    HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Session Manager
  • W2K和WXP

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

    • WXP:
      编辑
      新的
      多字符串值
      enter
      PendingFileRenameOperations

  • 在"数据"区域中,输入要删除的"\\??\\" + filename。 LFN可能
    无需嵌入引号即可输入。要删除C:\\Long Directory Name\\Long File Name.exe,请输入以下数据:

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

    然后按OK

  • "目标文件名"是一个空(零)字符串。输入
    如下:

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

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

  • 关闭REGEDT32/REGEDIT,然后重新启动以删除文件。

  • (为了后代,从一些随机的论坛上偷偷偷走了。)


    根据Orion Edwards的建议,我下载了Sysinternals ProcessExplorer,这反过来又使我发现删除困难的文件实际上不是由我认为的Excel.Applications对象保存的,而是由我的C#代码发送邮件代码的事实保存的创建了一个Attachment对象,该对象使该文件的句柄保持打开状态。

    看到此内容后,我很简单地调用了Attachment对象的dispose方法,并释放了句柄。

    Sysinternals资源管理器使我发现了与VisualStudio2005调试器结合使用的功能。

    我强烈推荐这个工具!


    您可以使用提供完整文件路径的代码,它将返回锁定该文件的所有内容的List

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    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;
        }
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
        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);
        }

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

    1
    2
    3
    4
    5
    6
    7
    public static void localProcessKill(string processName)
    {
        foreach (Process p in Process.GetProcessesByName(processName))
        {
            p.Kill();
        }
    }

    或网络计算机:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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 + @"\
    oot\\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();
            }
        }
    }

    参考文献:
    如何找出使用.NET锁定文件的进程?

    删除有人打开文件的目录


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

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


    噢,我几年前采用的一大技巧是Windows不允许您删除文件,但可以删除文件。

    伪代码排序:

    1
    2
    3
    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在下次重新启动整个系统时删除旧版本,效果很好。


    推荐阅读