Cum pot șterge un fișier care este blocat de un alt proces în C #?

voturi
51

Caut o modalitate de a șterge un fișier care este blocat de un alt proces folosind C #. Bănuiesc metoda trebuie să fie capabil de a găsi care procesul este blocarea fișierului (probabil prin urmărirea mânere, deși nu sunt sigur cum să facă acest lucru în C #) , apoi închideți acel proces înainte de a putea finaliza fișierul șterge folosind File.Delete().

Întrebat 04/08/2008 la 06:45
sursa de către utilizator
În alte limbi...                            


8 răspunsuri

voturi
34

Uciderea altor procese nu este un lucru sănătos de a face. În cazul în care scenariul dvs. implică ceva de genul dezinstalării, ai putea folosi MoveFileExfuncția API pentru a marca fișierul pentru ștergere la următoarea repornire.

În cazul în care se pare că într-adevăr nevoie pentru a șterge un fișier utilizat de un alt proces, mi-ar recomanda-re în considerare problema reală înainte de a lua orice soluții.

Publicat 04/08/2008 la 07:01
sursa de către utilizator

voturi
14

Metoda tipică este următoarea. Ai spus că vrei să faci acest lucru în C # așa că aici se duce ...

  1. Dacă nu știți ce proces are fișierul blocat, va trebui să examineze lista de mânerul fiecărui proces, iar interogarea fiecare mâner pentru a determina dacă acesta identifică fișierul blocat. Făcând acest lucru în C # va necesita probabil P / Invoke sau un intermediar C ++ / CLI pentru a apela API-uri native ai avea nevoie.
  2. După ce v-ați dat seama care procesul (ele) au blocat fișierul, va trebui să vă injectați în condiții de siguranță un mic DLL nativ în procesul (vă puteți injecta, de asemenea, un DLL gestionat, dar acest lucru este Messier, așa cum trebuie apoi să înceapă sau atașați la runtime .NET).
  3. Asta DLL bootstrap, apoi se închide mânerul folosind CloseHandle, etc.

În esență: modul de a debloca un fișier „blocat“ este de a injecta un fișier DLL în spațiul de adrese procesul de ofensatoare și aproape singur. Puteți face acest cod nativ sau gestionat folosind. Nu contează ce, vei avea nevoie de o cantitate mică de cod nativ sau cel puțin P / Invocă în același.

Link-uri utile:

Mult noroc!

Publicat 04/08/2008 la 07:15
sursa de către utilizator

voturi
7

Dacă doriți să - l facă prin programare. Nu sunt sigur ... și aș recomanda cu adevărat împotriva ei. Dacă sunteți doar depanare chestii pe cont propriu mașină, Sysinternals Process Explorer vă poate ajuta

Rulați-l, utilizați Find Manipulați comanda (cred că e fie în meniul găsi sau mâner), și căutați numele fișierului. După ce mânerul (e) este găsit, le puteți închide forțat.

Puteți șterge apoi fișierul și așa mai departe.

Feriți - vă , făcând acest lucru poate cauza programului care deține mânerele să se comporte ciudat, așa cum tocmai ai tras preșul proverbiala de sub ea, dar funcționează bine atunci când sunt depanare codul de rătăcitor proprii, sau atunci când Visual Studio / Windows Explorer este în curs de rahat și nu eliberează mânere de fișiere , chiar dacă le - ai spus să închidă vârstele fișier acum ... suspin :-)

Publicat 04/08/2008 la 07:12
sursa de către utilizator

voturi
4

Folosind sfatul Orion Edwards Am descarcat Sysinternals Explorer proces care , la rândul său , mi -a permis să descopere că fișierul am avut dificultăți la ștergerea a fost , de fapt , fiind deținut nu de Excel.Applicationsobiectul m - am gândit, ci mai degrabă a creat faptul că C codul meu # trimite codul de e - mail un obiect atașare care a lăsat un mâner pentru acest fișier deschis.

După ce am văzut acest lucru, am destul de simplu, numit pe metoda Evacuați a obiectului atașament, iar mânerul a fost eliberat.

Sysinternals Explorer mi-a permis să descopere acest lucru folosite în conjuncție cu Visual Studio 2005 debugger.

Am foarte recomanda acest instrument!

Publicat 04/03/2009 la 14:43
sursa de către utilizator

voturi
4

Puteți utiliza acest program, mâner , pentru a găsi proces care are incuietoarea fișierul. Este un instrument de linie de comandă, deci cred că utilizați de ieșire din asta ... Nu sunt sigur despre găsirea asta programatic.

Dacă ștergerea fișierului poate aștepta, ai putea specifica pentru ștergere atunci când computerul pornește în sus următoarea:

  1. Start REGEDT32 (W2K)sau REGEDIT (WXP)și navigați la:

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

    • W2K:
      Edit
      Add Value ...
      Tip de date: REG_MULTI_SZ
      Valoare Nume:PendingFileRenameOperations
      OK

    • WXP:
      Editare
      New
      Multi-String Value
      enter
      PendingFileRenameOperations

  3. În zona de date, introduceți "\??\" + filenamepentru a fi șterse. LFNs pot fi introduse fără a fi încorporate în ghilimele. Pentru a șterge C:\Long Directory Name\Long File Name.exe, introduceți următoarele date:

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

    Apoi apăsați OK.

  4. „Numele fișierului destinație“ este un nul (zero) șir de caractere. Acesta este introdus după cum urmează:

    • W2K:
      Editare
      binar
      Format selectați Date: Hex
      clic la sfârșitul șirului hex
      introduceți 0000 (patru zerouri)
      OK

    • WXP:
      Faceți clic dreapta pe valoarea
      alege „Modificare date binare“ ,
      faceți clic la sfârșitul șirului hex
      introduceți 0000 (patru zerouri)
      OK

  5. Închide REGEDT32/REGEDITși repornirea sistemului pentru a șterge fișierul.

(Nerusinat furat de la unele forum aleatoriu , de dragul posterității.)

Publicat 04/08/2008 la 06:59
sursa de către utilizator

voturi
3

Acest lucru pare promițător. O modalitate de a ucide mânerul de fișiere ....

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

Publicat 25/02/2009 la 18:41
sursa de către utilizator

voturi
3

Oh, o mare hack - am angajat cu ani în urmă, este faptul că Windows nu va permite să ștergeți fișiere, dar nu vă permite să mutați - le.

Pseudo-sort-de-cod:

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

În cazul în care cererile repornit (rețineți că nu am nevoie pentru a reporni aparatul), au încărcat noi mfc42.dll, și totul a fost bine. Aceasta, împreună cu PendingFileOperationspentru a șterge cel vechi data viitoare întregul sistem repornit, a lucrat destul de bine.

Publicat 04/08/2008 la 07:14
sursa de către utilizator

voturi
2

Puteți folosi codul pe care le furnizați calea de fișier complet la, și va returna o List<Processes>de blocare care fișier nimic:

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

Apoi, itera lista de procese și închideți-le și ștergeți fișierele:

    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, în funcție de cazul în care fișierul este pe computerul local:

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

sau un computer din rețea:

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

Referințe:
Cum pot afla care este procesul de blocare un fișier folosind .NET?

Ștergerea unui director în cazul în care cineva a deschis un fișier

Publicat 27/01/2017 la 20:45
sursa de către utilizator

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