Консольный редирект в обе стороны

Есть реализация редиректа, но односторонняя: CreatePipe() два штуки для StdOut и StdErr. Для StdIn я им хендл не даю. Затем CreateProcess().

Такая ситуация: запускаем под редиректом CMD.EXE, он в консоль выводит примерно следующее:

Microsoft ... bla-bla-bla

C:\>

Затем он пытается прочитать строку из StdIn, но у него ничего не получается, ибо хендла нет. В результате CMD.EXE падает, процесс завершен, хенделы закрылись. Например, другие проги в такой ситуации не падают, а просто игнорируют (в результате чего они тоже быстро завершаются, когда выведут в StdOut все, что надо).

Процесс завершился, а я из Pipe'ов получил все, что надо. Здесь все работает нормально, но только если мы работаем с прогами, которым все данные передаются в командной строке и они ничего не читают из StdIn.

Далее. Приделываю StdIn. Естественно, еще один CreatePipe() в другую сторону. Снова запускаю CMD.EXE, он снова выводит в StdOut ту же фигню и снова пытается прочитать строку из StdIn. На этот раз он читает строку до первого CRLF, но пока его нет, он там висит. Хендл StdOut естессно не закрыт, ибо процесс работает, а поэтому моя функция ReadFile() тупо висит. Либо Pipe так устроен, либо я где-то че-то не понимаю. Скорее всего я туплю, ибо в хелпе сказано, что ReadFile() вернется либо когда будет прочитано запрошенное количесто байт (пробовал запрашивать 1 байт - все равно не возвращает), либо когда на другой сторону Pipe'а закроется хендл записи, либо ошибка (нет ошибок). Ни одно условие не выполняется, и моя ReadFile() не возвращается, пока запущеный процесс не завершится. А запущеный процесс сидит и ждет данные из StrIn. Даже если ему записать данные в StdIn, он при следующих попытках чтения встанет, если данные закончатся.

В хелпе есть пример, но там рассчитано на ситуацию, когда мы знаем, чего и сколько хочет запускаемый процесс. Там сразу ему в StdIn отдаются все данные, а только потом из StdOut считывается, что он там сделал. Такая ерунда и у меня работает.

Задача заключается в том, чтобы сделать все наоборот: сначала успешно прочитать тот первый кусок данных из StdOut и увидеть, чего он там хочет. Только потом записать ему в StdIn то, что ему надо. Уже весь хелп перековырял, но так ничего и не понял.

Вот мой код:

DelphiВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

var pa          :SECURITY_ATTRIBUTES;
    sui         :STARTUPINFO;
    pi          :PROCESS_INFORMATION;
    hReadOut,
    hReadErr,
    hWriteOut,
    hWriteErr,
    hReadIn     :THandle;
    sBufferOut  :string;
    sBufferErr  :string;
    bReadOut,
    bReadErr    :Cardinal;
    b1,b2,b3    :Boolean;
begin
    FillChar(pa,SizeOf(pa),0);
    pa.nLength := SizeOf(pa);
    pa.lpSecurityDescriptor := nil;
    pa.bInheritHandle := true;
 
    b1 := CreatePipe(hReadOut,hWriteOut,@pa,0);
    b2 := CreatePipe(hReadErr,hWriteErr,@pa,0);
    b3 := CreatePipe(hReadIn,hWriteIn,@pa,0);
    if b1 and b2 and b3 then begin
        FillChar(sui,SizeOf(sui),0);
        sui.cb := SizeOf(sui);
        GetStartupInfo(sui);
        sui.hStdOutput := hWriteOut;
        sui.hStdError  := hWriteErr;
        sui.hStdInput  := hReadIn;
        sui.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
        sui.wShowWindow := SW_HIDE;
        if CreateProcess(nil, PChar(sCmdLine), nil,nil, true, 0,nil, Pointer(sWorkDir), sui, pi) then begin
            CloseHandle(hWriteOut); //закрываем отданные хенделы
            CloseHandle(hWriteErr);
            CloseHandle(hReadIn);
            SetLength(sBufferOut,l_BUFFER_SIZE+1);
            SetLength(sBufferErr,l_BUFFER_SIZE+1);
 
            repeat
                FillChar(sBufferOut[1],l_BUFFER_SIZE+1,0);
                FillChar(sBufferErr[1],l_BUFFER_SIZE+1,0);
{[B]здесь висит >[/B]} b1 := ReadFile(hReadOut, sBufferOut[1], l_BUFFER_SIZE, bReadOut, nil);
                b2 := ReadFile(hReadErr, sBufferErr[1], l_BUFFER_SIZE, bReadErr, nil);
                if (bReadOut<>$FFFFFFFF) and ((not b1)or(bReadOut=0)) then
                    bReadOut := $FFFFFFFF
                else if Assigned(zOnOutput) then
                    zOnOutput(self, Copy(sBufferOut,1,bReadOut));
                if (bReadErr<>$FFFFFFFF) and ((not b2)or(bReadErr=0)) then
                    bReadErr := $FFFFFFFF
                else if Assigned(zOnError) then
                    zOnError(self, Copy(sBufferErr,1,bReadErr));
                if (bReadOut=$FFFFFFFF)and(bReadErr=$FFFFFFFF) then break;
            until false;
            CloseHandle(pi.hThread);
            CloseHandle(pi.hProcess);
        end else begin
            CloseHandle(hWriteOut);
            CloseHandle(hWriteErr);
            CloseHandle(hReadIn);
        end;
        CloseHandle(hReadOut);
        CloseHandle(hReadErr);
        CloseHandle(hWriteIn);
    end;
end;

Также вот рабочий проэкт на Delphi. В нем сначала жмем на [Execute], а потом на [Write].