Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.01.09;
Скачать: CL | DM;

Вниз

Дочернее окно   Найти похожие ветки 

 
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 вся ветка

Текущий архив: 2005.01.09;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.022 c
3-1102488885
AHTOH
2004-12-08 09:54
2005.01.09
Чайницкий вопрос про отображение и редактирование записей в IBX


1-1103632922
izi
2004-12-21 15:42
2005.01.09
Компоненты для баз данных.


6-1098211849
magasoft
2004-10-19 22:50
2005.01.09
TidHTTP Client - что за блюдо и с чем едят?


1-1103704393
lost3000
2004-12-22 11:33
2005.01.09
Сортировка в ListBox с сохранением старого ItemIndex


1-1103650760
denik
2004-12-21 20:39
2005.01.09
Окаймление для Edit.