CreateProcess и CreatePipe для выполнения процесса и возврата вывода в виде строки в VC ++ - PullRequest
3 голосов
/ 18 декабря 2011

Я пытаюсь использовать CreateProcess и CreatePipe для выполнения процесса из приложения Windows Forms C ++ / CLR в Visual Studio 2010.

Из приложения Windows Forms я хочу выполнитьдочерний процесс (консольное приложение) и возвращает вывод в виде std::string, std::wstring или System::String^ в моем приложении Windows Forms.Кроме того, я не хочу, чтобы вновь созданный дочерний процесс порождал окно.

Консольное приложение - мое собственное создание, поэтому я также могу контролировать его источник.

Я виделследующие примеры, но я не понимаю, как изменить код, чтобы выполнить то, что я пытаюсь сделать:

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

Есть ли более простой способ сделать это, чем 200+ строк кода MSDN или 300+ строк кода kgui?

Ответ здесь был полезен, но слишком упрощен.Я надеялся увидеть базовый исходный пример (предпочтительнее тот, который не содержит сотен строк сложного кода).Я бы использовал код MFC, но мне было трудно адаптировать его к моим целям (я не использую MFC).

Ниже приведена адаптация кода из Code Project:

string ExecuteExternalFile(string csExeName, string csArguments)
{
  string csExecute;
  csExecute=csExeName + " " + csArguments;

  SECURITY_ATTRIBUTES secattr; 
  ZeroMemory(&secattr,sizeof(secattr));
  secattr.nLength = sizeof(secattr);
  secattr.bInheritHandle = TRUE;

  HANDLE rPipe, wPipe;

  //Create pipes to write and read data

  CreatePipe(&rPipe,&wPipe,&secattr,0);
  //

  STARTUPINFO sInfo; 
  ZeroMemory(&sInfo,sizeof(sInfo));
  PROCESS_INFORMATION pInfo; 
  ZeroMemory(&pInfo,sizeof(pInfo));
  sInfo.cb=sizeof(sInfo);
  sInfo.dwFlags=STARTF_USESTDHANDLES;
  sInfo.hStdInput=NULL; 
  sInfo.hStdOutput=wPipe; 
  sInfo.hStdError=wPipe;

  //Create the process here.

  CreateProcess(0,(LPWSTR)csExecute.c_str(),0,0,TRUE,NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,0,0,&sInfo,&pInfo);
  CloseHandle(wPipe);

  //now read the output pipe here.

  char buf[100];
  DWORD reDword; 
  string m_csOutput,csTemp;
  BOOL res;
  do
  {
                  res=::ReadFile(rPipe,buf,100,&reDword,0);
                  csTemp=buf;
                  m_csOutput+=csTemp;
  }while(res);
  return m_csOutput;
}

Я пытался использовать это из моего приложения Windows Forms, и хотя оно компилируется нормально и не вызывает никаких ошибок, оно, похоже, тоже не работает.Я понятия не имею, почему.

Вот как я выполнил приведенный выше код:

std::string ping = ExecuteExternalFile("ping.exe", "127.0.0.1");

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

Ответы [ 2 ]

3 голосов
/ 18 декабря 2011

Спасибо Гансу Пассанту за отведение , благодаря которому я получил этот простой и понятный код, который делает именно то, что я искал.

/// this namespace call is necessary for the rest of the code to work
using namespace System::Diagnostics;
using namespace System::Text;/// added for encoding

Process^ myprocess = gcnew Process;
Encoding^ Encoding;/// added for encoding
Encoding->GetEncoding(GetOEMCP());/// added for encoding

myprocess->StartInfo->FileName = "ping.exe";
myprocess->StartInfo->Arguments = "127.0.0.1";
myprocess->StartInfo->UseShellExecute = false;

/// added the next line to keep a new window from opening
myprocess->StartInfo->CreateNoWindow = true;

myprocess->StartInfo->RedirectStandardOutput = true;
myprocess->StartInfo->StandardOutputEncoding = Encoding;/// added for encoding
myprocess->Start();

String^ output = gcnew String( myprocess->StandardOutput->ReadToEnd() );

myprocess->WaitForExit();

/// OutputBox is the name of a Windows Forms text box in my app.
OutputBox->Text = output;

EDIT: добавлена ​​информация о кодировке. Смотри код выше.

3 голосов
/ 18 декабря 2011

Вы неправильно используете функцию ::ReadFile().

Прочтите об этом здесь: http://msdn.microsoft.com/en-us/library/ms891445.aspx

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

Кроме того, ::ReadFile() не приведет к обнулению ваших данных для вас, поэтому вы должны сделать это самостоятельно, например так: buf[reDword] = '\0'; (убедитесь, что ваш buf задан 101 символом задолго до того, как это сделать.)

РЕДАКТИРОВАТЬ: Поскольку меня попросили предоставить пример кода, вот он, хотя я немы столкнулись с проблемой фактической компиляции, чтобы убедиться, что он работает, поэтому, пожалуйста, остерегайтесь синтаксических ошибок и обычно рассматривайте его только как грубый указатель на направление, в котором это должно быть сделано:

#define BUFFER_SIZE 100
string csoutput;
for( ;; )
{
    char buf[BUFFER_SIZE+1];
    DWORD redword; 
    if( !::ReadFile(rPipe,buf,BUFFER_SIZE,&redword,0) )
    {
        DWORD error = ::GetLastError();
        //throw new Exception( "Error " + error ); //or something similar
    }
    if( redword == 0 )
        break;
    buf[redword] = '\0';
    string cstemp = buf;
    csoutput += cstemp;
}
return csoutput;
...