在我的Win32程序中,我實現了執行控制檯應用程序並讀取其std/err輸出。基本上它與MSDN中給出的代碼相同:Creating a Child Process with Redirected Input and Output在某些條件下從子控制檯管道讀取
到目前爲止,這麼好。它像一個魅力一樣,用我所有的控制檯應用程序讀取std和err流。但顯然(由於全局HANDLE變量)代碼被設計爲一個接一個地運行控制檯應用程序,而不是一起運行。所以我已經改變了一點:
- 全局
HANDLE
變量替換爲本地的。它們被傳遞給幫助函數。 - 添加了一個名爲
bWait
的參數。如果它是false
,啓動後沒有從控制檯管道讀取,也沒有等待進程句柄(異步的味道)。 - 取而代之,閱讀句柄被返回給調用者(通過給定的指針)。之後他們可以用於讀取管道。
爲什麼我需要這個?我想開始tshark(控制檯版本Wireshark,流量嗅探器)與bWait = false
,然後啓動我自己的實用程序與bWait = true
並等待,直到我的工具停止工作。然後我想檢查一下,我的公用設施是否可以ping通服務器。 (由於我們有很多實用程序,這將是我們自動測試過程的重要功能)。所以,我想從tshark中讀取控制檯管道並解析其日誌。
這裏是我的修改:
// Create a child process that uses the previously created pipes
// for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(HANDLE hChildStd_OUT_Wr, HANDLE hChildStd_ERR_Wr,
const std::wstring& cmd, bool& bSuccess, DWORD& exitCode, DWORD& lastError, bool bWait = true)
{
// Set the text I want to run
//char szCmdline[]="test --log_level=all --report_level=detailed";
bSuccess = false;
wchar_t wrBuffer[BUFSIZE];
::wcscpy_s(wrBuffer, cmd.c_str());
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDERR and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStd_ERR_Wr;
siStartInfo.hStdOutput = hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
wrBuffer, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo) != 0; // receives PROCESS_INFORMATION
if (!bSuccess)
{
lastError = ::GetLastError();
}
else
{
lastError = 0;
}
if (bWait && bSuccess && ::WaitForSingleObject(piProcInfo.hProcess, INFINITE) == WAIT_FAILED)
{
bSuccess = false;
}
if (bWait && FALSE == ::GetExitCodeProcess(piProcInfo.hProcess, &exitCode))
{
bSuccess = false;
}
if (bWait)
{
::CloseHandle(hChildStd_ERR_Wr);
::CloseHandle(hChildStd_OUT_Wr);
}
return piProcInfo;
}
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
void ReadFromPipe(HANDLE hChildStd_OUT_Rd, HANDLE hChildStd_ERR_Rd, std::wstring& stdS, std::wstring& errS)
{
DWORD dwRead;
CHAR chBuf[BUFSIZE];
bool bSuccess = FALSE;
std::string out = "", err = "";
for (;;)
{
bSuccess = ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL) != 0;
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
out += s;
}
dwRead = 0;
for (;;)
{
bSuccess = ReadFile(hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL) != 0;
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
err += s;
}
wchar_t utf[10000] = { 0 };
::MultiByteToWideChar(866, 0, (LPCCH) out.c_str(), -1, utf, sizeof(utf));
stdS = utf;
StringReplace(stdS, std::wstring(L"\n"), std::wstring(L"\r\n"));
::MultiByteToWideChar(866, 0, (LPCCH) err.c_str(), -1, utf, sizeof(utf));
errS = utf;
StringReplace(errS, std::wstring(L"\n"), std::wstring(L"\r\n"));
}
bool ExecuteCmd(std::wstring cmd, std::wstring& std, std::wstring& err, std::wstring& code, DWORD& lastError,
bool bWait = true, HANDLE* phChildStd_OUT_Rd = nullptr, HANDLE* phChildStd_ERR_Rd = nullptr)
{
HANDLE hChildStd_OUT_Rd = NULL;
HANDLE hChildStd_OUT_Wr = NULL;
HANDLE hChildStd_ERR_Rd = NULL;
HANDLE hChildStd_ERR_Wr = NULL;
SECURITY_ATTRIBUTES sa;
// Set the bInheritHandle flag so pipe handles are inherited.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDERR.
if (!CreatePipe(&hChildStd_ERR_Rd, &hChildStd_ERR_Wr, &sa, 0))
{
return false;
}
// Ensure the read handle to the pipe for STDERR is not inherited.
if (!SetHandleInformation(hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &sa, 0))
{
return false;
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Create the child process.
bool bSuccess = false;
DWORD dwExitCode = 9999;
PROCESS_INFORMATION piProcInfo = CreateChildProcess(hChildStd_OUT_Wr, hChildStd_ERR_Wr, cmd, bSuccess, dwExitCode, lastError, bWait);
if (phChildStd_OUT_Rd)
*phChildStd_OUT_Rd = hChildStd_OUT_Rd;
if (phChildStd_ERR_Rd)
*phChildStd_ERR_Rd = hChildStd_ERR_Rd;
if (!bWait)
return true;
wchar_t buffer[10] = { 0 };
code = ::_itow((int) dwExitCode, buffer, 10);
if (!bSuccess)
{
return false;
}
// Read from pipe that is the standard output for child process.
ReadFromPipe(hChildStd_OUT_Rd, hChildStd_ERR_Rd, std, err);
::CloseHandle(hChildStd_OUT_Rd);
::CloseHandle(hChildStd_ERR_Rd);
return true;
}
現在的問題。當我在無等待模式下嘗試啓動tshark時,從管道中讀取的內容被掛起。即,在ReadFile
。
if (g_iConnection != -1 && g_Products[i].PingbackDomain.size() > 0)
{
wchar_t buf[5] = { 0 };
std::wstring list, err, code;
DWORD dwErr = 0;
std::wstring cmd = L"C:\\Program Files\\Wireshark\\tshark -a duration:5 -l -i ";
cmd += ::_itow(g_iConnection + 1, buf, 10);
cmd += L" -f \"host ";
cmd += g_Products[i].PingbackDomain;
cmd += L"\"";
ExecuteCmd(cmd, list, err, code, dwErr, false, &hChildStd_OUT_Rd, &hChildStd_ERR_Rd);
::Sleep(500);
}
...
// Starting my utility (if this section is commented out, ReadFile still hangs).
...
if (hChildStd_OUT_Rd && hChildStd_ERR_Rd)
{
std::wstring traffic, tsharkErr;
ReadFromPipe(hChildStd_OUT_Rd, hChildStd_ERR_Rd, traffic, tsharkErr);
::CloseHandle(hChildStd_OUT_Rd);
::CloseHandle(hChildStd_ERR_Rd);
if (tsharkErr.size() > 0)
{
std::wstring msg = L"There has been an issue, while logging with Wireshark:\r\n\r\n";
msg += tsharkErr;
::MessageBox(NULL, msg.c_str(), L"uhelper", MB_ICONERROR | MB_OK);
}
else if (traffic.length() > 0)
{
newOutput += L"\r\nTraffic to ";
newOutput += g_Products[i].PingbackDomain;
newOutput += L":\r\n";
newOutput += traffic;
if (newOutput[newOutput.length() - 1] != L'\n')
newOutput += L"\r\n";
}
}
我用我的修改打破了MSDN代碼嗎?不幸的是,我無法找到(以及在哪裏)。