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

我正在寻找一种方法来删除被另一个进程使用C#锁定的文件。我怀疑该方法必须能够找到哪个进程锁定文件(可能通过跟踪句柄,虽然我不知道如何在C#中执行此操作),然后关闭该进程,然后才能使用 File.Delete()</代码>。

0
额外 作者 vapcguy,

6 答案

如果你想以编程方式做。我不确定...我真的会推荐它。 如果您只是在自己的计算机上排查问题,请 SysInternals Process Explorer 可以帮你

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

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

Beware, doing this may cause the program which owns the handles to behave strangely, as you've just pulled the proverbial rug out from under it, but it works well when you are debugging your own errant code, or when visual studio / windows explorer is being crap and not releasing file handles even though you told them to close the file ages ago... sigh :-)

0
额外

哦,我多年前雇佣的一个大黑客就是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 结合,以在下次整个系统重新启动时删除旧的,工作得很好。

0
额外

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

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

  1. Start REGEDT32 (W2K) or REGEDIT (WXP) and navigate to:

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

    • W2K:
      Edit
      Add Value...
      Data Type: REG_MULTI_SZ
      Value Name: PendingFileRenameOperations
      OK

    • WXP:
      Edit
      New
      Multi-String Value
      enter
      PendingFileRenameOperations

  3. In the Data area, enter "\??\" + filename to be deleted. LFNs may be entered without being embedded in quotes. To delete C:\Long Directory Name\Long File Name.exe, enter the following data:

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

    Then press OK.

  4. The "destination file name" is a null (zero) string. It is entered as follows:

    • W2K:
      Edit
      Binary
      select Data Format: Hex
      click at the end of the hex string
      enter 0000 (four zeros)
      OK

    • WXP:
      Right-click the value
      choose "Modify Binary Data"
      click at the end of the hex string
      enter 0000 (four zeros)
      OK

  5. Close REGEDT32/REGEDIT and reboot to delete the file.

(无耻地从一些随机论坛盗取,为了后人的缘故。)

0
额外

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

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

0
额外
请注意MSDN备注: MOVEFILE_DELAY_UNTIL_REBOOT - “...仅当进程位于属于管理员组或本地系统帐户的用户的上下文中时,才能使用此值...”
额外 作者 Uwe Keim,

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

http://www.timstall.com/ 2009/02 /杀戮-文件句柄,但并非process.html

0
额外

You can use code that you supply the full file path to, and it will return a List of anything locking that file:

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);

    /// 
/// Find out what process(es) have a lock on the specified file. ///
 
    /// 
Path of the file.
    /// Processes locking the file
    /// 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)
    /// 
    /// 
    static public List WhoIsLocking(string path)
    {
        uint handle;
        string key = Guid.NewGuid().ToString();
        List processes = new List();

        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((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 lstProcs = new List();

    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();
        }
    }
}

References:
How do I find out which process is locking a file using .NET?

删除某人打开文件的目录

0
额外