| Die RichTextBox (im Folgenden RTB genannt) ist schon von sich aus Unicode-fähig. Ist der IME-Editor auf Chinesisch umgeschaltet, kann man in eine RTB jedes beliebige Schriftzeichen direkt eintragen. An die Grenzen stößt man jedoch in dem Moment, wo man das eingegebene Schriftzeichen weiterverarbeiten will, oder ein Schriftzeichen per Quellcode in die RTB einfügen möchte. Der folgende Quellcode produziert leider nur ein Fragezeichen in der RTB: RichTextBox1.Text = ChrW(30020) Da die RTB genau wie die oben schon besprochene PictureBox über die Eigenschaft "hWnd" verfügt, kann man auch in der RTB Schriftzeichen per API darstellen. Öffnen wir dazu ein neues Projekt, fügen die Komponente "Microsoft Rich Textbox Control 6.0", ordnen eine RichTextBox auf dem Formular an und kopieren folgenden Quellcode in das Projekt: Private Declare Function SendMessage Lib "user32.dll" _
Alias "SendMessageA" (ByVal hWnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, _
ByRef lParam As Any) As Long
Private Type SETTEXTEX
flags As Long
codepage As Long
End Type
Private Const WM_USER As Long = &H400&
Private Const EM_SETTEXTEX As Long = WM_USER + 97
Private Const ST_DEFAULT As Long = 0
Private Const CODEPAGE_UNICODE As Long = 1200
Private Sub Form_Load()
Call CH_schreiben (RichTextBox1, 23721)
End Sub
Private Sub CH_schreiben (c As RichTextBox, ByVal UCode As Long)
Dim Data As SETTEXTEX
Data.flags = ST_DEFAULT
Data.codepage = CODEPAGE_UNICODE
Call SendMessage(c.hWnd, EM_SETTEXTEX, VarPtr(Data), UCode)
End Sub Listing 5 Als Codepage wurde Unicode (= 1200) gewählt. Die Codepage für vereinfachtes Chinesisch ist zwar eigentlich 950, nur werden die chinesischen Schriftzeichen dann nicht in der Reihenfolge ausgegeben, wie sie das Unicode-Konsortium im Unicode-Standard festgelegt hat. Und das würde in unserem Fall zu einem ganz anderen als dem gewünschten "Felsen"-Schriftzeichen führen. Um den Unicode-Wert eines eingegebenen Schriftzeichens ermitteln zu können, benötigt man die WinAPI-Funktion SendMessageA. Die allgemeine Deklaration muss folgendes enthalten: Private Declare Function SendMessageA Lib "user32.dll" _
(ByVal hWnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Private Type GETTEXTEX
cb As Long
flags As Long
codepage As Long
End Type
Private Const WM_USER As Long = &H400&
Private Const EM_GETTEXTEX As Long = WM_USER + 94
Private Const GT_DEFAULT As Long = 0
Private Const CODEPAGE_UNICODE As Long = 1200 Listing 6 Der WinAPI-Funktion SendMessageA muss als letztes Argument ein Leerstring in genau der Länge übergeben werden, wie der Inhalt der auszulesenden RTB lang ist. Da ein einzugebendes chinesisches Schriftzeichen jedoch immer noch mit einem Return abgeschlossen werden muss, ist es zwingend notwendig, den String um ein Leerzeichen länger zu machen, als er tatsächlich ist. Um die Länge des Inhaltes einer RichTextBox auszulesen, gibt es zweierlei Möglichkeiten, entweder mit Len(), oder mittels einer weiteren WinAPI-Funktion. Warum API, wenn's auch ohne geht ..., darum hier zunächst erst einmal ohne die API-Funktion GetWindowTextLengthA. Nennen wir unsere neue Funktion UC_ermitteln. Übergeben wird wieder die Adresse einer RichTextBox, sowie die Länge des auszulesenden Inhaltes. Die funktionsaufrufende Codezeile, mit der der Unicode des in einer RTB stehenden Schriftzeichens in die Titelleiste des Forms geschrieben wird, würde dann z.B. so aussehen: Me.Caption = UC_ermitteln(RichTextBox1, Len(RichTextBox1.Text)) Listing 7 Es ist dabei unerheblich, dass Visual Basic das in der RTB dargestellte Schriftzeichen gar nicht "lesen" kann, es wird intern als "?" dargestellt, pro Schriftzeichen ein "?", die Textlänge wird mit Len() also auf jeden Fall korrekt angegeben. Private Function UC_ermitteln(c As RichTextBox, Laenge As Long) As Long
Dim DataG As GETTEXTEX
Dim CH As String
Laenge = Laenge + 1
CH = String$(Laenge, 0)
DataG.cb = Laenge * 2
DataG.flags = GT_DEFAULT
DataG.codepage = CODEPAGE_UNICODE
Laenge = SendMessageA(c.hWnd, EM_GETTEXTEX, VarPtr(DataG), StrPtr(CH))
UC_ermitteln = AscW(Left$(CH, Laenge))
End Function Listing 8 Um die Länge des Strings hingegen per API auszulesen, muss die API Private Declare Function GetWindowTextLengthA Lib "user32.dll" _
(ByVal hWnd As Long) As Long Listing 9 eingebunden werden. Der Funktion UC_ermitteln braucht nun nicht die Stringlänge übergeben werden, sondern das wird innerhalb der Funktion selbst gemacht: Dim Laenge As Long
Laenge = GetWindowTextLengthA(c.hWnd) + 1 Listing 10 AscW gibt nur Integerwerte zurück, kann also keine Werte ausgeben, die größer als 32767 sind - alles was größer ist, wird zur negativen Integer-Zahl. Da Unicode jedoch Long-Werte sind, kann solch eine Überschreitung des Wertebereiches häufig vorkommen. Folglich bedarf es einer weiteren Funktion, diesen Integer-Wert in einen korrekten Long-Wert umzuwandeln. Private Function Int_zu_Long(tmpUC As Long) As Long
If tmpUC < 0 Then
tmpUC = tmpUC * (-1)
tmpUC = 32769 - tmpUC + 32767
End If
Int_zu_Long = tmpUC
End Function Listing 11 Bevor die Funktion UC_ermitteln einen Wert zurückgibt, kann dieser nun zunächst an diese Umwandlungsfunktion übergeben werden, so dass wir sicher sein können, wirklich einen Long-Wert zurückzubekommen. UC_ermitteln = Int_zu_Long(AscW(Left$(CH, Laenge))) Listing 12 Da man selten nur ein einziges Schriftzeichen in einer RTB verarbeiten möchte, kann man die Prozeduren und Funktionen so modifizieren, dass man ihnen ein Array übergeben kann, bzw. sie ein Array zurückgeben. Public Sub CH_schreiben(c As RichTextBox, ByRef UCode() As Long, Optional ZeichenNr As Integer = 1)
Dim Data As SETTEXTEX
Data.flags = ST_DEFAULT
Data.codepage = CODEPAGE_UNICODE
Call SendMessage(c.hWnd, EM_SETTEXTEX, VarPtr(Data), UCode(ZeichenNr - 1))
End Sub Listing 13 So, wie diese Funktion nun ist, kann man z.B. aus einem längeren chinesischen Text ein einzelnes Schriftzeichen in eine andere RTB übertragen lassen. Man kann sie aber noch beliebig erweitern, dass nicht nur einzelne Schriftzeichen, sondern z.B. auch ganze Abschnitte verarbeitet werden... Ebenso wäre sinnvoll, dass die Funktion UC_ermitteln sämtliche Unicode-Werte eines Strings in einer RTB auf einmal zurückgeben könnte. Public Function Unicode_ermitteln(c As RichTextBox) As Long() Listing 14 Der erste Teil der Funktion bleibt unverändert. Unmittelbar hinter den API-Aufruf, der ja den kompletten String in die Variable CH schreibt, kann man den String in einzelne Schriftzeichen auf ein Array aufteilen und das komplette Array dann zurückgeben lassen. Auch hier ist unerheblich, dass Visual Basic z.B. im Debug-Modus statt eines Schriftzeichens nur ein Fragezeichen anzeigt - die Übermittlung funktioniert trotzdem korrekt. Dim s() As Long, i As Integer, ss As String
ReDim s(0 To Len(CH) - 1)
For i = 0 To UBound(s)
ss = Mid$(CH, i + 1, 1)
s(i) = AscW(ss)
s(i) = Int_zu_Long(s(i))
Next i
Unicode_ermitteln = s Listing 15 Oder je nach Geschmack in Kurzfassung - mit identischem Ergebnis: For i = 0 To UBound(s)
s(i) = Int_zu_Long(AscW(Mid$(CH, i + 1, 1)))
Next i Listing 16 Der Inhalt des zurückgegebenen Arrays kann z.B. in einer Liste ausgegeben werden. Der Aufruf und das übertragen in eine Listbox würden so aussehen: Private Sub Command1_Click()
Dim x() As Long, i As Integer
List1.Clear
x = Unicode_ermitteln(RichTextBox3)
For i = 0 To UBound(x)
If x(i) > 0 Then List1.AddItem (CStr(x(i)))
Next i
End Sub Listing 17 Wie oben erwähnt, gibt die VB-eigene Funktion Len() die korrekte Anzahl der in einer RTB geschriebenen chinesischen Schriftzeichen zurück. Hingegen liefern RichTextBox-eigene Funktionen wie z.B. ".SelStart" nicht immer das gewünschte Ergebnis, da die chinesischen Schriftzeichen intern von der RTB auf Grund ihrer zwei Bytes als zwei Zeichen gezählt werden. Öffnen wir ein neues Projekt, fügen ihm eine RichTextBox hinzu und ändern den Namen in RTB, ausserdem wird noch eine Textbox benötigt, bei der die Eigenschaft "MultiLine" auf True gesetzt ist, sowie eine Schaltfläche. Im Quellcode sollte stehen: Private Sub Form_Load()
RTB.Text = "Dieser Text hat 26 Zeichen"
End Sub
Private Sub Command1_Click()
Text1.Text = "Len() = " & Len(RTB.Text) & vbNewLine & _
vbNewLine & _
"Länge der Selektion = " & RTB.SelLength & vbNewLine & _
"Start der Selektion hinter dem Zeichen Nr." & RTB.SelStart & vbNewLine & _
"Ende der Selektion hinter dem Zeichen Nr." & RTB.SelStart + RTB.SelLength
End Sub Listing 18 Wenn man nun einige Buchstaben in der RTB auswählt, erfährt man, hinter welchem Zeichen jeweils der Start- und der Endpunkt der Auswahl ist, sowie die Menge an ausgewählten Zeichen. Gibt man nun einige chinesische Schriftzeichen in die RTB ein und wählt z.B. das allererste Schriftzeichen, so wird der Startpunkt der Auswahl wie erwartet mit 0 angegeben, der Endpunkt jedoch mit 2. Grundsätzlich gilt also bei Verarbeitung chinesischer Texte in einer RTB, dass man solche Werte zunächst einmal halbieren muss, um das gewünschte Ergebnis zu erhalten. Wie man sehen kann, arbeitet das RichTextBox-Steuerelement also tatsächlich recht gut mit chinesischen Texten zusammen. Wenn man bedenkt, dass man andernfalls vollkommen auf die Integration chinesischer Schriftzeichen in ein Visual Basic-Projekt verzichten müsste, ist das eine durchaus brauchbare Alternative. Beispielprojekt 2: CH RTF |