5

こんにちは、この素晴らしいコミュニティへ

パイプを使用して子の stdout をファイルにリダイレクトするとき('\n') 0x0Aのの自動変換に問題があります。子の出力はバイトであり、 text ではありません('\n\r') 0x0D 0x0A

最初に、これらの例を使用しました MSDN-Creating a Child Process with Redirected Input and Outputおよびhttp://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++ の知識があることを考慮して、「子のコードにアクセスできないことを考慮して、親から子の stdout に対して実行できる方法はありますか?」_setmodeと尋ねました。

'0x0D' '0x0A'解決策として、すべてを見つけてに置き換えることを真剣に検討してい'0x0A'ます。私は本当にこの問題に夢中になっています...誰かが私を助けることができれば、私はとても感謝しています.

関連する質問: Win32 ストリーム ハンドル - バイナリ モードに変更しますが、彼は子供のコードにアクセスできます。

編集

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

1 に答える 1

3

問題は、「ストリーム モード」が Windows の一部ではないため、他のプログラムの外部から変更できるものではないことです。これは C および C++ システムの一部であるため、実行する個々の C または C++ プログラムのプライベートな部分です。

「C++ 標準ライブラリ」と呼ばれる、C++ でコンパイルされたすべてのプログラムと組み合わされる関数のライブラリがあります。C++ 標準ライブラリには、 のようなストリームのすべての関数が含まれていますstdout。0x0A がストリームに書き込まれる前に 0x0D 0x0A に変換されるのは、他のプログラムの C++ 標準ライブラリ内です。 _setmodeは、その変換をオンまたはオフにする C++ 標準ライブラリ内の関数であるため、 内に呼び出しを追加すると、の C++ 標準ライブラリにそのままにしておくchild.cppように指示します。しかし、他のプログラムにその関数を強制的に呼び出す方法はありません。child.cppstdout_setmode

したがって、最高のものは、あなたが提案した「クレイジーな」ソリューションです。

解決策として、すべての「0x0D」「0x0A」を見つけて「0x0A」に置き換えることを真剣に検討しています。

child.exe がバイナリ モードではなくテキスト モードで書き込みを行っていることがわかっている限り、0x0D 0x0A が出現するたびに、最初は 1 つの 0x0A である必要があります。(プログラムが 2 バイト 0x0D 0x0A を書き込もうとすると、3 バイト 0x0D 0x0D 0x0A として出力されます。) したがって、再度変換して出力を「修正」することは完全に安全で正しいことです。

最も簡単な方法は、現在行っているのとまったく同じように書くことだと思いますresult.outが、最後に 0x0D 0x0A を 0x0A に変換して、正しい新しいファイルを作成します。ダウンロードできるツール プログラムはほとんどなく、このようなことを行います。そのうちの 1 つが と呼ばれdos2unixます。実際、これが最も簡単な方法かもしれません。プログラムの最後のステップを実行するだけdos2unix < result.out.with_bad_newlines > result.outです。なんらかの理由でこれができない場合は、chBuf書き出す前にプログラムで 0x0D 0x0A を内部で 0x0A に変更し、進行中に変換することができます。(ただし、chBufが 0x0D で終わる場合は注意が必要です...)

(Windows で制御している別のプログラムに、少しのコードを "挿入" できる特定の手法があります。これらは少し危険であり、多くの問題があります。翻訳のアイデアに本当に不満がある場合は、次のことができます。 「DLLインジェクション」を調べてください。)

于 2013-08-18T02:34:12.977 に答える