Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Редирект консольного вывода


Страницы:   1 2 3 4

бывший ©   (2005-07-18 22:22:00) 

Есть клиент-серверное приложение. сервер сделан в виде консоли. необходим редирект вывода информации из консоли в мое приложение в "реальном масштабе времени" ) понимаю что необходим отдельный поток в котором я и буду совершать действо.

Пробовал создавать пайп и переадресовывать вывод при запуске консоли (createprocess) на этот пайп, постепенно читая из него. во-первых - передается не все, во-вторых в определенный момент консоль виснит. Подобные решения видел на www.sources.ru, к примеру.

В общем, мне нужна именно постепенная передача всего текста, отображаемого в консоле в мое приложение по мере его появления.
Я так понял выхода два - переадресация вывода (к примеру через пайп) - с этим вариантом по непонятным мне причинам облом вышел, чтение из некоего буфера консоли. Мастера, помогите!

Если необходимо привести те коды, что пробовал использовать, приведу. Пока для экономии места оставляю вопрос в таком виде.


бывший   (2005-07-20 10:41:00) [41]

@aplet. во-первых консоль и так (SW_HIDE) иногжа почему-то появлялась ))) но не суть. с SW_SHOW всегда видна точно ) но она пустая, понятно почему.

@Ботвин Дмитрий: ща попробую

@Digitman [22]: я про загрузку сервера говорю по той причине, что когда слепил thread-пример сервер перестал загружаться вообще. Редиректа тоже нету. Т.е.: 1. моя программа работает нормально, основной поток жив ) 2. сервер запускается, но не начинает инициализацию (по какой-то неведомой причине), пока жива моя программа. Как только я вырубаю мое ПО - работа сервера возобновляется. Складывается ощущение, что созданный поток держит и противится загрузке сервера. Бред... В коде ничего лишнего.

Вот такие вот пироги с капустой и луком...


alpet   (2005-07-20 10:30:00) [42]

А если указать start.wShowWindow := SW_SHOWконсоль появляется?


Ботвин Дмитрий   (2005-07-20 10:29:00) [43]

Для начала проверь свою прогу с потоками на примере того же
пинга, тока без параметра -t. Если всё работает и второй поток
отслеживает завершение создаваемого процесса, тогда переходи
на свой сервер. А без уверенности, что код рабочий к серверу
вообще прикасаться не надо....


Digitman   (2005-07-20 10:06:00) [44]


> совсем я понял по поводу второго потока: "следить за целевым
> процессом" - это как раз и имеется в виду то о чем вы говорили
> ранее?


да, в [19]


> пока жива моя прога - загрузка сервера не трогается с места


не понял ..

что есть "загрузка с сервера" ?

речь идет о пайпе, а не о "серверах" и "загрузках" ...


> что-то упустил в RunDosInMemo применимо к потоку


да нет, все вроде бы выглядит вполне благопристойно ..

а что по этому поводу говорит отладчик ?


бывший   (2005-07-20 09:54:00) [45]

оба ) thnx 2Digitman ) утвердил мою уверенность ) будем кроить дальше. не совсем я понял по поводу второго потока: "следить за целевым процессом" - это как раз и имеется в виду то о чем вы говорили ранее?

я с потоками не на ты ) с ходу слепил нечто подобное:

unit uConsole;

interface

uses
 Controls, Windows, SysUtils, Forms, uMain, Classes, StdCtrls;

type
 TDosCapture = class(TThread)
 private
  CmdRun: String;
  AddLog: String;
  procedure DoChangeLog;
 protected
  procedure Execute; override;
  procedure ChangeLog(Text: String);
  procedure RunDosInMemo(CmdLine: string);
 public
  constructor Create(CmdLine: String);
 end;

implementation

procedure TDosCapture.RunDosInMemo(CmdLine: string);
const
ReadBuffer = 2400;
var
Security: TSecurityAttributes;
ReadPipe, WritePipe: THandle;
start: TStartUpInfo;
ProcessInfo: TProcessInformation;
Buffer: Pchar;
BytesRead: DWord;
Apprunning: DWord;
begin
with Security do
begin
 nlength := SizeOf(TSecurityAttributes);
 binherithandle := true;
 lpsecuritydescriptor := nil;
end;
if Createpipe(ReadPipe, WritePipe,
  @Security, 0) then
begin
 Buffer := AllocMem(ReadBuffer + 1);
 FillChar(Start, Sizeof(Start), #0);
 start.cb := SizeOf(start);
 start.hStdOutput := WritePipe;
 start.hStdInput := ReadPipe;
 start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
 start.wShowWindow := SW_HIDE;

 if CreateProcess(nil,
    PChar(CmdLine),
    @Security,
    @Security,
    true,
    NORMAL_PRIORITY_CLASS,
    nil,
    nil,
    start,
    ProcessInfo) then
  begin
    repeat
      Apprunning := WaitForSingleObject
        (ProcessInfo.hProcess, 100);
      ReadFile(ReadPipe, Buffer[0],
        ReadBuffer, BytesRead, nil);
      Buffer[BytesRead] := #0;
      OemToAnsi(Buffer, Buffer);
      ChangeLog(String(Buffer));
    until (Apprunning <> WAIT_TIMEOUT);
  end;
  FreeMem(Buffer);
  CloseHandle(ProcessInfo.hProcess);
  CloseHandle(ProcessInfo.hThread);
  CloseHandle(ReadPipe);
  CloseHandle(WritePipe);
end;
end;

{ TDosCapture }

procedure TDosCapture.ChangeLog(Text: String);
begin
AddLog:=Text;
Synchronize(DoChangeLog);
end;

constructor TDosCapture.Create(CmdLine: String);
begin
CmdRun:=CmdLine;
FreeOnTerminate:=True;
inherited Create(False);
end;

procedure TDosCapture.DoChangeLog;
begin
with uMain.MainForm.richLog do
 text:=text+AddLog;
end;

procedure TDosCapture.Execute;
begin
RunDosInMemo(CmdRun);
end;

end.


Использую соответственно:
DCapture:=TDosCapture.Create('server.exe');

Что происходит - пока жива моя прога - загрузка сервера не трогается с места ) понимаю что виноват я, т.к. что-то упустил в RunDosInMemo применимо к потоку. Натолкните меня на мысль, господа. Плиз.


Digitman   (2005-07-20 09:17:00) [46]


> в отдельном потоке пока не пробовал


в самую пору попробовать.

придется создать два доп.потока - один будет работать с пайпом, другой будет следить за целевым процессом


Digitman   (2005-07-20 09:15:00) [47]


> в случае падения серва будем в холостую работать


в этом случае поток, вызвавший ReadFile(), "зависнет" в бесконечном ожидании, ибо пайп по прежнему существует, а процесса уже нет

необходимо принять какие-либо меры по слежению за процессом, и как только будет обнаружен факт его терминирования (неважно почему он был терминирован - завершен по инициативе пользователя или снят системой с выполнения по иным причинам) тут же закрыть хэндл пайпа ... как только пайп будет закрыт, ф-ция ReadFile() немедленно вернет управление с кодом ошибки 6 (неверный дескриптор)


бывший   (2005-07-20 09:07:00) [48]

в общем на боевом примере - провал.

1. виснит моя программа, не откликается даже после истечения таймаута и умервщленном сервере.

2. судя по task manager'у виснит и сам сервер. обычно он сразу после инициализации начинает жрать порядка 500-600 mb, а тут всего пару метров ест.

в отдельном потоке пока не пробовал.
в чем может быть беда? братцы, помогите!


бывший   (2005-07-19 17:23:00) [49]

офигенно ) супер! просто супер! вопрос есть такой еще ) как лучше поступить с таймаутом, если есть вероятность "простоя" сервера пустым в течение часа-двух и более, но в тоже время необходимо продолжать наблюдение? ставить большой таймаут - бессмысленно, т.к. в случае падения серва будем в холостую работать. что можете посоветовать?

@Ботвин Дмитрий: спасибо огромное! вы реально меня выручили! щас буду тестировать непосредственно на сервере. но ваш пример работает идеально )


бывший   (2005-07-19 17:17:00) [50]

спасибо. щас смотреть буду )


Ботвин Дмитрий   (2005-07-19 17:00:00) [51]

Если ничего не произойдёт, то увеличить таймаут в строке:
Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 200);


Ботвин Дмитрий   (2005-07-19 16:47:00) [52]

Попробуй такой вариант:

procedure RunDosInMemo(CmdLine: string; AMemo: TMemo);
const
 ReadBuffer = 2400;
var
 Security: TSecurityAttributes;
 ReadPipe, WritePipe: THandle;
 start: TStartUpInfo;
 ProcessInfo: TProcessInformation;
 Buffer: Pchar;
 BytesRead: DWord;
 Apprunning: DWord;
begin
 Screen.Cursor := CrHourGlass;
 Form1.Button1.Enabled := False;
 with Security do
 begin
   nlength := SizeOf(TSecurityAttributes);
   binherithandle := true;
   lpsecuritydescriptor := nil;
 end;
 if Createpipe(ReadPipe, WritePipe,
   @Security, 0) then
 begin
   Buffer := AllocMem(ReadBuffer + 1);
   FillChar(Start, Sizeof(Start), #0);
   start.cb := SizeOf(start);
   start.hStdOutput := WritePipe;
   start.hStdInput := ReadPipe;
   start.dwFlags := STARTF_USESTDHANDLES +
     STARTF_USESHOWWINDOW;
   start.wShowWindow := SW_HIDE;

   if CreateProcess(nil,
     PChar(CmdLine),
     @Security,
     @Security,
     true,
     NORMAL_PRIORITY_CLASS,
     nil,
     nil,
     start,
     ProcessInfo) then
   begin
     repeat
       Apprunning := WaitForSingleObject
         (ProcessInfo.hProcess, 100);
       ReadFile(ReadPipe, Buffer[0],
         ReadBuffer, BytesRead, nil);
       Buffer[BytesRead] := #0;
       OemToAnsi(Buffer, Buffer);
       AMemo.Text := AMemo.text + string(Buffer);

       Application.ProcessMessages;
     until (Apprunning <> WAIT_TIMEOUT);
   end;
   FreeMem(Buffer);
   CloseHandle(ProcessInfo.hProcess);
   CloseHandle(ProcessInfo.hThread);
   CloseHandle(ReadPipe);
   CloseHandle(WritePipe);
 end;
 Screen.Cursor := CrDefault;
 Form1.Button1.Enabled := True;
end;

Пример:
procedure TForm1.Button1Click(Sender: TObject);
begin
 Memo1.Clear;
 RunDosInMemo('ping -t 192.168.28.200', Memo1);
end;


бывший   (2005-07-19 16:05:00) [53]

в примере Digitman'a есть интересная особенность - как я раньше не допер до этого! Вначале ждем пока отработает целиком программа, потом читаем с пайпа, куда на протяжении работы консоли редиректился вывод. Отлично =) Но как теперь быть мне, когда редирект нужен постепенный. Ждать пока отработает сервер мне никак нельзя - я парсю его логи. Какие есть предложения?


Digitman   (2005-07-19 11:36:00) [54]


> isasa ©   (19.07.05 10:52) [10]


точно.
255 я прозевал.

но в ориг.коде, откуда это было содрано, было 256, потому и не обратил сразу внимание


> бывший   (18.07.05 22:22)  


http://delphi.about.com/cs/adptips2001/a/bltip0201_2.htm


бывший   (2005-07-19 10:57:00) [55]

хм... ну и в итоге? ) вы мне скажите почему читает не полностью? причем вариант предложенный P.N.P в несколько другом исполнении дает тот же результат ( что за буфер ест me консоли и как обрабатывать его содержимое? какими макаром может случиться так, что информация выводимая на консоль не может быть редиректнута? видел в свое время plugin для Frigate (аналог ТС), где подобная фича была реализована, жаль он без исходных кодов...


isasa   (2005-07-19 10:52:00) [56]

да нет, похоже чисто

WasOK := ReadFile(StdOutPipeRead, Buffer, 255 , BytesRead, nil);
...
Buffer[BytesRead] := #0;

больше 255 не прочтет


Digitman   (2005-07-19 10:39:00) [57]


> бывший   (19.07.05 10:19) [8]


> вы мне скажите одно


я тебе ничего не скажу)
я увидел грубую ошибку в этом коде и рекомендую тебе обратить на нее столь же серьезное внимание, исправить ее, опробовать результат.. а потом уже будет все остальное ..


бывший   (2005-07-19 10:19:00) [58]

хм...

@Digitman - спасибо за разъяснения. Код был взят в качестве примера с www.sources.ru вроде или с какого-то фака.

application.processmasseges в самом начале затесался случайно )

вы мне скажите одно, данный код с внесением соответствующих исправлений указанных Digitman'ом даст ли 100% редирект?


Digitman   (2005-07-19 09:03:00) [59]


> понимаю что необходим отдельный поток в котором я и буду
> совершать действо


действительно необходим, потому что ReadFile[Ex] в дан.случае не удастся использовать в неблокирующем режиме


Digitman   (2005-07-19 08:55:00) [60]

где-то я этот код уже видел ...

одна и та же ошибка фигурирует


> Buffer[BytesRead] := #0;


а если BytesRead будет равен 256 ?
подумай, что произойдет ..


> Application.ProcessMessages


а это, кстати, зачем ?


Страницы:   1 2 3 4

Наверх

Титульная страница Поиск, карта сайта Написать письмо