2017-09-22 21 views
2

2つのアプリがあります。CreateProcessとリダイレクト出力

AppCMDはコマンドラインアプリケーションであり、AppMAINはコマンドライン引数であるAppCMDを起動します。 残念ながらAppMAINは出力を処理していないようですAppCMD非常にうまくいくものがあります。

AppCMDとその出力を呼び出して、何が起こっているのかを確認したいと思います。

AppCMDを別のバイナリAppWRAPに置き換えて、名前を変更したAppCMDへの転送を転送し、その出力をログに記録します。 AppWRAPは透明なMan-In-The-Middleのように行動する必要があります。テスト目的のために

私はちょうどそれのコマンドライン引数を出力する簡単なAppCMDを書いた:

#include <iostream> 

using namespace std; 

int main(int argc, char *argv[]) 
{ 
    cout << "#### Hello, I'm the test binary that wants to be wrapped." << endl; 

    if (argc < 2) { 
     cout << "#### There where no command line arguments." << endl; 
    } 
    else { 
     cout << "#### These are my command line arguments:"; 
     for (int i = 1; i < argc; ++i) cout << " " << argv[i]; 
     cout << endl; 
    } 

    cout << "#### That's pretty much everything I do ... yet ;)" << endl; 

    return 0; 
} 

私はAppWrapを実装するためにMSDN: Creating a Child Process with Redirected Input and Outputを追ったが、それは返さないと、私はその理由を把握カント以来、私は捕まってしまった:

#include <iostream> 
#include <sstream> 
#include <Windows.h> 


using namespace std; 


const string TARGET_BINARY("TestBinary.exe"); 
const size_t BUFFSIZE = 4096; 

HANDLE in_read  = 0; 
HANDLE in_write  = 0; 
HANDLE out_read  = 0; 
HANDLE out_write  = 0; 

int main(int argc, char *argv[]) 
{ 
    stringstream call; 

    cout << "Hello, I'm BinTheMiddle." << endl; 

//-------------------------- CREATE COMMAND LINE CALL -------------------------- 

    call << TARGET_BINARY; 
    for (int i = 1; i < argc; ++i) { 
     call << " " << argv[i]; 
    } 

    cout << "Attempting to call '" << call.str() << "'" << endl; 

//------------------------------ ARRANGE IO PIPES ------------------------------ 

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

    if (!CreatePipe(&out_read, &out_write, &security, 0)) { 
     cout << "Error: StdoutRd CreatePipe" << endl; 
     return -1; 
    } 
    if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) { 
     cout << "Stdout SetHandleInformation" << endl; 
     return -2; 
    } 
    if (!CreatePipe(&in_read, &in_write, &security, 0)) { 
     cout << "Stdin CreatePipe" << endl; 
     return -3; 
    } 
    if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) { 
     cout << "Stdin SetHandleInformation" << endl; 
     return -4; 
    } 
//------------------------------ START TARGET APP ------------------------------ 

    STARTUPINFO   start; 
    PROCESS_INFORMATION proc; 

    ZeroMemory(&start, sizeof(start)); 
    start.cb   = sizeof(start); 
    start.hStdError = out_write; 
    start.hStdOutput = out_write; 
    start.hStdInput = in_read; 
    start.dwFlags |= STARTF_USESTDHANDLES; 

    ZeroMemory(&proc, sizeof(proc)); 

    // Start the child process. 
    if (!CreateProcess(NULL, (LPSTR) call.str().c_str(), NULL, NULL, TRUE, 
         0, NULL, NULL, &start, &proc)) 
    { 
     cout << "CreateProcess failed (" << GetLastError() << ")" << endl; 
     return -1; 
    } 

    // Wait until child process exits. 
    WaitForSingleObject(proc.hProcess, INFINITE); 
    // Close process and thread handles. 
    CloseHandle(proc.hProcess); 
    CloseHandle(proc.hThread); 

//----------------------------------- OUTPUT ----------------------------------- 

    DWORD dwRead; 
    CHAR chBuf[127]; 

    while (ReadFile(out_read, chBuf, 127, &dwRead, NULL)) { 
     cout << "Wrapped: " << chBuf << endl; 
    } 

    return 0; 
} 

ReadFileが復帰するのを待っているようです。誰かが私が間違っていることを発見することはできますか?

私は、バイナリをこのように呼ん:

> shell_cmd_wrapper.exe param1 param2 

これは、コンソール出力であるが、バイナリは戻りません。

Hello, I'm BinTheMiddle. 
Attempting to call 'TestBinary.exe param1 param2' 
Wrapped:#### Hello, I'm the test binary that wants to be wrapped. 
#### These are my command line arguments: param1 param2 
#### That'sD 
Wrapped: pretty much everything I do ... yet ;) 
s to be wrapped. 
#### These are my command line arguments: param1 param2 
#### That'sD 

(私はバッファをクリアしていないことを無視してください)

+0

利用可能なデータのサイズを調べるために、読み込む前に 'PeekNamedPipe'を使う必要があるでしょう。 – VTT

+0

@eryksunありがとうございました。私は 'CloseHandle(out_write);と呼んだ。 CloseHandle(in_read); 'そして、それは何をすべきかをしているようです:)私は、あなたがそれを書く計画を持っているときに答えを受け入れる計画を持っています。 – lupz

+0

'ReadFile()'の後ろに、 'ReadFile'がそれをしないので、ヌルターミネータを' chBuf'に追加するべきです。あなたは 'chBuf'が当初ゼロで埋められていたのは幸運かもしれませんが、それはランダムなデータでもよいので、' cout'は実際の文字列の後にゴミを書き込んだり、クラッシュすることさえあります。 – zett42

答えて

3

CreateProcessを呼び出した後out_writein_readハンドルを閉じます。それ以外の場合は、のReadFileは、パイプが空のときにブロックされます。これは、子プロセスが終了した後でも、現在のプロセスのハンドルがout_writeである可能性があるためです。

また、Harry Johnstonがコメントで述べたように、パイプから読み込む前にプロセスが終了するのを待つと、デッドロックが発生する可能性があります。パイプがいっぱいになると、子供はWriteFileでブロックされます。

+0

**最後の**ハンドルのハンドルが閉じているとき - もう一方のパイプの端が切断されます。 'ReadFile'は' FALSE'を返し、 'GetLastErro'rは' ERROR_BROKEN_PIPE'を返します。 OPには、子プロセスと親プロセスの2つのハンドルハンドルがあります。子のexit - ハンドルが閉じているが、子ハンドルにはまだアクティブなハンドルが残っています。他の終端は切断されず、 'ReadFile'は返されません。親の追加ハンドルを閉じてこの問題を修正してください。 2 – RbMm

+0

@RbMmの代わりに1つのパイプパイアを作成することもできますが、OPはハンドル 'out_write'ハンドルが閉じられたと仮定して、子プロセスが終了したときに' ERROR_BROKEN_PIPE'になる 'ReadFile'からエラーを無視します。他のエラーを黙って無視しないようにこのケースをチェックする方がよいでしょう。私はあなたがなぜ「パイプパイア」を意味するのであれば、ただ1本のパイプが必要と言うのか分かりません。 OPは標準入力も設定しています。それがどこで使われているのかはわかりませんが、AppCMDの入力を促進するために必要となります。デッドロックは問題であり、もう1つのスレッドで最も簡単に解決されます。 – eryksun

+0

'ReadFile'は返されません。なぜなら、子プロセスの終了時にパイプの終了が切断されないためです。私たちがそれを閉じるとき - この修正の問題。約1組 - 私は、CreateNamedPipe(親ハンドル)+ 'CreateFile'(それを子にコピーして親で閉じる)という2つのハンドルしか作成できないことを意味します。これは仕事には十分です。ボットハンドルには読み取り/書き込みアクセス権が必要です。ほぼすべてが2つのペア(4つのハンドル)を使用しています。ここでよく知られているデッドロックについては – RbMm

関連する問題