Nehmen Sie einen Job, der fast nichts tut: eine Monatsarbeitsmappe öffnen, das heutige Datum in eine Zelle schreiben, sie zurückspeichern. Lassen Sie das oft genug über einen Dienst laufen, und trotzdem trifft eine Beschwerde ein. Die Makros sind weg, oder die verknüpften Wechselkurse zeigen jetzt #REF!, und das Betriebsteam ist überzeugt, Ihr Code habe sie gelöscht. Er hat gar nichts gelöscht. Was gewöhnlich passiert ist: Eine makrofähige Arbeitsmappe ging unter einem schlichten .xlsx-Namen hinaus, und Excel hat die Inhaltstyp-Regeln von ECMA-376 befolgt: Ein Paket, dessen Inhaltstyp kein VBA deklariert, darf kein VBA-Projekt laden, ganz gleich, ob die Bytes direkt dort liegen. Die Datei ist nicht kaputtgegangen. Sie wurde in einen Zustand umbenannt, in dem Excel einen Teil von ihr ignorieren muss
Makros und externe Arbeitsmappen-Verknüpfungen sind die zwei Dinge, die Automatisierung am zuverlässigsten verliert, und zwar aus demselben zugrunde liegenden Grund. Beide leben außerhalb des Zellrasters, das bearbeitender Code tatsächlich anfasst, sodass Code, der in Zeilen und Spalten denkt, sie fallen lässt, ohne je ein Löschen auszuführen. HotXLS ist eine native Delphi- und C++Builder-Bibliothek, die XLS und XLSX ohne installiertes Excel liest und schreibt, und sie behandelt beide Bestände als Nutzlasten, die sie bewusst trägt, statt als Daten, die sie zufällig mitkopiert. Es folgt, was jeder von beiden von Ihrem Speicherpfad braucht und wo die Garantien enden
Warum sich diese beiden Bestände beim Neuschreiben unterschiedlich verhalten
Ein VBA-Projekt ist eine einzige undurchsichtige Binärdatei. In einem OOXML-Paket ist es die Datei vbaProject.bin; in einer alten BIFF-Datei ist es ein OLE-Speicher. Es gibt genau zwei Wege, es zu verlieren: Der Writer kopiert es nie in die Ausgabe, oder die Ausgabe bekommt einen Dateityp, der es verbietet. Beide Fehlschläge sind vollständig und lautlos. Das Projekt ist vorhanden oder es ist es nicht
Eine externe Verknüpfung ist überhaupt kein Blob. Sie ist ein kleiner Beziehungsgraph: ein Zielpfad oder eine URL, die auf eine andere Arbeitsmappe zeigt, die Liste der Blattnamen, die dieses Ziel bereitstellt, und ein optionaler Zwischenspeicher der zuletzt in diesen Blättern gesehenen Werte, damit Excel etwas anzeigen kann, wenn das Ziel offline ist. Diese drei Teile haben beim Neuschreiben unterschiedliche Lebensdauern, und eine Bibliothek kann einige getreu bewahren, während sie andere still fallen lässt. Gerade diese Asymmetrie lohnt es genau zu benennen, denn nichts im zellbearbeitenden Code bringt sie an die Oberfläche
Ein VBA-Projekt durch ein XLSX-Neuschreiben tragen
Auf der XLSX-Seite bewahrt TXLSXWorkbook die Makro-Nutzlast wortgetreu. Die Eigenschaft VbaProject hält die rohen vbaProject.bin-Bytes in einem AnsiString, und ein leerer String ist die Art, wie das Modell sagt, dass es keine Makros gibt. Darum herum liegen drei Operationen: HasVbaProject beantwortet, ob ein Projekt vorhanden ist, ClearVbaProject entfernt es absichtlich, und LoadVbaProjectFromFile spielt eines ein, das aus einer Vorlage extrahiert wurde. Dieser letzte Aufruf ist mehr wert, als er aussieht. Er lässt erzeugte Arbeitsmappen ein standardisiertes Makroprojekt aufnehmen, ohne eine vollständige Vorlagendatei durch die Pipeline zu schleppen
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Data');
Sheet.Cells[1, 1].Value := 'Refreshed ' + DateTimeToStr(Now);
Book.LoadVbaProjectFromFile('macros\vbaProject.bin');
if not Book.HasVbaProject then
raise Exception.Create('VBA payload failed to load');
// Die Endung .xlsm ist nicht kosmetisch: Sie wählt den
// makrofähigen Inhaltstyp innerhalb des Pakets aus.
Book.SaveAs('monthly-report.xlsm');
finally
Book.Free;
end;
end;
Die Speicherzeile ist der Punkt, an dem sich das ganze Problem entscheidet. Eine Arbeitsmappe, die ein VBA-Projekt hält, muss mit makrofähiger Semantik geschrieben werden, und HotXLS wendet sie an, wenn der Zielname auf .xlsm endet. Geben Sie ihr stattdessen .xlsx, verweigert Excel die Makros, obwohl die Bytes physisch im Paket liegen und sich einwandfrei deserialisieren ließen. Die Endung ist keine Zierde; sie wählt den Inhaltstyp aus, der Excel sagt, dass ein VBA-Projekt existieren darf. Meistens müssen Sie die Nutzlast nur durchreichen. Wenn Sie hineinlesen müssen, etwa um Modulnamen für einen Prüfbericht aufzulisten, stellt ParsedVBAProject ein geparstes Modulmodell bereit, während VbaProject die ursprünglichen unberührten Bytes bleibt
Makros aus alten XLS-Arbeitsmappen wiederverwenden
Die BIFF-Fassade spiegelt diesen Werkzeugsatz mit einem zusätzlichen Schritt. HasVBAProject prüft eine geladene Datei, SaveVBAProjectToFile schreibt den Projektspeicher auf die Platte, und LoadVBAProjectFromFile liest ihn in eine andere Arbeitsmappe zurück. Der Umweg über eine Datei macht eine verbreitete Modernisierungsaufgabe unkompliziert: die Makros aus einem Modell der 2003er-Ära herausheben und sie in frisch erzeugte XLS-Ausgaben pflanzen, ohne dass zur Laufzeit eine Originalvorlage nötig wäre
var
Src, Dst: IXLSWorkbook; // Interface-Referenzen: kein manuelles Free
begin
Src := TXLSWorkbook.Create;
if Src.Open('legacy-model.xls') <= 0 then
raise Exception.Create('Cannot open legacy model');
if Src.HasVBAProject then
Src.SaveVBAProjectToFile('extracted-vba.bin');
Dst := TXLSWorkbook.Create;
Dst.Sheets.Add.Name := 'Report2026';
Dst.LoadVBAProjectFromFile('extracted-vba.bin');
Dst.SaveAs('report-with-macros.xls');
end;
Das Speichermodell ist hier die Falle, und es läuft der XLSX-Klasse entgegen. TXLSWorkbook wird über die referenzgezählte Schnittstelle IXLSWorkbook gehalten, Sie geben es also nie von Hand frei; das XLSX-TXLSXWorkbook ist ein gewöhnliches Objekt, das Sie in try..finally einpacken und freigeben müssen. Vermischen Sie die beiden Konventionen in einer Unit, folgen Abstürze durch doppeltes Freigeben. Eine weitere Grenze verdient Beachtung: Halten Sie Extraktion und Einspielung innerhalb eines Dateiformats. Der BIFF-Projektspeicher und das OOXML-vbaProject.bin sind Vettern, nicht derselbe Container, und eine Pipeline, die Makros in beiden Formaten ausgeben muss, sollte je Format eine eigene Makrovorlage vorhalten
Externe Verknüpfungen: Die Karte überlebt, die zwischengespeicherten Werte nicht
Für XLSX-Arbeitsmappen legt HotXLS externe Verknüpfungen über die Sammlung ExternalLinks offen. Jedes TXLSXExternalLink trägt ein Target, den Pfad oder die URL der entfernten Arbeitsmappe, sowie eine Liste SheetNames, die die referenzierten Blätter benennt. Beides übersteht einen Zyklus aus Öffnen und Speichern unversehrt, und Sie können eine Verknüpfung auch von Grund auf bauen:
var
Link: TXLSXExternalLink;
begin
Link := Book.ExternalLinks.Add('\\fileserver\finance\fx-rates-2026.xlsx');
Link.SheetNames.Add('FX');
if Book.ExternalLinks.Count > 0 then
Writeln(Format('%d external link(s): delivery requires reachable targets',
[Book.ExternalLinks.Count]));
end;
Die Grenze liegt eine Ebene tiefer als die Zielliste. HotXLS führt die Verknüpfungskarte, also das Ziel und die Blattnamen, durch den Round-Trip, parst und schreibt aber die zwischengespeicherten Zellwerte nicht neu, die OOXML im sheetDataSet-Element der Verknüpfung hält. Dieser Zwischenspeicher ist es, der Excel eine zuletzt bekannte Zahl anzeigen lässt, wenn die Quelldatei offline ist, und eine erzeugte Arbeitsmappe wird ohne ihn ausgeliefert. Die Folge trifft den Empfänger, nicht Sie. Öffnet man eine solche Datei dort, wo das Ziel unerreichbar ist, ein Laptop außerhalb des VPN oder eine umbenannte Freigabe, lösen sich die von der Verknüpfung abhängigen Formeln zu #REF! auf oder bleiben hinter einer Aktualisierungsabfrage hängen. Daraus fallen zwei Regeln. Versprechen Sie nicht, dass eine erzeugte Arbeitsmappe ihre extern verknüpften Werte offline anzeigt. Und lesen Sie ein ExternalLinks.Count ungleich null als Auslieferungsvoraussetzung statt als Funktion: Jedes Ziel muss von dort erreichbar sein, wo die Datei tatsächlich geöffnet wird
Was der XLS-Reader Byte für Byte bewahrt
Für Strukturen, die er nicht modelliert, hat die BIFF-Seite eine andere Antwort: sie genau so zu lassen, wie sie vorgefunden wurden. Pivot-Caches und Pivot-Ansichten (die Datensatzfamilie SX*), QueryTable-Definitionen, externe Datenverbindungen, benutzerdefinierte Ansichten, Kopfzeilenbilder und Design-Datensätze durchlaufen einen Zyklus aus Öffnen und Speichern allesamt als rohe Datensatzblöcke, ungeparst und unverändert. Externe Referenzen selbst überstehen den Round-Trip über die zugrunde liegenden Datensätze EXTERNSHEET und SupBook. Auf der XLS-Seite gibt es für sie keine typisierte Erzeugungs-API, doch eine vorhandene Verknüpfung übersteht das Bearbeiten unberührt
Die Bewahrung Byte für Byte ist eine echte Garantie mit einer scharfen Kante. Weil nichts eine bewahrte Struktur liest, können Ihre Bearbeitungen sie nicht beschädigen. Aus demselben Grund aktualisiert sie auch nichts. Fügen Sie Zeilen durch einen Bereich ein, auf den ein bewahrter Pivot-Cache oder eine Abfragetabelle zeigt, und die Struktur behält ihre ursprünglichen Koordinaten, während die Daten darunter verrutschen. Die Datei ist weiterhin gültiges XML oder BIFF; die Bedeutung ist still aus der Ausrichtung gedriftet, und kein Fehler meldet es Ihnen. Das vertretbare Layout besteht darin, erzeugte Bearbeitungen auf Blättern zu halten, die keine bewahrten Strukturen tragen, dieselbe Disziplin, die gesperrte und druckfertig eingerichtete Blätter in unserem Artikel zu Arbeitsblattschutz und Seiteneinrichtung schützt
Die Datei prüfen, die Sie tatsächlich geschrieben haben
Beide Fehlermodi sind zur Schreibzeit lautlos, die Zusicherung, auf die es ankommt, entsteht also durch erneutes Öffnen der Ausgabe statt durch Vertrauen in den Code, der sie erzeugt hat. Drei Prüfungen decken nahezu alles ab. Öffnen Sie die Datei erneut und bestätigen Sie, dass HasVbaProject weiterhin wahr zurückgibt, wann immer Makros erwartet wurden, was eine verlorene Nutzlast und eine falsche Endung in einem einzigen Test abfängt. Lesen Sie ExternalLinks.Count und vergleichen Sie es mit der Zahl von vor dem Neuschreiben. Öffnen Sie die Datei dann einmal in Excel mit deaktivierten Makros, denn Excels Inhaltstypprüfung ist strenger als die jeder Bibliothek, und Excel ist das Programm, an dem Ihre Kunden die Datei messen werden
Nichts davon verlangt beim Einlesen ein vollständiges Parsen. Wenn Arbeitsmappen in Massen eintreffen und Sie nur sortieren müssen, welche davon regulierte Inhalte tragen, lässt Sie das leichtgewichtige Sondieren in unserem Artikel zur Blattauflistung und leichtgewichtigen Arbeitsmappenprüfung makrotragende und verknüpfte Dateien in eine strengere Pipeline lenken, bevor das erste Neuschreiben überhaupt läuft
Ein paar Fragen kommen oft genug auf, um sie direkt zu beantworten. HotXLS führt die Makros, die es bewahrt, nie aus: Es gibt keine VBA-Laufzeit in der Bibliothek, nur die Maschinerie, um das Projekt als Daten zu speichern, zu kopieren, zu extrahieren und einzuspielen. Auf einem Server ist das eine Sicherheitseigenschaft, die man nennen sollte, denn ein feindliches Makro, das die Pipeline durchläuft, bleibt untätig, bis ein Desktop-Excel die Datei öffnet und ein Anwender die Inhalte aktiviert. Eine .xlsm in eine .xlsx umzuwandeln und dabei die Makros zu behalten ist nicht möglich, und das ist die Regel des Formats, keine Beschränkung der Bibliothek: Der Inhaltstyp .xlsx deklariert eine makrofreie Arbeitsmappe, die einzigen ehrlichen Ausgänge sind also, bei .xlsm zu bleiben oder ClearVbaProject aufzurufen und eine Datei auszuliefern, die wirklich keine hat. Das stille Umbenennen ist die eine Wahl, die niemanden zufriedenstellt. Und wenn verknüpfte Zellen nach einem Neuschreiben #REF! zeigen, ist die Ursache der oben besprochene fehlende Wertezwischenspeicher: Die neue Datei trägt das Ziel, aber nicht die zwischengespeicherten Zahlen, Excel muss die Quelle also beim Öffnen auflösen, und ein unerreichbarer oder umgebungsrelativer Pfad vereitelt das. Stellen Sie entweder sicher, dass das Ziel erreichbar ist, oder schreiben Sie berechnete Werte vor der Auslieferung in die Zellen und lassen Sie die Abhängigkeit ganz fallen
Fremde Arbeitsmappen zu bearbeiten heißt größtenteils, Dinge zu bewahren, die Sie nicht geschrieben haben und nicht vollständig verstehen. Die hier beschriebenen Round-Trip-Einrichtungen für VBA und externe Verknüpfungen werden mit der HotXLS Delphi Component für Delphi und C++Builder ausgeliefert, zusammen mit den Prüfeigenschaften, mit denen Sie regulierte Inhalte in dem Moment erkennen, in dem eine Datei eintrifft