Page 1 of 1

Formatierten Text per RichEdit ausgeben

Posted: 27 Feb 2018, 05:10
by just me
Vorwort
TOM.PNG
TOM.PNG (5.96 KiB) Viewed 1389 times
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:
  1. HTML -> MSIE-Browser Control (ActiveX)
  2. RTF -> RichEdit Control (Custom)
Wir beschränken uns hier auf die zweite Variante. Dabei beschäftigen wir uns auch notgedrungen mit der Implementierung von Custom Controls.

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")
Schritt 2:

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
Weil AHK die als Custom hinzugefügten Controls nicht kennt, werden auch nur die wesentlichen Stile automatisch gesetzt, wie z.B. WS_CHILD, WS_TABSTOP, WS_VISIBLE. Scrollbars kann man mit den üblichen Optionen +HScroll/VScroll setzen, weil die nicht controlspezifisch sind (WS_HSCROLL, WS_VSCROLL). Allerdings kann AHK auch bei einer Höhenangabe wie h400 bzw. r20 nicht erkennen, dass es sich um ein mehrzeiliges RichEdit Control handelt. Man muss den passenden Stil ES_MULTILINE (0x04) deshalb selbst setzen. Das gilt auch für alle anderen möglichen RichEdit Stile, wie z.B. ES_READONLY (0x0800). Für ein mehrzeiliges schreibgeschütztes RichEdit Control mit einer Breite und Höhe von jeweils 400 Pixeln und einer vertikalen Scrollbar braucht es deshalb:

Code: Select all

Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0804 ; ES_MULTILINE | ES_READONLY
Schritt 3:

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)
Eine komplette Funktion sieht z.B. so aus:

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
}
Schritt 4:

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)
Zusammenfassung

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
}
Zugaben unter Win 8+

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

Re: Formatierten Text per RichEdit ausgeben

Posted: 27 Feb 2018, 07:16
by LuckyJoe
Super! - Danke: schon oft gesucht, nie gefunden ... wieder etwas Neues gelernt :-)

VG - Lucky Joe

Re: Formatierten Text per RichEdit ausgeben

Posted: 06 Mar 2018, 02:44
by moefr01
Klasse just me ... absolut brauchbar und vorbildlich erklärt :thumbup:
Da ich zZ. den Gedanken pflege einen einfachen RichText-Anzeiger zu basteln, kommt mir dein Code 100% entgegen.
Habe da aber noch ein Problem: Ich nutzte deinen Beispielcode und bekam die Grafik in einer mit WordPad/Word erstellten rtf-Datei jedoch nicht angezeigt (Windows 10 Pro 64Bit).
Nur Leerzeichen mit Zeilenumbruch an der Position der Grafik. In deiner genialen Class_RichEdit habe ich auch schon recherchiert, bin aber nicht fündig geworden.
Du hast sicherlich einen Tipp hierzu parat ?!
Herzlichen Gruß
moefr01 :wave:

Re: Formatierten Text per RichEdit ausgeben

Posted: 06 Mar 2018, 03:43
by just me
Moin,

hast Du es mit der unter Zugaben unter Win 8+ beschriebenen Methode versucht? D.h.

Code: Select all

...
Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0004 ; ES_MULTILINE (ES_READONLY hier noch nicht setzen)
Gui, Show, , RichEdit

TomDoc := GetTomDoc(HRE)
TomFile := "MeinRTF.rtf"
TomDoc.Open(TomFile, 0x01, 0)
DllCall("SendMessage", "Ptr", HRE, "UInt", 0x044D, "Ptr", 0x02, "Ptr", 0x0800) ; EM_SETOPTIONS: ECOOP_OR, ECO_READONLY (nach dem Einlesen der Datei setzen)
Return

Re: Formatierten Text per RichEdit ausgeben

Posted: 06 Mar 2018, 04:57
by moefr01
Sorry... wer lesen kann ist klar im Vorteil!
Parameter 0x0004 fälschlicherweise auf 0x0804 belassen :crazy:
Anzeige funktioniert einwandfrei! ...Danke für die prompte Reaktion/Lösung