Tipps & Tricks: Word-VBA
Auf dieser Seite finden Sie 7 Tipps & Tricks für Word-VBA. Soweit keine Einschränkungen dazu genannt werden, gelten diese Tipps & Tricks für alle Versionen von Word.
Wenn in den folgenden Erläuterungen Texte wie MsgBox formatiert sind, handelt es sich um konkret einzugebende Inhalte wie VBA-Code oder die Eingabe von Werten. Menüs wie Datei Speichern sind wie hier zu sehen formatiert. Schaltflächen oder Registerkarten auf Dialogen werden wie Menüs behandelt.
Alle Tipps sind nach bestem Wissen geprüft, aber selbstverständlich ohne Gewähr. Sollten Sie doch einen Fehler darin entdecken, würden wir uns freuen, wenn Sie uns per E-Mail Bescheid sagen.
Seit dem Office-ServicePack2 verändert eine Formular-Datei beim Speichern ihr (per VBA mit Protect gesetztes) Passwort, so dass sie nach dem nächsten Öffnen nicht mehr den Passwort-Schutz aufheben kann. Folgendes Szenario erzeugt den Fehler:
Schritt 1: Erstellen Sie ein neues Dokument und führen Sie diesen Code aus:
Public Const p_cstrPW = "abc"
Sub MachZu()
ActiveDocument.Protect wdAllowOnlyFormFields, True, p_cstrPW
End Sub
Schritt 2: Jetzt speichern Sie das Dokument.
Schritt 3: Schließen Sie es und öffnen es wieder, jetzt ist das Passwort ungültig. Der Formularschutz für diese Datei lässt sich ab jetzt weder manuell noch mit diesem Code aufheben:
Sub MachAuf()
ActiveDocument.UnProtect p_cstrPW
End Sub
Ein manuell vergebenes Passwort funktioniert weiterhin, es liegt klar am Protect-Befehl. Ohne das ServicePack 2 funktioniert das Passwort, mit der gleichen Datei und ServicePack 2 nicht mehr!
Es gab inzwischen einen ersten Hotfix (KB 969961), aber der war noch schlimmer: der Passwort-Fehler ist beseitigt, aber dafür werden die Inhalte der ActiveDocument.Variables-Collection ohne Fehlermeldung gelöscht und durch "????????" ersetzt. Seit Anfang Juli 2009 existiert ein zweiter Hotfix (KB 970942), der endlich beide Probleme löst und Word 2007/SP2 damit wieder funktionsfähig macht.
Während es in sonstigem VBA-Code durchaus üblich ist, das Vorhandensein einer Datei mit
abzufragen, führt das in Callback-Prozeduren von Ribbons zu Laufzeitfehlern. Sie können stattdessen die Anzahl der (geöffneten) Dokumente mit folgendem Code prüfen:
Das ist robust und erzeugt keine Probleme.
Kontext-Register ausblenden ID 478
In geschützten Formularen von Word ist es ausgesprochen lästig, dass die so genannten contextualTabs (also die Kontext-abhängigen Register) wie Tabellentools oder Kopf- und Fußzeilentools sichtbar werden. Das geschieht, wenn das Formular in einer Tabelle organisiert ist, ist da aber völlig überflüssig, weil die Tabellenbearbeitung in den geschützten Bereichen ja gar nicht möglich ist.
Wenn Sie den XML-Code in der jeweiligen Dokumentvorlage wie folgt ändern, werden die Kontext-abhängigen Register für deren Dokumente immer automatisch ausgeblendet:
<tabSet idMso="TabSetHeaderAndFooterTools" visible="false" />
Diese Änderung wirkt sich ausdrücklich nur auf das jeweilige Dokument aus. Parallel geöffnete Word-Dateien auf Basis anderer Dokumentvorlagen zeigen bei Bedarf auch diese Kontext-abhängigen Register wie gewohnt an.
In Word2003 war es kein Problem, eine Option umzustellen, das ging rasend schnell. Der folgende Code ändert eine beliebige Option:
Dim datEnd As Date
Dim lngZahl As Long
datStart = Now()
For lngZahl = 1 To 100
datEnd = Now()
MsgBox lngZahl - 1 & " Durchläufe in " & Format(datEnd - datStart, "hh:nn:ss")
Das dauert in Word2003 für 100 Wiederholungen weniger als 1 Sekunde, in Word2007 jedoch volle 24 Sekunden! Natürlich werden Sie in Ihrem VBA-Code nicht 100 Mal eine einzige Option ändern, aber das gleiche Verhalten gilt auch für die Änderung mehrerer Optionen und kostet richtig viel Zeit. Vor allem müssen Sie es nach ihrem Code ja auch wieder zurückstellen!
Wenn Sie trotzdem noch solchen Code beschleunigen wollen, dann müssen Sie vorher immer den Wert auslesen und nur ändern, wenn es nötig ist:
Dim datEnd As Date
Dim lngZahl As Long
datStart = Now()
For lngZahl = 1 To 100
datEnd = Now()
MsgBox lngZahl - 1 & " Durchläufe in " & Format(datEnd - datStart, "hh:nn:ss")
In diesem Fall braucht es auch in Word2007 nur 1 Sekunde, was erklärbar ist, weil nach spätestens der ersten Änderung ja die Bedingung immer False ist. Aber auch für mehrere Optionen (bei denen jede einzelne so vorgeprüft wird!), ist eine deutliche Verbesserung festzustellen.
Auch das Range-Objekt ist in Word2007 deutlich langsamer geworden. Bisher war es selbstverständlich, dass nur mit Range-Objekten gearbeitet wurde, weil ein Select immer zu einer langsamen Bildschirmaktualisierung führt.
Inzwischen ist es umgekehrt, Sie sollten also Range-Objekte nicht in zeitkritischen Schleifen einsetzen. Das lässt sich anhand einer einfachen Prozedur nachmessen, die in der Datei eine Textmarke Test erwartet:
Dim datEnd As Date
Dim lngZahl As Long
datStart = Now()
For lngZahl = 1 To 1000
datEnd = Now()
MsgBox lngZahl - 1 & " Durchläufe in " & Format(datEnd - datStart, "hh:nn:ss")
Diese Prozedur ist in Word2003 in 1 Sekunde fertig. Hätten Sie dort mit der Select-Methode gearbeitet, wären Sie damals mit 11 Sekunden bestraft worden:
Dim datEnd As Date
Dim lngZahl As Long
datStart = Now()
For lngZahl = 1 To 1000
With Selection.Range.Font
datEnd = Now()
MsgBox lngZahl - 1 & " Durchläufe in " & Format(datEnd - datStart, "hh:nn:ss")
In Word2007 ist es umgekehrt: die Prozedur AendereFontRange benötigt 10 Sekunden, aber AendereFontSelect "nur" 7 Sekunden.
Mit einem Griff in die Trickkiste weisen Sie nicht der Selection direkt die neuen Eigenschaften zu, sondern machen es über den (unsichtbaren) FormatZeichen-Dialog:
Dim datEnd As Date
Dim lngZahl As Long
datStart = Now()
For lngZahl = 1 To 1000
With Dialogs(wdDialogFormatFont)
.Execute
datEnd = Now()
MsgBox lngZahl - 1 & " Durchläufe in " & Format(datEnd - datStart, "hh:nn:ss")
Und siehe da: die Prozedur AendereFontSelectDialog benötigt "nur noch" 5 Sekunden, das ist doppelt so schnell wie im ersten Versuch. Und nur noch fünfmal so langsam wie mit dem alten Word...
Ribbons unfreiwillig löschen ID 482
Es gibt einen gefährlichen Fehler in Word 2007, durch den unfreiwillig die Ribbons einer Datei gelöscht werden! Wer aus einer alten (*.dot- oder *.doc-)Datei per Zwischenablage Text mit einem Positionsrahmen in eine neue (*.dot?- oder *.doc?-)Datei kopiert, verliert in diesem Moment ohne Warnung alle XML-Daten für das Ribbon.
Alte Textfelder (statt Positionsrahmen) sind offenbar ungefährlich, aber das Kopieren neu erzeugter Textfelder aus Word2007-Dateien zerstört das Ribbon ebenso. Neue Textfelder lassen sich in der Word2007-Datei ohne Probleme anlegen.
Das Konvertieren der alten Datei in das neue Word2007-Format vor dem Kopieren hilft nicht gegen den Fehler.
Statuszeile ist extrem langsam ID 479 Top-Tipp!
Während in bisherigen Word-Versionen selbstverständlich längere Aktionen (Auslesen vieler Datensätze, Bearbeiten umfangreicher Textmarken, o.Ä.) mit einer Fortschritts-Anzeige in der Statuszeile begleitet wurde, muss für Word2007 (und Access2007, vermutlich auch für Excel2007) dringend von der ausführlichen Nutzung der Statuszeile abgeraten werden!
Der folgende Code macht nichts, ausser den Wert von lngZahl in der Statuszeile anzuzeigen:
Dim datEnd As Date
Dim lngZahl As Long
datStart = Now()
For lngZahl = 1 To 10000
datEnd = Now()
MsgBox lngZahl - 1 & " Durchläufe in " & Format(datEnd - datStart, "hh:nn:ss")
In Word2003 können Sie kaum etwas lesen, so schnell läuft diese Prozedur: sie braucht weniger als 1 Sekunde. Lassen Sie die gleiche Prozedur jedoch in Word2007 laufen, dauert es geschlagene 48 Sekunden, bis die Meldung erscheint!
Als "Lösung" bleibt eigentlich nur, die Statuszeile in Schleifen auf keinen Fall zu benutzen, Machen Sie lieber einen grafischen Fortschrittsbalken in einem Formular, das ist tatsächlich schneller!