Форум: "WinAPI";
Текущий архив: 2005.01.09;
Скачать: [xml.tar.bz2];
ВнизДочернее окно Найти похожие ветки
← →
DeScriptor (2004-11-20 15:25) [0]Всем доброго времени суток!
У меня такая вот задача: мне нужно перехватить окно (вражеское, не из моей проги), как только оно появится, получить заголовок его дочернего окна (я знаю только, что это - статичный текст, типа Label) и прибить его. Как поймать окно по заголовку и как его прибить я знаю. Хотя мне смутно представляется, как устроить постоянную слежку за системой, чтобы поймать окно (вряд ли рациональным будет организация цикла слежки, наверное, можно как-то попросить систему направить мне какое-нть соответствующее сообщение (опять же - как?)).
А вот как добраться до дочернего окна и поработать с ним - я не знаю. Единственное, в чем я абсолютно уверен - все нужно писать на WinAPI.
Помогите, плз, а то я уже совсем запутался! =(
← →
GuAV © (2004-11-20 22:37) [1]DeScriptor (20.11.04 15:25)
Хотя мне смутно представляется, как устроить постоянную слежку за системой, чтобы поймать окно (вряд ли рациональным будет организация цикла слежки, наверное, можно как-то попросить систему направить мне какое-нть соответствующее сообщение (опять же - как?)).
WWM_CREATE посылается свежесозданому окну.
DeScriptor (20.11.04 15:25)
А вот как добраться до дочернего окна и поработать с ним - я не знаю.
EnumChildWindows,FindWindowEx
DeScriptor (20.11.04 15:25)
как только оно появится,
WaitForInptIdle ?
===
GuAV is drunk
← →
DeScriptor (2004-11-21 23:49) [2]В продолжение этой темы:
var TextBuff:String;
hMessage:THandle;
...
GetWindowText(hMessage,@TextBuff,GetWindowTextLength(hMessage));
Выдает ошибку про то, что случилось нарушение доступа к памяти при обращении к TextBuff. Тада я подумал, и предложил такой вариант:
GetWindowText(hMessage,@TextBuff[1],GetWindowTextLength(hMessage));
Но получил то же, что и в случае подсовывания в качестве переменной PChar - GetWindowTextLength(hMessage) говорит, шо там нное количество символов есть, а вот дать мне этот текст в руки не хочет, дает 0! =(((( Но я проверял - хэндл правильный, все нормально! Вон, по нему даже можно пробить количество символов.
Сразу говорю, хелп по апям (тот, что в дельфях) я уже весь обчитался! И ничего по этому поводу не нашел.
← →
GuAV © (2004-11-22 00:22) [3]SetLength для TextBuff делаешь ?
hMessage валидный ? (можно проверитьIsWindow)
← →
DeScriptor (2004-11-22 00:43) [4]Все, со всем разобрался: действительно, надо было длину строки сначала установить! А я-то идиот! =))))
Теперь еще такой момент:
чтобы отлавливать нужное мне окно (я ж не знаю, када оно появицца), я сделал вот такой вот отдельный поток:
unit TWinCaptureCode;
interface
uses
Classes, Windows, Messages;
type
TWinCapture = class(TThread)
private
StopMonitoring:boolean;
protected
procedure Execute; override;
end;
implementation
uses MainWinCode;
procedure TWinCapture.Execute;
var hText, hWin: THandle;
asshole:string;
begin
StopMonitoring:=false;
repeat
hWin:=FindWindow("#32770","VictimWin =) ");
if hWin<>0 then begin
hText:=FindWindowEx(hWin,0,"Static",nil);
SetLength(asshole,GetWindowTextLength(hText));
GetWindowText(hText,@asshole[1],GetWindowTextLength(hText));
form1.memo1.lines.Add(asshole);
SendMessage(hWin,WM_CLOSE,0,0);
end;
until StopMonitoring;
end;
end.
А он, зараза, как я и ожидал, изо всех сил хавает процессорное время! Как мне от этого эффекта избавиться? Скорее всего, тут нужно как-то по-другому мониторинг, но как? Я пока ничего не придумал...
← →
Piter © (2004-11-22 00:45) [5]Нда, уровень знаний относительно запросов поражает...
Насчет обнаружения "вражеского" окна - я бы поставил глобальную ловушку типа WH_SHELL.
Статья по ловушкам (hook) в частности есть на этом сайте.
Насчет GetWindowText:
--------------------------------------------
Вопрос: как прочитать заголовок или Edit из чужого окна
Ответ: воспользоваться функией:
function GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer; stdcall;
hWnd - указатель на окно (Edit тоже окно)
lpString - адрес буфера для сохранения результата
MaxCount - максимальное число символов, которые можно записать в буфер, включая завершающий символ
Сама функция возвращает число символов, которые записаны в буфер, исключая завершающий символ.
Чтобы узнать сколько нужно выделять места под буфер перед тем, как вызвать GetWindowText и передать адрес буфера, можно воспользоваться функцией:
function GetWindowTextLength(hWnd: HWND): Integer; stdcall;
которая возвращает число символов в заголовке окна по переданному указателю окна
Для примера можно кинуть на форму таймер и в обработчике написать:
procedure TForm1.Timer1Timer(Sender: TObject);
var
s: string;
i: integer;
pt: TPoint;
Handle: HWND;
begin
GetCursorPos(pt); // узнаем координаты курсора мышки
Handle := WindowFromPoint(pt); // узнаем указатель на окно, который находится под курсором мышки
i := GetWindowTextLength(Handle); // узнаем размер заголовка в символах
SetLength(s, i); // выделяем место под буфер
GetWindowText(Handle, PChar(s), i+1); // узнаем непосредственно текст заголовка
Edit1.Text := s; // выводим текст заголовка в Edit
end;
Можете запустить программу и посмотреть как меняется значение в Edit при передвижении курсора мыши по экрану и наведению на различные окна.
Отвечал: Piter
← →
Piter © (2004-11-22 01:38) [6]DeScriptor, хотя насчет тебя я мнение изменил. Вроде ты парень неглупый, мыслишь - это хорошо.
Сначала покритикую код:
DeScriptor (22.11.04 0:43) [4]
private
StopMonitoring:boolean;
зачем ты заводишь дополнительную переменную? У класса TThread есть свойство FTerminate или как-то так. Соответственно, чтобы остановить поток нужно сделать ему WinCapture1.Terminate - этот метод просто присвоит переменной FTerminate значение True
Соответственно, вместо:until StopMonitoring;
нужно так:until Terminated;
Далее:
form1.memo1.lines.Add(asshole);
НИКОГДА ТАК НЕ ДЕЛАЙ В НЕ ОСНОВНОМ ПОТОКЕ. VCL построена таким образом, что может работать только в основном потоке, эта библиотека НЕ потоко-независимая. Соответственно, вызывая ее методы в НЕ основном потоке ты сильно рискуешь.
Это грозит непредсказуемыми результатами. У тебя может быть даже все и добавится, но потом где-нибудь схватишь AV или типа того.
Чтобы сделать грамотно - почитай про Synchronize
Еще, возможно, ты забыл про FreeOnTerminate := true - если сам не уничтожаешь экземпляр...
GetWindowText(hText,@asshole[1],GetWindowTextLength(hText))
тогда уж:
GetWindowText(hText,@asshole[1],GetWindowTextLength(hText) +1 );
А лучше так:
GetWindowText(hText,@asshole[1], Length(asshole) + 1);
Это чтобы не натолкнутся на маловероятную, но все же ситуацию, когда между вызовами SetLength и GetWindowText произойдет смена заголовка у приложения, что вполне может привести к краху твоего приложения.
А он, зараза, как я и ожидал, изо всех сил хавает процессорное время!
естественно. У тебя там бесконечный цикл начинает крутится, система и кидает все свободное время на выполнение этого цикла.
Простым выходом из данного положения будет создание таймера с интервалом, например, в 1 секунду. Ну тут можно и без доп. потока обойтись, просто кинуть на форму таймер с нужным тебе интервалом. недостаток - у тебя будет некоторое время реагирования, в худшем случае равное интервалу таймера.
Правильным рещением будет установка хука, но это более трудоемко. Вообще, читай мой пост [5]
← →
DeScriptor (2004-11-22 12:07) [7][b]2Piter[/b] Ну, во-первых, я и не претендовал на знатока в Дельфях, наоборот, я всегда утверждал, что мои познания - сверхмалы, ибо занимаюсь я программингом только для души, да и не шибко много. =) Это было Л.О., я, собственно, не обижаюсь, а вношу ясность. =)
Теперь по делу. Что касаецца синхронайза - тут я полностью с Вами согласен - ступил не по-деццки, видать, усталость сказалась. Обычно я всегда синхронайз юзаю, но тут вот решил про него забыть.
Что касаецца ловли через таймер... С одной стороны - вариант. С другой стороны, мне очень не хочется, чтобы получилось так, что окошко-то уже появилось, а я на него еще не среагировал. Посему поищу статью про хуки, поффтыкаю, глядишь - разберусь! =)
С терминэйтом вот я тоже как-то не подумал! =) Кстати, уж если о нем зашла речь, то что он из себя представляет? Просто присваивает свойству Terminated значение True? Или что-то еще делает? А как потом это значение обратно в False поменять? И еще вопрос на тему потоков, который меня тревожит уже не первый год и на который мне еще никто вразумительно не ответил:
Как грамотно прибить поток из него самого? Мне нужно, например, чтобы отработал метод Execute, после чего поток САМ, без чьей-либо помощи, себя убивает, освобождает память и так далее. Или это лишь мечты, а на практике такое невозможно? Потому как попытки воткнуть в конце Execute что-нть типа Destroy давали мне, естественно AV. А попытки сделать то же обходным путем - через синхронайз дать основному потоку знать, что пришло время, чтобы основной поток убил наш поток - тоже ничего не дали, кроме AV.
Уффф. Вот.
← →
GuAV © (2004-11-22 12:55) [8]DeScriptor (22.11.04 12:07) [7]
Мне нужно, например, чтобы отработал метод Execute, после чего поток САМ, без чьей-либо помощи, себя убивает, освобождает память и так далее.
Пишешь свой контсруктор. Обзываешь его тоже Create чтоб он скрыл конструктор TThread. После вызова унаследованного конструктора выставляешь в нём FreeOnTerminate в True.
DeScriptor (22.11.04 12:07) [7]
Просто присваивает свойству Terminated значение True?
Да.
← →
DeScriptor (2004-11-22 13:27) [9]Почитал я про ловушки... Озадачился: в статье рассказывается, как поставить ловушку и обработать пойманное сообщение функцией из внешней библиотеки. А мне это не нужно! Неужели нельзя провернуть подобный трюк с функцией из моей программы?
← →
Digitman © (2004-11-22 13:50) [10]
> Неужели нельзя провернуть подобный трюк с функцией из моей
> программы?
нельзя.
маш.код ф-ции должен находиться в АП того процесса, трэды которого будут вызывать эту ф-цию.
а каким образом этот маш.код окажется в АП целевого процесса - иной вопрос .. это м.б. и результат использования механизма установки глоб.хуков и любой иной допустимый.
← →
Piter © (2004-11-22 15:41) [11]DeScriptor (22.11.04 13:27) [9]
в статье рассказывается, как поставить ловушку и обработать пойманное сообщение функцией из внешней библиотеки. А мне это не нужно!
А что же тебе нужно? Разве тебе не нужно узнать, когда удаленная программа создаст окно? Ты ставишь хук типа WH_SHELL, потом отлавливаешь в своей ShellProc события HSHELL_WINDOWCREATED. Когда оно наступает - окно уже создано.
То есть, задача сводится к одному - у каждого вновь созданного окна ты узнаешь заголовок и класс по переданному тебе Handle и сверяешь с нужным. Время реагирования - время между созданием окна и вызовом ловушки, практически мгновенно. Разве не это тебе нужно?
Читай статью внимательнее...
← →
DeScriptor (2004-11-22 18:56) [12]То есть, по-любому мне нужно написать махонькую библиотечку про то, как по полученному хэндлу проверить заголовок соответствующего окна и вернуть true/false в зависимости результата? Или я опять не так что-то понял?..
← →
Piter © (2004-11-23 00:15) [13]DeScriptor (22.11.04 18:56) [12]
Э-э-э-х... отступлю от общепринятых правил. Вот код библиотеки:library piter_hook;
uses
Windows;
const
cMMFileName = "piter-hook_{E891D15B-DD53-4BC8-81F4-E0BF8A5573B6}";
type
PGlobalData = ^TGlobalData;
TGlobalData = packed record
HookProcessId: DWORD;
HookHandle: HHOOK;
WindowHandle: THandle;
idMsg: LongWord;
end;
var
GlobalData: PGlobalData ;
MapHandle: THandle;
function DeleteHook(CheckProcess: boolean): boolean;
begin
Result := false;
if GlobalData <> nil then
with GlobalData^ do
if HookHandle<>0 then
if (not CheckProcess) or (GetCurrentProcessId = HookProcessId) then
begin
Result := UnHookWindowsHookEx(HookHandle) ;
if Result then
HookHandle := 0;
end;
end;
function RemoveShellHook: boolean; stdcall;
begin
Result := DeleteHook(True);
end;
function ShellProc(nCode: integer; wParam: LongWord;
lParam: LongWord): integer; stdcall;
begin
Result := 0;
if GlobalData <> nil then
with GlobalData^ do
begin
if IsWindow(WindowHandle) then
SendMessage(WindowHandle, idMsg, wParam, nCode)
else
DeleteHook(False);
CallNextHookEx(HookHandle, nCode, wParam, lParam);
end;
end;
function SetShellHook(CallbackWindow: THandle;
idCallBackMessage: LongWord): boolean; stdcall;
begin
Result := false;
if GlobalData <> nil then
with GlobalData^ do
if ( HookHandle = 0 ) and ( IsWindow(CallbackWindow) ) then
begin
HookHandle := SetWindowsHookEx(WH_SHELL, @ShellProc, HInstance, 0);
Result := HookHandle <> 0;
if Result then
begin
HookProcessId := GetCurrentProcessId ;
WindowHandle := CallbackWindow ;
idMsg := idCallBackMessage ;
Result := true;
end;
end;
end;
procedure OpenSharedData;
var
Size: integer;
begin
Size := SizeOf(TGlobalData);
MapHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE,
0, Size, cMMFileName);
if MapHandle = 0 then exit;
GlobalData := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);
end;
procedure CloseSharedData;
begin
UnmapViewOfFile(GlobalData);
CloseHandle(MapHandle);
end;
procedure DLLEntryProc(dwReason: DWord);
begin
case dwReason of
DLL_PROCESS_ATTACH: OpenSharedData;
DLL_PROCESS_DETACH:
begin
RemoveShellHook ;
CloseSharedData;
end;
end;
end;
exports
SetShellHook, RemoveShellHook;
begin
DLLProc := @DLLEntryProc;
DLLEntryProc(DLL_PROCESS_ATTACH);
end.
вот пример программы, которая эту библиотеку использует:unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
const
piter_hook = "piter_hook.dll";
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure WMUser(var Msg: TMessage); message WM_USER;
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
function SetShellHook(CallbackWindow: THandle;
idCallBackMessage: LongWord): boolean; stdcall;
function RemoveShellHook: boolean; stdcall;
var
Form1: TForm1;
implementation
{$R *.dfm}
function SetShellHook; external "piter_hook.dll" name "SetShellHook";
function RemoveShellHook; external "piter_hook.dll" name "RemoveShellHook";
procedure TForm1.FormCreate(Sender: TObject);
begin
Button2.Enabled := false;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if SetShellHook(handle, WM_USER) then
begin
Button1.Enabled := false;
Button2.Enabled := true;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if RemoveShellHook then
begin
Button1.Enabled := true;
Button2.Enabled := false;
end;
end;
procedure TForm1.WMUser(var Msg: TMessage);
begin
case Msg.LParam of
HSHELL_WINDOWCREATED:
ListBox1.Items.Add( "Создано окно, handle=" + inttostr(Msg.WParam) );
HSHELL_WINDOWDESTROYED:
ListBox1.Items.Add( "Уничтожается окно, handle=" + inttostr(Msg.WParam) );
end;
end;
end.
Только не дай бог ты не попробуешь данный код, не разберешься в нем, у тебя не получится и ты не скажешь мне спасибо. Убью нах :)
Библиотека вообще тебе подходит, а программа хоть и не выполняет нужный ТЕБЕ функционал, но она получает хэндлы всех создаваемых окон, а уж по handle ты сможешь определить - нужное это окно или нет?
DeScriptor (22.11.04 12:07) [7]
Кстати, уж если о нем зашла речь, то что он из себя представляет? Просто присваивает свойству Terminated значение True?
да. Только не Terminated, а внутреннему полю FTerminated. Вообще, запомни волшебную комбинацию [CTRL] + Click левой кнопкой мыши.
Вот берешь строчкуTThread.terminate
, зажимаешь [CTRL] и кликаешь левой кнопкой по слову terminate - и ты чудом перенесешься в модуль classes и увидешь:procedure TThread.Terminate;
begin
FTerminated := True;
end;
И вопросов более таких задавать не будешь, потому как все очевидно
А как потом это значение обратно в False поменять?
а зачем? Если уж ты решил прервать поток - так прерывай
Как грамотно прибить поток из него самого? Мне нужно, например, чтобы отработал метод Execute, после чего поток САМ, без чьей-либо помощи, себя убивает, освобождает память и так далее
Потоку не нужно себя убивать. Как только выполнение кода "выйдет" из процедуры Execute, объект ядра типа поток будет уничтожен. И ничего уже не поделаешь. То есть, если в Execute выполнится Exit - то считай, что твой поток мертв. Это что касается объекта ядра. В дельфи же класс TThread является только оболочкой
← →
Piter © (2004-11-23 00:16) [14]над объектом ядра типа поток, сам экземпляр класса TThread жив, правда он уже бесполезен (ну с него можно считать, допустим, значения каких-то свойств, но сам поток умер). Поэтому такому экземпляру нужно или сделать Free (как и любым другим экземплярам классов), или при работе (например, в методе Execute) присвоить FreeOnTerminate:
procedure TWinCapture.Execute;
var hText, hWin: THandle;
asshole:string;
begin
FreeOnTerminate := true;
StopMonitoring:=false;
repeat
hWin:=FindWindow("#32770","VictimWin =) ");
if hWin<>0 then begin
hText:=FindWindowEx(hWin,0,"Static",nil);
SetLength(asshole,GetWindowTextLength(hText));
GetWindowText(hText,@asshole[1],GetWindowTextLength(hText));
form1.memo1.lines.Add(asshole);
SendMessage(hWin,WM_CLOSE,0,0);
end;
until StopMonitoring;
end;
Тогда после отработки метода Execute умрет не только поток, но и сам экземпляр класса TThread "очистится". Все переменные, указывающие на поток будут недействительны и их использование, вероятно, приведет к AV.
← →
DeScriptor (2004-11-23 21:21) [15]2Piter У меня просто нет слов, чтобы выразить свою благодарность!!! Сижу, печатаю всю эту ветку, чтобы внимателльно во всем разобраться! =) Када разберусь и напишу - обязательно откликнусь еще раз! =) И произойти это должно до субботы.
Кстати, отдельное спасибо за контрол-клик: часа полтора сидел, с счастливой улыбкой разбирал всякие разные методы всяких разных классов! Вообще, ужасно юзабельная шштука!...
← →
Piter © (2004-11-23 23:26) [16]DeScriptor (23.11.04 21:21) [15]
ну в код библиотеки можешь пока особо не вникать - просто создай проект библиотеки и откомпилируй, получишь черный ящик, DLL :)
А с самой программой вообще фигня, она единственное, что делает - импортирует функции из библиотеки - SetShellHook и RemoveShellHook.
При вызове SetShellHook просто указывается Handle окна и номер сообщения, которые будут посылаться библиотекой
Страницы: 1 вся ветка
Форум: "WinAPI";
Текущий архив: 2005.01.09;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.037 c