2008-10-08 9 views
7

J'ai des System.Diagnotics.Processes à exécuter. Je voudrais appeler la méthode de fermeture sur eux automatiquement. Apparemment, le mot-clé "using" fait cela pour moi. Est-ce la façon d'utiliser le mot-clé using? Je voudrais démarrer plusieurs processus pour exécuter simultanément.Comment utiliser par programme le mot-clé "using" en C#?

Répondre

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

Cela compilera, comme la classe Process implémente IDisposable, mais vous voulez vraiment appeler la méthode Close.
Logique voudrait que la méthode Dispose appelle Close pour vous, et en creusant dans le CLR en utilisant le réflecteur, nous pouvons voir que cela fait en fait pour nous. Jusqu'ici tout va bien.

Toujours en utilisant le réflecteur, j'ai regardé ce que la méthode Close fait - il libère le handle de processus natif win32 sous-jacent, et efface certaines variables membres. Ceci (libérant des ressources externes) est exactement ce que le modèle IDisposable est censé faire.

Cependant Je ne sais pas si c'est ce que vous voulez réaliser ici. Relâcher les poignées sous-jacentes indique simplement aux fenêtres «Je ne suis plus intéressé par le suivi de cet autre processus». À aucun moment, cela n'entraîne l'arrêt de l'autre processus ou l'attente de votre processus.

Si vous voulez les forcer à quitter, vous devez utiliser la méthode p.Kill() sur les processus - cependant, il est conseillé de ne pas tuer les processus car ils ne peuvent pas nettoyer après eux-mêmes, et peuvent laisser derrière des fichiers corrompus, et ainsi de suite.

Si vous voulez attendre qu'ils quittent d'eux-mêmes, vous pouvez utiliser p.WaitForExit() - mais cela ne fonctionnera que si vous attendez un processus à la fois. Si vous voulez les attendre tous simultanément, cela devient compliqué.

Normalement vous utilisez WaitHandle.WaitAll pour cela, mais comme il n'y a pas moyen d'obtenir un objet WaitHandle d'un System.Diagnostics.Process, vous ne pouvez pas le faire (sérieusement, wtf songeaient microsoft?).

Vous pouvez créer un thread pour chaque processus et appeler `WaitForExit dans ces threads, mais c'est également la mauvaise façon de le faire.

Vous devez utiliser p/invoke pour accéder à la fonction natif win32 WaitForMultipleObjects.

Voici un échantillon (que je l'ai testé et fonctionne réellement)

[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 raison pour laquelle cela fonctionne est parce que quand l'exception se produit, la variable p tombe hors de portée et donc sa méthode Dispose est appelée qui ferme le processus est de savoir comment cela irait. De plus, je pense que vous voudriez faire tourner un thread pour chaque commande plutôt que d'attendre qu'un exécutable finisse avant de passer au suivant.

3

Pour référence: Le utilise mot-clé pour IDisposable objets:

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

est juste compilateur syntaxe. Ce qu'il compile à est:

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

using est un peu mieux depuis le compilateur vous empêche de réattribuer la référence écrivain à l'intérieur du bloc à l'aide.

Les directives-cadres Section 9.3.1 p. 256 état:

EXAMINER fournissant méthode Close(), en plus de l'Dispose(), si proche est terminologie standard dans le domaine.


Dans votre exemple de code, le try-catch externe est inutile (voir ci-dessus). L'utilisation probablement ne fait pas ce que vous voulez ici puisque Dispose() est appelé dès que p sort de la portée. Cela ne ferme pas le processus (testé).

Les processus sont indépendants, donc à moins que vous n'appelez p.WaitForExit() ils essaient de faire leur propre chose complètement indépendant de votre programme.

Contre-intuitif, pour un processus, Close() libère uniquement des ressources mais laisse le programme en cours d'exécution. CloseMainWindow() peut fonctionner pour certains processus, et Kill() fonctionnera pour tuer n'importe quel processus. CloseMainWindow() et Kill() peuvent toutes deux lever des exceptions, donc faites attention si vous les utilisez dans un bloc finally. Pour finir, voici un code qui attend que les processus se terminent mais ne tue pas les processus lorsqu'une exception se produit. Je ne dis pas que c'est mieux qu'Orion Edwards, juste différent. Je n'ai pas dérangé Kill() à cause des processus parce que le code commence à être encore plus laid. Lisez la documentation msdn pour plus d'informations.

Questions connexes