我想获得正在运行的进程的实时输出。因此我在stdout,stdin和stderr的ExecuteCommand()
管道中创建了。之后我开始了一个过程,并在一秒后发送QTimer::singleShot()
执行CheckOutput()
。代码:
bool CommandExecutor_C::ExecuteCommand(QString &aCommand)
{
aCommand.push_front(std::getenv("ComSpec") + QString(" /c "));
mCommand = aCommand;
LOGINFO(FB_TDI,"Launch command: " + aCommand.toStdString());
SECURITY_ATTRIBUTES secattr;
ZeroMemory(&secattr,sizeof(secattr));
secattr.nLength = sizeof(secattr);
secattr.bInheritHandle = TRUE;
//in/out/err Handle
HANDLE StdInHandleRd, StdInHandleWr;//[0] read -> [1] write
HANDLE StdOutHandleRd, StdOutHandleWr;
HANDLE StdErrHandleRd, StdErrHandleWr;
//creating pipes to have access to handle contents
CreatePipe(&StdInHandleRd , &StdInHandleWr , &secattr, 4096);
CreatePipe(&StdOutHandleRd, &StdOutHandleWr , &secattr, 4096);
CreatePipe(&StdErrHandleRd, &StdErrHandleWr , &secattr, 4096);
//information for process
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = StdInHandleRd; /**< read handle/pipe */
si.hStdOutput = StdOutHandleWr; /**< write handle/pipe */
si.hStdError = StdErrHandleWr; /**< write handle/pipe */
mHandles.StdOutHandle = StdOutHandleRd;
mHandles.StdErrHandle = StdErrHandleRd;
LPWSTR cmdTmp = reinterpret_cast<LPWSTR>(aCommand.data());
//creates process
bool res = CreateProcess(NULL,
cmdTmp, //command casted to LPWSTR
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,
NULL, // use parent's environment
NULL, // use parent's current directory
&si, // STARTUPINFO pointer
&pi); // receives PROCESS_INFORMATION
if(!res) //not successfully created
{
LOGERROR(FB_TDI,"Failed to create Process for cmd: " + aCommand.toStdString() + boost::lexical_cast<std::string>(GetLastError()));
CloseHandle(StdInHandleRd);
CloseHandle(StdInHandleWr);
CloseHandle(StdOutHandleRd);
CloseHandle(StdOutHandleWr);
CloseHandle(StdErrHandleRd);
CloseHandle(StdErrHandleWr);
return false; //failed
}
else
{
DWORD bytesWritten;
//wait for 1 second
QTimer::singleShot(1000, this, SLOT(CheckOutput()));
}
return true;
}
void CommandExecutor_C::CheckOutput()
{
QString StdOut;
QString StdErr;
//Logs Process output
LogProcessOutput(mHandles.StdOutHandle, StdOut);
//Logs Process error
LogProcessOutput(mHandles.StdErrHandle, StdErr);
mProcessStatus = CheckTdiAutomationInterface(StdOut.toStdString(), StdErr.toStdString());
if(mProcessStatus == AI_UNKNOWN)
{
//check output again 1 second later
QTimer::singleShot(1000, this, SLOT(CheckOutput()));
}
else
{
OnTdiActive(mProcessStatus);
}
}
提示::函数CheckTdiAutomationInterface()
只是将给定字符串与其他一些已定义的字符串进行比较,然后返回进程状态。
但问题似乎出现在LogProcessOutput()
中。该函数被调用,但PeekNamedPipe()
总是返回0字节可用。仅当进程已完成时,字节才可用。因此,似乎进程输出被缓冲并仅在进程完成时发送到stdout。代码:
void CommandExecutor_C::LogProcessOutput(HANDLE &aReadOutHandle, QString &aProcessOutput)
{
//Copy data from pipe
DWORD bytesAvailable = 0;
if(!PeekNamedPipe(aReadOutHandle,NULL,0,NULL,&bytesAvailable,NULL))
{
LOGERROR(FB_TDI,"Failed to call PeekNamedPipe to print process output");
}
if(bytesAvailable)
{
DWORD dwRead;
char chBuf[BUFSIZ];
bool bSuccess = FALSE;
std::string out;
do
{
//blocks process and waits for more data, this cause errors because no more data is produced
//thats why PeekNamedPipe should look before if data is available
bSuccess=::ReadFile( aReadOutHandle, chBuf, BUFSIZ, &dwRead, NULL);
std::string s(chBuf, dwRead);
out += s;
//1. when the last read was successful
//2. when there is more data available then the bufsize size
//3. when the read bytes are not less then the BUFSIZE > would mean no more data..
}
while( bSuccess && (bytesAvailable>BUFSIZ) && !(dwRead<BUFSIZ) );
LOGINFO(FB_TDI,"OUTHANDLE:\n"+out);
aProcessOutput = QString::fromStdString(out);
}
else
{
aProcessOutput = "";
}
}
编辑:我想在进程运行时获取输出。如果Process已经完成,一切正常,输出就可以读出来。