![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Вывод в командную строку (cmd.exe)
Faceless © (2011-10-27 13:09:00)Через командную строку запускаю приложение с определенными параметрами, приложение в зависимости от параметров выполняет некую операцию. Необходимо возвращать в комадную строку результат выполнения операции. Примера, как это делается не на нашел.
han_malign (2011-10-27 17:24:00) [1]
> Есть FreeConsole функция.
- к сожалению консоль родительского процессапосле этого не разблокирется...
DiamondShark (2011-10-27 16:44:00) [2]
> han_malign (27.10.11 15:28) [23]
> > отсоединяешься от консоли
> - угу, единственный вариант - CreateProcess(...DETACHED_PROCESS...)
Есть FreeConsole функция.
Anatoly Podgoretsky (2011-10-27 15:53:00) [3]
>
> > Anatoly Podgoretsky © (27.10.11 13:37) [8]
> >
> > Выводи текст в stdout
>
>
> Разве у оконного приложения есть stdout?
Посмотри CreateProcess и найдешь если захочешь.
Faceless (2011-10-27 15:51:00) [4]
> А разве есть достоверная информация, что у автора консольное
> или GUI приложение
GUI
Нашел рабочий пример чтение / запии через pipes. Только в нем процесс cmd.exe создается CreateProcess, выполняется запись / чтение и процесс завершается, а мне нужно присоединиться к уже запущеному, передать сообщение и не закрывать. как это сделать пока не понятно.
unit uChildProc;
interface
uses
Windows, SysUtils;
resourcestring
//resourcestring sSetStdHandleMsg = 'Ошибка при перенаправлении стандартного ввода.'#13#13'%s;
sCreatePipeMsg = 'Ошибка при создании "трубы" (Pipe).'#13#13'CreatePipe(FChildStdoutRd, FChildStdoutWr, saAttr, 0)';
sDuplicateHandleMsg = 'Ошибка при создании копии хэндла.'#13#13'DuplicateHandle(GetCurrentProcess(), FChildStdoutRd, GetCurrentProcess(), @FChildStdoutRdDup, 0, False, DUPLICATE_SAME_ACCESS)';
sCreateChildProcessMsg = 'Ошибка при создании дочернего процесса.'#13#13'CreateChildProcess(ExeName, CommadLine, FChildStdoutWr)';
type
ESetStdHandleErr = class(Exception);
ECreatePipeErr = class(Exception);
EDuplicateHandleErr = class(Exception);
ECreateChildProcessErr = class(Exception);
{ TChildProc }
{Класс дочернего процесса. Позволяет создать дочерний процесс и работать с его stdIn и stdOut}
TChildProc = class(TObject)
FChildStdoutRd: THandle;
FChildStdoutWr: THandle;
FChildStdinRd: THandle;
FChildStdinWr: THandle;
private
function CreateChildProcess(ExeName, CommadLine: String; StdIn: THandle; StdOut: THandle): Boolean;
public
constructor Create(ExeName, CommadLine: String);
destructor Destroy; override;
function ReadStrFromChild(Timeout: Integer=1000): String;
function WriteStrToChild(Data: String): Boolean;
end;
implementation
{ TChildProc }
constructor TChildProc.Create(ExeName, CommadLine: String);
Var
StdoutRdTmp, StdinWrTmp: THandle; //Временные хэндлы.
saAttr: TSecurityAttributes;
begin
ZeroMemory(@saAttr, SizeOf(TSecurityAttributes));
saAttr.nLength := SizeOf(TSecurityAttributes);
saAttr.bInheritHandle := True;
saAttr.lpSecurityDescriptor := Nil;
If not CreatePipe(StdoutRdTmp, FChildStdoutWr, @saAttr, 0) Then //Создаем "читальный" Pipe
raise ECreatePipeErr.CreateRes(@sCreatePipeMsg)
Else
If not CreatePipe(FChildStdinRd, StdinWrTmp, @saAttr, 0) Then //Создаем "писальный" Pipe
raise ECreatePipeErr.CreateRes(@sCreatePipeMsg)
Else
begin
//Делаем НЕ наследуемые дубликаты хэндлов тех концов Pipe'ов, которые передаются в запускаемый процесс...
If not DuplicateHandle(GetCurrentProcess(), StdoutRdTmp, GetCurrentProcess(), @FChildStdoutRd, 0, False, DUPLICATE_SAME_ACCESS) Then
raise EDuplicateHandleErr.CreateRes(@sDuplicateHandleMsg)
Else
If not DuplicateHandle(GetCurrentProcess(), StdinWrTmp, GetCurrentProcess(), @FChildStdinWr, 0, False, DUPLICATE_SAME_ACCESS) Then
raise EDuplicateHandleErr.CreateRes(@sDuplicateHandleMsg)
Else
begin
CloseHandle(StdoutRdTmp);//Закроем наследуемый вариант "Читального" хэндла
CloseHandle(StdinWrTmp); //Закроем наследуемый вариант "Писального" хэндла
//Собственно создание дочернего процесса и присоединение его StdIn/Out'a к нашим Pipe'ам
If not CreateChildProcess(ExeName, CommadLine, FChildStdinRd, FChildStdoutWr) Then
raise ECreateChildProcessErr.CreateRes(@sCreateChildProcessMsg)
end;
end;
end;
function TChildProc.CreateChildProcess(ExeName, CommadLine: String; StdIn,
StdOut: THandle): Boolean;
Var
piProcInfo: TProcessInformation;
siStartInfo: TStartupInfo;
begin
// Set up members of STARTUPINFO structure.
ZeroMemory(@siStartInfo, SizeOf(siStartInfo));
siStartInfo.cb := SizeOf(siStartInfo);
siStartInfo.hStdInput := StdIn;
siStartInfo.hStdOutput := StdOut;
siStartInfo.dwFlags := STARTF_USESTDHANDLES;
Result := CreateProcess(Nil,
PChar(ExeName + ' ' + CommadLine), // command line
Nil, // process security attributes
Nil, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
Nil, // use parent's environment
Nil, // use parent's current directory
siStartInfo, // STARTUPINFO pointer
piProcInfo); // receives PROCESS_INFORMATION
end;
destructor TChildProc.Destroy;
begin
//Закрываем хэндлы пайпов
CloseHandle(FChildStdoutRd);
CloseHandle(FChildStdoutWr);
CloseHandle(FChildStdinRd);
CloseHandle(FChildStdinWr);
end;
function TChildProc.ReadStrFromChild(Timeout: Integer): String;
Const
SleepDevider = 10; //Делим весь таймаут на "кусочки" по (Timeout / SleepDevider) мс
Var
i: Integer;
dwRead, BufSize, DataSize: DWORD;
Res: Boolean;
SleepTime: Integer;
begin
Try
SleepTime := Timeout div SleepDevider;
Repeat
For i := 0 to SleepDevider - 1 do //Ждем данные
begin
Res := PeekNamedPipe(FChildStdoutRd, nil, 0, nil, @DataSize, nil);
Res := Res and (DataSize > 0);
If Res Then
Break;
Sleep(SleepTime);
end;
If Res Then //Если есть данные
begin
BufSize := Length(Result);
SetLength(Result, BufSize + DataSize);
Res := ReadFile(FChildStdoutRd, Result[BufSize + 1], DataSize, dwRead, Nil);
end;
Until not Res;
Except
Result := 'Read Err!';
End;
end;
function TChildProc.WriteStrToChild(Data: String): Boolean;
Var
dwWritten, BufSize: DWORD;
chBuf: PChar;
begin
//Обратите внимание на #13#10!!! Без них - будет работать с ошибками. Многие (но не все!) процессы ждут этих символов для обработки ввода
//Это пара символов (конец строки и перевод каретки - CR/LF)
//Вообще-то можно обойтись только LF(#10) или вообще ничем (для некоторых юниксоидных творений), но это самый надежный вариант.
//Но помните! Программы бывают разные поэтому в некоторых случаях может потребоваться поиграться с символами завершения ввода.
chBuf := PChar(Data + #13#10);
BufSize := Length(chBuf);
Result := WriteFile(FChildStdinWr, chBuf^, BufSize, dwWritten, Nil);
Result := Result and (BufSize = dwWritten);
end;
end.
procedure TForm1.Button1Click(Sender: TObject);
Var
i: Integer;
begin
With TChildProc.Create(Edit1.Text, '') do
Try
For i:=0 to Memo2.Lines.Count-1 do
WriteStrToChild(Memo2.Lines[i]);
Memo1.Lines.Add(ReadStrFromChild(500));
Finally
Free;
End;
end;
han_malign (2011-10-27 15:28:00) [5]
> stdin, stdout это все чем располагает консольное приложение
- если бы, IMAGE_OPTIONAL_HEADER.Subsystem=WINDOWS_CUI - определяет бытие...
(а еще stderr)
> отсоединяешься от консоли
- угу, единственный вариант - CreateProcess(...DETACHED_PROCESS...)
Anatoly Podgoretsky (2011-10-27 15:19:00) [6]> Ega23 (27.10.2011 15:13:20) [20]
Все это должен очень точно сообщить автор
Inovet (2011-10-27 15:17:00) [7]> [20] Ega23 © (27.10.11 15:13)
> Ну если FormCreate, то явно не консоль.
Никио не запрещает в консольном создать форму.
Ega23 (2011-10-27 15:13:00) [8]
> А разве есть достоверная информация, что у автора консольное
> или GUI приложение
Ну если FormCreate, то явно не консоль.
Anatoly Podgoretsky (2011-10-27 14:46:00) [9]
> Ega23 © (27.10.11 13:47) [11]
А разве есть достоверная информация, что у автора консольное или GUI приложение, но кроме того stdin, stdout это все чем располагает консольное приложение. А он что то говорит про вывод в косоль.
Anatoly Podgoretsky (2011-10-27 14:43:00) [10]
> Вот тут Розыч говорит, что можно pipe создать и через него
> "общаться".
Мало ли что он говорит, когда автор молчит.
DiamondShark (2011-10-27 14:35:00) [11]
> Ega23 © (27.10.11 14:23) [15]
> Вот тут Розыч говорит, что можно pipe создать и через него "общаться".
Передай Розычу, чтоб тему читал, а не буковки узнавал.
> Но, ИМХО, либо GUI, либо CMD.
Они не взаимоисключающие.
Собираешь приложение с ключом $APPTYPE CONSOLE.
Анализируешь параметры запуска. Если решаешь работать в пакетном режиме, пишешь всё в консоль. Если решаешь работать в граф. интерфейсе, отсоединяешься от консоли и работаешь как оконное приложение.
Утилит, которые так себя ведут, полным-полно.
DiamondShark (2011-10-27 14:29:00) [12]
> До меня в приложении уже была реализована вся логика по
> разбору параметров командной строки и запуску сервисов приложения,
> но все сообщения выводились через MessageBox'а. Нужно сделать
> так, чтобы все сообщения перенаправлять в командную строку.
Ну так и замени MessageBox на WriteLn.
Ega23 (2011-10-27 14:23:00) [13]Вот тут Розыч говорит, что можно pipe создать и через него "общаться".
Но, ИМХО, либо GUI, либо CMD.
А что мешает товарищу 2 разных приложения поставлять?
Faceless (2011-10-27 14:20:00) [14]
> Неконсольные программы выводят свои сообщения с помощью
> messagebox и не надо фигнëй страдать.
человек хочет, чтобы была возможность выполнять административные сервисы приложения через командную строку (импорт / экспорт товаров в магизин, импорт / экспорт классификатора товаров, создание резервной копии магазина и проч.) и возвращать результат выполнения в командную строку. До меня в приложении уже была реализована вся логика по разбору параметров командной строки и запуску сервисов приложения, но все сообщения выводились через MessageBox'а. Нужно сделать так, чтобы все сообщения перенаправлять в командную строку.
Jeer (2011-10-27 14:07:00) [15]
> и не надо фигнëй страдать.
Это уже не фигня, а болезнь в крайне запущенной стадии.
Плохиш (2011-10-27 13:53:00) [16]
> Faceless © (27.10.11 13:36) [7]
Неконсольные программы выводят свои сообщения с помощью messagebox и не надо фигнëй страдать.
Ega23 (2011-10-27 13:47:00) [17]
> Anatoly Podgoretsky © (27.10.11 13:37) [8]
>
> Выводи текст в stdout
Разве у оконного приложения есть stdout?
И. Павел (2011-10-27 13:40:00) [18]> [7] Faceless © (27.10.11 13:36)
Создайте батник (тем более что ваш вызов exe будет для пользователя запутанный и не помешает сделать его более дружественным) и анализируйте код завершения программы.
MyApp.exe BackUp -StoreName="MyStore".
if errorlevel 1 (
echo Нет такого магазина
)
Faceless (2011-10-27 13:38:00) [19]Как, например, по нажатию кнопки в приложении, вывести строку в командную строку Windows (cmd.exe).
Anatoly Podgoretsky (2011-10-27 13:37:00) [20]Выводи текст в stdout