Jak usunąć plik, który jest zablokowany przez inny proces w C#?

Szukam sposobu na usunięcie pliku, który jest zablokowany przez inny proces za pomocą C#. Podejrzewam, że metoda musi być w stanie znaleźć, który proces blokuje plik (być może śledząc uchwyty, chociaż nie jestem pewien, jak to zrobić w C#), a następnie zamknąć ten proces, zanim będzie w stanie zakończyć usuwanie pliku za pomocą File.Delete().

Author: ckittel, 2008-08-04

8 answers

Zabijanie innych procesów nie jest rzeczą zdrową. Jeśli twój scenariusz wiąże się z czymś takim jak Deinstalacja, możesz użyć MoveFileEx funkcja API oznaczająca plik do usunięcia przy następnym restarcie.

Jeśli okaże się, że naprawdę musisz usunąć plik używany przez inny proces, zalecam ponowne rozważenie rzeczywistego problemu przed rozważeniem jakichkolwiek rozwiązań.

 34
Author: Ishmaeel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-22 07:15:09

Typowa metoda jest następująca. Powiedziałeś, że chcesz to zrobić w C# więc zaczynamy...

  1. Jeśli nie wiesz, który proces ma zablokowany plik, musisz sprawdzić listę obsługiwanych procesów i odpytywać każdy z nich, aby ustalić, czy identyfikuje on zablokowany plik. Robienie tego w C# będzie prawdopodobnie wymagało P / Invoke lub pośredniego C++ / CLI do wywołania natywnych API, których będziesz potrzebować.
  2. Gdy już zorientujesz się, które procesy mają zablokowany plik, będziesz musiał bezpiecznie wstrzyknąć mały natywny DLL do procesu (możesz również wstrzyknąć zarządzaną bibliotekę DLL, ale jest to messier, ponieważ musisz uruchomić lub dołączyć do środowiska uruchomieniowego. NET).
  3. Ten Bootstrap DLL następnie zamyka uchwyt za pomocą CloseHandle, itd.

Zasadniczo: sposobem odblokowania "zablokowanego" pliku jest wstrzyknięcie pliku DLL do przestrzeni adresowej procesu i zamknięcie go samodzielnie. Możesz to zrobić za pomocą kodu natywnego lub zarządzanego. Bez względu na wszystko, będziesz potrzebował niewielkiej ilości kodu natywnego lub na najmniej P / wywołaj do tego samego.

Pomocne linki:

Powodzenia!

 15
Author: Peter Mortensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 22:41:04

Jeśli chcesz to zrobić programowo. Nie jestem pewien... i naprawdę polecam. Jeśli tylko rozwiązujesz problemy na własnej maszynie, SysInternals Process Explorer może Ci pomóc

Uruchom go, użyj polecenia Znajdź Uchwyt (myślę, że jest to albo w menu Znajdź lub uchwyt) i wyszukaj nazwę pliku. Po znalezieniu uchwytu(uchwytów) można je siłą zamknąć.

Możesz następnie usunąć plik i tak dalej.

Strzeż się , robiąc to może program, który posiada uchwyty, zachowuje się dziwnie, ponieważ właśnie wyciągnąłeś przysłowiowy dywan spod niego, ale działa dobrze, gdy debugujesz swój własny błędny kod, lub gdy visual studio / windows explorer jest gówniany i nie zwalnia uchwytów plików, mimo że kazałeś im zamknąć plik wieki temu... westchnienie: -)

 7
Author: Orion Edwards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-04 06:12:11

Możesz użyć tego programu Handle , aby dowiedzieć się, który proces ma blokadę w Twoim pliku. To narzędzie wiersza poleceń, więc domyślam się, że używasz wyjścia z tego... Nie jestem pewien, czy uda mi się go znaleźć programowo.

Jeśli usunięcie pliku może poczekać, możesz określić go do usunięcia przy następnym uruchomieniu komputera:

  1. Uruchom REGEDT32 (W2K) lub REGEDIT (WXP) i przejdź do:

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

    • W2K:
      Edytuj
      Dodaj wartość...
      typ danych: REG_MULTI_SZ
      Nazwa wartości: PendingFileRenameOperations
      OK

    • WXP:
      Edit
      New
      Multi-String Value
      enter
      PendingFileRenameOperations

  3. W obszarze danych wpisz "\??\" + filename do usunięcia. LFNs maj być wprowadzane bez osadzania w cudzysłowach. Aby usunąć C:\Long Directory Name\Long File Name.exe, wprowadź następujące dane:

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

    Następnie naciśnij OK .

  4. "nazwa pliku docelowego" jest łańcuchem null (zero). Wpisuje się jak następuje:

    • W2K:
      Edit
      Binary
      select Data Format: Hex
      kliknij na końcu ciągu szesnastkowego
      enter 0000 (cztery zera)
      OK

    • WXP:
      kliknij prawym przyciskiem myszy wartość
      wybierz "Modyfikuj dane binarne"
      kliknij na końcu ciągu szesnastkowego
      wprowadź 0000 (cztery zera)
      OK

  5. Zamknij REGEDT32/REGEDIT i uruchom ponownie, aby usunąć plik.

(bezwstydnie skradzione z jakiegoś przypadkowego forum, dla potomności.)

 4
Author: Ryan Fox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-30 16:13:42

Korzystając z porad Oriona Edwardsa pobrałem Sysinternals Process Explorer co z kolei pozwoliło mi odkryć, że plik, który miałem problemy z usunięciem, w rzeczywistości nie był utrzymywany przez obiekt Excel.Applications, o którym myślałem, ale raczej fakt, że mój kod C# send mail code stworzył obiekt załącznika, który zostawił uchwyt do tego pliku otwarty.

Kiedy to zobaczyłem, dość prosto wywołałem metodę usuwania obiektu mocującego i uchwyt został zwolniony.

The Sysinternals explorer pozwolił mi odkryć to używane w połączeniu z debugerem Visual Studio 2005.

Gorąco polecam to narzędzie!

 4
Author: Peter Mortensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 22:44:03

Oh, jeden wielki hack, którego użyłem lata temu, jest to, że Windows nie pozwala Ci usunąć pliki, ale pozwala Ciprzenieść je.

Pseudo-sort-of-code:

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

Po ponownym uruchomieniu aplikacji (zauważ, że nie musieliśmy ponownie uruchamiać maszyny), załadowali nową mfc42.dll i wszystko było dobrze. To, w połączeniu z PendingFileOperations usunięciem starego przy następnym ponownym uruchomieniu całego systemu, działało całkiem nieźle.

 3
Author: Orion Edwards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-20 20:52:11

To wygląda obiecująco. Sposób na zabicie obsługi plików....

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

 3
Author: solrevdev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-15 14:25:53

Możesz użyć kodu, do którego podasz pełną ścieżkę do pliku, a zwróci List<Processes> wszystkiego, co blokuje ten plik:

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

Następnie iteruj listę procesów, zamknij je i usuń pliki:

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

I w zależności od tego, czy plik znajduje się na komputerze lokalnym:

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

Lub komputer sieciowy:

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

Linki:
Skąd mam wiedzieć, który proces blokuje plik za pomocą. NET?

Usuń katalog, w którym ktoś otworzył plik

 3
Author: vapcguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:58