Как удалить файл, который заблокирован другим процессом в C #?

голоса
51

Я ищу способ удаления файла , который заблокирован другим процессом с помощью C #. Я подозреваю , что этот метод должен быть в состоянии найти какой процесс блокировки файла (возможно путем отслеживания ручек, хотя я не знаю , как сделать это в C #) , а затем закрыть этот процесс , прежде чем быть в состоянии выполнить файл удалить , используя File.Delete().

Задан 04/08/2008 в 06:45
источник пользователем
На других языках...                            


8 ответов

голоса
34

Убийство других процессов не является здоровым , что нужно сделать. Если ваш сценарий предполагает нечто вроде удаления, вы можете использовать MoveFileExфункцию API , чтобы пометить файл для удаления при следующей перезагрузке.

Если окажется, что вам действительно нужно удалить файл используется другим процессом, я бы рекомендовал повторно рассматривать реальную проблему, прежде чем рассматривать какие-либо решения.

Ответил 04/08/2008 в 07:01
источник пользователем

голоса
14

Типичный метод состоит в следующем. Вы сказали, что вы хотите сделать это в C #, так что здесь идет ...

  1. Если вы не знаете, какой процесс файл закрывается, вам нужно изучить список ручки каждого процесса, и запрашивать каждый дескриптор, чтобы определить, если он идентифицирует заблокированный файл. Делать это в C #, вероятно, потребует P / Invoke или посредник C ++ / CLI для вызова API, нативные вам нужно.
  2. После того, как вы выяснили, какой процесс (ы) есть файл закрывается, вам нужно безопасно вводить небольшую родную DLL в процесс (вы также можете придать управляемый DLL, но это Мессье, а затем вы должны начать или прикрепить к .NET времени выполнения).
  3. Это загрузчик DLL затем закрывает дескриптор, используя CloseHandle и т.д.

По существу: способ разблокировать «запертой» файл, чтобы впрыснуть DLL-файла в адресное пространство процесса нарушившей и закрыть его самостоятельно. Вы можете сделать это, используя родной или управляемый код. Независимо от того, что вы не будете нуждаться небольшое количество нативного кода или, по крайней мере, P / Invoke в то же самое.

Полезные ссылки:

Удачи!

Ответил 04/08/2008 в 07:15
источник пользователем

голоса
7

Если вы хотите сделать это программно. Я не уверен ... и я очень рекомендую против него. Если вы только устранение неполадок вещи на вашей собственной машине, SysInternals Process Explorer может помочь вам

Запустите его, используйте Найти Handle команды (я думаю, что это либо в меню найти или ручку), и поиск по имени файла. После того, как ручка (ы) найден, вы можете принудительно закрыть их.

Затем вы можете удалить файл и так далее.

Осторожно , делая это может вызвать программу , которая владеет ручками вести себя странно, как вы только что вытащили пресловутый ковер из - под него, но он хорошо работает при отладке своей странствующей коды, или когда Visual Studio / Windows Explorer , это быть дерьмом и не отпуская дескрипторы файлов , даже если вы им сказали , чтобы закрыть файл давным - давно ... вздох :-)

Ответил 04/08/2008 в 07:12
источник пользователем

голоса
4

Используя советы Orion Edwards Я скачал Sysinternals Process Explorer , который , в свою очередь позволило мне узнать , что файл я имел трудности удаляющие был на самом деле проводится не по Excel.Applicationsобъекту , я подумал, а тот факт , что мой C # код отправки почты код был создан объект Attachment , который оставил ручку этот файл открыт.

Как только я увидел это, я довольно просто называется по методу Dispose объекта Attachment, а ручка была выпущена.

Исследователь Sysinternals позволил мне обнаружить это использовать в сочетании с Visual Studio 2005 отладчик.

Я очень рекомендую этот инструмент!

Ответил 04/03/2009 в 14:43
источник пользователем

голоса
4

Вы можете использовать эту программу, ручки , чтобы узнать , какой процесс имеет блокировку на файл. Это инструмент командной строки, так что я предполагаю , что вы используете выход из этого ... Я не уверен , что найти его программно.

Если удаление файла может ждать, вы можете указать его для удаления, когда ваш компьютер следующего запуска:

  1. Пуск REGEDT32 (W2K)или REGEDIT (WXP)и перейдите к:

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

    • W2K:
      Редактировать
      Добавить значение ...
      Тип данных: REG_MULTI_SZ
      Значение имени:PendingFileRenameOperations
      OK

    • WXP:
      Edit
      New
      Multi-String Значение
      enter
      PendingFileRenameOperations

  3. В области данных, введите "\??\" + filenameбыть удалены. LFNs могут быть введены без встроенного в кавычки. Для удаления C:\Long Directory Name\Long File Name.exe, введите следующие данные:

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

    Затем нажмите OK.

  4. «Имя файла назначения» является нулевым (ноль) строка. Он вводится следующим образом:

    • W2K:
      Edit
      Binary
      Format выберите Data: Hex
      нажмите на конце шестнадцатеричной строки
      введите 0000 (четыре нуля)
      OK

    • WXP:
      Щелкните правой кнопкой мыши значение
      выберите «Изменить двоичных данных»
      нажмите на конце шестнадцатеричной строки
      введите 0000 (четыре нуля)
      OK

  5. Закрыть REGEDT32/REGEDITи перезагрузите компьютер , чтобы удалить файл.

(Бессовестно украдены из некоторого случайного форума , ради потомства.)

Ответил 04/08/2008 в 06:59
источник пользователем

голоса
3

Это выглядит многообещающим. Способ убийства описателя файла ....

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

Ответил 25/02/2009 в 18:41
источник пользователем

голоса
3

О, один большой хак я использовал лет назад, является то , что ОС 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чтобы удалить старую в следующий раз , вся система перезапускается, работал очень хорошо.

Ответил 04/08/2008 в 07:14
источник пользователем

голоса
2

Вы можете использовать код , который вы указываете полный путь к файлу, и он будет возвращать 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();
        }
    }
}

Ссылки:
Как узнать, какой процесс блокировки файла с использованием .NET?

Удалить каталог, в котором кто-то открыл файл

Ответил 27/01/2017 в 20:45
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more