使用重定向输入和输出的 C++ 进程间通信不适用于发布版本

Posted

技术标签:

【中文标题】使用重定向输入和输出的 C++ 进程间通信不适用于发布版本【英文标题】:C ++ Inter Process Communication using redirected Input and Output not work with release build 【发布时间】:2015-09-23 10:43:12 【问题描述】:

我正在处理一个需要创建子进程并将其输入和输出重定向到父进程的项目。 我在 MSDN (link) 上关注这个例子。目前它适用于调试版本,但不适用于发布版本。我不知道为什么。请帮我 子进程的编码如下(与示例非常相似):

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string>
#include <Iepmapi.h>
#include <Wininet.h>
#include <vector>

#define BUFSIZE 4096 
using namespace std;

vector<wstring> subStringByString(wstring input, wstring delimiter)

    int pos = input.find(delimiter);
    vector<wstring> arr;
    while (pos != wstring::npos)
    
        wstring token = input.substr(0, pos);
        arr.push_back(token);
        input = input.substr(pos + delimiter.size(), input.size());
        pos = input.find(delimiter);
    
    arr.push_back(input);
    return arr;


int main(void)

    WCHAR chBuf[BUFSIZE];
    DWORD dwRead, dwWritten;
    HANDLE hStdin, hStdout;
    BOOL bSuccess;

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    hStdin = GetStdHandle(STD_INPUT_HANDLE);
    if (
        (hStdout == INVALID_HANDLE_VALUE) ||
        (hStdin == INVALID_HANDLE_VALUE)
        )
        ExitProcess(1);

    // Send something to this process's stdout using printf.
    printf("\n ** This is a message from the child process. ** \n");

    // This simple algorithm uses the existence of the pipes to control execution.
    // It relies on the pipe buffers to ensure that no data is lost.
    // Larger applications would use more advanced process control.
    int rc = 0;
    for (;;)
    
        // Read from standard input and stop on error or no data.
        bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);
        wstring input = wstring(chBuf);
        wstring delimiter = L"  ";
        vector<wstring> arr = subStringByString(input, delimiter);

        int rc = 0;

        if (!bSuccess || dwRead == 0) 
            break;
        if (IESetProtectedModeCookie(arr[0].c_str(), arr[1].c_str(), arr[2].c_str(), INTERNET_COOKIE_THIRD_PARTY) != S_OK)
        
            DWORD error = GetLastError();
            rc = -27;
        
        // Write to standard output and stop on error.
        if (rc == 0 )
            bSuccess = WriteFile(hStdout, L"0", dwRead, &dwWritten, NULL);
        else 
            bSuccess = WriteFile(hStdout, L"-27", dwRead, &dwWritten, NULL);
        if (!bSuccess)
            break;
    

    return 0;

谢谢, 文赫

父进程代码:

std::wstring invoke(const std::wstring input)

    if (!CreateChildProcess())
        return L"error";
    DWORD dwRead, dwWritten;
    WCHAR chBuf[BUFSIZE] ;
    const WCHAR* temp = input.c_str();
    wcscpy(chBuf, temp);
    BOOL bSuccess = FALSE;

    for (;;)
    
        bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
        if (!bSuccess) break;
    
    CloseHandle(g_hChildStd_IN_Wr);
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    for (;;)
    
        bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if (!bSuccess) break;

        bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
        if (!bSuccess) break;
    
    //TerminateProcess(_processId, 0);
    return std::wstring(&chBuf[0]);


bool CreateChildProcess(void)

    SECURITY_ATTRIBUTES saAttr;
    // Set the bInheritHandle flag so pipe handles are inherited. 
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;
    // Create a pipe for the child process's STDIN. 
    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
        return false;
    if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
    
        cleanUpHandle();
        return false;
    
    // Create a pipe for the child process's STDOUT.
    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
    
        cleanUpHandle();
        return false;
    
    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
    
        cleanUpHandle();
        return false;
    

    // Create a child process that uses the previously created pipes for STDIN and STDOUT.
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    BOOL bSuccess = FALSE;
    // Set up members of the PROCESS_INFORMATION structure. 
    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    // Set up members of the STARTUPINFO structure. 
    // This structure specifies the STDIN and STDOUT handles for redirection.
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError = g_hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.hStdInput = g_hChildStd_IN_Rd;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    // Create the child process. 
    HANDLE hToken = NULL;
    HANDLE hNewToken = NULL;
    bSuccess = OpenProcessToken(GetCurrentProcess(),
        TOKEN_DUPLICATE |
        TOKEN_ADJUST_DEFAULT |
        TOKEN_QUERY |
        TOKEN_ASSIGN_PRIMARY,
        &hToken);

    if (!bSuccess) 
        return 0;
    
    hNewToken = CreateLowLevelToken(hToken);
    wa_wstring deploymentPath;
    if (WAAPI_FAILED(ProcessUtils::getDeploymentPath(deploymentPath)))
    
        return false;
    
    wa_wstring path = deploymentPath + wa_wstring(PROCESS_PATH);

    LPTSTR szCmdline = wstring_to_LPTSTR(path);
    if (_processId != 0)
    
        TerminateProcess(_processId, 0);
    

    TCHAR szCmdline2[] = PROCESS_PATH;
    bSuccess = CreateProcessAsUser(hNewToken, NULL, szCmdline2, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo, &piProcInfo);

    DWORD error = GetLastError();
    if (!bSuccess)
    
        cleanUpHandle();
        return false;
    
    else 
        _processId = piProcInfo.dwProcessId;
        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);
    

    return true;

【问题讨论】:

开启所有编译器警告,这是某处未定义行为的典型症状。 【参考方案1】:

我发现了问题。 在"debug build 中,dwReaddwWritten 的值为垃圾值,而在 relase build 中的值为 0,导致 parent 挂起。 要解决这个问题:

DWORD dwRead = -1, dwWritten = -1;

【讨论】:

以上是关于使用重定向输入和输出的 C++ 进程间通信不适用于发布版本的主要内容,如果未能解决你的问题,请参考以下文章

高级IO中socketpair实现进程间通信以及重定向

Android C++系列:Linux进程间关系

使用重定向 I/O 与子进程通信时出现死锁

如何处理 C++ 重定向进程的输出

C#-C 进程间通信

十Shell篇——管道与重定向