Wie lösche ich eine Datei, die von einem anderen Prozess in C # gesperrt wurde?

Translate

Ich suche nach einer Möglichkeit, eine Datei zu löschen, die von einem anderen Prozess mit C # gesperrt wurde. Ich vermute, dass die Methode in der Lage sein muss, herauszufinden, welcher Prozess die Datei sperrt (möglicherweise durch Verfolgen der Handles, obwohl ich nicht sicher bin, wie dies in C # zu tun ist), und diesen Prozess dann zu schließen, bevor das Löschen der Datei mit abgeschlossen werden kannFile.Delete().

This question and all comments follow the "Attribution Required."

Alle Antworten

Translate

Andere Prozesse zu töten ist keine gesunde Sache. Wenn Ihr Szenario so etwas wie eine Deinstallation beinhaltet, können Sie die verwendenMoveFileExAPI-Funktionum die Datei beim nächsten Neustart zum Löschen zu markieren.

Wenn es den Anschein hat, dass Sie eine Datei, die von einem anderen Prozess verwendet wird, wirklich löschen müssen, würde ich empfehlen, das eigentliche Problem erneut zu prüfen, bevor Sie Lösungen in Betracht ziehen.

Quelle
Louise Lee
Translate

Das typische Verfahren ist wie folgt. Sie haben gesagt, dass Sie dies in C # tun möchten, also geht es weiter ...

  1. Wenn Sie nicht wissen, für welchen Prozess die Datei gesperrt ist, müssen Sie die Handle-Liste jedes Prozesses untersuchen und jedes Handle abfragen, um festzustellen, ob die gesperrte Datei identifiziert wird. Um dies in C # zu tun, ist wahrscheinlich P / Invoke oder eine zwischengeschaltete C ++ / CLI erforderlich, um die nativen APIs aufzurufen, die Sie benötigen.
  2. Sobald Sie herausgefunden haben, für welche Prozesse die Datei gesperrt ist, müssen Sie sicher eine kleine native DLL in den Prozess einfügen (Sie können auch eine verwaltete DLL einfügen, dies ist jedoch unübersichtlicher, da Sie dann starten müssen oder an die .NET-Laufzeit anhängen).
  3. Diese Bootstrap-DLL schließt dann das Handle mit CloseHandle usw.

Im Wesentlichen: Um eine "gesperrte" Datei zu entsperren, müssen Sie eine DLL-Datei in den Adressraum des betreffenden Prozesses einfügen und selbst schließen. Sie können dies mit nativem oder verwaltetem Code tun. Egal was passiert, Sie benötigen eine kleine Menge nativen Codes oder mindestens P / Invoke.

Hilfreiche Links:

Viel Glück!

Quelle
Translate

Wenn Sie es programmatisch tun möchten. Ich bin mir nicht sicher ... und ich würde es wirklich empfehlen. Wenn Sie nur Fehler auf Ihrem eigenen Computer beheben,SysInternals Process Explorerkann dir helfen

Führen Sie es aus, verwenden Sie den Befehl "Handle suchen" (ich glaube, er befindet sich entweder im Menü "Suchen" oder "Handle") und suchen Sie nach dem Namen Ihrer Datei. Sobald die Griffe gefunden sind, können Sie sie zwangsweise schließen.

Sie können dann die Datei löschen und so weiter.

In acht nehmenDies kann dazu führen, dass sich das Programm, dem die Handles gehören, seltsam verhält, da Sie gerade den sprichwörtlichen Teppich darunter herausgezogen haben. Dies funktioniert jedoch gut, wenn Sie Ihren eigenen fehlerhaften Code debuggen oder wenn Visual Studio / Windows Explorer dies ist Mist sein und keine Dateihandles freigeben, obwohl du ihnen vor Ewigkeiten gesagt hast, sie sollen die Datei schließen ... seufz :-)

Quelle
Translate

Sie können dieses Programm verwenden,Griff, um herauszufinden, welcher Prozess die Sperre für Ihre Datei hat. Es ist ein Befehlszeilentool, also verwenden Sie wahrscheinlich die Ausgabe davon ... Ich bin mir nicht sicher, ob ich sie programmgesteuert finden soll.

Wenn das Löschen der Datei warten kann, können Sie sie beim nächsten Start Ihres Computers zum Löschen angeben:

  1. StartREGEDT32 (W2K)oderREGEDIT (WXP)und navigieren Sie zu:

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

    • W2K:
      Bearbeiten
      Mehrwert...
      Datentyp:REG_MULTI_SZ
      Wert Name:PendingFileRenameOperations
      OK

    • WXP:
      Bearbeiten
      Neu
      Multi-String-Wert
      eingeben
      PendingFileRenameOperations

  3. Geben Sie im Bereich Daten ein"\??\" + filenamegelöscht werden. LFNs können eingegeben werden, ohne in Anführungszeichen eingebettet zu sein. LöschenC:\Long Directory Name\Long File Name.exeGeben Sie folgende Daten ein:

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

    Dann drückenOK.

  4. Der "Name der Zieldatei" ist eine Nullzeichenfolge (Null). Es wird wie folgt eingegeben:

    • W2K:
      Bearbeiten
      Binär
      Wählen Sie Datenformat: Hex
      Klicken Sie auf das Ende der Hex-Zeichenfolge
      Geben Sie 0000 ein (vier Nullen).
      OK

    • WXP:
      Klicken Sie mit der rechten Maustaste auf den Wert
      Wählen Sie "Binärdaten ändern".
      Klicken Sie auf das Ende der Hex-Zeichenfolge
      Geben Sie 0000 ein (vier Nullen).
      OK

  5. SchließenREGEDT32/REGEDITund neu starten, um die Datei zu löschen.

(Schamlos gestohlen vonirgendein zufälliges Forumum der Nachwelt willen.)

Quelle
Armand Lee
Translate

Mit dem Rat von Orion Edwards habe ich die Sysinternals heruntergeladenProcess Explorerwas mir wiederum erlaubte herauszufinden, dass die Datei, die ich beim Löschen hatte, tatsächlich nicht von der gehalten wurdeExcel.ApplicationsObjekt, dachte ich, sondern die Tatsache, dass mein C # -Code-Sende-Mail-Code ein Attachment-Objekt erstellt hatte, das ein Handle für diese Datei offen ließ.

Als ich das sah, rief ich ganz einfach die Entsorgungsmethode des Attachment-Objekts auf, und der Handle wurde freigegeben.

Mit dem Sysinternals-Explorer konnte ich feststellen, dass dies in Verbindung mit dem Visual Studio 2005-Debugger verwendet wurde.

Ich kann dieses Tool nur empfehlen!

Quelle
Translate

Oh, ein großer Hack, den ich vor Jahren eingesetzt habe, ist, dass Windows Sie nicht zulässtlöschenDateien, aber es lässt SieBewegungSie.

Pseudo-Art-Code:

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

Beim Neustart der Anwendungen (beachten Sie, dass der Computer nicht neu gestartet werden musste) wurde die neue geladenmfc42.dllund alles war gut. Das gepaart mitPendingFileOperationsDas alte beim nächsten Neustart des gesamten Systems zu löschen, funktionierte ziemlich gut.

Quelle
Translate

Das sieht vielversprechend aus. Eine Möglichkeit, das Dateihandle zu beenden ....

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

Quelle
Translate

Sie können Code verwenden, für den Sie den vollständigen Dateipfad angeben, und es wird a zurückgegebenList<Processes>von irgendetwas, das diese Datei sperrt:

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

Wiederholen Sie dann die Liste der Prozesse, schließen Sie sie und löschen Sie die Dateien:

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

Und je nachdem, ob sich die Datei auf dem lokalen Computer befindet:

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

oder ein Netzwerkcomputer:

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

Verweise:
Wie finde ich heraus, welcher Prozess eine Datei mit .NET sperrt?

Löschen Sie ein Verzeichnis, in dem jemand eine Datei geöffnet hat

Quelle