2016-08-12 4 views
0

私のWin32プログラムでは、コンソールアプリケーションを実行し、std/errの出力を読み込むことを実装しました。基本的にはMSDNのコードと同じです:Creating a Child Process with Redirected Input and Outputいくつかの条件の下で子コンソールのパイプがぶら下がっている

これまでのところ、とても良いです。それは、すべてのコンソールアプリケーションでstdストリームとerrストリームの両方を読むチャームのように機能しました。しかし明らかに(グローバルなHANDLE変数のため)、コードはコンソールアプリケーションを1つずつ実行するように設計されています。そこで私はそれをちょっと変えました:

  • グローバル変数HANDLEはローカル変数に置き換えられました。それらはヘルパー関数に渡されます。
  • bWaitというパラメータが追加されました。それがfalseの場合、起動後にコンソールパイプからの読み込みがなく、プロセスハンドル(非同期の風味)を待つこともありません。
  • 代わりに、読み取りハンドルが(指定されたポインターを使用して)呼び出し元に返されます。パイプから後で読み取るために使用できます。

なぜこれが必要ですか? (コンソールバージョンの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コードが壊れていませんか?残念ながら、私はどのように(そしてどこで)見つけることができません。

答えて

0

これは、(プロセスを作成する前に!)問題を解決:

PIPE_NOWAIT読書を適用した後は、ぶら下がって停止しました。

関連する問題