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
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
Danke & Grüße,
Max