Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Ein paar weitere Fragen zu Windows-Hooks (https://www.delphipraxis.net/98587-ein-paar-weitere-fragen-zu-windows-hooks.html)

Prototypjack 29. Aug 2007 12:34


Ein paar weitere Fragen zu Windows-Hooks
 
Moin,

Ich habe da noch ein paar weitere Fragen zu Hooks, ich hoffe ihr könnt mir da ein wenig auf die Sprünge helfen!

Wie man in dem Source unten sieht, handelt es sich um einen Global WndProc-Hook, der bestimmte Bewegungen (wenn ein Fenster den Monitor wechselt), aufzeichnen und an mein Hauptprogramm weiterleiten soll.

Da aber erst eine Message geschickt wird, wenn das passiert, möchte ich meinen Hook direkt im Programm an/abmelden. Das heißt: Ich will, sobald das Fenster, welches ich mit der geladenen Instanz beobachte, einsatzbereit ist eine Message an mein Programm schicken, welche dieses Bestätigt. Soweit wäre das wahrscheinlich kein Problem, aber ich habe keine Ahnung wie ich an das Fensterhandle komme (Wenn ich einfach eine Globale Variable setze, die ich dazu benutze beim ersten WndProc-Ereignis eine Message zu schicken (und dann nie wieder), dann schickt der Hook extrem viele Nachrichten dieser Art auf einmal und Windows verabschiedet sich mit einem Bluescreen, was die Vermutung nahelegt, dass die WndProc mehrmals Asynchron aufgerufen wird, und deshalb meine Variable nach dem ersten Senden noch nicht gesetzt ist, was mehrmaliges Senden verursacht.)

Selbiges will ich auch beim Entladen des Hooks (durch schließen des Prozesses/Fenster, an dem die geladenen Instanz des Hooks momentan hängt, realisieren. Das funktioniert soweit auch in der Theorie (Das ist der Abschnitt, wo ich auf WM_Destroy prüfe). Die Praxis jedoch sagt mir, dass der Vergleich
Delphi-Quellcode:
if IsWindowVisible(PCWPStruct(lParam)^.hwnd) and not boolean(GetWindowLong(PCWPStruct(lParam)^.hwnd, GWL_HWNDPARENT)) then
, in dem Moment, wo ich die WM_Destroy abfangen könnte, nicht mehr zutrifft. Hrm :?

Ein weiteres Kuriosum, ist, wenn ich diesen Check (in dem Delphi Tag, über diesem Satz), auslasse. Denn dann verabschieden sich, scheinbar nach keiner erkennbaren Ordnung verschiedene Prozesse. Vor allem Antivir scheint sich recht früh zu verabschieden (Aber ich brauche ja sowieso nur die Fenster, die auch in der Taskleiste, also Visible, sichtbar sind).
Dies veranlasst mich, als einen Menschen der gerade vor ein paar Tagen mit Hooks zum ersten mal zu tun hatte, unsicher zu werden, ob der Code überhaupt fehlerfrei laufen kann oder ob ich kompletten Unfug zusammengebastelt habe. (Der Grund wieso der komplette Code unten hängt. Ich würde mich freuen, wenn mal schnell eine Grob drüber schaut ;) ).

Außerdem fiel mir auf, dass ich, wenn ich "CallNextHookEx" aufrufe, ja kein Handle übergeben kann, denn dieses ist ja nur in der Instanz des Hooks gesetzt, welche im Mainprogramm steckt. Ein wenig Suchen machte mich darauf aufmerksam, dass dieser Gedanke gar nicht falsch ist, und man da irgendwie Globale Variable (die Prozess/Dll-Übergreifend sind?!) benutzen sollte, da sonst das Handle 0 bleibt und Windows diesen Hook als den letzten in der Kette ansieht. Wie realisieren ich solche übergreifenden Variablen?

Delphi-Quellcode:
library GlobalHook;

uses
  Windows,
  Messages,
  SysUtils,
  Classes,
  Dialogs,
  Forms;

type
  TWindowRec = record
    Handle: HWND;
    Monitor: TMonitor;
  end;

{$R *.res}

var
  HookHandle: Cardinal = 0;
  MainAppHandle: Cardinal = 0;

  WindowHandle: HWND = 0;
  Monitor: TMonitor;

function EnumWindowsProc(hWnd: hWnd; lParam: LParam): Boolean; stdcall;
var
  lTmp: PChar;
  lWindowTitle: string;
  lWindowClass: string;
begin
  GetMem(lTmp, 128);
  FillChar(lTmp^, 128, 0);
  GetWindowText(hWnd, lTmp, 127);
  lWindowTitle := lTmp;

  FillChar(lTmp^, 128, 0);
  GetClassName(hWnd, lTmp, 127);
  lWindowClass := lTmp;
  FreeMem(lTmp);

  if (Pos('tfrmmain', LowerCase(lWindowClass)) <> 0) and
    (Pos('narf', LowerCase(lWindowTitle)) <> 0) then
  begin
    MainAppHandle := hWnd;
    Result := False;
  end
  else
  begin
    Result := True;
  end;
end;

procedure SendContent(AWindow: HWND; AMessage: string);
var
  cdWork: TCopyDataStruct;
begin
  // CopyData Struktur füllen
  cdWork.dwData := 0;
  cdWork.cbData := Length(AMessage) + 1;
  cdWork.lpData := AllocMem(cdWork.cbData); // Speicher reservieren
  try
    // Und Dateinamen eintragen
    CopyMemory(cdWork.lpData, @AMessage[1], cdWork.cbData - 1);
    // Fertig, Daten kopieren
    SendMessage(AWindow, WM_COPYDATA, application.handle, lParam(@cdWork));
  finally
    // Speicher wieder freigeben
    FreeMem(cdWork.lpData, cdWork.cbData);
  end;
end;

function GlobalWindowHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM):
 LRESULT; stdcall;
var
  CurrentWinMonitor: TMonitor;
begin
  Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
  case nCode < 0 of
    False:
    begin
      if IsWindowVisible(PCWPStruct(lParam)^.hwnd) and not
        boolean(GetWindowLong(PCWPStruct(lParam)^.hwnd, GWL_HWNDPARENT)) then
      begin
        if WindowHandle = 0 then
        begin
          WindowHandle := PCWPStruct(lParam)^.hwnd;
          Monitor := Screen.MonitorFromWindow(PCWPStruct(lParam)^.hwnd);
        end;
        if MainAppHandle = 0 then
        begin
          EnumWindows(@EnumWindowsProc, 0);
        end;
        if PCWPStruct(lParam)^.message = WM_MOVING then
        begin
          CurrentWinMonitor := Screen.MonitorFromWindow(PCWPStruct(lParam)^.hwnd);
          if Monitor <> CurrentWinMonitor then
          begin
            Monitor := CurrentWinMonitor;
            SendContent(MainAppHandle, IntToStr(Monitor.MonitorNum) + ':' +
              IntToStr(WindowHandle));
          end;
        end;
        if PCWPStruct(lParam)^.message = WM_DESTROY then
        begin
          SendContent(MainAppHandle, 'd:' + IntToStr(WindowHandle));
        end;
      end;
    end;
  end;
end;

function InstallHook(Hwnd: Cardinal): Boolean; stdcall;
begin
  Result := False;
  if HookHandle = 0 then
  begin
    HookHandle := SetWindowsHookEx(WH_CALLWNDPROC, @GlobalWindowHook, HInstance,
      0);
    MainAppHandle := Hwnd;
    Result := TRUE;
  end;
end;

function UninstallHook: Boolean; stdcall;
begin
  Result := UnhookWindowsHookEx(HookHandle);
  HookHandle := 0;
end;

exports
  InstallHook,
  UninstallHook;

begin

end.
Hmm, ich würde mich alleine schon deswegen freuen, wenn jemand meinen Roman hier überhaupt liest :mrgreen:

Danke & Grüße,
Max

Apollonius 29. Aug 2007 13:12

Re: Ein paar weitere Fragen zu Windows-Hooks
 
Über das Problem mit den globalen Variablen in Hooks bin ich auch schon gestolpert. Wenn es um das Verschicken von Daten geht, solltest du dir unbedingt Mailslots anschauen, damit lässt sich das Problem recht gut lösen (Hilfe zu Mailslot ist natürlich in der MSDN). Du musst dann allerdings den Namen des Mailslots hartcodieren. Selbiges gilt für Memory-Mapped-Files, die ebenfalls für "ganz globale Variablen" eingesetzt werden können.
Hoffe geholfen zu haben
Apollonius


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:18 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz