Autor Beitrag
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 481

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: So 12.02.06 16:34 
Ich hab mir gerade mal das Hook-Tutorial aus der Library angeguckt und leicht modifiziert (brauche keinen Key-Hook, sondern einen WH_SHELL-Hook). Mein dll-Code sieht so aus:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
library KeyboardHook;

uses   
  Windows,
  Messages, Dialogs, sysutils; // da kommt später wieder was weg ;-)

var
  HookHandle: Cardinal = 0;
  MyWindowHandle: Cardinal = 0;

const APPCOMMAND_MEDIA_NEXTTRACK  = 720896; 

function KeyboardHookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  case nCode < 0 of
    FALSE:
      begin
        if lparam = APPCOMMAND_MEDIA_NEXTTRACK then
        begin
          // weils nicht klappt, hab ich mir mal die Werte ausgeben lassen...
          showmessage('Fange ab - ' + inttostr(MyWindowHandle));
          PostMessage(MyWindowHandle,WM_APPCOMMAND,wparam,lParam);
          Result := 1;
        end
        else
          Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
      end;
    else
      Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
  end;
end;

function InstallHook(Hwnd: Cardinal): Boolean; stdcall;
begin
  Result := False;
  if HookHandle = 0 then begin   
//Erstmal Hook installieren
    HookHandle := SetWindowsHookEx(WH_SHELL, @KeyboardHookProc, HInstance, 0);
//Uebergebenes Fensterhandle sichern
    MyWindowHandle := Hwnd;
    Result := True;
// Testweise ne Ausgabe
    showmessage('Installiert ' + inttostr(hwnd) + ' ' + inttostr(MyWindowHandle));
  end;
end;

function UninstallHook: Boolean; stdcall;   
begin
//Hook aus der Hookchain entfernen
  Result := UnhookWindowsHookEx(HookHandle);
  HookHandle := 0;
end;

exports
//Installations- und Deinstallationsroutine exportieren
  InstallHook,   
  UninstallHook;
end.

Mein Programm sieht im Wesentlichen so aus:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  protected
    // um den Krempel geht es mir ;-)
    procedure MediaKey (Var aMSG: tMessage); message WM_APPCOMMAND;
  end;

var
  Form1: TForm1;

type
  TInstallHook = function(Hwnd: THandle): Boolean; stdcall;
  TUninstallHook = function: Boolean; stdcall;

var
  InstallHook: TInstallHook;
  UninstallHook: TUninstallHook;
  lib: Cardinal;
  HWND: Cardinal;


procedure TForm1.FormCreate(Sender: TObject);
begin
  lib := LoadLibrary('KBHook.dll');
  if lib <> INVALID_HANDLE_VALUE then begin
    InstallHook := GetProcAddress(lib, 'InstallHook');
    UnInstallHook := GetProcAddress(lib, 'UninstallHook');
  end
  else showmessage('Fehler');

  if installHook(Form1.Handle) then showmessage('Hook installiert');
  Label1.Caption := intTostr(Form1.Handle);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  UninstallHook;
end;

procedure TForm1.MediaKey (Var aMSG: tMessage);
begin
  case aMSG.LParam of
    APPCOMMAND_MEDIA_NEXTTRACK:
    begin
      showmessage('Next');
      aMsg.Result := 1;
    end;
  end;
end;
Das Problem liegt darin, dass der Hook zwar scheinbar richtig installiert wird (zumindest zeigen das die ShowMessages), aber wenn dann meine Form mal nicht den Focus hat, dann ergibt dieser Zeile aus der Hook.dll showmessage('Fange ab - ' + inttostr(MyWindowHandle)); nicht mehr das FensterHandle meiner Form als integer Wert aus, sondern 0. Was mache ich falsch?

_________________
We are, we were and will not be.
digi_c
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1905

W98, XP
D7 PE, Lazarus, WinAVR
BeitragVerfasst: So 12.02.06 19:13 
Beim InstallHook() kriegt die DLL aber den richtigen Handle? Weil du initialisierst den ja mit 0, ich würde vermuten das es da ein Problem gibt.
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: So 12.02.06 19:29 
Da der initialsisierte Wert von MyWindowHandle 0 ist und du erst mit InstallHook den Wert setzt gilt dies nur für alle Programme die InstallHook aufrufen.

Und das macht nur dein Programm welches den Hook lädt, alle anderen Programme führen sozusagen LoadLibrary('KBhook.dll') auf, wonach der MyWindowHandle wert auf 0 steht.

Enwteder du basut dir eine Fenstersuche mit der du dein Programm findest, oder nutzt IPC. Anders wüßte ich auch nicht wie du an das WindowHandle von dem Programm kommst welches InstallHook aufruft.

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 481

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: So 12.02.06 19:49 
Die eine Möglichkeit ist also, in der KeyboardHookProc das Handle meiner Anwendung zu bestimmen, richtig?
Und das andere könnte ich z.B. hiermit machen, indem ich in InstallHook mein Handle als IPC-Objekt in den Speicher lege, und in der KeyboardHookProc auf dieses Objekt zugreife?

Heute fang ich damit nicht mehr an, aber habe ich das in etwa richtig verstanden?

_________________
We are, we were and will not be.
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: So 12.02.06 20:01 
Das 1. ist richtig, das 2. nicht.

zu 1) FindWindow etc.

zu 2) Etwa mit MMFs. Hab mir jetzt nicht den Link genauer angeschaut, aber da InstallHook nur von deinem Programm aufgerufen wurde bringt es den anderen Anwendungen nichts. Du musst halt ein Merkmal nehmen wonach beim laden der DLL die dll selbst rasufinden kann wie sie mit deinem Programm kommuniziert.

D.h. irgendetwas muss statisch sein. z.b. Fenstername (siehe Punkt 1) oder per MMF der PChar im letzten Parmater
CreateFileMapping(....,'meinPlayer')

Dann greifen alle Programme die ebenfalls das selbe FileMapping erstellen auf den selben Speicherbereich zu. Den kannst du dann z.b. in InstallHook initialsieren indem du dan einfach dein WindowHandle reinschreibst. In allen anderen Prozessen kann die dll dann diesen Wert auslesen und kann dann an dieses Fenster daten schicken. ODer halt die MMF selbst dafür benutzen.

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 481

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: So 12.02.06 20:35 
Doch, das scheint doch zu klappen :D

Im HookInstall setze ich sowas:
ausblenden Delphi-Quelltext
1:
2:
oDemoObj := TWDB_IPCDemoObj.Create('NempNochEinMP3Player');
oDemoObj.Data := Hwnd;
und später beim eigentlichen hooken dann das:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
 //Das Object mit selben Namen wieder erstellen - da es schon existiert (nach install), komme ich so an das FensterHandle ran
oDemoObj := TWDB_IPCDemoObj.Create('NempNochEinMP3Player');
// Und dann die entsprechende nachricht weitergeben
PostMessage(oDemoObj.Data,WM_APPCOMMAND,wparam,lParam);

Zumindest mein Testprogramm ist jetzt empfänglich für die Next-Taste. Ohne iTouch, ohne Focus.
Ob da jetzt MMFs oder ne andere Technik benutzt wird, konnte ich in dem Quelltext nicht so auf Anhieb rausfinden.

_________________
We are, we were and will not be.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 481

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mo 13.02.06 12:12 
Den Hook habe ich jetzt also zum Laufen gebracht, allerdings habe ich jetzt ein neues Problem. Mein Hook-Code sieht jetzt so aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
function KeyboardHookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  // Damit kriege ich das FensterHandle meiner Anwendung
  // im HookInstall wird die Eigenschaft MainWindow dieses Objects
  // auf mein Handle gesetzt.
  oDemoObj := TWDB_IPCDemoObj.Create('NempNochEinMP3Player');
  case nCode < 0 of
    FALSE:
      begin
        if (lparam = APPCOMMAND_MEDIA_NEXTTRACK)
            or (lparam = APPCOMMAND_MEDIA_PREVIOUSTRACK)
            or (lparam = APPCOMMAND_MEDIA_STOP)
            or (lparam = APPCOMMAND_MEDIA_PLAY_PAUSE)
        then
        begin
          //Nachricht an mein Fenster senden
          PostMessage(oDemoObj.MainWindow,WM_APPCOMMAND,wparam,lParam);
          // Nachricht "erledigt"
          Result := 1;
        end
        else
          // Weiterleiten
          Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
      end;
    else
      // Weiterleiten
      Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
  end;
end;


Das funktioniert ganz gut. Das Problem ist aber, dass meine Anwendung (an die die so gehookten Nachrichten gesendet werden) die Nachricht mehrfach erhält (ca. 20 mal), wenn die Logitech-Software iTouch läuft, die (wahrscheinlich?) auch einen Hook einsetzt, um die Nachrichten an (alle?) Fenster weiterzuleiten. Wenn ich iTouch stoppe (oder nach Start meiner Anwendung starte), dann kommt die nachricht immer nur einmal an, so wie es sein sollte.

Und wieder mal die Frage: was mache ich falsch? Oder (als Workaround): Wie kann ich in meiner Anwendung feststellen, ob dieses Nachrichten schon gehookt werden, so dass ich dann ggf. meinen Hook nicht installiere?

_________________
We are, we were and will not be.
digi_c
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1905

W98, XP
D7 PE, Lazarus, WinAVR
BeitragVerfasst: Mo 13.02.06 13:12 
Konnte man die Nachricht nicht aus der Queue nehmen? Aber der Logitech Prozess hat ja als erstes gehookt. Hmm...

Du kannst natürlich eine FindWindow Such Orgie loslassen aber das muss doch besser gehen.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 481

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mo 13.02.06 13:36 
user profile icondigi_c hat folgendes geschrieben:
Konnte man die Nachricht nicht aus der Queue nehmen? Aber der Logitech Prozess hat ja als erstes gehookt. Hmm...
Ich dachte eigentlich, dass das mit Result := 1; passiert. In der Hauptanwendung, wo ich dann die WM_APPCOMMAND Message verarbeite, setze ich aMsg.Result auch auf 1. Eigentlich sollte dann die Nachricht dann als abgearbeitet gelten, oder?

user profile icondigi_c hat folgendes geschrieben:
Du kannst natürlich eine FindWindow Such Orgie loslassen aber das muss doch besser gehen.
Meinst du zum Suchen meiner Anwendung im Hook, oder zum Finden von iTouch? Meine Form suchen brauch ich nicht, da ich das Handle meiner Form im InstallHook in ein MMF ablege (hoffe, die Sprechweise ist so richtig ;-)). Und iTouch hat eigentlich keine Form - da könnte ich höchstens den Prozess suchen. Allerdings gibt das mir keine garantie darüber, dass evtl. ein anderes Ding die Nachricht auch schon hooked.

_________________
We are, we were and will not be.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 481

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 15.02.06 15:42 
Ich push hier mal.

Problem ist geblieben. Wenn iTouch vor meinem Programm läuft, dann wird der iTouch-Hook zuerst abgearbeitet. So, wie ich mir das denke, leitet dieser an jedes Fenster den entsprechenden Befehl weiter. Diese weitergeleitete Message wird dann von meinem Hook abgefangen, und an meine Anwendung geschickt, wo sie verarbeitet wird.
Es scheint jetzt so zu sein, dass in diesem Fall iTouch die Nachricht nochmal an das nächste Fenster sendet, und wieder und wieder und wieder. Jedes mal wird das von mir abgefangen, und an meine Anwendung weitergeleitet. (Die Anzahl der ankommenden Nachrichten hängt tatsächlich von der Anzahl der geöffneten Fenster ab)

Jemand ne Idee?

_________________
We are, we were and will not be.
digi_c
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1905

W98, XP
D7 PE, Lazarus, WinAVR
BeitragVerfasst: Do 16.02.06 08:29 
Vielleicht kann Assarbard dir da direkt helfen?
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 481

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 22.02.06 17:30 
Da es scheinbar auch in der DP keinen gibt, der mir da weiterhelfen kann (*klick*), habe ich erstmal einen Workaround ausprobiert, der allerdings auf Benutzer-Eingaben angewiesen ist:

Zunächst wird der Hook installiert und ein Flag "MediaTest" auf True gesetzt. Dieser bewirkt, dass in der MediaKey-Prozedur der Hauptform nur eine globale Variable "MediaCount" hochgezählt wird.
Dann wird eine "TestForm" Modal geöffnet, die dazu auffordert, einmal eine MediaTaste zu drücken. Drückt der User eine Taste, dann wird über Form1 der OK-Button auf Enabled gesetzt (und MediaCount um eins erhöht).

Ist nach Schließen der TestForm MediaCount=0 (d.h. Userklick auf "Abbrechen", weil z.B. gar keine Mediatasten da sind ;-)) oder größer als 1 (d.h. aus dem einen Tastendruck wurden auf magische Art und Weise mehrere), dann wird der Hook wieder deinstalliert. Anschließend wird "MediaTest" wieder auf False gesetzt.

Unschön daran ist, dass man nicht entscheiden kann, ob der User zweimal gedrückt hat, oder die mehrfachen Counts durch den hier beschriebenen Fehler zustande kommen. Ist der User also ein Mehrfach-Drücker, dann ist die Entscheidung, den Hook zu deinstallieren möglicherweise verkehrt.

Die Entscheidung wird dann in der Ini gespeichert, damit der Test beim nächsten Start nicht wieder automatisch gestartet wird.


Aber die Frage bleibt: Was ist an meiner Hook-Technik verkehrt? :gruebel:

_________________
We are, we were and will not be.