Ok, ich versuch mal, das ganze anschaulich zu erklären. Also, wie du weißt, kommuniziert der Anwender mit seinem PC über die Maus und die Tastatur. Das Betriebssystem empfängt die Befehle und sendet diese weiter über mehrere Ebenen zu dem Fenster, das im Vordergrund ist. Das Fenster empfängt die Befehle und verarbeitet diese. Die Befehle werden also im Prozessspeicher des Fensters (oder besser gesagt der dazugehörigen Anwendung) verarbeitet. Wenn man aber nun erst zur Laufzeit einen neuen Menüeintrag erstellt und darauf klickt, weiß das Programm nicht, wie es diesen unbekannten Befehl verarbeiten soll. Für dieses Problem gibt es nur eine Lösung: Man muss zur Laufzeit den Prozessspeicher geringfügig manipulieren, genauer gesagt, man muss dem Programm eine neue Adresse für die Prozedur geben, in der die Befehle verarbeitet werden. Der Clou ist nun, dass man als neue Adresse eine selbst geschriebene Funktion verwendet. Dadurch werden dann automatisch alle Befehle, die an das Fenster gesendet werden, an eine selbstgeschriebene Funktion gesendet und man kann selbst entscheiden, wie man weiter damit verfahren will. Das Umleiten der Befehle wird in einem so genannten Hook realisiert. Doch nun genug der Theorie, schauen wir uns das ganze mal praktisch an:
Für einen Fenster-Hook stellt Windows die API SetWindowLong zur Verfügung. Mit dieser API kann man verschiedene Dinge anstellen, in unserem Fall verwenden wir sie wie gesagt für einen Hook. Die API sieht im Detail so aus:
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd _
As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long hwnd: Handle des Fensters
nIndex: hier können verschiedene Konstanten eingesetzt weren, in unserem Fall GWL_WNDPROC
dwNewLong: in unserem Fall die neue Speicheradresse
Die Funktion hat in unserem Fall als Rückgabewert die alte Speicheradresse, die wir ja ersetzen.
Hier ist mal ein kleines Beispiel für so einen Hook:
Declare Function CallWindowProc Lib "user32" Alias _
"CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
ByVal hwnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias _
"SetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Const GWL_WNDPROC = -4
Global lpPrevWndProc As Long
Public Sub Hook()
lpPrevWndProc = SetWindowLong(Form1.hwnd, GWL_WNDPROC, _
AddressOf WindowProc)
End Sub
Public Sub Unhook()
Dim temp As Long
temp = SetWindowLong(Form1.hwnd, GWL_WNDPROC, lpPrevWndProc)
End Sub
Function WindowProc(ByVal hw As Long, ByVal uMsg As _
Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Form1.List1.AddItem "Message: " & hw & " " & uMsg
WindowProc = CallWindowProc(lpPrevWndProc, hw, _
uMsg, wParam, lParam)
End Function Wie du siehst, setzen wir als neue Speicheradresse die Adresse von der Funktion WindowProc. Es werden also alle Befehle, die an Form1 gesendet werden, in dieser Funktion verarbeitet, naja, also eigentlich nicht ganz, aber dazu gleich mehr. Das Betriebssystem sendet als Parameter hw (das Fensterhandle), sowie die Hauptnachricht (uMsg) mit zwei Parametern (wParam, lParam). Das lassen wir uns einfach mal anzeigen mit Form1.List1.AddItem. Natürlich wäre es viel zu viel Arbeit, alle Befehle selbst zu verarbeiten, deshalb schicken wir sie mit CallWindowProc einfach weiter an die alte Speicheradresse, die dann alles weitere macht. Wichtig: Vergiss nicht, vor dem Beenden wieder Unhook aufzurufen, sonst kann der PC abstürzen.
Weiter gehts in der nächsten Nachricht.... |