Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "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.55 MB
Время: 0.029 c
11-1085240878
Денис
2004-05-22 19:47
2005.01.09
TKOLTreeView пример


4-1100687524
z0ne
2004-11-17 13:32
2005.01.09
Размеры страницы принтера


3-1102591146
stone
2004-12-09 14:19
2005.01.09
Интересное поведение Locate


11-1085226971
Yustas
2004-05-22 15:56
2005.01.09
Не работает Message


3-1102488885
AHTOH
2004-12-08 09:54
2005.01.09
Чайницкий вопрос про отображение и редактирование записей в IBX





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский