2013-08-18 82 views
4

你好這個偉大的社區,的Win32改變爲二進制模式,孩子的標準輸出(管道)

我用的管,以孩子的標準輸出重定向到一個文件時,必須與('\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你會發現,如果我設定的標準輸出爲二進制模式,一切都運行得很好(我得到公正的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從父stdout的孩子的一個方式,考慮到我不無法訪問孩子的代碼

作爲一種解決方案,我認真考慮找到每個'0x0D' '0x0A'並用'0x0A'替換它。我對這個問題真的很瘋狂......所以如果有人能幫助我,我會非常感激。

相關問題Win32 Stream Handles - Changing To Binary Mode但他卻獲得了孩子的代碼!

編輯

如,@librik點,最終的解決方案將具有由的0x0A取代0X0D的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; 
} 

回答

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即可。如果出於某種原因無法執行此操作,則可以在寫出之前將您的程序在chBuf之內更改0x0D 0x0A至0x0A,然後進行翻譯。 (但是,當chBuf 在0x0D結束時要小心......)

(某些技術可以將一點代碼「注入」到Windows控制的另一個程序中,它們有點危險,和很多麻煩,如果你對翻譯理念感到不滿意,你可以查看「DLL注入」。)

+0

謝謝!還有一件事,_my孩子正在用二進制模式編寫_,這個解決方案也適用於二進制模式嗎? – lmiguelmh

+1

我以爲你說過這個孩子在文本模式下有stdout。如果孩子的stdout處於二進制模式,那麼我不明白爲什麼你會看到0x0D 0x0A。你的問題現在很混亂,似乎與自己相矛盾。如果流是文本模式流,則不能以二進制模式寫入,如果尚未調用'_setmode(stdout,O_BINARY)',則stdout流是文本模式流。 – librik

+1

抱歉。我應該說:孩子寫字節而不是文字。我添加了最終解決方案。再次感謝。 – lmiguelmh

相關問題