在vc++中使用CreateProcess和CreatePipe来执行进程并以字符串形式返回输出

CreateProcess and CreatePipe to execute a process and return output as a string in VC++

本文关键字:进程 字符串 输出 返回 执行 vc++ CreateProcess CreatePipe      更新时间:2023-10-16

我正在尝试使用CreateProcessCreatePipe在Visual Studio 2010中从Windows Forms c++/CLR应用程序中执行进程。

在我的Windows窗体应用程序中,我想执行一个子进程(控制台应用程序),并在我的Windows窗体应用程序中返回std::string, std::wstringSystem::String^的输出。此外,我不希望新创建的子进程产生一个窗口。

控制台应用程序是我自己创建的,所以我也可以控制它的源代码。

我已经看到了下面的例子,但是我不明白如何修改代码来完成我想做的事情:

  • MSDN
  • kgui
  • 一个更简单的基于MFC的函数

MSDN代码似乎写成了两个控制台应用程序,一个调用另一个。这代码把我搞糊涂了。我使用c++才4个月左右,所以我还不是完全理解。它似乎引用了一个文本文件,我不需要这样做。

有没有比MSDN的200多行代码或kgui的300多行代码更简单的方法来做到这一点?

这里的答案是有用的,但过于简单。我希望看到一个基本的源代码示例(最好是不涉及数百行复杂代码的示例)。我本来会使用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窗体应用程序中使用这个,虽然它编译好,不会引起任何错误,但它似乎也不起作用。我不知道为什么。

我是这样执行上面的代码的:
std::string ping = ExecuteExternalFile("ping.exe", "127.0.0.1");

它似乎没有做任何事情,除了在第一次执行时它给出一个非常奇怪的3个字符作为输出,然后在后续执行时,什么也没有。

您没有正确使用::ReadFile()函数。

阅读这里:http://msdn.microsoft.com/en-us/library/ms891445.aspx

基本上,如果函数不返回TRUE,则希望以错误失败,并且希望继续循环,直到它产生零reDword。

此外,::ReadFile()不会为您零终止您的数据,所以您必须自己做,像这样:buf[reDword] = '';(确保您的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] = '';
    string cstemp = buf;
    csoutput += cstemp;
}
return csoutput;

感谢Hans Passant的引导,让我得到了这段清晰简单的代码,这正是我所寻找的。

/// 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;

编辑:添加编码信息。