Форум: "WinAPI";
Текущий архив: 2005.09.11;
Скачать: [xml.tar.bz2];
ВнизРедирект консольного вывода Найти похожие ветки
← →
бывший (2005-07-18 22:22) [0]Есть клиент-серверное приложение. сервер сделан в виде консоли. необходим редирект вывода информации из консоли в мое приложение в "реальном масштабе времени" ) понимаю что необходим отдельный поток в котором я и буду совершать действо.
Пробовал создавать пайп и переадресовывать вывод при запуске консоли (createprocess) на этот пайп, постепенно читая из него. во-первых - передается не все, во-вторых в определенный момент консоль виснит. Подобные решения видел на www.sources.ru, к примеру.
В общем, мне нужна именно постепенная передача всего текста, отображаемого в консоле в мое приложение по мере его появления.
Я так понял выхода два - переадресация вывода (к примеру через пайп) - с этим вариантом по непонятным мне причинам облом вышел, чтение из некоего буфера консоли. Мастера, помогите!
Если необходимо привести те коды, что пробовал использовать, приведу. Пока для экономии места оставляю вопрос в таком виде.
← →
P.N.P. © (2005-07-18 22:33) [1]Посмотри http://www.renderman.ru/forum/download.php/1,336,24/FoxUtils.txt
Оно?
← →
бывший (2005-07-18 22:55) [2]спасибо. интересное решение. оно работает, но все равно получает из консоли далеко не весь текст. консоль все время пополняется информацией (идет лог соединений клиентов, обмена данными, ошибок и т.д.) - этим вариантом получаю только 40% лога инициализации сервера и наступает тишина (не из-за таймаута точно).
этож каким макаром надо в этой консоле писать, чтобы редирект вывода не помогал? )
Мастера, жду вашей помощи и совета.
← →
Гаврила © (2005-07-18 23:43) [3]
> Если необходимо привести те коды, что пробовал использовать,
> приведу
Видимо, пора
← →
бывший (2005-07-19 08:30) [4]
function GetDosOutput(const CommandLine:string): string;
var
SA: TSecurityAttributes;
SI: TStartupInfo;
PI: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of Char;
BytesRead: Cardinal;
WorkDir, Line: String;
begin
Application.ProcessMessages;
with SA do
begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
// создаём пайп для перенаправления стандартного вывода
CreatePipe(StdOutPipeRead, // дескриптор чтения
StdOutPipeWrite, // дескриптор записи
@SA, // аттрибуты безопасности
0 // количество байт принятых для пайпа - 0 по умолчанию
);
try
// Создаём дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
// а так же проверяем, чтобы он не показывался на экране.
with SI do
begin
FillChar(SI, SizeOf(SI), 0);
cb := SizeOf(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_SHOW; //SW_HIDE
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // стандартный ввод не перенаправляем
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
// Запускаем компилятор из командной строки
WorkDir := ExtractFilePath(CommandLine);
WasOK := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
// Теперь, когда дескриптор получен, для безопасности закрываем запись.
// Нам не нужно, чтобы произошло случайное чтение или запись.
CloseHandle(StdOutPipeWrite);
// если процесс может быть создан, то дескриптор, это его вывод
if not WasOK then
raise Exception.Create("Could not execute command line!")
else
try
// получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
Line := "";
repeat
// читаем блок символов (могут содержать возвраты каретки и переводы строки)
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
// есть ли что-нибудь ещё для чтения?
if BytesRead > 0 then
begin
// завершаем буфер PChar-ом
Buffer[BytesRead] := #0;
// добавляем буфер в общий вывод
Line := Line + Buffer;
end;
uMain.MainForm.richLog.Lines.Add(Line);
until not WasOK or (BytesRead = 0);
// ждём, пока завершится консольное приложение
WaitForSingleObject(PI.hProcess, INFINITE);
finally
// Закрываем все оставшиеся дескрипторы
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
finally
result:=Line;
CloseHandle(StdOutPipeRead);
end;
end;
сделано на пайпах. как такового "реал-тайма" нету. мне главное было проверить весь ли текст с консоли будет редиректнут. не весь ( пробовал и много других вариантов, в том числе и предложенный товарищем P.N.P.
← →
бывший (2005-07-19 08:34) [5]наверняка у многих мастеров есть свои секреты работы с чужой консолью. поделитесь, плиз )
← →
Digitman © (2005-07-19 08:55) [6]где-то я этот код уже видел ...
одна и та же ошибка фигурирует
> Buffer[BytesRead] := #0;
а если BytesRead будет равен 256 ?
подумай, что произойдет ..
> Application.ProcessMessages
а это, кстати, зачем ?
← →
Digitman © (2005-07-19 09:03) [7]
> понимаю что необходим отдельный поток в котором я и буду
> совершать действо
действительно необходим, потому что ReadFile[Ex] в дан.случае не удастся использовать в неблокирующем режиме
← →
бывший (2005-07-19 10:19) [8]хм...
@Digitman - спасибо за разъяснения. Код был взят в качестве примера с www.sources.ru вроде или с какого-то фака.
application.processmasseges в самом начале затесался случайно )
вы мне скажите одно, данный код с внесением соответствующих исправлений указанных Digitman"ом даст ли 100% редирект?
← →
Digitman © (2005-07-19 10:39) [9]
> бывший (19.07.05 10:19) [8]
> вы мне скажите одно
я тебе ничего не скажу)
я увидел грубую ошибку в этом коде и рекомендую тебе обратить на нее столь же серьезное внимание, исправить ее, опробовать результат.. а потом уже будет все остальное ..
← →
isasa © (2005-07-19 10:52) [10]да нет, похоже чисто
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
...
Buffer[BytesRead] := #0;
больше 255 не прочтет
← →
бывший (2005-07-19 10:57) [11]хм... ну и в итоге? ) вы мне скажите почему читает не полностью? причем вариант предложенный P.N.P в несколько другом исполнении дает тот же результат ( что за буфер ест me консоли и как обрабатывать его содержимое? какими макаром может случиться так, что информация выводимая на консоль не может быть редиректнута? видел в свое время plugin для Frigate (аналог ТС), где подобная фича была реализована, жаль он без исходных кодов...
← →
Digitman © (2005-07-19 11:36) [12]
> 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 16:05) [13]в примере Digitman"a есть интересная особенность - как я раньше не допер до этого! Вначале ждем пока отработает целиком программа, потом читаем с пайпа, куда на протяжении работы консоли редиректился вывод. Отлично =) Но как теперь быть мне, когда редирект нужен постепенный. Ждать пока отработает сервер мне никак нельзя - я парсю его логи. Какие есть предложения?
← →
Ботвин Дмитрий (2005-07-19 16:47) [14]Попробуй такой вариант:
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 17:00) [15]Если ничего не произойдёт, то увеличить таймаут в строке:
Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 200);
← →
бывший (2005-07-19 17:17) [16]спасибо. щас смотреть буду )
← →
бывший (2005-07-19 17:23) [17]офигенно ) супер! просто супер! вопрос есть такой еще ) как лучше поступить с таймаутом, если есть вероятность "простоя" сервера пустым в течение часа-двух и более, но в тоже время необходимо продолжать наблюдение? ставить большой таймаут - бессмысленно, т.к. в случае падения серва будем в холостую работать. что можете посоветовать?
@Ботвин Дмитрий: спасибо огромное! вы реально меня выручили! щас буду тестировать непосредственно на сервере. но ваш пример работает идеально )
← →
бывший (2005-07-20 09:07) [18]в общем на боевом примере - провал.
1. виснит моя программа, не откликается даже после истечения таймаута и умервщленном сервере.
2. судя по task manager"у виснит и сам сервер. обычно он сразу после инициализации начинает жрать порядка 500-600 mb, а тут всего пару метров ест.
в отдельном потоке пока не пробовал.
в чем может быть беда? братцы, помогите!
← →
Digitman © (2005-07-20 09:15) [19]
> в случае падения серва будем в холостую работать
в этом случае поток, вызвавший ReadFile(), "зависнет" в бесконечном ожидании, ибо пайп по прежнему существует, а процесса уже нет
необходимо принять какие-либо меры по слежению за процессом, и как только будет обнаружен факт его терминирования (неважно почему он был терминирован - завершен по инициативе пользователя или снят системой с выполнения по иным причинам) тут же закрыть хэндл пайпа ... как только пайп будет закрыт, ф-ция ReadFile() немедленно вернет управление с кодом ошибки 6 (неверный дескриптор)
← →
Digitman © (2005-07-20 09:17) [20]
> в отдельном потоке пока не пробовал
в самую пору попробовать.
придется создать два доп.потока - один будет работать с пайпом, другой будет следить за целевым процессом
← →
бывший (2005-07-20 09:54) [21]оба ) 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 10:06) [22]
> совсем я понял по поводу второго потока: "следить за целевым
> процессом" - это как раз и имеется в виду то о чем вы говорили
> ранее?
да, в [19]
> пока жива моя прога - загрузка сервера не трогается с места
не понял ..
что есть "загрузка с сервера" ?
речь идет о пайпе, а не о "серверах" и "загрузках" ...
> что-то упустил в RunDosInMemo применимо к потоку
да нет, все вроде бы выглядит вполне благопристойно ..
а что по этому поводу говорит отладчик ?
← →
Ботвин Дмитрий (2005-07-20 10:29) [23]Для начала проверь свою прогу с потоками на примере того же
пинга, тока без параметра -t. Если всё работает и второй поток
отслеживает завершение создаваемого процесса, тогда переходи
на свой сервер. А без уверенности, что код рабочий к серверу
вообще прикасаться не надо....
← →
alpet © (2005-07-20 10:30) [24]А если указать start.wShowWindow := SW_SHOW консоль появляется?
← →
бывший (2005-07-20 10:41) [25]@aplet. во-первых консоль и так (SW_HIDE) иногжа почему-то появлялась ))) но не суть. с SW_SHOW всегда видна точно ) но она пустая, понятно почему.
@Ботвин Дмитрий: ща попробую
@Digitman [22]: я про загрузку сервера говорю по той причине, что когда слепил thread-пример сервер перестал загружаться вообще. Редиректа тоже нету. Т.е.: 1. моя программа работает нормально, основной поток жив ) 2. сервер запускается, но не начинает инициализацию (по какой-то неведомой причине), пока жива моя программа. Как только я вырубаю мое ПО - работа сервера возобновляется. Складывается ощущение, что созданный поток держит и противится загрузке сервера. Бред... В коде ничего лишнего.
Вот такие вот пироги с капустой и луком...
← →
Ботвин Дмитрий (2005-07-20 10:56) [26]Ну чё, с пингом работает?
← →
бывший (2005-07-20 10:57) [27]господа, будите смеяться ) используя thread с ping -t все работает нормально. вопрос дня: каким образом редирект консоли может влиять на работоспособность сервера? сервер запущенный без моего ПО работает нормально. как можно отладить и разобраться в чем траблы?
на данный момент использую только 1 поток (см. пример [21]), как я уже говорил работает с пингом, не работает с сервером - держит сервер, не дает ему грузиться, пока не завершу поток/приложение.
жду ваших советов, Мастера.
← →
Ботвин Дмитрий (2005-07-20 10:59) [28]Попробуй задать своему потоку приоритет Idle...
← →
Digitman © (2005-07-20 11:01) [29]
> бывший (20.07.05 10:41) [25]
отладчик-то что говорит ?!
ставь брейкпойнт на первую же строчку в теле Execute() и пройди пошагово весь код .. убедись что в ходе работы Execute() исключений не возникает
← →
alpet © (2005-07-20 11:02) [30]Ботвин Дмитрий (20.07.05 10:59) [28]
WaitForSingleObject вроде выполняет роль своеобразного Sleep, сомнительно что здесь дело в приоритетах.
бывший (20.07.05 10:57) [27]
Можно узнать на чем и с какой библиотекой написан злополучный сервер?
← →
Ботвин Дмитрий (2005-07-20 11:07) [31]> бывший
А ты не пробывал изменить таймаут у WaitForSingleObject? Это я в примере поставил 100. Реально с таким значением может и не работать...
← →
alpet © (2005-07-20 11:09) [32]Попробуй также для начала обойтись без перехвата пайпов. Я тут посмотрел одну рабочую программу ориентированную на Dos приложение - там start.hStdInput используется для передачи в буфер консоли данных (эмуляции клавиатурного ввода), а в твоем случае производится попытка читать из этого дескриптора. Если судить по логике моей программы должно быть так:
ReadFile(WritePipe, Buffer[0],
ReadBuffer, BytesRead, nil);
...или я чего-то не понял.
← →
Ботвин Дмитрий (2005-07-20 11:10) [33]> alpet
Дело, конечно, может быть и не в приоритете, просто для подобных
утилит рекомендуется именно простаивающий приоритет, что-бы не
нагружать излишне проц. Хотя это дело каждого...
← →
бывший (2005-07-20 11:10) [34]alpet © (20.07.05 11:02) [30]
сервер... э... а хто его знает ) написан на C++ но с какой библиотекой сходу не скажу - надо копаться. исходников нету ) самое главное, что до потока - редирект был, хотя и 20% текста.
Digitman © (20.07.05 11:01) [29]
исключений точно не возникает. щас еще перепроверю всяко разно.
Ботвин Дмитрий (20.07.05 10:59) [28]
можно попытаться. как задать приоритет потоку если он создан не через CreateThread? я просто в потоках не особо бум-бум. я так понимаю нужен идентификатор потока для этого?
← →
бывший (2005-07-20 11:13) [35]alpet © (20.07.05 11:09) [32]
простите, я вас не до конца понял. как без пайпов то? куда редиректить? или вы имеете в виду что достаточно read-пайпа?
еще раз акцентирую: самое главное, что до потока - редирект был, хотя и 20% текста.
предлагаю вначале разобраться почему поток мешает корректной работоспособности сервера )
з.ы. спасибо вам всем, я рад что вы со мной в трудную минуту =)
← →
alpet © (2005-07-20 11:14) [36]бывший (20.07.05 11:10) [34]
1. SetThreadPriority - метод класса TThread + F1.
2. Посмотрел сейчас в MSDN - походу в моей программе правильно юзается hStdInput для записи (см. пост [32]). Непонятно тогда почему у тебя пинг с таким кодом работал...
← →
Ботвин Дмитрий (2005-07-20 11:15) [37]Установка приоритета:
constructor TDosCapture.Create(CmdLine: String);
begin
CmdRun:=CmdLine;
FreeOnTerminate:=True;
Priority := tpIdle;
inherited Create(False);
end;
← →
alpet © (2005-07-20 11:15) [38]Блин, точнее метод называется SetPriority. Отвык я от TThread :)
← →
alpet © (2005-07-20 11:17) [39]бывший (20.07.05 11:13) [35]
В смысле не без пайпов, замени код инициации структуры:
start.hStdOutput := WritePipe;
start.hStdInput := ReadPipe;
на
start.hStdOutput := ReadPipe; // output - source
start.hStdInput := WritePipe; // keyboard simulation handle
← →
бывший (2005-07-20 11:22) [40]ага, ща изменю приоритет и попробую поменять пайпы местами...
Страницы: 1 2 вся ветка
Форум: "WinAPI";
Текущий архив: 2005.09.11;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.011 c