Win32改为二进制模式child的Stdout(管道)

时间:2013-08-18 00:37:57

标签: c++ winapi visual-c++

您好,这个伟大的社区,

使用管道将子项的stdout重定向到文件时,('\n') 0x0A自动转换为('\n\r') 0x0D 0x0A时出现问题,子项的输出是字节而不是文本

首先,我使用了这些示例MSDN-Creating a Child Process with Redirected Input and Outputhttp://support.microsoft.com/kb/190351),现在我有了这个基本应用程序,它创建了一个管道并将子项的STDOUT重定向到二进制文件。所有这些都在Visual C ++ 6.0中的Win32控制台应用程序中(是的,它是旧的但是是必需的)。

#define BUFSIZE 256

HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;

int _tmain(int argc, TCHAR *argv[]) 
{ 

    SECURITY_ATTRIBUTES saAttr; 
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 

    if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) 
        ErrorExit(TEXT("StdoutRd CreatePipe")); 

    if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
        ErrorExit(TEXT("Stdout SetHandleInformation")); 

    CreateChildProcess();

    if (!CloseHandle(g_hChildStd_OUT_Wr)) 
        ErrorExit("CloseHandle");

    ReadFromPipe(); 

    if (!CloseHandle(g_hChildStd_OUT_Rd)) 
        ErrorExit("CloseHandle");

    return 0; 
} 


void CreateChildProcess()
{ 
    TCHAR szCmdline[]=TEXT("child.exe");
    PROCESS_INFORMATION piProcInfo; 
    STARTUPINFO siStartInfo;
    BOOL bSuccess = FALSE; 

    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
    siStartInfo.cb = sizeof(STARTUPINFO); 
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    bSuccess = CreateProcess(NULL, 
        szCmdline,  // 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);   // receives PROCESS_INFORMATION 

    if ( ! bSuccess ) 
        ErrorExit(TEXT("CreateProcess"));
    else 
    {
        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);
    }
}


void ReadFromPipe(void) 
{ 
    DWORD dwRead, dwWritten; 
    CHAR chBuf[BUFSIZE]; 
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    DWORD nTotalBytesRead = 0;
    fstream filePk;
    filePk.open("result.out", ios::out | ios::trunc | ios::binary);

    for (;;) 
    { 

        bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) {
            if (GetLastError() == ERROR_BROKEN_PIPE)
                break; // pipe done - normal exit path.
            else
                ErrorExit("ReadFile"); // Something bad happened.
        }

        filePk.write(chBuf, dwRead);
        nTotalBytesRead += dwRead;
    } 
    filePk.close();

    char ibuff[24]; 
    sprintf(ibuff,"%d bytes." , (int)nTotalBytesRead);
    ::MessageBox(NULL, ibuff, "", 0);
} 

在这个虚拟的child.cpp中你会注意到,如果我将STDOUT设置为二进制模式,一切正常(我得到的只是0x0A 0x0A!),但我真正的孩子是一个EXE我不喜欢可以访问该代码

int main(int argc, char* argv[])
{
    _setmode( _fileno( stdout ), _O_BINARY );
    printf("\n");

    unsigned char buffer[] = {'\n'};
    fwrite(buffer, sizeof(unsigned char), sizeof(buffer), stdout);
    return 0;
}

所以,在搜索了大约2天并且考虑到我有基本的C ++知识后,我问:我是否有办法_setmode对父母的孩子们进行调查,考虑到我无法访问孩子的代码

作为解决方案,我正在认真考虑查找每个'0x0D' '0x0A'并将其替换为'0x0A'。我真的对这个问题感到疯狂......所以,如果有人能帮助我,我将非常感激。

  

相关问题Win32 Stream Handles - Changing To Binary Mode但他可以访问孩子的代码!

修改

as,@ librik指出,最终的解决方案必须将每次出现的0x0D 0x0A替换为0x0A。为此,文件内容必须在内存中。有一些问题,但我可以忍受它(分配的内存过多)。我希望这会有所帮助:

void ReadFromPipe(void) 
{ 
    DWORD dwRead, dwWritten; 
    CHAR *chBuf = NULL, *chBufTmp = NULL; 
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    DWORD nTotalBytesRead = 0;
    fstream filePk;
    filePk.open("result.out", ios::out | ios::trunc | ios::binary);

    int nIter = 0;
    for (;;) 
    {
        if(chBuf == NULL) {
            if((chBuf = (CHAR*)malloc(BUFSIZE*sizeof(CHAR))) == NULL) {
                ErrorExit("Malloc");
            }
        } else {
            chBufTmp = chBuf;   // save pointer in case realloc fails
            if((chBuf = (CHAR*)realloc(chBuf, (nIter+1)*(BUFSIZE*sizeof(CHAR)))) == NULL) {
                free(chBufTmp); // free original block
                ErrorExit("Realloc");
            }
        }

        CHAR* chBufNew = chBuf+nTotalBytesRead;
        bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBufNew, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) {
            if (GetLastError() == ERROR_BROKEN_PIPE) {
                break; // pipe done - normal exit path.
            } else {
                ErrorExit("ReadFile"); // Something bad happened.
            }
        }

        nTotalBytesRead += dwRead;
        nIter ++;
    } 

    // 0xD 0xA -> 0xA
    nTotalBytesRead = ClearBuffer(chBuf, nTotalBytesRead);

    filePk.write(chBuf, nTotalBytesRead);
    filePk.close();
    free(chBuf);

    char ibuff[24]; 
    sprintf(ibuff,"%d bytes." , (int)nTotalBytesRead);
    ::MessageBox(NULL, ibuff, "", 0);
} 

int ClearBuffer(char *buffer, int bufferlength) {
    // lmiguelhm-es requerido que TODO el buffer esté en memoria 
    int chdel = 0;
    for (int i = 0; (i+chdel) < bufferlength; i++) {
        char firstChar = buffer[i+chdel];
        buffer[i] = firstChar;
        if (firstChar == 0x0D) {
            if ((i+chdel+1) < bufferlength) {
                char secondChar = buffer[i+chdel+1];
                if (secondChar == 0x0A) {
                    buffer[i] = secondChar;
                    chdel++;
                }
            }
        }
    }
    return bufferlength - chdel;
}

1 个答案:

答案 0 :(得分:2)

您的问题是“流模式”不是Windows的一部分,因此您无法从其他程序外部进行更改。它是C和C ++系统的一部分,因此它是您运行的每个单独的C或C ++程序的私有部分。

有一个函数库,它与用C ++编译的每个程序相结合,称为“C ++标准库”。 C ++标准库包含stdout等流的所有功能。在其他程序的C ++标准库中,0x0A在写入流之前被转换为0x0D 0x0A。 _setmode是C ++标准库中的一个函数,用于打开和关闭该转换,因此当您在child.cpp中添加对它的调用时,它会告诉child.cpp的C ++标准库离开仅stdout。但是你无法强迫其他程序调用它的_setmode函数。

所以最好的事情就是你建议的“疯狂”解决方案:

  

作为解决方案,我正在认真考虑找到每个'0x0D''0x0A'   并将其替换为'0x0A'。

只要您知道那个child.exe正在以文本模式而不是二进制模式写入,那么每次出现的0x0D 0x0A 必须最初都是单个0x0A 。 (如果程序试图写入两个字节0x0D 0x0A,那么它将作为三个字节0x0D 0x0D 0x0A出现。)因此,通过再次转换来“修复”输出是绝对安全和正确的。

我认为最简单的方法就是编写result.out,就像你现在正在做的那样,但最后将0x0D 0x0A转换为0x0A,创建一个正确的新文件。您可以下载的小工具程序会为您执行此类操作 - 其中一个名为dos2unix。事实上,这可能是最简单的方法 - 只需让程序的最后一步运行dos2unix < result.out.with_bad_newlines > result.out。如果由于某种原因,你不能这样做,你可以让你的程序在你写出来之前将{0}来改变0x0D 0x0A到0x0A,然后进行翻译。 (但是当chBuf 在0x0D中结束时要小心......)

(某些技术可以将一些代码“注入”到Windows控制之下的另一个程序中。它们有点危险,而且很麻烦。如果你对翻译的想法真的不满意的话,你可以查找“DLL注入。”)