Articolo tecnico

Proprietà dei documenti Excel in Delphi con HotXLS

Un foglio di calcolo porta due strati di identità. C'è la griglia di celle, e ci sono i metadati del documento che le viaggiano accanto: titolo, autore, azienda, parole chiave, le marche temporali. Excel non mostra mai quel secondo strato nella griglia, eppure è lo strato che Windows Search indicizza, quello che SharePoint legge per intitolare un documento e quello con cui un sistema di gestione documentale archivia. Quando una cartella di lavoro generata eredita Autore e Titolo dal modello da cui è stata costruita, ogni sistema a valle registra il progettista del modello come autore di quattromila estratti conto. I metadati non sono corretti da nessuna parte e vengono consultati ovunque

HotXLS porta in superficie questo strato come normali proprietà a livello di cartella di lavoro su entrambi i suoi motori: la facciata BIFF per .xls e la facciata OOXML per .xlsx. Leggete un campo dopo aver aperto un file e ne scrivete uno prima di salvarlo. È la libreria a decidere in quale contenitore fisico finisca il valore. Ciò che vale la pena capire prima di scrivere un generatore è quali campi ciascun formato supporti davvero, dove quei campi risiedano fisicamente e qual è l'unica regola di sbarramento che governa se un .xlsx registri o meno metadati

Due formati, due modelli di memorizzazione

La ragione per cui una libreria per fogli di calcolo ha bisogno di due implementazioni dei metadati, e la ragione per cui gli strumenti fatti a metà timbrano correttamente un formato e dimenticano l'altro, è che .xls e .xlsx tengono le proprie proprietà in luoghi non imparentati. Una cartella BIFF le scrive dentro flussi OLE di file composto, principalmente l'insieme di proprietà SummaryInformation che precede Excel stesso, accanto al record WRITEACCESS nel flusso che nomina chi ha salvato il file per ultimo. Una cartella OOXML le tiene come parti XML dentro il pacchetto zip, divise per scopo: docProps/core.xml contiene i campi Dublin Core (titolo, creatore, oggetto, parole chiave, date) e docProps/app.xml contiene i campi a livello di applicazione come azienda e applicazione generatrice, secondo ECMA-376 Parte 1

HotXLS appiattisce entrambi quei modelli di memorizzazione in proprietà dirette dell'oggetto cartella di lavoro. Non aprite mai un flusso di insieme di proprietà né modificate a mano una parte XML. Assegnate stringhe e date alla cartella di lavoro, e il contenitore corretto si materializza per il formato con cui salvate

Diagramma HotXLS in Delphi che confronta la memorizzazione BIFF SummaryInformation con le parti docProps di OOXML per le proprietà dei documenti Excel
HotXLS appiattisce due modelli di memorizzazione non imparentati in una sola superficie di proprietà della cartella di lavoro — è il motore a scegliere il contenitore fisico quando il file viene salvato

Timbrare le cartelle di lavoro generate dal record di business

Sul lato XLSX, TXLSXWorkbook espone Title, Subject, Author, Keywords, Description, Category, LastModifiedBy, Company, Application e AppVersion come stringhe, più Created e Modified come valori TDateTime dove lo zero significa non impostato. La regola che chiude la falla dell'ereditarietà sta in una frase: assegnate ogni campo a ogni esecuzione, prendendo i valori dal record di business invece di fidarvi di quello che il modello si portava dietro

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

    // Sovrascrivete ogni campo: tutto ciò che lasciate intatto viene
    // ereditato da chi ha progettato il modello.
    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;

Il campo Keywords ripaga più attenzione di quanta di solito ne riceva. L'infrastruttura di ricerca lo indicizza alla lettera, tanto Windows Search quanto SharePoint e la maggior parte dei prodotti DMS, quindi una convenzione separata da punti e virgola che porti il numero di conto e il periodo trasforma ogni cartella consegnata in un documento rintracciabile senza un giro sul database. La stessa portata è l'insidia. Le proprietà viaggiano con ogni copia del file, ben oltre i controlli di accesso del sistema che le ha scritte, quindi i dati personali non ci vanno

La coppia di marche temporali porta una semantica che vale la pena fissare in una politica anziché lasciare all'abitudine. Created dovrebbe segnare il momento in cui la vostra pipeline ha generato il documento e poi restare congelato. Modified è il campo che Excel aggiorna ogni volta che un destinatario salva il file, quindi una divergenza fra i due dopo la consegna è prova positiva che qualcuno ha modificato la cartella a valle, il che risolve più di una disputa su quali numeri contenga davvero un foglio inoltrato. Una trappola si nasconde nello stato non impostato: è il valore letterale zero, non un'eccezione e non un null, quindi il codice di verifica deve controllare esplicitamente lo zero. Formattate un TDateTime non impostato senza quella protezione e i vostri log si riempiono di una data di dicembre 1899 sbagliata con grande sicurezza

DocPropsTouched: la cartella di lavoro che parte senza docProps

Un flag in sola lettura, DocPropsTouched, sbarra il writer delle proprietà XLSX. Una cartella di lavoro in cui non è mai stata assegnata alcuna proprietà non produce alcuna parte docProps; HotXLS si rifiuta di scrivere uno scheletro di metadati vuoto. Il comportamento è pulito, e ha due conseguenze attorno a cui vale la pena progettare

Il codice di ricezione dal lato consumatore non deve dare per scontato che core.xml esista in ogni pacchetto. Uno strumento che lo richieda in modo rigido rifiuterà file minimali perfettamente validi. E se la vostra postura di conformità impone che ogni documento in uscita porti almeno l'identità del generatore, quell'imposizione diventa codice anziché una proprietà del formato: assegnate Application e Author senza condizioni nel percorso di salvataggio, dato che una cartella intatta è del tutto lecita secondo le specifiche pur violando in silenzio la vostra politica

Diagramma di flusso HotXLS in Delphi che mostra il flag DocPropsTouched mentre sbarra l'output docProps nelle cartelle XLSX salvate
DocPropsTouched sbarra il writer docProps di XLSX — assegnate Application e Author senza condizioni quando la politica impone l'identità del generatore

La superficie XLS legacy e la trappola di Comments

La facciata BIFF porta l'insieme di campi più vecchio e ridotto: Title, Subject, Author, Keywords, Comments, Company e Manager, più LastSavedBy, alias di UserName, che scrive il record WRITEACCESS che Excel mostra quando un altro utente tiene bloccato il file

var
  Legacy: IXLSWorkbook;     // interfaccia a conteggio di riferimenti: nessuna Free manuale
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';   // record WRITEACCESS di BIFF

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

Una collisione di nomi causa confusione ricorrente. La proprietà Comments a livello di documento qui è l'annotazione in testo libero mostrata nella finestra delle proprietà del file. Non ha nulla a che vedere con i commenti di cella, che sono oggetti del livello di disegno agganciati agli intervalli attraverso una API completamente separata. Una revisione del codice che accetta "scriviamo già Comments" senza controllare quale dei due si intenda ha accettato un'affermazione sulla funzionalità sbagliata, e succede più spesso di quanto il nome condiviso suggerisca. I due condividono otto lettere e nemmeno un byte di memorizzazione

Leggere i metadati in ingresso, e la lacuna nel sondaggio

La lettura è simmetrica. Dopo Open, le stesse proprietà tornano popolate dal file, il che trasforma una verifica dei metadati sulle cartelle in ingresso in un ciclo breve

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;

Nel farlo, tenete conto di un limite. Non esiste un sondaggio delle sole proprietà. GetSheetNames può elencare i fogli senza caricare una cartella di lavoro, ma leggere Title o Author significa un Open completo, quindi uno smistamento sui metadati di un archivio grande paga il costo pieno di analisi su ogni file. Sul lato BIFF potete ridurre quel costo per le verifiche in sola lettura impostando _DisableGraphics a vero prima di aprire, il che salta di netto il livello di disegno. Si adatta a un ciclo che legge solo proprietà e statistiche sulle celle, ed è esattamente sbagliato nel momento in cui la stessa istanza potrebbe salvare, perché il contenuto di disegno saltato andrebbe perso. Quando la sola struttura dei fogli può pre-filtrare l'insieme, con gli export a foglio singolo come cosa ovvia da saltare, le tecniche economiche descritte nel nostro articolo sull'elenco dei fogli e l'ispezione leggera riducono quanti file raggiungano la passata costosa. E sui lavori di timbratura in blocco, dove migliaia di risultati vengono scritti anziché ispezionati, gli schemi di throughput in scrittura del nostro articolo sulle scritture a flusso per i lavori batch si applicano senza modifiche, dato che l'assegnazione delle proprietà non aggiunge nulla di misurabile al tempo di salvataggio

Attraversare i formati e contenere la fuga

Le proprietà fanno un round-trip pulito dentro una sola facciata: aprite un .xlsx, lo modificate, lo salvate, e l'insieme torna intatto. Attraversare i formati è dove l'assunzione di parità si rompe, perché gli insiemi di campi BIFF e OOXML non si corrispondono uno a uno. BIFF ha Manager e nessuna marca temporale; OOXML ha Category, Description e la coppia Created e Modified. Un convertitore che copi alla cieca perde tutto ciò che il formato di destinazione non può contenere, quindi mappate i campi in modo esplicito e mettete la mappatura nella vostra lista di controllo per la conversione accanto a tutto il resto che non sopravvive al viaggio

Mappa dei campi HotXLS in Delphi che mostra quali proprietà dei documenti Excel sopravvivano a una conversione fra i formati XLS e XLSX
Una copia alla cieca fra formati perde ogni campo che la destinazione non può contenere — mappate esplicitamente gli insiemi di proprietà BIFF e OOXML nella lista di controllo per la conversione

La fuga che l'ereditarietà dal modello apre corre nella direzione opposta: informazioni che non intendevate affatto spedire fuori. Nomi di autori, etichette di progetto interne parcheggiate nelle parole chiave, un titolo di bozza che nessuno ha approvato. La disciplina del sovrascrivere tutto vista nel generatore qui sopra è l'intera difesa, e vale la pena verificarla come farebbe un estraneo, aprendo la finestra Proprietà che qualsiasi cliente può raggiungere oppure decomprimendo l'.xlsx e leggendo docProps/core.xml direttamente dal pacchetto. Quello che vedete lì è esattamente ciò che vede ogni indicizzatore a valle

Quella visibilità a valle è anche la ragione per cui alcuni campi meritano più cura degli altri. Titolo, Autore, Parole chiave (che compaiono come Tag) e Commenti o Descrizione portano gran parte del peso di indicizzazione in SharePoint e in Windows Search. Un Titolo davvero distinto per ogni documento, che porti il periodo e il conto, fa per la rintracciabilità più di qualsiasi schema di denominazione delle cartelle impilato sopra, e costa un'assegnazione per salvataggio

Le proprietà del documento sono la rifinitura professionale più economica che una cartella di lavoro generata possa portare, e il difetto consegnato più di frequente quando nessuno se ne fa carico. Entrambe le superfici di proprietà descritte qui appartengono a HotXLS Delphi Component, che le scrive nativamente per XLS e XLSX senza automazione di Excel