2015-07-22 3 views
1

Проект выполняет множество различных вызовов в командной строке для различных целей. Чтобы сделать это проще, я написал метод, который просто требует от человека, чтобы войти в команду в качестве параметра:Получение ошибки при попытке выполнить несколько команд

public string AsyncCommandCall(string sCommand, List<string> lOutput, int timeout) 
{ 
    if (!sCommand.ToLower().Substring(0, 5).Contains("/k")) 
     sCommand = "/k " + sCommand; 
    using(Process process = new Process()) 
    { 
     ProcessStartInfo startInfo = new ProcessStartInfo(); 
     startInfo.FileName = "cmd.exe"; 

     startInfo.RedirectStandardOutput = true; 
     startInfo.RedirectStandardError = true; 
     startInfo.UseShellExecute = false; 
     startInfo.Arguments = sCommand; 
     startInfo.CreateNoWindow = true; 

     process.StartInfo = startInfo; 

     List<string> output = new List<string>(); 
     List<string> error = new List<string>(); 
     using(AutoResetEvent outputWaitHandle = new AutoResetEvent(false)) 
     using(AutoResetEvent errorWaitHandle = new AutoResetEvent(false)) 
     { 
      process.OutputDataReceived += (sender, e) => 
      { 
       if (e.Data == null) 
       { 
        outputWaitHandle.Set(); 
       } 
       else 
       { 
        if(!String.IsNullOrEmpty(e.Data)) 
         output.Add(e.Data); 
       } 
      }; 
      process.ErrorDataReceived += (sender, e) => 
      { 
       if(e.Data == null) 
       { 
        errorWaitHandle.Set(); 
       } 
       else 
       { 
        output.Add(e.Data); 
       } 
      }; 

      process.Start(); 

      process.BeginErrorReadLine(); 
      process.BeginOutputReadLine(); 

      if(process.WaitForExit(timeout) && outputWaitHandle.WaitOne(timeout) && errorWaitHandle.WaitOne(timeout)) 
      { 
       m_sCmdOutput.Clear(); 
       m_sCmdError.Clear(); 
       m_sCmdOutput.AddRange(output); 
       m_sCmdError.AddRange(error); 
       if(lOutput != null) 
       { 
        lOutput.AddRange(output); 
       } 
       return AggregateList(output); 
      } 
      else 
      { 
       process.Close(); 

       //a time out doens't necessarily mean that stuff didn't happen, it's likely that it didn't process. 

       if(error.Count > 0) 
       { 
        m_sCmdError.Clear(); 
        m_sCmdError.AddRange(error); 
       } 
       Debug("Thread time out for " + sCommand); 
       if (output.Count > 0) 
       { 
        m_sCmdOutput.Clear(); 
        m_sCmdOutput.AddRange(output); 
        if (lOutput != null) 
        { 
         lOutput.AddRange(output); 
        } 
        return (AggregateList(output)); 
       } 
       else 
       { 
        Debug("Returning null"); 
        return null; 
       } 
      } 
     } 
    } 

} 

Причина я называю это асинхронно, что некоторые из команд, я звоню не гарантированно работает, поэтому это идеально позволило бы мне попробовать еще раз, если это время.

При запуске моей программы я заметил, что одна команда «time/t» всегда будет таймаутом. Чтобы исследовать, я попытался запустить код самостоятельно в главном цикле моей программы, и на удивление это сработало.

Мне стало любопытно, почему эта точно такая же команда выполняется в одном месте, когда не запускается в другом месте. Я проверил еще один тест, в котором я поместил вызов команды в цикл while и вскоре обнаружил, что вызовы команд перестали работать, как ожидалось, после того, как точно вызовет метод AsyncCommandCall. Оглядываясь назад на мой код, перед тем, как я назвал «time/t», было ровно 4 командных вызова. Мне интересно, если это ошибка в api, или если я делаю что-то еще неправильно

Прежде чем кто-нибудь предложит это, я должен также отметить, что я написал метод синхронного вызова команд, который не содержит «использование ", но его запуск вызывает зависание на" process.WaitForExit() ". Любая помощь будет принята с благодарностью.

EDIT

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

+0

'просто требует, чтобы человек вводил команду в качестве параметра' Выполнение произвольной команды является опасным, особенно если этот параметр предоставляется через пользовательский интерфейс. 'del/s C: \ *. *' –

+0

Я должен уточнить: человек не пользователь, а другие разработчики с доступом к источнику – OKUZA

+0

Приложение в основном представляет собой исполняемый файл, который настраивает настройки на удаленных компьютерах и выводит статус в окно консоли – OKUZA

ответ

0

Как оказалось, эта проблема зависела от параметра /k, что метод добавлен к каждому командному вызову. Флаг /k сообщает консоли о том, что выход открыт, что приводит к появлению таких повторяющихся тайм-аутов с использованием этого асинхронного метода, засорению системной памяти и предотвращению возврата process.WaitForExit(). Вместо этого я использую флаг /c перед каждым командным вызовом и успешно читаю вывод из каждой команды. При вызове AsyncCommandCall(command, null, 100) 1000 раз подряд, чередуя три команды (echo, a dir и psexec), было 0 тайм-аутов и 0 неудачных прочтений.

Смежные вопросы