Technischer Artikel

Excel-Dokumenteigenschaften in Delphi mit HotXLS setzen

Eine Tabellenkalkulation trägt zwei Identitätsebenen. Da ist das Zellraster, und da sind die Dokument-Metadaten, die nebenher mitreisen: Titel, Autor, Firma, Schlüsselwörter, die Zeitstempel. Excel zeigt diese zweite Ebene nie im Raster, und doch ist sie die Ebene, die die Windows-Suche indiziert, die SharePoint liest, um ein Dokument zu betiteln, und nach der ein Records-Management-System ablegt. Wenn eine generierte Arbeitsmappe Autor und Titel von der Vorlage erbt, aus der sie gebaut wurde, verzeichnet jedes nachgelagerte System den Vorlagen-Designer als Autor von viertausend Kundenabrechnungen. Die Metadaten stimmen nirgends und werden überall herangezogen

HotXLS stellt diese Ebene auf beiden Engines als gewöhnliche Eigenschaften auf Arbeitsmappenebene bereit: der BIFF-Fassade für .xls und der OOXML-Fassade für .xlsx. Sie lesen ein Feld nach dem Öffnen einer Datei und schreiben ein Feld vor dem Speichern. Die Bibliothek entscheidet, in welchem physischen Container der Wert landet. Was es sich zu verstehen lohnt, bevor Sie einen Generator schreiben, ist, welche Felder jedes Format tatsächlich unterstützt, wo diese Felder physisch liegen, und die eine Torregel, die bestimmt, ob eine .xlsx überhaupt irgendwelche Metadaten aufzeichnet

Zwei Formate, zwei Speichermodelle

Der Grund, warum eine Tabellenkalkulationsbibliothek zwei Metadaten-Implementierungen braucht, und der Grund, warum halbfertige Werkzeuge ein Format korrekt stempeln und das andere vergessen, ist, dass .xls und .xlsx ihre Eigenschaften an nicht verwandten Orten aufbewahren. Eine BIFF-Arbeitsmappe schreibt sie in Streams der OLE-Verbunddatei, vor allem in den Eigenschaftssatz SummaryInformation, der älter ist als Excel selbst, neben dem im Stream liegenden WRITEACCESS-Datensatz, der denjenigen benennt, der die Datei zuletzt gespeichert hat. Eine OOXML-Arbeitsmappe bewahrt sie als XML-Parts im Zip-Paket auf, nach Zweck getrennt: docProps/core.xml enthält die Dublin-Core-Felder (Titel, Ersteller, Thema, Schlüsselwörter, Daten) und docProps/app.xml die Felder auf Anwendungsebene wie Firma und erzeugende Anwendung, gemäß ECMA-376 Teil 1

HotXLS flacht beide Speichermodelle zu direkten Eigenschaften des Arbeitsmappenobjekts ab. Sie öffnen nie einen Eigenschaftssatz-Stream und bearbeiten nie von Hand einen XML-Part. Sie weisen der Arbeitsmappe Zeichenketten und Daten zu, und der richtige Container materialisiert sich für das Format, in dem Sie speichern

HotXLS-Delphi-Diagramm, das die BIFF-SummaryInformation-Speicherung mit den OOXML-docProps-Parts für Excel-Dokumenteigenschaften vergleicht
HotXLS flacht zwei nicht verwandte Speichermodelle zu einer Eigenschaftsoberfläche der Arbeitsmappe ab — die Engine wählt den physischen Container beim Speichern der Datei

Generierte Arbeitsmappen aus dem Geschäftsdatensatz stempeln

Auf der XLSX-Seite stellt TXLSXWorkbook Title, Subject, Author, Keywords, Description, Category, LastModifiedBy, Company, Application und AppVersion als Zeichenketten bereit, dazu Created und Modified als TDateTime-Werte, bei denen null „nicht gesetzt“ bedeutet. Die Regel, die das Vererbungsloch schließt, ist ein Satz: Weisen Sie jedes Feld bei jedem Lauf zu, und nehmen Sie die Werte aus dem Geschäftsdatensatz, statt darauf zu vertrauen, was die Vorlage zufällig mitbringt

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('statement-template.xlsx') <> 1 then
      raise Exception.Create('Template not available');

    // Jedes Feld überschreiben: alles, was unangetastet bleibt,
    // wird von demjenigen geerbt, der die Vorlage entworfen hat.
    Book.Title := 'Account Statement 2026-06 / ACME Corp';
    Book.Subject := 'Monthly account statement';
    Book.Author := 'Billing Service 4.2';
    Book.LastModifiedBy := 'Billing Service 4.2';
    Book.Company := 'Northwind Financial';
    Book.Category := 'Customer Delivery';
    Book.Keywords := 'statement;billing;2026-06;acct-10024';
    Book.Description := 'Generated document - manual edits are not retained';
    Book.Created := Now;
    Book.Modified := Now;

    Book.SaveAs('statement-10024.xlsx');
  finally
    Book.Free;
  end;
end;

Das Feld Keywords verdient mehr Überlegung, als es gewöhnlich bekommt. Suchinfrastruktur indiziert es wörtlich, Windows-Suche, SharePoint und die meisten DMS-Produkte gleichermaßen, sodass eine durch Semikolons getrennte Konvention mit Kontonummer und Zeitraum jede ausgelieferte Arbeitsmappe zu einem auffindbaren Datensatz ohne Datenbankzugriff macht. Dieselbe Reichweite ist der Haken. Eigenschaften reisen mit jeder Kopie der Datei, weit über die Zugriffskontrollen des Systems hinaus, das sie geschrieben hat, daher gehören personenbezogene Daten dort nicht hinein

Das Zeitstempelpaar trägt eine Semantik, die es sich lohnt, per Richtlinie festzulegen, statt sie der Gewohnheit zu überlassen. Created sollte den Moment markieren, in dem Ihre Pipeline das Dokument erzeugt hat, und dann eingefroren bleiben. Modified ist das Feld, das Excel aktualisiert, wann immer ein Empfänger die Datei speichert, sodass ein Auseinanderlaufen der beiden nach der Auslieferung ein positiver Beleg dafür ist, dass jemand die Arbeitsmappe nachträglich bearbeitet hat, was mehr als einen Streit darüber klärt, wessen Zahlen eine weitergeleitete Tabelle wirklich enthält. Eine Falle steckt im ungesetzten Zustand: Er ist der wörtliche Wert null, keine Exception und kein Null-Wert, sodass Audit-Code explizit auf null prüfen muss. Formatieren Sie ein ungesetztes TDateTime ohne diese Absicherung, füllen sich Ihre Logs mit einem selbstbewusst falschen Datum aus dem Dezember 1899

DocPropsTouched: die Arbeitsmappe, die ohne docProps ausgeliefert wird

Ein Nur-Lese-Flag, DocPropsTouched, steuert den XLSX-Eigenschaftsschreiber. Eine Arbeitsmappe, in der nie eine Eigenschaft zugewiesen wurde, erzeugt überhaupt keine docProps-Parts; HotXLS verzichtet darauf, ein leeres Metadaten-Skelett zu schreiben. Das Verhalten ist sauber, und es hat zwei Konsequenzen, um die herum man planen sollte

Eingangscode auf der konsumierenden Seite darf nicht annehmen, dass core.xml in jedem Paket existiert. Ein Werkzeug, das es zwingend voraussetzt, wird völlig gültige minimale Dateien zurückweisen. Und wenn Ihre Compliance-Haltung verlangt, dass jedes ausgehende Dokument mindestens eine Generator-Identität trägt, wird diese Forderung zu Code statt zu einer Eigenschaft des Formats: Weisen Sie Application und Author im Speicherpfad bedingungslos zu, denn eine unangetastete Arbeitsmappe ist nach der Spezifikation völlig legal, während sie Ihre Richtlinie stillschweigend verletzt

HotXLS-Delphi-Flussdiagramm, das zeigt, wie das Flag DocPropsTouched die docProps-Ausgabe in gespeicherten XLSX-Arbeitsmappen steuert
DocPropsTouched steuert den XLSX-docProps-Schreiber — weisen Sie Application und Author bedingungslos zu, wenn die Richtlinie eine Generator-Identität verlangt

Die Legacy-XLS-Oberfläche und die Comments-Falle

Die BIFF-Fassade trägt den älteren, kleineren Feldsatz: Title, Subject, Author, Keywords, Comments, Company und Manager, dazu LastSavedBy, ein Alias von UserName, der den WRITEACCESS-Datensatz schreibt, den Excel anzeigt, wenn ein anderer Benutzer die Datei gesperrt hat

var
  Legacy: IXLSWorkbook;     // referenzgezählte Schnittstelle: kein manuelles Free
begin
  Legacy := TXLSWorkbook.Create;
  if Legacy.Open('archive-1999.xls') <= 0 then
    raise Exception.Create('Cannot open archive file');

  Legacy.Title := 'FY1999 ledger (migrated copy)';
  Legacy.Author := 'Archive Migration Batch';
  Legacy.Company := 'Northwind Financial';
  Legacy.Comments := 'Migrated 2026-06-11; source retained in cold storage';
  Legacy.LastSavedBy := 'migration-svc';   // BIFF-WRITEACCESS-Datensatz

  Legacy.SaveAs('archive-1999-stamped.xls');
end;

Eine Namenskollision sorgt für wiederkehrende Verwirrung. Die Eigenschaft Comments auf Dokumentebene ist hier die Freitextbemerkung, die im Eigenschaftendialog der Datei erscheint. Sie hat nichts mit Zellkommentaren zu tun, die Objekte der Zeichnungsebene sind und über eine völlig separate API an Bereiche angehängt werden. Ein Code-Review, das „wir schreiben Comments bereits“ akzeptiert, ohne zu prüfen, welches gemeint ist, hat eine Behauptung über das falsche Feature akzeptiert, und das passiert häufiger, als der gemeinsame Name vermuten lässt. Die beiden teilen vier Buchstaben und nicht ein Byte Speicher

Metadaten beim Eingang lesen, und die Sondierungslücke

Das Lesen ist symmetrisch. Nach Open kommen dieselben Eigenschaften aus der Datei befüllt zurück, was ein Metadaten-Audit eingehender Arbeitsmappen zu einer kurzen Schleife macht

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) = 1 then
    begin
      Writeln(Format('%s | title="%s" author="%s" created=%s',
        [ExtractFileName(FileName), Book.Title, Book.Author,
         FormatDateTime('yyyy-mm-dd', Book.Created)]));
      if Book.Created = 0 then
        Writeln('  no creation date recorded');
    end;
  finally
    Book.Free;
  end;
end;

Planen Sie dabei um eine Einschränkung herum. Es gibt keine reine Eigenschaften-Sonde. GetSheetNames kann Blätter auflisten, ohne eine Arbeitsmappe zu laden, aber Title oder Author zu lesen bedeutet ein vollständiges Open, sodass eine Metadaten-Triage über ein großes Archiv bei jeder Datei die vollen Parsing-Kosten zahlt. Auf der BIFF-Seite können Sie diese Kosten für Nur-Lese-Audits senken, indem Sie _DisableGraphics vor dem Öffnen auf true setzen, was die Zeichnungsebene gänzlich überspringt. Das passt zu einer Schleife, die nur Eigenschaften und Zellstatistiken liest, und ist in dem Moment genau falsch, in dem dieselbe Instanz speichern könnte, denn der übersprungene Zeichnungsinhalt würde verworfen. Wenn die Blattstruktur allein die Menge vorfiltern kann, wobei Einzelblatt-Exporte das Offensichtliche sind, was man überspringt, reduzieren die günstigen Techniken in unserem Artikel über Blattauflistung und leichtgewichtige Inspektion, wie viele Dateien den teuren Durchlauf erreichen. Und bei Massen-Stempeljobs, bei denen Tausende Ausgaben geschrieben statt inspiziert werden, lassen sich die schreibseitigen Durchsatzmuster aus unserem Artikel über Streaming-Schreibvorgänge für Batch-Jobs unverändert übertragen, da die Eigenschaftszuweisung der Speicherzeit nichts Messbares hinzufügt

Formate überschreiten und das Leck eindämmen

Eigenschaften machen innerhalb einer einzelnen Fassade einen sauberen Roundtrip: Öffnen Sie eine .xlsx, bearbeiten Sie sie, speichern Sie sie, und der Satz kommt unversehrt zurück. Beim Formatwechsel bricht die Annahme der Parität, denn die Feldsätze von BIFF und OOXML decken sich nicht eins zu eins. BIFF hat Manager und keine Zeitstempel; OOXML hat Category, Description und das Paar Created/Modified. Ein Konverter, der blind kopiert, verliert alles, was das Zielformat nicht aufnehmen kann, also bilden Sie die Felder explizit ab und nehmen Sie die Abbildung in Ihre Konvertierungs-Checkliste auf, neben allem anderen, was die Reise nicht überlebt

HotXLS-Delphi-Feldabbildung, die zeigt, welche Excel-Dokumenteigenschaften eine formatübergreifende Konvertierung zwischen XLS und XLSX überstehen
Eine blinde formatübergreifende Kopie verwirft jedes Feld, das das Ziel nicht aufnehmen kann — bilden Sie die BIFF- und OOXML-Eigenschaftssätze in der Konvertierungs-Checkliste explizit ab

Das Leck, das die Vorlagenvererbung öffnet, verläuft in die andere Richtung: Informationen, die Sie nie hinausschicken wollten. Autorennamen, interne Projektbezeichnungen, die in Schlüsselwörtern geparkt sind, ein Entwurfstitel, den niemand freigegeben hat. Die Disziplin des Alles-Überschreibens aus dem Generator oben ist die ganze Verteidigung, und es lohnt sich, sie so zu prüfen, wie ein Außenstehender es täte: indem Sie den Eigenschaftendialog öffnen, den jeder Kunde erreichen kann, oder indem Sie die .xlsx entpacken und docProps/core.xml direkt aus dem Paket lesen. Was Sie dort sehen, ist genau das, was jeder nachgelagerte Indexer sieht

Diese nachgelagerte Sichtbarkeit ist auch der Grund, warum einige Felder mehr Sorgfalt verdienen als der Rest. Title, Author, Keywords (die als Tags erscheinen) und Comments oder Description tragen in SharePoint und der Windows-Suche den Großteil des Indexierungsgewichts. Ein Title, der pro Dokument wirklich eindeutig ist und Zeitraum und Konto trägt, leistet für die Auffindbarkeit mehr als jedes darübergelegte Ordnerbenennungsschema, und er kostet eine Zuweisung pro Speichervorgang

Dokumenteigenschaften sind der günstigste professionelle Schliff, den eine generierte Arbeitsmappe tragen kann, und der am häufigsten ausgelieferte Defekt, wenn niemand sie verantwortet. Beide hier beschriebenen Eigenschaftsoberflächen gehören zur HotXLS Delphi Component, die sie für XLS und XLSX nativ ohne Excel-Automatisierung schreibt