Von Zeit zu Zeit möchte immer wieder mal ein Entwickler in einem GUI formatierten Text anzeigen. AHK stellt dafür kein passendes Control zur Verfügung. AHK 1.1 bietet aber zwei Möglichkeiten für das Einbetten von weiteren im System registrierten Controls: ActiveX Controls sind in der Regel COM-Objekte, die nur mit einigem Aufwand so in ein GUI integriert werden können, dass sich sich ähnlich wie die eingebauten Controls nutzen lassen.
Custom Controls verhalten sich dagegen meist ähnlich wie die eingebauten Controls. Man kann Ihnen z.b. Variablen und Subroutinen zuweisen. Im Prinzip lassen sich auch alle eingebauten Controls als Custom Controls nutzen. Das bringt aber nur in seltenen Ausnahmen einen praktischen Nutzen.
Für die 'einfache' Ausgabe von formatiertem Text drängen sich zwei Alternativen auf:
- HTML -> MSIE-Browser Control (ActiveX)
- RTF -> RichEdit Control (Custom)
RichEdit Control
Das RichEdit Control gehört wie das Edit Control zu den von Microsoft über das System bereitgestellten Controls, wurde aber im Gegensatz zum 'normalen' Edit Control bisher nicht in AHK integriert.
Ich habe schon vor längerer Zeit eine RichEdit-Klasse geschrieben, mit der man RichEdit Controls in eigene GUIs integrieren kann. Der eigentliche Grund dafür war, dass ich eine Möglichkeit brauchte, formatierten Text aus AHK heraus zu drucken, ohne von einer anderen Anwendung abhängig zu sein. Ich habe die Klasse aber trotzdem mit vielen Eigenschaften und Methoden ausgestattet, die man nur zum Editieren braucht. Das Gesamtpaket ist deshalb recht 'sperrig' und bedarf schon einiger Einarbeitung.
Als ich die Klasse entwickelt habe und auch später immer wieder mal bin ich auf die Dokumentation zum Text Object Model (TOM) gestoßen. Am Anfang konnte ich aber damit gar nichts anfangen, und auch später bin ich immer wieder davor zurückgescheut, mich in dieses 'Zeugs' einzuarbeiten. Vor Kurzem fand ich aber diesen Beitrag von teadrinker. Der hat mich motiviert, mir das doch mal etwas genauer anzuschauen. Dabei habe ich erkannt, dass TOM die Handhabung von RichEdit Controls bei einfachen Aufgaben - wie z.B. der Anzeige von RTF Dateien - deutlich vereinfacht. Und deshalb kam mir die Idee für das folgende Tutorial.
Und ab dafür!
Schritt 1:
Das RichEdit Control wird über die im System enthaltene Msftedit.dll bereitgestellt und registriert. Die seit Win XP registrierte Controlklasse ist RICHEDIT50W. Um es überhaupt nutzen zu können, muss das Skript diese Dll explizit laden:
Code: Select all
RE_Dll := DllCall("LoadLibrary", "Str", "Msftedit.dll", "Ptr")
Nachdem das erledigt ist, kann man das RichEdit Control dem GUI als Custom Control hinzufügen:
Code: Select all
Gui, Add, Custom, ClassRICHEDIT50W vRE hwndHRE
Code: Select all
Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0804 ; ES_MULTILINE | ES_READONLY
Jetzt haben wir ein leeres RichEdit Control. Um das dazu zu bringen, eine fertige RTF-Datei anzuzeigen, brauchen wir das zugehörige TOM Objekt, genauer gesagt das TextDocument Objekt. Das lässt sich dem Control über die Nachricht EM_GETOLEINTERFACE entlocken. Wie das geht, ist hier grob beschrieben. Für AHK sieht das dann z.B. so aus (ohne näher auf die ComObj... Anweisungen einzugehen):
- Man sendet eine EM_GETOLEINTERFACE Nachricht an das RichEdit Control. Die gibt einen Zeiger auf das IRichEditOle Interface zurück.
Code: Select all
DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") ; EM_GETOLEINTERFACE
- Diesem Interface kann man per ComObjQuery() einen Zeiger auf das ITextDocument Interface abgewinnen.
Code: Select all
ITextDocument := ComObjQuery(IRichEditOle, IID_ITextDocument)
- Das widerum lässt sich per ComObject() in ein TextDocument Objekt umwandeln.
Code: Select all
DocObject := ComObject(9, ITextDocument, 1)
Code: Select all
GetTomDoc(HRE) {
; TextDocument Objekt für ein RichEdit Control abrufen
Static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
DocObj := 0
If DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") { ; EM_GETOLEINTERFACE
DocObj := ComObject(9, ComObjQuery(IRichEditOle, IID_ITextDocument), 1) ; ITextDocument
ObjRelease(IRichEditOle)
}
Return DocObj
}
Jetzt haben wir ein RichEdit Control und dessen TextDocument Objekt. Dieses Objekt verfügt über die Methode Open, mit der man Dateien in das RichEdit Control einlesen kann. Dabei wird im Parameter Flags festgelegt, welchen Inhalt die Datei hat. Für Dateien mit RTF-Text steht das Flag tomRTF (0x01). Außerdem kann man bestimmen, of die eingelesenen Daten den Inhalt des RichEdit Controls komplett ersetzen sollen oder ob der Dateiinhalt eingefügt werden soll (tomPasteFile (0x1000). Mehr braucht man nicht, um die Datei anzuzeigen:
Code: Select all
DocObj.Open("MeineDatei.rtf", 0x01, 0)
Nun frisch ans Werk. Starte Microsofts WordPad und erstelle einen formatierten Text. Den speicherst Du unter dem Namen MeinRTF.rtf in einem Verzeichnis, in dem auch das folgende Skript liegt:
Code: Select all
#NoEnv
SetWorkingDir, %A_ScriptDir%
RE_Dll := DllCall("LoadLibrary", "Str", "Msftedit.dll", "Ptr")
Gui, +hwndhGui
Gui, Margin, 10, 10
Gui, Font, s10, Arial
Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0804 ; ES_MULTILINE | ES_READONLY
Gui, Show, , RichEdit
TomDoc := GetTomDoc(hRE)
TomFile := "MeinRTF.rtf"
TomDoc.Open(TomFile, 0x01, 0)
Return
GuiClose:
ExitApp
GetTomDoc(HRE) {
; Get the document object of the specified RichEdit control
Static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
DocObj := 0
If DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") { ; EM_GETOLEINTERFACE
DocObj := ComObject(9, ComObjQuery(IRichEditOle, IID_ITextDocument), 1) ; ITextDocument
ObjRelease(IRichEditOle)
}
Return DocObj
}
Mit Win 8 hat Microsoft ein 'großes' Update für das RichEdit Control verteilt. Damit wird z.B. die Anzeige von 'eingebetten Objekten' wie Bildern stark vereinfacht. Für RTF-Dateien, die unter Win 8+ erstellt wurden und angezeigt werden, sind dafür keine weiteren Maßnahmen notwendig. Nach meinen Tests darf das RichEdit Control aber nicht ReadOnly sein, wenn die Datei gelesen wird (warum auch immer!?!). Weil man diesen Stil nicht einfach isoliert setzen und entfernen kann, muss man dafür die Nachricht EM_SETOPTIONS bemühen, nachdem das Control erstellt und gefüllt worden ist:
Code: Select all
DllCall("SendMessage", "Ptr", HRE, "UInt", 0x044D, "Ptr", 0x02, "Ptr", 0x0800) ; EM_SETOPTIONS: ECOOP_OR, ECO_READONLY