简体   繁体   English

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

[英]Win32 changing to binary mode child's Stdout (pipe)

Hello to this great community, 你好这个伟大的社区,

I have problems with the automatic conversion of ('\\n') 0x0A to ('\\n\\r') 0x0D 0x0A when using a pipe to redirect child's stdout to a file, the child's output are bytes and not text . 当使用管道将子节点的stdout重定向到文件时,我将('\\n') 0x0A自动转换为('\\n\\r') 0x0D 0x0A时遇到问题,子节点的输出是字节而不是文本

First, I have used these examples MSDN-Creating a Child Process with Redirected Input and Output and http://support.microsoft.com/kb/190351 ), and now I have this basic application, it creates a pipe and redirect the child's STDOUT to a binary file. 首先,我使用了这些示例MSDN-创建具有重定向输入和输出的子进程http://support.microsoft.com/kb/190351 ),现在我有了这个基本应用程序,它创建了一个管道并重定向了孩子的STDOUT到二进制文件。 All this in a Win32 Console Application in Visual C++ 6.0 (yes it is old but is a requirement). 所有这些都在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);
} 

And in this dummy child.cpp you'll notice that if I set the STDOUT to binary mode everything works just fine (I get just 0x0A 0x0A!), but my real child is an EXE and I don't have access to that code . 在这个虚拟的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;
}

So, after searching for about 2 days and considering that I have a basic C++ knowledge I ask: Is there a Way that I could do _setmode to the childs stdout from the parent, considering that I don't have access to the child's code . 所以,在搜索了大约2天后,考虑到我有一个基本的C ++知识,我问: 有没有一种方法可以做到_setmode到来自父级的childs stdout,考虑到我无法访问孩子的代码

As a solution, I am seriously considering finding every '0x0D' '0x0A' and replacing it with '0x0A' . 作为一个解决方案,我正在认真考虑找到每个'0x0D' '0x0A'并将其替换为'0x0A' I am really going crazy with this problem... So if someone could help me I will be very grateful. 我真的对这个问题感到疯狂......所以,如果有人能帮助我,我将非常感激。

Related Question : Win32 Stream Handles - Changing To Binary Mode but he has access to the child's code! 相关问题Win32流句柄 - 更改为二进制模式,但他可以访问子代码!

Edit 编辑

As, @librik point, the final solution will have to replace every single occurrence of 0x0D 0x0A by 0x0A. 至于@librik指出,最终的解决方案必须将每次出现的0x0D 0x0A替换为0x0A。 For this to work the file contents must be in memory. 为此,文件内容必须在内存中。 There are certain issues but I can live with it (excess of memory allocated). 有一些问题,但我可以忍受它(分配的内存过多)。 I hope this will be helpful: 我希望这会有所帮助:

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;
}

Your problem is that the "stream mode" isn't part of Windows, so it isn't something you can change from outside the other program. 您的问题是“流模式”不是Windows的一部分,因此您无法从其他程序外部进行更改。 It's part of the C and C++ system, and so it's a private part of each separate C or C++ program you run. 它是C和C ++系统的一部分,因此它是您运行的每个单独的C或C ++程序的私有部分。

There's a library of functions that is combined with every program compiled in C++, called the "C++ Standard Library." 有一个函数库,它与用C ++编译的每个程序相结合,称为“C ++标准库”。 The C++ Standard Library holds all the functions for streams like stdout . C ++标准库包含stdout流的所有功能。 It's inside the other program's C++ Standard Library that the 0x0A is being converted to 0x0D 0x0A before it's written to the stream. 在其他程序的C ++标准库中,0x0A在写入流之前被转换为0x0D 0x0A。 _setmode is a function inside the C++ Standard Library which turns on and off that conversion, so when you add a call to it inside child.cpp , that tells child.cpp 's C++ Standard Library to leave stdout alone. _setmode是C ++标准库中的一个函数,它打开和关闭转换,所以当你在child.cpp添加一个调用时,它会告诉child.cpp的C ++标准库单独留下stdout But you have no way to force the other program to call its _setmode function. 但是您无法强制其他程序调用其_setmode函数。

So the best thing really is the "crazy" solution you suggested: 所以最好的事情就是你建议的“疯狂”解决方案:

As a solution, I am seriously considering finding every '0x0D' '0x0A' and replacing it with '0x0A'. 作为一个解决方案,我正在认真考虑找到每个'0x0D''0x0A'并将其替换为'0x0A'。

So long as you know that child.exe is writing in text mode, not binary mode, then every single occurrence of 0x0D 0x0A must have originally been a single 0x0A. 只要您知道 child.exe正在以文本模式而不是二进制模式写入,那么每次出现的0x0D 0x0A 必须最初都是单个0x0A。 (If the program tried to write the two bytes 0x0D 0x0A, it would come out as the three bytes 0x0D 0x0D 0x0A.) Therefore you are absolutely safe and correct to "fix" the output by converting back again. (如果程序试图写入两个字节0x0D 0x0A,它将作为三个字节0x0D 0x0D 0x0A出现。)因此,通过再次转换来“修复”输出是绝对安全和正确的。

I think the easiest approach is just to write result.out exactly like you're doing it now, but then translate 0x0D 0x0A to 0x0A at the end, creating a new file that is correct. 我认为最简单的方法就是编写result.out就像你现在正在做的那样,但最后将0x0D 0x0A转换为0x0A,创建一个正确的新文件。 There are little tool programs you can download that will do this sort of thing for you -- one of them is called dos2unix . 您可以下载的小工具程序可以为您执行此类操作 - 其中一个名为dos2unix That might be the easiest way, in fact -- just make the last step of your program run dos2unix < result.out.with_bad_newlines > result.out . 事实上,这可能是最简单的方法 - 只需让程序的最后一步运行dos2unix < result.out.with_bad_newlines > result.out If, for some reason, you can't do this, you could have your program change 0x0D 0x0A to 0x0A inside chBuf before you write it out, translating as you go. 如果由于某种原因,你不能这样做,你可以让你的程序在你写出来之前在chBuf里面改变0x0D 0x0A到0x0A, chBuf时间的chBuf进行翻译。 (But be careful when chBuf ends in 0x0D...) (但是当chBuf以0x0D 结尾时要小心......)

(There are certain techniques that can "inject" a little bit of code into another program that's under your control on Windows. They are a little dangerous, and a lot of trouble. If you're really unhappy with the translation idea, you can look up "DLL injection.") (某些技术可以将一些代码“注入”到Windows控制之下的另一个程序中。它们有点危险,而且很麻烦。如果你真的对翻译想法不满意,你可以查找“DLL注入。”)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM