У меня есть консольное приложение, которое я запускаю от GUI applicaiton. Консольное приложение берет параметры для имен файлов, чтобы проанализировать и обработать. В настоящее время я в состоянии получить его вывод и отобразить его в приложении GUI, но я хотел бы иметь возможность отправить команды в него, чтобы управлять или даже остановить его выполнение.
Как я могу отправить команду или строку или что-нибудь к консольному приложению, предпочтительно с помощью каналов, которые я открыл для чтения его вывода?
const
CReadBuffer = 2400;
var
saSecurity: TSecurityAttributes;
hRead: THandle;
hWrite: THandle;
suiStartup: TStartupInfo;
piProcess: TProcessInformation;
pBuffer: array[0..CReadBuffer] of AnsiChar;
dRead: DWord;
dRunning: DWord;
dWritten: DWord;
Command: String;
BytesLeft: Integer;
BytesAvail: Integer;
begin
saSecurity.nLength := SizeOf(TSecurityAttributes);
saSecurity.bInheritHandle := True;
saSecurity.lpSecurityDescriptor := nil;
if CreatePipe(hRead, hWrite, @saSecurity, 0) then
begin
FillChar(suiStartup, SizeOf(TStartupInfo), #0);
suiStartup.cb := SizeOf(TStartupInfo);
suiStartup.hStdInput := hRead;
suiStartup.hStdOutput := hWrite;
suiStartup.hStdError := hWrite;
suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
suiStartup.wShowWindow := SW_HIDE;
Command := 'messageparser.exe c:\messagefile.msg';
UniqueString(Command);
if CreateProcess(nil, PChar(Command), @saSecurity,
@saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess) then
begin
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
Application.ProcessMessages;
repeat
dRead := 0;
if not PeekNamedPipe(hread, @pbuffer, CReadBuffer, @dRead, @BytesAvail, @BytesLeft) then
RaiseLastOSError;
if dRead <> 0 then
begin
ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);
pBuffer[dRead] := #0;
OemToCharA(pBuffer, pBuffer);
// do something with the data
// if a condition is present then do the following:
// WriteFile(hWrite, some_command, size_of_buffer, DWritten, nil);
end;
until (dRead < CReadBuffer);
until (dRunning <> WAIT_TIMEOUT);
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
end;
CloseHandle(hRead);
CloseHandle(hWrite);
end;
Тогда на консольной стороне, существует поток, ожидающий входа. Вот выполнить метод:
while not Terminated do
begin
ReadLn(Command);
// process command
Sleep(10);
end;
Это плохо мне знакомо поэтому, если существуют подсказки относительно того, как делают его правильно, я приветствую их:). Однако каждый раз, когда я отправляю Команду, она прибывает через как, независимо от того, что я считал в pBuffer из ReadPipe и не, какова команда.
Надежда это помогает.
--
Найденный решением на основе подсказки Nat.
Вам нужны два канала: один для процесса, отправляющего вам выходные данные ( stdout
), и один для вас, чтобы отправлять входные данные процессу ( stdin
).
Судя по вашему коду, вы помещаете оба конца того же канала в запись TStartupInfo
. Таким образом, вы фактически заставляете процесс говорить сам с собой. : -)
Итак, вам нужно дважды вызвать CreatePipe ()
, чтобы создать два канала, один для stdin
, один для stdout
(и stderr
).
Затем поместите дескриптор чтения stdin
в suiStartup.hStdInput
и дескриптор записи stdout
в suiStartup.hStdOutput
Чтобы отправить данные процессу, записать в дескриптор записи канала stdin
. Чтобы прочитать вывод процесса, прочтите дескриптор чтения канала stdout
.
Изменить: (снова)
Что касается всех дублирующих дескрипторов, а также наследуемого и ненаследуемого материала, описанного на этой странице (особенно в примере кода), вам понадобится чтобы убедиться, что дескрипторы, которые вы отправляете процессу, наследуются (как вы это сделали).
Вы должны также убедиться, что дескрипторы каналов, которые использует родительский процесс, не наследуются . Но у вас нет для этого ... Мне сошло с рук не делать этого раньше.
Вы можете сделать это, либо вызвав DuplicateHandle ()
для дескрипторов, указав, что они не наследуются и закрыв старые дескрипторы, либо вызвав SetHandleInformation ()
с 0, указанным для флаги (как описано здесь ).
Прошло много времени с тех пор, как я делал это сам, но я почти уверен, что это так, что счетчик ссылок для дескрипторов связан с вызывающим процессом, а не с дочерним процессом. Это предотвращает закрытие дескриптора, пока вы все еще его используете (например, вызывающий процесс может закрыть 'stdin'). Убедитесь, что вы закрыли ручки, иначе ручки протекут.
HTH.
N @
Проверьте это, чтобы увидеть, что вам нужно создать обе трубы (дважды вызывая WINAPI), как повторял Нат, но что насчет наследуемых ручек - не уверен, зачем это нужно?
http://support.microsoft.com/kb/190351.
Я думаю, что также может ввести в заблуждение то, что когда вы создаете трубку, вы создаете ручку для чтения и ручку для записи для этой трубки. В случае с трубкой stdin консоли вы будете использовать только ручку записи. Затем вы создаёте ещё одну трубку для stdout консоли (которая также будет иметь обработчик чтения и записи), но вы будете использовать только обработчик чтения.
Думаю, я правильно понял, но уже поздно и я иду спать.
Вместе с выходным трубопроводом есть входной трубопровод. Просто напишите на этот канал, используя WriteFile().