2009-11-19 16 views
5

Ho una situazione in cui le persone sono connesse ai file su una condivisione e mi impedisce di sovrascrivere il file. Sto provando a scrivere un metodo che cercherà di vedere se un filePath che fornisco è attualmente bloccato in questo modo e chiude la sessione di rete di questa risorsa.Chiudere i file aperti utilizzando C#

Ho esaminato il provider Winnt ADSI ma il membro Resources.Remove non è implementato. Poi ho guardato Win32_ServerSession e mentre sono in grado di usare Delete member, uccide tutte le risorse per un determinato utente. Devo capire come essere più specifico.

Ho camminato per GetRelationsShips e Properties ma sono momentaneamente bloccato.

+0

Hi Chris. A mio avviso, la distribuzione automatica su file di risorse di lettura/scrittura su una rete è sempre errata. L'unico caso in cui lo farei è se i file stessi sono TUTTI di sola lettura in base alla progettazione, ma poi, come sviluppatori di installazione, affrontiamo sempre la mentalità "Just do it". Ho implementato un correttore per le scritture su file in una condivisione di rete usando C# e FileSystemWatcher. Non ha mai funzionato perché gli eventi generati sono diversi a seconda dell'hardware sottostante. Ecco alcuni dettagli: http: //www.codeproject.com/KB/files/AdvancedFileSystemWatcher.aspx? msg = 2982716 –

+0

In realtà questa domanda non ha nulla a che fare con la distribuzione/installazione. Era una situazione in cui una build automatizzata stava cercando di archiviare in una cartella conosciuta e c'erano dei file bloccati. –

+0

Non ho idea se puoi anche farlo - e se puoi è saggio? Alternativa: puoi inviare un messaggio all'utente in qualche modo - email, tramite l'app, ecc.? –

risposta

1

Sarebbe difficile prendere in considerazione tutte le implicazioni di ciò poiché non è possibile prevedere necessariamente il comportamento risultante dell'applicazione che al momento ha il file bloccato.

C'è un altro modo per farlo? Ad esempio, devi sovrascrivere il file subito o puoi avere qualche processo esterno che tenta continuamente di sovrascrivere il file ogni pochi minuti finché non riesce?

+0

Ti darò la risposta "non farlo". :-) So che non ho fornito le ragioni del perché avevo bisogno di farlo ma erano reali. Fortunatamente ho consegnato il compito a qualcun altro però. :-) –

1

Ho affrontato lo stesso problema. Finora so, l'unico modo per farlo, sta utilizzando il Win32API:

 
[DllImport("Netapi32.dll", SetLastError=true, CharSet = CharSet.Unicode)] 
public static extern int NetFileClose(string servername, int id); 

ho fatto un breve tentativo di realizzare questo e posso solo enum i file a destra, ma nel mio codice - ho avuto solo un'occhiata in esso - il codice per chiudere un file è impostato per essere un commento. Se vuoi provare, posso spedire una libreria [wrapper around NetFileXXX] e una breve demo, ma, come ho detto, non ho mai chiuso un file. Ma questo è forse un modo breve per farlo.

non so, come scambiare file sul StackOverflow ora :-(

br -?!? Mabra

1

È possibile utilizzare il codice che si fornisce il percorso completo del file di, e lo farà restituire un List<Processes> di tutto il blocco quel file:

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

Poi, scorrere l'elenco dei processi e chiuderle:

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

E a seconda se il file è sul computer locale:

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

o un computer di rete:

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

Riferimenti:
How do I find out which process is locking a file using .NET?

Delete a directory where someone has opened a file

Problemi correlati