Subclassing

Hilfreiche Erklärungen und Tipps zum Lernen von Autohotkey

Moderator: jNizM

just me
Posts: 4796
Joined: 02 Oct 2013, 08:51
Location: Germany

Subclassing

19 May 2014, 03:39

Was ist das?

Na ja, eine treffende Übersetzung fällt mir schwer. Eine Möglichkeit wäre "Subklassieren" im Sinne von "zu einer Subklasse (Unterklasse) machen". Alle Fenster unter Windows müssen mit einem Klassennamen registriert werden. Auch die mit Windows gelieferten vorgefertigten Common Controls, die AHK für seine Gui Controls verwendet, haben registrierte Klassennamen. Das deutet darauf hin, das Microsoft die Grunddefinitionen der Controls als Klassen versteht, deren Instanzen dann die wirklichen Controls bilden.

Die Eigenschaften der Controls werden über Stile (styles / ex(tended) styles) und teilweise über Nachrichten (messages) gesteuert. Die Interaktion mit dem Benutzer / System erfolgt durch Nachrichten. Für die Verarbeitung der Nachrichten ist für jedes Fenster oder Control eine spezielle Routine verantwortlich, die Nachrichtenschleife (message loop). Und genau hier ist der Ansatz für das Subclassing. Wenn man per Subclassing eine eigene Funktion bereitstellt, wird diese für die Nachrichtenverarbeitung anstelle der originären Routine des Controls aufgerufen. Damit sind direkte Eingriffe in die Nachrichtenverarbeitung möglich.

Wozu ist das gut?

Als Beispiel soll ein kürzlich im Forum diskutiertes Problem dienen: Wie verhindere ich, dass Text aus einem Edit-Control herauskopiert wird?

Die Kopieraktionen werden über die Nachrichten WM_COPY (kopieren) bzw. WM_CUT (ausschneiden) gesteuert. So sieht das dann auf den ersten Blick einfach aus: Einfach die Nachrichten WM_COPY und WM_CUT mit zwei OnMessage() Anweisungen auf eine eigene Funktion umleiten und dort die Verarbeitung verhindern. Gesagt, getan, und der Erfolg? Keiner! Offensichtlich kann man diese Nachrichten zumindest in diesem Fall nicht per OnMessage() abfangen.

Jetzt schlägt die Stunde für das Subclassing. Indem man die komplette Nachrichtenverarbeitung des Edit-Controls auf eine eigene Routine umleitet, kann man auch die dem Edit-Control gesendeten Nachrichten WM_COPY und WM_CUT abgreifen und verhindern, dass sie an die originäre Nachrichtenschleife des Controls durchgeleitet werden. Und siehe, Kopieren und Ausschneiden funktionieren nicht mehr.

Wie wird das gemacht?

Mit der Version 6 der ComCtl32.dll (also mit Win XP) hat Microsoft ein vereinfachtes Verfahren für das Subclassing eingeführt. Es besteht im Wesentlichen aus nur drei API-Funktionen:
Dazu gibt es noch unter SUBCLASSPROC eine Beschreibung der Parameter, die die aufzurufende Funktion entgegennehmen muss.

Wie die wortgetreuen Übersetzungen der Namen nahelegen, wird mit SetWindowSubclass() eine Subclassfunktion gesetzt und mit RemoveWindowSubclass() wieder entfernt. Die aufzurufende Funktion wird als stinknormale AHK Funktion mit genau sechs Parametern definiert:

Code: [Select all] [Download] GeSHi © Codebox Plus

MeineSubclassFunktion(HWND, Msg, wParam, lParam, IdSubclass, RefData) {
; Parameter:
; HWND - Das Handle (HWND) / die ID des Controls.
; Msg - Die Nummer der Nachricht als vorzeichenloser Integerwert.
; lParam - Ein nachrichtenabhängiger Wert in Pointergröße.
; wParam - Ein nachrichtenabhängiger Wert in Pointergröße.
; IdSubclass - Die eindeutige ID in Pointergröße, die in SetWindowSubclass() festgelegt wurde.
; RefData - Ein zusätzlicher Wert in Pointergröße, der in SetWindowSubclass() festgelegt wurde.
...
}
Ihre Adresse, die sowohl an SetWindowSubclass() als auch an RemoveWindowSubclass() im Parameter pfnSubclass übergeben werden muss, holt man sich mit der Funktion RegisterCallback(): SubclassProcAddr := RegisterCallback("MeineSubClassFunktion").

Und wie sieht das dann in AHK aus?

Na ja, wie bereits im vorigen Abschnitt gesagt, es braucht folgende Schritte:

  1. Zunächst braucht es die Subclassfunktion mit ihren 6 Parametern. Für alle Nachrichten, die innerhalb der Funktion bearbeitet werden sollen, muss man sich im MSDN und/oder anderen Quellen die Nachrichtennummer und die Beschreibung des Inhalts der Parameter wParam und lParam besorgen. Dann kann dieser Teil mit Leben gefüllt werden. Alle Nachrichten, die in der Subclassfunktion nicht vollständig verarbeitet oder unterdrückt werden sollen, müssen der ursprünglichen Nachrichtenschleife des Controls übergeben werden, damit das Control sie verarbeiten kann.
    Das erledigt ein Aufruf von DefSubclassProc().
    Für den Returnwert der Subclassfunktion gilt:
    • wenn eine Nachricht in der Funktion komplett verarbeitet wird, sucht man sich den passenden Wert im MSDN (in vielen Fällen reicht eine 0).
    • wenn die Nachricht an DefSubclassProc() übergeben wird, wird der Returnwert dieser Funktion zurückgegeben.
    Das Ganze sieht dann in etwa so aus:

    Code: [Select all] [Download] GeSHi © Codebox Plus

    MeineSubclassFunktion(HWND, Msg, wParam, lParam, IdSubclass, RefData) {
    ; Definitionen
    ; ...
    ; Eigene Verarbeitungsroutinen
    If (Msg = ????) {
    ; mach das!
    Return 0 ; oder auch nicht!!!
    }
    ; ...
    Return DllCall("Comctl32.dll\DefSubclassProc", "Ptr", HWND, "UInt", Msg, "Ptr", wParam, "Ptr", lParam, "Ptr")

  2. Dann brauchen wir die Adresse der Funktion. Das macht man mit:

    Code: [Select all] [Download] GeSHi © Codebox Plus

    SubclassProcAddr := RegisterCallback("MeineSubclassFunktion")

  3. Jetzt kann die Subclassfunktion mit SetWindowSubclass() aktiviert werden. Die beiden ersten Funktionsparameter sind fast selbsterklärend. hWnd ist (natürlich) das Handle (HWND) des Controls. pfnSubclass ist die Adresse der aufzurufenden Funktion, hier also SubclassProcAddr.
    Im dritten Parameter uIdSubclass wird ein numerischer Wert übergeben, der als fünfter Parameter an die Subclassfunktion durchgereicht wird. Microsoft sagt dazu, dass die Kombination aus zweitem und drittem Parameter die Subklasse eindeutig idenfiziert. Hintergrund ist, dass man einem Control durchaus mehrere Subclassfunktionen bzw. ein und dieselbe Funktion auch mehrfach mit unterschiedlichen IDs zuweisen kann (der Sinn hat sich mir noch nicht erschlossen), die per DefSubclassProc() schön nacheinander aufgerufen werden. Im letzeren Fall kann man dann (wozu auch immer) anhand der ID zwischen den Aufrufen unterscheiden. Wenn man das nicht braucht, darf es auch eine 0 sein.
    Im letzten Parameter dwRefData kann ein weiterer Integerwert übergeben werden, der dann als sechster Parameter an die Subclassfunktion durchgereicht wird. Das kann auch die Adresse einer Variablen oder eines Objekts sein. Auf das Objekt kann dann in der Subclassfunktion per MeinObjekt := Object(RefData) zugegriffen werden. Wenn man das nicht braucht, darf es auch eine 0 sein.
    Als AHK-Code sieht das dann z.B. so aus:

    Code: [Select all] [Download] GeSHi © Codebox Plus

    DllCall("Comctl32.dll\SetWindowSubclass", "Ptr", HCTRL, "Ptr", SubclassProcAddr, "Ptr", 0, "Ptr", 0)

  4. Sollte man das Subclassing wieder beenden wollen, ist das jederzeit per RemoveWindowSubclass() möglich. Im MSDN wird empfohlen, das Subclassing so zu beenden, bevor das Control per Gui, Destroy zerstört wird. Die drei Parameter kennen wir bereits von SetWindowSubclass(). Sie haben hier identische Bedeutungen, d.h. sie müssen dieselben Inhalte haben wie im zugehörigen SetWindowSubclass() Aufruf. Als AHK-Code sieht das dann z.B. so aus:

    Code: [Select all] [Download] GeSHi © Codebox Plus

    DllCall("Comctl32.dll\RemoveWindowSubclass", "Ptr", HCTRL, "Ptr", SubclassProcAddr, "Ptr", 0)


Beispiel:

Als praktisches Beispiel soll eine Umsetzung des unter "Wozu ist das gut?" beschriebenen Problems dienen. Die DllCalls für das Subclassing habe ich darin in der Funktion SubclassControl() verpackt, weil ich das so übersichtlicher finde. Auf den Aufruf von RemoveWindowSubclass() habe ich hier verzichtet, weil keine betroffenen Controls zerstört werden, bevor das Skript endet.

Code: [Select all] [Expand] [Download] GeSHi © Codebox Plus

Remaining with AHK 1.1.25.02 until v2 will become beta.
User avatar
nnnik
Posts: 2280
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Subclassing

08 Feb 2015, 06:59

Ich war grade in der Lage das Wissen, dass ich durch dieses Tutorial erarbeitet habe in die Praxis umzusetzen.
Ich kann den Tipp geben, dass es nicht ratsam ist ein ActiveX control zu subclassen.
Recommends AHK Studio
just me
Posts: 4796
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Subclassing

08 Feb 2015, 07:04

Diese Art des Subclassing wird im MSDN ja auch für die Common Controls beschrieben. Ich habe keine Ahnung, ob für die ActiveX Controls so etwas wie DefSubclassProc() überhaupt existiert.
Remaining with AHK 1.1.25.02 until v2 will become beta.
tmplinshi
Posts: 1091
Joined: 01 Oct 2013, 14:57

Re: Subclassing

05 Mar 2015, 09:11

I don't understand German language. But found the example code useful :D, especially EM_SHOWBALLOONTIP(). Thank you just me!

Return to “Tutorials”

Who is online

Users browsing this forum: Yahoo [Bot] and 1 guest