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

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

Связанные - stackoverflow.com/questions/1760481/…

vapcguy 27.01.2017 23:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
56
1
51 921
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

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

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

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

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

    • W2K:
      Edit
      Добавить значение ...
      Тип данных: REG_MULTI_SZ
      Имя значения: PendingFileRenameOperations
      OK

    • WXP:
      Edit
      New
      Многострочное значение
      enter
      PendingFileRenameOperations

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

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

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

  4. «Имя файла назначения» - это пустая (нулевая) строка. Введено следующее:

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

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

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

(Shamelessly stolen from some random forum, for posterity's sake.)

Ответ принят как подходящий

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

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

Обратите внимание на это замечание MSDN: MOVEFILE_DELAY_UNTIL_REBOOT - «... Это значение может использоваться, только если процесс находится в контексте пользователя, который принадлежит к группе администраторов или учетной записи LocalSystem ...»

Uwe Keim 22.08.2012 11:21

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

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

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

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

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

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

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

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

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

Удачи!

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

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

Воспользовавшись советом Orion Edwards, я загрузил Sysinternals Обозреватель процессов, который, в свою очередь, позволил мне обнаружить, что файл, который у меня возникли трудности с удалением, на самом деле удерживался не объектом Excel.Applications, как я думал, а скорее тем фактом, что мой код отправки почты на C# создал объект Attachment, который оставил дескриптор этого файла открытым.

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

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

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

Это тоже была моя проблема. Спасибо +1

Fandango68 28.03.2014 04:02

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

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

Другие вопросы по теме