2008-10-08 12 views
7

Tengo algunos System.Diagnotics.Processes para ejecutar. Me gustaría llamar al método de cierre automáticamente. Aparentemente, la palabra clave "usar" hace esto por mí.¿Cómo uso programáticamente la palabra clave "usar" en C#?

¿Es esta la manera de utilizar la palabra clave using?

foreach(string command in S) // command is something like "c:\a.exe" 
{ 
    try 
    { 
     using(p = Process.Start(command)) 
     { 
      // I literally put nothing in here. 
     } 
    } 
    catch (Exception e) 
    { 
     // notify of process failure 
    } 
} 

Me gustaría iniciar varios procesos para que se ejecuten simultáneamente.

Respuesta

15
using(p = Process.Start(command)) 

Esto compilará, como la clase implementa ProcessIDisposable, sin embargo en realidad se quiere llamar al método Close.
Lógica quiere que el método Dispose llame al Close, y al profundizar en el CLR utilizando el reflector, podemos ver que de hecho lo hace por nosotros. Hasta aquí todo bien.

De nuevo, usando el reflector, miré lo que hace el método Close - libera el identificador de proceso win32 nativo subyacente y borra algunas variables miembro. Esto (liberar recursos externos) es exactamente lo que se supone que el patrón IDisposable debe hacer.

Sin embargo No estoy seguro de si esto es lo que quiere lograr aquí.

Liberar los controles subyacentes simplemente dice a Windows 'Ya no estoy interesado en seguir este otro proceso'. En ningún momento, esto hace que el otro proceso se cierre o que el proceso espere.

Si desea forzarlos a dejar de fumar, necesitará usar el método p.Kill() en los procesos; sin embargo, tenga en cuenta que nunca es una buena idea matar procesos ya que no pueden limpiarlos y pueden irse. detrás de archivos corruptos, etc.

Si desea esperar a que se retiren por su cuenta, puede usar p.WaitForExit(); sin embargo, esto solo funcionará si está esperando un proceso a la vez. Si quieres esperar a todos al mismo tiempo, se vuelve complicado.

Normalmente tendrá que utilizar WaitHandle.WaitAll para esto, pero ya que no hay manera de conseguir un objeto WaitHandle cabo de un System.Diagnostics.Process, no se puede hacer esto (en serio, wtf estaban pensando Microsoft?).

Puede girar un hilo para cada proceso y llamar a `WaitForExit en esos hilos, pero esta también es la manera incorrecta de hacerlo.

En su lugar, tiene que usar p/invoke para acceder a la función win12 WaitForMultipleObjects nativa.

He aquí una muestra (que he probado y realmente funciona)

[System.Runtime.InteropServices.DllImport("kernel32.dll")] 
static extern uint WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds); 

static void Main(string[] args) 
{ 
    var procs = new Process[] { 
     Process.Start(@"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 2'"), 
     Process.Start(@"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 3'"), 
     Process.Start(@"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 4'") }; 
    // all started asynchronously in the background 

    var handles = procs.Select(p => p.Handle).ToArray(); 
    WaitForMultipleObjects((uint)handles.Length, handles, true, uint.MaxValue); // uint.maxvalue waits forever 

} 
0
try 
{ 
    foreach(string command in S) // command is something like "c:\a.exe" 
    { 
     using(p = Process.Start(command)) 
     { 
     // I literally put nothing in here. 
     } 

    } 
} 
catch (Exception e) 
{ 
    // notify of process failure 
} 

La razón funciona es porque cuando la excepción ocurre, la variable p es competencia del técnico y por lo tanto es método Dispose se llama que cierra el proceso es la forma en que iría. Además, creo que te gustaría separar un hilo para cada comando en lugar de esperar a que termine un ejecutable antes de pasar al siguiente.

3

Como referencia: El utilizando de palabras clave para IDisposable objetos:

using(Writer writer = new Writer()) 
{ 
    writer.Write("Hello"); 
} 

es sólo compilador sintaxis. Lo que compila reduce a:

Writer writer = null; 
try 
{ 
    writer = new Writer(); 
    writer.Write("Hello"); 
} 
finally 
{ 
    if(writer != null) 
    { 
     ((IDisposable)writer).Dispose(); 
    } 
} 

using es un poco mejor ya que el compilador no le permite reasignar la referencia dentro de la escritora utilizando el bloque.

Las directrices marco Sección 9.3.1 p. 256 estado:

CONSIDERA proporcionar método Close(), además de la Dispose(), si cerca es la terminología estándar en la zona.


En el ejemplo de código, el try-catch externa es innecesaria (véase más arriba).

Usar probablemente no está haciendo lo que quiere aquí ya que Dispose() se llama tan pronto como p sale del alcance. Esto no cierra el proceso (probado).

Los procesos son independientes, por lo que a menos que llame al p.WaitForExit() se separan y hacen sus cosas independientemente de su programa.

De forma intuitiva, para un proceso, Cerrar() solo libera recursos pero deja el programa en ejecución. CloseMainWindow() puede funcionar para algunos procesos, y Kill() funcionará para eliminar cualquier proceso. Tanto CloseMainWindow() como Kill() pueden lanzar excepciones, así que ten cuidado si las estás usando en un bloque finally.

Para finalizar, aquí hay un código que espera a que los procesos finalicen pero no cancela los procesos cuando se produce una excepción. No digo que sea mejor que Orion Edwards, simplemente diferente.

List<System.Diagnostics.Process> processList = new List<System.Diagnostics.Process>(); 

try 
{ 
    foreach (string command in Commands) 
    { 
     processList.Add(System.Diagnostics.Process.Start(command)); 
    } 

    // loop until all spawned processes Exit normally. 
    while (processList.Any()) 
    { 
     System.Threading.Thread.Sleep(1000); // wait and see. 
     List<System.Diagnostics.Process> finished = (from o in processList 
                where o.HasExited 
                select o).ToList(); 

     processList = processList.Except(finished).ToList(); 
     foreach (var p in finished) 
     { 
      // could inspect exit code and exit time. 
      // note many properties are unavailable after process exits 
      p.Close(); 
     } 
    } 
} 
catch (Exception ex) 
{ 
    // log the exception 
    throw; 
} 
finally 
{ 
    foreach (var p in processList) 
    { 
     if (p != null) 
     { 
      //if (!p.HasExited) 
      // processes will still be running 
      // but CloseMainWindow() or Kill() can throw exceptions 
      p.Dispose(); 
     } 

    } 
} 

no me molesté Kill() 'ing fuera de los procesos debido a que el código se inicia llegar aún más feo. Lea la documentación msdn para obtener más información.

Cuestiones relacionadas