Здравствуйте
Вопрос в следующем: как скопировать содержимое консольного окна как текст. Делать это надо периодически, скажем раз в секунду-две. Windows позволяет выделять участки или всё консольное окно и копировать. Как это сделать на языке. Содержимое консольного окна сложное (a'la NortonCommander) и поэтому перехват стандартного вывода не катит.
Здравствуйте, axiv, Вы писали:
A>Здравствуйте A> A>Вопрос в следующем: как скопировать содержимое консольного окна как текст. Делать это надо периодически, скажем раз в секунду-две. Windows позволяет выделять участки или всё консольное окно и копировать. Как это сделать на языке. Содержимое консольного окна сложное (a'la NortonCommander) и поэтому перехват стандартного вывода не катит.
Попробуй найти окно по классу ConsoleWindowClass и по заголовку через EnumWindows() и отослать окну WM_GETTEXT.
Здравствуйте, DarkMaster, Вы писали:
DM>Попробуй найти окно по классу ConsoleWindowClass и по заголовку через EnumWindows() и отослать окну WM_GETTEXT.
Здравствуйте, axiv, Вы писали:
A>Здравствуйте A> A>Вопрос в следующем: как скопировать содержимое консольного окна как текст. Делать это надо периодически, скажем раз в секунду-две. Windows позволяет выделять участки или всё консольное окно и копировать. Как это сделать на языке. Содержимое консольного окна сложное (a'la NortonCommander) и поэтому перехват стандартного вывода не катит.
A>Заранее благодарю за помощь
Здравствуйте, Pavel Dvorkin, Вы писали:
NS>>Редирект то не потянет?
PD>Думаю, что в общем случае нет. Вывод в ДОС-окно — не всегда вывод в stdout. В те времена была еще и манера писать напрямую в видеобуфер.
А сейчас разве через WriteConsoleOutput() нельзя вывести сразу блок символов(+атрибутов), не занимаясь вызовом связки SetCosoleCursorPosition()/WriteConsole().
Замечательно, то что надо!! НО, чем получить требуемый HANDLE hConsoleOutput? GetStdHandle(STD_OUTPUT_HANDLE);?
Хочу еще раз заметить, что консольное приложение — DOS-приложение, а копировать содержимое его окна надо в свое приложение(в моём случае в Memo)
Гуру хэндлов, потоков и вин-апи, кто-нибудь может предложить рабочий код, который мог бы копировать содержимое окна, скажем с запущенным NortonCommander'ом в Memo?
Здравствуйте, axiv, Вы писали:
A>BOOL WINAPI ReadConsoleOutput( A> __in HANDLE hConsoleOutput, A> __out PCHAR_INFO lpBuffer, A> __in COORD dwBufferSize, A> __in COORD dwBufferCoord, A> __inout PSMALL_RECT lpReadRegion A>);
A>Замечательно, то что надо!! НО, чем получить требуемый HANDLE hConsoleOutput? GetStdHandle(STD_OUTPUT_HANDLE);?
Задай себе сначала вопрос — какой консоли и сам ответишь на вопрос. Если у тебя Виста или ХП — попробуй через AttachConsole(ProcessID) где ProcessID — ИД искомого процесса.
Ну а дальше:
function ReadAttributesAt(x,y: smallint): Byte;
var
WasRead: Longint;
Coor: TCoord;
Temp: Smallint;
begin
Coor.x := x;
Coor.y := y;
ReadConsoleOutputAttribute(SysConOut, @Temp, 1, Coor, WasRead);
Result := Temp;
end;
function ReadCharAt(x,y: Smallint): Char;
var
WasRead: Longint;
Coor: TCoord;
begin
Coor.x := x;
Coor.y := y;
ReadConsoleOutputCharacter(SysConOut, @Result, 1, Coor, WasRead);
if WasRead = 0 then
Result := #0;
end;
Здравствуйте, axiv, Вы писали:
A>Гуру хэндлов, потоков и вин-апи, кто-нибудь может предложить рабочий код, который мог бы копировать содержимое окна, скажем с запущенным NortonCommander'ом в Memo?
Uses jclSysInfo,Windows....;
procedure TForm1.Button2Click(Sender: TObject);
type
TAttachConsole=function (ProcessID:DWORD):boolean; stdcall;
var L:TStringList;
i:integer;
ProcessID:DWORD;
AC:TAttachConsole;
AKernel:Cardinal;
x,y:integer;
S:String;
begin
L:=TStringList.Create;
try
jclSysInfo.RunningProcessesList(L,True); // получаем список процессов (из JCL)
ProcessID:=0;
for i:=0 to Pred(L.Count) do
if L.Strings[i]='C:\Windows\system32\cmd.exe'then// ищем CMD.EXEbegin
ProcessID:=DWORD(L.Objects[i]);
Break;
end;
finally L.Free; end;
AC:=nil;
if ProcessID>0 then
begin
AKernel:=LoadLibrary('kernel32.dll');
@AC:=GetProcAddress(AKernel,'AttachConsole');
if @AC<>nil then AC(ProcessID);
begin
S:='';
for Y:=0 to 24 do
begin
for X:=0 to 79 do S:=S+ReadCharAt(X,Y);
Memo1.Lines.Add(S);
S:='';
end;
FreeConsole; // консоль нужно отпуститьend;
end;
end;
Кстати, консоли нонче юникодные пошли, так что на это стоит обратить внимание.
Здравствуйте, DarkMaster, Вы писали:
DM>Здравствуйте, axiv, Вы писали:
A>>Гуру хэндлов, потоков и вин-апи, кто-нибудь может предложить рабочий код, который мог бы копировать содержимое окна, скажем с запущенным NortonCommander'ом в Memo?
Блин, поспешил... Функция чтения символа с консоли — ниже.
function ReadCharAt(x,y: Smallint): Char;
var
WasRead: Cardinal;
Coor: TCoord;
begin
Coor.x := x;
Coor.y := y;
ReadConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), @Result, 1, Coor, WasRead);
if WasRead = 0 then
Result := #0;
end;
И основной код нужно поправить чтобы:
if ( @AC<>nil ) and AC(ProcessID) then// <--- мы можем обломится на атаче к консолиbegin
for Y:=0 to 24 do
Здравствуйте, DarkMaster, Вы писали:
DM>А сейчас разве через WriteConsoleOutput() нельзя вывести сразу блок символов(+атрибутов), не занимаясь вызовом связки SetCosoleCursorPosition()/WriteConsole().
Я думаю, что внутри она все же вызовет WriteConsole и далее, WriteFile. Просто потому, что иных механизмов в Windows ИМХО нет.
А вот
mem[$8000:0] = 65;
в DOS не редиректится точно, так как никакого файлового вывода тут нет.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Я думаю, что внутри она все же вызовет WriteConsole и далее, WriteFile. Просто потому, что иных механизмов в Windows ИМХО нет. PD>А вот PD>mem[$8000:0] = 65; PD>в DOS не редиректится точно, так как никакого файлового вывода тут нет.
Хм... А вот интересно, через ReadProcessMemory() к этому куску памяти можно добраться? По идее — ничего не мешает. Через AtachConsole() я пример уже приводил в этой же ветке.
Здравствуйте, DarkMaster, Вы писали:
DM> ProcessID:=0; DM> for i:=0 to Pred(L.Count) do DM> if L.Strings[i]='C:\Windows\system32\cmd.exe' then // ищем CMD.EXE DM> begin DM> ProcessID:=DWORD(L.Objects[i]); DM> Break; DM> end; DM> finally L.Free; end;
OpenProcess
GetProcessId
DM> AC:=nil;
DM> for Y:=0 to 24 do DM> begin DM> for X:=0 to 79 do S:=S+ReadCharAt(X,Y);
Нет никакой гарантии, что 25*80. GetConsoleScreenBufferInfo.
И проще все же вместо цикла GetConsoleOutput, то есть весь буфер сразу.
Здравствуйте, Pavel Dvorkin, Вы писали:
DM>> ProcessID:=DWORD(L.Objects[i]); PD>OpenProcess PD>GetProcessId
А зачем? В МСДН говорят нужен ProcessID, а не хэндл процесса, который мы через OpenProcess достаем.
DM>> for X:=0 to 79 do S:=S+ReadCharAt(X,Y); PD>Нет никакой гарантии, что 25*80. GetConsoleScreenBufferInfo.
Конечно нет.
PD>И проще все же вместо цикла GetConsoleOutput, то есть весь буфер сразу.
Ну дык я просто привел рабочую болванку кода. Неоптимальную, не учитывающую кучку нюансов и т.п. Но допиливать напильником буду только в том случае, если это мне будет нужно или интересно.
Здравствуйте, DarkMaster, Вы писали:
DM>Здравствуйте, Pavel Dvorkin, Вы писали:
DM>>> ProcessID:=DWORD(L.Objects[i]); PD>>OpenProcess PD>>GetProcessId
DM>А зачем? В МСДН говорят нужен ProcessID, а не хэндл процесса, который мы через OpenProcess достаем.
Проще открыть процесс, чем перебирать все. ИМХО.
DM>Ну дык я просто привел рабочую болванку кода. Неоптимальную, не учитывающую кучку нюансов и т.п. Но допиливать напильником буду только в том случае, если это мне будет нужно или интересно.