【问题标题】:Reading from child console pipe hanging under some conditions在某些情况下从挂起的子控制台管道读取
【发布时间】:2016-12-19 16:25:00
【问题描述】:

在我的 Win32 程序中,我实现了执行控制台应用程序并读取其 std/err 输出。基本上与 MSDN 中给出的代码相同:Creating a Child Process with Redirected Input and Output

到目前为止,一切都很好。它就像一个魅力,用我所有的控制台应用程序读取 std 和 err 流。但显然(由于全局 HANDLE 变量)代码被设计为一个一个地运行控制台应用程序,从不一起运行。所以我稍微改了一下:

  • 全局变量HANDLE 替换为本地变量。它们被传递到辅助函数中。
  • 添加了一个名为bWait 的参数。如果是false,则启动后不会从控制台管道读取数据,也不会等待进程句柄(有点异步)。
  • 相反,读取句柄返回给调用者(通过给定的指针)。它们可用于稍后从管道中读取。

我为什么需要这个?我想用bWait = false 启动tsharkWireshark 的控制台版本,一个流量嗅探器),然后用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 代码?不幸的是,我找不到如何(以及在​​哪里)。

【问题讨论】:

    标签: c++ winapi pipe console-application


    【解决方案1】:

    这解决了问题(在创建进程之前!):

    if (!bWait)
        {
            DWORD mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
            ::SetNamedPipeHandleState(hChildStd_OUT_Rd, &mode, NULL, NULL);
            ::SetNamedPipeHandleState(hChildStd_ERR_Rd, &mode, NULL, NULL);
        }
    

    申请PIPE_NOWAIT后阅读停止挂起。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-23
      • 1970-01-01
      • 1970-01-01
      • 2021-03-30
      • 2023-03-09
      相关资源
      最近更新 更多