Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi HILFE! Noch einmal der Tastaturhook (https://www.delphipraxis.net/150984-hilfe-noch-einmal-der-tastaturhook.html)

idefix2 2. Mai 2010 12:05


HILFE! Noch einmal der Tastaturhook
 
Hallo,

Jetzt habe ich die diversen Hindernisse bei der Programmierung meines Tastaturhooks umschifft, und das Ding tut in meinem Testprogramm, was ich mir vorgestellt habe. Aber im Moment scheitere ich daran, aus dem lokalen Hook einen globalen zu machen, und ich habe keine Idee, was da falsch sein könnte.

Ich habe:
Die eigentliche Hookunit aus dem Testprogramm entfernt und in eine DLL ausgelagert.
Im Testprogramm an die Stelle der Hookunit eine Loadhook Unit gesetzt, die die DLL lädt und SetWindowsHookEx als ThreadId den Wert 0 und als HInstance die DLL-Handle übergibt.

Der Hook funktioniert einwandfrei, solange ich in dem Testprogramm Eingaben mache, mit dem ich die DLL geladen habe. Sobald ich eine Tastaureingabe in einem anderen Programm mache, wird mein Hook deaktiviert und bleibt dann auch inaktiv, wenn ich in mein Programm zurückkehre. Wenn ich in ein anderes Programm wechsle und dort nur die Maus verwende, passiert nichts, dann ist meine Hookroutine in meinem Testprogramm weiterhin aktiv, sobald es wieder den Focus bekommt.

So lade ich meine DLL:
Delphi-Quellcode:
unit LoadCapsLock;

interface
procedure loaddll;
procedure unloaddll;

implementation

uses windows, dialogs;

var
LibHandle: THandle;  
HookHandle: Cardinal;

inst: procedure (UseHookHandle: HHook); stdcall; // Initialisierung
uninst: procedure; stdcall; // Aufräumen

procedure error (const s: string); inline;
begin
MessageDlg (s, mtError, [mbOK], 0);
end;

procedure loaddll;
begin
LibHandle:=LoadLibrary('capslock.dll');
if LibHandle=0 then begin error ('Library not loaded'); exit end;

inst := GetProcAddress(LibHandle,'AfterInstall');
uninst := GetProcAddress(LibHandle,'AfterUninstall');

if HookHandle <> 0
then begin Error ('Hook ist bereits installiert'); exit end;

HookHandle := SetWindowsHookEx(WH_KEYBOARD,
                               GetProcAddress(LibHandle, 'KeyboardHookProc'),
                               LibHandle, 0);

if HookHandle = 0
then begin Error ('Hook Installation fehlgeschlagen'); exit end;

inst (HookHandle); // Initialisierung in der DLL
end;

...

Astat 3. Mai 2010 09:30

Re: HILFE! Noch einmal der Tastaturhook
 
Zitat:

Zitat von idefix2
..und ich habe keine Idee, was da falsch sein könnte.

Ohne Worte.

http://www.delphipraxis.net/internal...t.php?t=172835

Thread #20

lg.

SirThornberry 3. Mai 2010 09:37

Re: HILFE! Noch einmal der Tastaturhook
 
In dem oben genannten Code kann ich auch keinen Fehler finden. Wenn du wirklich willst das du jemand hilft erschwere es den Leuten am besten nicht durch unformatierten Code. Halte dich an die Styleguides und eventuell siehst du dann auch selbst den Fehler wenn Einrückung etc. stimmt.

idefix2 3. Mai 2010 21:48

Re: HILFE! Noch einmal der Tastaturhook
 
@ASTAT
Danke für den Hinweis - Vorlagen, wie es gehen sollte, gibt es im Internet einige. Deinen Code habe ich jetzt auch ausprobiert, in fast unveränderter Form (ich habe am Anfang der Hookroutine ein beep eingebaut) hat er auch als globaler Hook funktioniert. Ein paar kleine Änderungen, jetzt geht wieder nichts, dabei habe ich bis jetzt eigentlich nur "toten Code", also Deklarationen von variablen und Unterprogrammen, die noch gar nicht aufgerufen werden, eingebaut - wenn ich jetzt eine einzige Taste drücke, fängt die Hookroutine zu beepen an und hört nicht mehr auf.

Naja, zumindest habe ich einen Ansatz, mit dem ich weitersuchen kann. Das frustrierende ist, dass mein Hookprogramm eigentlich schon fertig war, als lokaler Hook hat es einwandfrei funktioniert, aber nach der Übersiedlung in die DLL funktioniert es eben immer noch nur lokal.

@SirThornberry
Über die Frage, wie Code formatiert gehört, lassen sich Religionskriege führen. Ich halte mich seit 30 Jahren strikt an meine Styleguides und stufe meinen Code als sicher nicht weniger übersichtlich ein als die meisten Quellcodes, die z.B. hier in der Delphi Praxis oder sonstwo zu finden sind. Delphi ist für mich ziemlich neu, Pascal nicht.

hoika 3. Mai 2010 22:37

Re: HILFE! Noch einmal der Tastaturhook
 
Hallo,

Also.
Du möchtest, dass wir dir helfen,
und entschuldige, der Code ist einfach schwer lesbar.

Was hälst du von folgendem ?

Delphi-Quellcode:
procedure loaddll;
begin
  LibHandle:=LoadLibrary('capslock.dll');
  if LibHandle=0 then
  begin
    error ('Library not loaded');
    exit
  end;

  inst := GetProcAddress(LibHandle,'AfterInstall');
  uninst := GetProcAddress(LibHandle,'AfterUninstall');

  if HookHandle <> 0 then
  begin
    Error ('Hook ist bereits installiert');
    exit
  end;

  HookHandle := SetWindowsHookEx(WH_KEYBOARD,
                  GetProcAddress(LibHandle, 'KeyboardHookProc'),
                  LibHandle, 0);

  if HookHandle = 0 then
  begin
    Error ('Hook Installation fehlgeschlagen');
    exit
  end;

  inst (HookHandle); // Initialisierung in der DLL
end;

Wenn dir die Formatierungs-"Hinweise" nicht gefallen.
OK

Wundere dich dann aber nicht, wenn keiner über deinen Code
"drüberschaut".

Mir hatte dein Code gereicht, nicht näher hinzusehen.
Nun ja, aber trotzdem.

Was mich eh wundert, ist, dass du hier nicht als Admin "elevated" bist.

Globale Hooks kann ein nomaler User nicht setzen.


Ausserdem fehlt zumindestes der Pdeuso-Code für KeyboardHookProc.


Heiko

Delphi-Laie 3. Mai 2010 22:47

Re: HILFE! Noch einmal der Tastaturhook
 
Daß der Hook nur lokal und nicht global funktioniert, wundert mich nicht. Auch die Installation des Hooks muß in eine DLL ausgelagert werden (oder irre ich mich?).

Sehr gut beschrieben ist das alles in Assarbads Hooktutorial. Letzlich sind es 3 Funktionen, die mit dem Hook zu tun haben: Installation, Deinstallation und die eigentliche Hookfunktion. Ich hielt mich genau an seine Vorlage und konnte den globalen Hook erfolgreich installieren. Jegliche Experimente, so z.B. DLL und Exe vereinigen oder auch den Hook in das Hostprogramm zu integrieren, führten zum Verlust der Globalität oder gar zur völligen Dysfunktion des Hooks.

idefix2 4. Mai 2010 16:06

Re: HILFE! Noch einmal der Tastaturhook
 
Ich habe jetzt den Fehler eingegrenzt, und bin auf ein ganz überraschendes Ergebnis gekommen.

Ich habe meine Hookprozedur schrittweise von einer ganz leeren Prozedur weg aufgebaut. Zur Kontrolle, ob mein Hook noch da ist, macht er jedesmal ein beep, wenn er aufgerufen wird. Damit habe ich überprüft, dass das Laden der DLL und die Installation des Hooks wirklich funktioniert.

Sobald ich in meiner Hook-Prozedur einen Timer aktiviere (der gar nichts tut), verabschiedet sich der Hook, wenn er über eine nicht lokale Taste aktiviert wird:

ALSO:
Solange ich Timer1.enabled auskommentiere, habe ich einen globalen Hook, der einwandfrei funktioniert, ich kann auch Tastaturereignisse in die Tatstaurqueue absetzen, die dort richtig ankommen, egal ob ich der Fokus in meinem Testprogramm oder in einem ganz anderen Programm liegt.

Sobald ich diese Programmzeile mitkompiliere, funktioniert der Hook noch immer einwandfrei, wenn ich Tastatureingaben in das Fenster des Testprogramms mache, das die DLL geladen hat. Wenn ich aber eine einzige Tastatureingabe in ein Fenster ausserhalb dieses Programms mache, gibt es für diese Taste noch einmal einen letzten Beep, der mir sagt, die Hookroutine wird aufgerufen. Weitere Tastatureingaben, egal ob in das Fenster meines Testprogrammes oder in ein anderes, produzieren kein Geräusch mehr, d.h. meine Hookroutine ist fortan nicht mehr in der Hook-Kette drinnen.
Wenn das passiert ist, schlägt nachher auch UnhookWindowsHookEx fehl!

In der Prozedur Timer1Timer ist der einzige Befehl: timer1.enabled := false, der Timer tut also im Moment gar nichts. Ist irgend etwas über eine Inkompatibilität zwichen der delphi Timerkomponente und globalen Hooks bekannt?

Delphi-Laie 4. Mai 2010 16:36

Re: HILFE! Noch einmal der Tastaturhook
 
Durchaus möglich, daß meine gestrige Vermutung doch nicht stimmte, also daß man den Hook auch direkt in der Hostanwendung starten und beenden kann (ohne entsprechende DLL-Funktionen aufzurufen). Ich werde das selbst noch ausprobieren.

Warum Du/Sie einen Timer verwendest/verwenden, ist mir insofern nicht ganz klar, weil Hooks ja dafür eingeschaltet werden, auf bestimmte Systemereignisse (hier Tastaur-) zu reagieren.

Über Inkompatibilitäten mit Timern ist mir nichts bekannt, aber es gibt ja auch andere als die von Borland & Co. gelieferten. So ist z.B. in der Komponente „CoolTrayIcon“ ein anderer enthalten, der über eine andere Vererbung (Elternklassen) generiert wird und ressourcenschonender als ersterer sein soll. Vielleicht damit mal probieren?!

idefix2 4. Mai 2010 17:27

Re: HILFE! Noch einmal der Tastaturhook
 
Du :-D

Der Hook selbst muss auf jeden Fall in eine DLL ausgelagert sein, sonst funktioniert er nur als lokaler Hook. Ich habe auf Grund Deines gestrigen Postings als erstes den Aufruf von setwindowshookex auch in die DLL übersiedelt, ich glaube jetzt aber eigentlich nicht mehr, dass das sein muss - werd ich jedenfalls auch noch ausprobieren. Ich wollte ursprünglich alles Hook-spezifische in eine eigene Unit auslagern, damit ich es als lokaler Hook bequem testen und dann, wenn es fertig ist, ohne Änderung des Codes in eine DLL übersiedeln kann. Weil die Parameter von setwindowshookex zwischen lokalem und globalem Hook differieren, hab ich diesen Aufruf eben nicht in der Hook-Unit, sondern im aufrufendem Programm gemacht.

Einen Timer brauche ich, weil ich die Shift-Lock Taste entschärfen will, sie nervt in zweierlei Hinsicht: erstens schaltet sie auch die Zifferntasten und manche (nicht alle!) Sonderzeichentasten um, was wirklich kein Mensch braucht, auch wenn man einmal in Grossbuchstaben schreiben will. Zweitens, und dazu brauche ich den Timer, will ich Shift Lock deaktivieren, wenn die Taste beim Schreiben irrtümlich zeitnah mit einer anderen Taste gedrückt wird - jetzt hab ich 300 ms eingestellt. Die Umschaltung soll nur aktiviert werden, wenn vor und nach dem Drücken der Umschalttaste 300ms lang keine andere Taste kommt.

Danke für den Hinweis auf Cooltrayicon. Bevor ich mir einen Timer mit API Aufrufen selbst bastle, werde ich mir die Komponente anschauen.

P.S.: Wird eine DLL eigentlich automatisch entladen, wenn das Programm, das sie lädt, geschlossen wird, ohne FreeLibrary aufzurufen? Weil eigentlich brauch ich das Hauptprogramm nach dem Laden der DLL nimmer, aber der Hook soll wirksam bleiben, bis der PC abgeschaltet wird.

Delphi-Laie 4. Mai 2010 18:29

Re: HILFE! Noch einmal der Tastaturhook
 
Zitat:

Zitat von idefix2
P.S.: Wird eine DLL eigentlich automatisch entladen, wenn das Programm, das sie lädt, geschlossen wird, ohne FreeLibrary aufzurufen? Weil eigentlich brauch ich das Hauptprogramm nach dem Laden der DLL nimmer, aber der Hook soll wirksam bleiben, bis der PC abgeschaltet wird.

Diesmal bin ich mir sicher: Ja, und zwar ab Windows 2000!

Es geisterte sogar bis vor einiger Zeit im Internet und sogar in Computerzeitschriften (!) der „Geheimtip“, mit dem Registryeintrag „AlwaysUnLoadDLL“ (soweit ich mich entsinne, hieß er so) dieses automatische Entladen in jedem Falle sicherzustellen. Jedoch schrieb Mikroweich selbst, daß das seit Windows 2000 immer automatisch erfolgt und ab dann mithin überflüssig ist. Eine sauberere (und abwärtskompatible) Programmierung, das Entladen in das Programm explizit aufzunehmen, ist es aber allemal.

Edit: Auch ohne automatische Entladung könnte dieses Ansinnen aussichtslos sein, denn DLLs dürften, soweit ich weiß, nie ohne Hostprogramm funktionieren. Ist aber nur eine Vermutung meinerseits.

Edit2: Mit LoadLibrary mache ich gar nicht erst rum, das ist wohl der dynamischer Aufruf? Damit bin ich noch nie erfolgreich gewesen. Ich definiere einfach das Ziel der Funktionen in der Implementation (eben eine DLL), und damit bin ich erfolgreich und zufrieden. Wie gesagt, ab Windows 2000 wird das erfolglos sein, die DLL allein vor sich hinwerkeln zu lassen, und ältere Betriebsprogramme sind ziemlich selten geworden.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:56 Uhr.
Seite 1 von 2  1 2      

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