Как программно использовать ключевое слово «using» в C #? - PullRequest
7 голосов
/ 09 октября 2008

У меня есть несколько System.Diagnotics.Processes для запуска. Я хотел бы вызвать метод close для них автоматически. Очевидно, ключевое слово "using" делает это для меня.

Это способ использовать ключевое слово 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
    }
}

Я бы хотел запустить несколько процессов одновременно.

Ответы [ 3 ]

15 голосов
/ 09 октября 2008
using(p = Process.Start(command))

Это скомпилируется, поскольку класс Process реализует IDisposable, однако вы действительно хотите вызвать метод Close.
Логика будет иметь то, что метод Dispose будет вызывать для вас Close, и, копаясь в CLR с помощью рефлектора, мы видим, что он действительно делает это для нас. Пока все хорошо.

Снова используя отражатель, я посмотрел, что делает метод Close - он освобождает основной дескриптор процесса win32 и очищает некоторые переменные-члены. Это (высвобождение внешних ресурсов) точно , что должен делать шаблон IDisposable.

Однако Я не уверен, что это то, чего вы хотите достичь здесь.

Выпуск базовых дескрипторов просто говорит окнам: «Я больше не заинтересован в отслеживании этого другого процесса». Это ни в коем случае не приводит к тому, что другой процесс завершает работу, и не заставляет ваш процесс ждать.

Если вы хотите принудительно завершить их, вам нужно использовать метод p.Kill() для процессов - однако имейте в виду, что убивать процессы никогда не будет хорошей идеей, поскольку они не могут очистить себя, и могут оставить поврежденные файлы и т. д.

Если вы хотите подождать, пока они выйдут самостоятельно, вы можете использовать p.WaitForExit() - однако это будет работать, только если вы ожидаете один процесс за раз. Если вы хотите дождаться их всех одновременно, это будет сложно.

Обычно вы бы использовали WaitHandle.WaitAll для этого, но, поскольку нет никакого способа получить WaitHandle объект из System.Diagnostics.Process, вы не можете этого сделать (серьезно, если бы Майкрософт подумал?).

Вы можете раскрутить поток для каждого процесса и вызвать `WaitForExit в этих потоках, но это также неправильный способ сделать это.

Вместо этого вам нужно использовать p / invoke для доступа к собственной функции win32 WaitForMultipleObjects.

Вот пример (который я тестировал и на самом деле работает)

[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

}
3 голосов
/ 09 октября 2008

Для справки: , использующее ключевое слово для IDisposable объектов:

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

- это просто синтаксис компилятора. То, к чему это сводится, является:

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

using немного лучше, поскольку компилятор не позволяет переназначить ссылку на запись в блоке using.

Рамочные руководящие принципы, раздел 9.3.1 с. 256 состояние:

CONSIDER предоставляющий метод Close (), в дополнение к Dispose (), если close является стандартной терминологией в области.


В вашем примере кода внешний try-catch не нужен (см. Выше).

Использование, вероятно, не делает того, что вы хотите, поскольку Dispose () вызывается, как только p выходит из области видимости. Это не закрывает процесс (проверено).

Процессы независимы, поэтому, если вы не позвоните p.WaitForExit(), они раскрутятся и сделают свое дело совершенно независимо от вашей программы.

Необычно, что для Процесса Close () только высвобождает ресурсы, но оставляет программу работающей. CloseMainWindow () может работать для некоторых процессов, а Kill () - для уничтожения любого процесса. И CloseMainWindow (), и Kill () могут генерировать исключения, поэтому будьте осторожны, если вы используете их в блоке finally.

В завершение приведем код, который ожидает завершения процессов, но не уничтожает процессы при возникновении исключения. Я не говорю, что он лучше, чем Орион Эдвардс, просто другой.

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

    }
}

Я не потрудился Kill () отключить процессы, потому что код стал еще хуже. Прочитайте документацию msdn для получения дополнительной информации.

0 голосов
/ 09 октября 2008
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
}

Причина, по которой это работает, заключается в том, что когда происходит исключение, переменная p выпадает из области видимости, и, таким образом, вызывается ее метод Dispose, который закрывает процесс так, как это будет происходить. Кроме того, я бы подумал, что вы захотите отключить поток для каждой команды, а не ждать завершения исполняемого файла, прежде чем переходить к следующей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...