13208 / 6596 / 1041
Регистрация: 10.01.2008
Сообщений: 15,069
1

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

24.08.2008, 20:49. Показов 6329. Ответов 3
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Есть реализация редиректа, но односторонняя: 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].
Вложения
Тип файла: rar Redirect.rar (5.6 Кб, 118 просмотров)
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.08.2008, 20:49
Ответы с готовыми решениями:

Движение автотранспорта в обе стороны
Здравствуйте. Я пытаюсь сделать так чтобы заданное число машин ездило по дороге в обе стороны. ...

Словарь, работающий в обе стороны
решили написать программу,реализующую небольшой словарь слов(работающий в обе стороны): domains...

Сдвиг массива в обе стороны
Доброе время суток... Помогите справиться с задачкой... Поясню, что хочу: 1) Как в массиве...

Транслитерация текста в обе стороны (кириллица <-> латиница)
Здравствуйте господа! Нужна помощь. За плечами только написание программы «Hello world» Задача:...

3
Эксперт С++
2255 / 770 / 25
Регистрация: 27.05.2008
Сообщений: 1,496
24.08.2008, 21:50 2
Кажется, анонимные каналы не лучший способ решения такой задачи... они не созданы для обмена информацией,однако отлично перенаправляют вывод одного приложения на ввод другому. Я не уверен,но,возможно,стоит попробовать CreateNamedPipe и все,что с этими именованными каналами связано. Вот только не знаю,сработает ли.Совершенно точно можно сделать клиент-серверное взаимодействие, но не факт,что это возможно без внесения изменений в клиентское приложение. Мне интересно,сам хочу попробовать,но времени сейчас совсем нет. Как выдастся пара свободных часов - посмотрю. Если у тебя ченить раньше получится - дай мне знать
0
13208 / 6596 / 1041
Регистрация: 10.01.2008
Сообщений: 15,069
25.08.2008, 20:23  [ТС] 3
NamedPipe - это фича для обмена данными (в том числе по сети) между независимыми процессами. А вот анонимный пайп как раз для обмена между родительским и дочермин процессами.

На свежую голову поковырял хелп еще раз и обнаружил упущение. Те концы пайпов, которые мы себе оставляем (для чтения StdOut и StdErr и для записи в StdIn) надо обязательно отдублировать через DuplicateHandle(), исходные хендлы закрыть и юзать дубликаты.
Теперь StdOut читается нормально до того, как дочерний процесс встанет на чтении StdIn. Однако возникли другие грабли.

Изначально хотелось производить отдельно чтение из StdOut и отдельно из StdErr. В одной потоке двумя последовательными вызовами ReadFile() в большинстве случаев этого сделать нельзя. ReadFile() все равно не возвращает, пока не прочитает из пайпа хотя бы один байт. А посколько StdErr в большинстве случаев не пользуется спросом, он стоит пустой и ReadFile() из StdErr висит, пока не завершится процесс.

Мне было лень возиться сразу с двумя потоками, и я решил дать дочернему процессу один и тот же хендл для StdOut и StdErr от одного и того же пайпа. И данные все поступают, и с одним потоком меньше возни.

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

Вот готовый модуль с примером. Можно работать с CMD.EXE с своем окне вместо консольного.
Вложения
Тип файла: rar Redirect.rar (7.6 Кб, 446 просмотров)
0
5 / 4 / 1
Регистрация: 27.02.2010
Сообщений: 29
28.02.2010, 19:36 4
Vovan-VE, а можно готовый exe-шник выложить? уж очень хочется посмотреть ,а Delphi у меня нет
0
28.02.2010, 19:36
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
28.02.2010, 19:36
Помогаю со студенческими работами здесь

Как сделать линии по обе стороны текста
Привет всем! Подскажите как сделать линии по обе стороны текста. Пример на фото.

Binding на IsChecked в ToggleButton не работает в обе стороны
Здравствуйте. У меня есть необходимость по нажатию на одну из togglebutton дизэйблить остальные. Я...

Доказать ассоциативность в обе стороны(через аксиомы)
Помогите пожалуйста доказать... первые шаги вроде понял, а дальше ступор...

Cisco 2960. Не дает гигабит в обе стороны
Есть и этим оборудованием (циско 2960) проблема. При копировании файло вк примеру от меня на комп...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru