Отследить активацию окна в Windows

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Отследить активацию окна в Windows

Сообщение Gott » 12.10.2018 16:18:12

Всем привет. Делаю небольшую утилитку и возникла задача отследить активацию окна приложения.
С поиском окна которое надо отслеживать проблем нет, пусть для примера это будет блокнот, он есть в каждой винде.
Загружаю dll, вешаю обработчик, а дальше начинаются странности. Какие-то окна сообщают об активации, а какие-то нет, закономерности я не заметил. После перезапуска винды те программы, что не сообщали об активации, начинают сообщать, зато перестают работать другие, вот такая любопытнота. ЧЯНТД?

Код программы
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, windows, LConvEncoding, RegExpr,
  ExtCtrls;

type


  { TForm1 }

  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    WM_WORKER_CHECK: DWORD;
    HookHandle: HWND;
    HLib: HWND;
    CheckWnd: HWND;
  public
    procedure WndProc(var TheMessage: TMessage); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
var
  WND: qword;
  Buf: array[0..127] of char;
  x: Boolean;
  Capt: string;
  RE: TRegExpr;
begin
  // Почтовый голубь для dll
  WM_WORKER_CHECK := RegisterWindowMessage('WorkerCheckMessage');

  // Загрузка библиотеки и установка хука
  HLib := LoadLibrary('workcheckhook.dll');
  HookHandle := SetWindowsHookEx(WH_CBT,HOOKPROC(GetProcAddress(HLib,'WndHook')),HLib,0);

  // Регулярка для поиска окна по кусочку имени
  RE := TRegExpr.Create('.+ Блокнот$');

  WND:=GetWindow(Handle, GW_HWNDFIRST);
  while (WND <> 0) do begin
    if (WND <> Handle) and IsWindowVisible(WND) and (GetWindowText(WND,Buf,SizeOf(Buf)) <> 0) then begin
      GetWindowText(WND,Buf,SizeOf(Buf));
      Capt := ConvertEncodingToUTF8(Buf,'cp1251',x);
      RE.InputString:=Capt;
      if RE.Exec(1) then begin
        Memo1.Append(Capt);
        CheckWnd:=WND; // Окно найдено
        break;
      end;
    end;
    WND:=GetWindow(WND, GW_HWNDNEXT);
  end;

end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  UnhookWindowsHookEx(HookHandle);
  FreeLibrary(HLib);
end;

procedure TForm1.WndProc(var TheMessage: TMessage);
begin
  inherited WndProc(TheMessage);
  if TheMessage.msg = WM_WORKER_CHECK then begin
    // Запись данных, полученных от dll об активации окна
    if TheMessage.wParam <> Handle then
    Memo1.Append(IntToStr(TheMessage.wParam)+' - '+IntToStr(TheMessage.lParam)+' - '+inttostr(CheckWnd));
   
    // Сравнение активированного окна с нужным
    if TheMessage.wParam = CheckWnd then begin
      Memo1.Append('Активировано целевое окно.');
    end;
  end;
end;

end.


Код dll
Код: Выделить всё
library workcheckhook;

{$mode objfpc}{$H+}

uses
  Classes, windows;
var
  WM_WORKER_CHECK: DWORD;

function WndHook(Code: integer; wParam: WPARAM; lParam: LPARAM): Int64;stdcall;export;
begin
  Result := CallNextHookEx(0, Code, wParam, lParam);
  if Code = HCBT_ACTIVATE then PostMessage(FindWindow(nil,'Form1'),WM_WORKER_CHECK,wParam,lParam);
end;

exports WndHook;
begin
  WM_WORKER_CHECK := RegisterWindowMessage('WorkerCheckMessage');
end.
Gott
незнакомец
 
Сообщения: 2
Зарегистрирован: 12.10.2018 15:59:07

Re: Отследить активацию окна в Windows

Сообщение MysticCoder » 14.10.2018 23:25:42

вообще вроде все норм. ток на 64 битной системе 32х битные хуки будут работать только для 32х битных приложений, 64 битные только для 64 битных... вроде бы... поэтому для полного охвата надо бы 2 длл... и 2 хука...
могу посоветовать вставить в секцию дллки begin end отправку сообщения в окно проги, что дллка загрузилась в такое то приложение.
еще раньше вроде первый параметр для CallNextHookEx не игнорировался и надо было передавать правильный HHook, и переменную хранящую этот HHook в дллке надо было правильно присвоить, т.к. дллка загружается во все хукируемые приложения и у каждого инстанса дллки эта переменная своя. т.е. этот HHook надо было запрашивать в дллке у самого приложения через сообщения или глобальную расшаренную память или через файлы там. просто сделать LoadLibrary в приложении, потом через еще одну экспортируемую процедуру передать этот HHook не прокатит. сейчас почему то этот параметр описан как ignored, хотя там же примеры в которых они его заполняют. попробуй и ты её передавать, может от версии к версии ОС где то надо указывать, а где то нет.
MysticCoder
постоялец
 
Сообщения: 146
Зарегистрирован: 14.09.2013 00:20:28

Re: Отследить активацию окна в Windows

Сообщение Gott » 15.10.2018 00:18:05

Утилита будет тарахтеть на серверной семерке и восьмерке, а для них в описании прямо сказано, что первый параметр CallNextHookEx игнорируется. Мировой разум подсказал, что это пошло начиная то ли с Висты, то ли с 2к.
Про 32 и 64 бита я догадывался, а потом еще и в описании прочитал. Ловить надо будет 32-разрядное приложение, с ним проблем пока не было, надеюсь это не просто везение :) Проблема возникает с 64-разрядными приложениями, причем именно так загадочно, как я написал, вот и решил докопаться до истины. Компилировал обе версии dll и приложения кросс-компилятором.

MysticCoder писал(а):могу посоветовать вставить в секцию дллки begin end отправку сообщения в окно проги, что дллка загрузилась в такое то приложение.

Благодарю, этот момент я как-то упустил.
С другой стороны я смотрел процессы апи монитором и dll инжектировались в процессы, однако хуки не работали.
Gott
незнакомец
 
Сообщения: 2
Зарегистрирован: 12.10.2018 15:59:07

Re: Отследить активацию окна в Windows

Сообщение MysticCoder » 15.10.2018 01:55:21

Gott писал(а):первый параметр CallNextHookEx игнорируется

хозяин - барин, конечно, я бы все таки попробовал, все таки они у себя указывают - https://docs.microsoft.com/ru-ru/windows/desktop/winmsg/using-hooks

Gott писал(а):однако хуки не работали.

тут еще не факт, что хуки не работали, в саму хук процедуру тож можно логирование встроить, что типа они вызываются с такими то параметрами из такого то приложения. еще по хорошему посылать не Form1, а дать нормальное уникальное имя классу и форме, мало ли этих Form1 в системе, причем лучше одно из приложений 32х или 64х битных сделать без формы, которое просто ставит хук своей разрядности и висит в фоне и запускать из второго оконного, которому и слать сообщения. А то, возможно, в обоих приложениях Form1... ну и имена дллок тож разные лучше... что в коде, что в файловой системе...
ну и если code < 0 то по инструкции надо передать дальше по цепочке и выходить сразу же без обработки. не знаю с чем связано, но допускаю вариант, что в системе что то идет не так, сообщение должно пройти по хукам ибо не вызвать хуки в силу архитектурных особенностей не можем, но на все про все есть пара миллисекунд, если время вышло, то недобросовестные тормозные хуки просто удаляются) это конечно мои фантазии, но кто знает как на самом деле там все устроено и с чем связано это указание про code < 0. у тебя же там отправка сообщения и поиск окна)
RegisterWindowMessage опять же может не сработать. лучше логировать все по максимуму.
MysticCoder
постоялец
 
Сообщения: 146
Зарегистрирован: 14.09.2013 00:20:28


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: MailRu[bot] и гости: 4

Рейтинг@Mail.ru