Consegni una cartella di lavoro finita a un collega e gli chiedi di filtrarla, non di riscriverla. Quindi proteggi il foglio. Nelle versioni più vecchie di HotXLS quel gesto scriveva una sola cosa nel file: <sheetProtection sheet="1" objects="1" scenarios="1"/>, codificata in modo fisso, ogni volta. Il foglio si bloccava, l'hash della password veniva associato e l'utente non poteva fare nulla, nemmeno ordinare e filtrare come volevi lasciare aperto. La finestra di dialogo di Excel “Proteggi foglio” ha quindici caselle di controllo proprio per questo motivo, e il motore non poteva esprimerne nessuna. È quel divario che il modello di protezione v2.91.0 colma
HotXLS è un componente VCL nativo per fogli di calcolo per Delphi e C++Builder che legge e scrive XLS e XLSX senza Excel installato. Questo articolo riguarda il lato XLSX della protezione dei fogli di lavoro: il nuovo enum TXLSXSheetProtectionOption, la proprietà AllowOption che attiva o disattiva ogni autorizzazione, e la regola di codifica OOXML che manda in crisi chiunque scriva un elemento <sheetProtection> scritto a mano
Cosa protegge davvero la protezione del foglio
Prima il confine, perché da quello dipende quanto puoi fidarti di tutto questo. La protezione del foglio nel formato spreadsheet OOXML (ECMA-376) è una policy di interazione, non una crittografia. Indica a un'applicazione conforme quali modifiche rifiutare mentre il foglio è protetto. I valori delle celle sono ancora lì in xl/worksheets/sheetN.xml in testo normale; decomprimi il .xlsx e li trovi subito lì. La password opzionale è memorizzata come un breve hash legacy, non come una chiave che cifra qualcosa. Chiunque rinomini il file, apra la parte e rimuova la riga <sheetProtection> legge e modifica tutto
Quindi la protezione risponde a “impedire al mio collega di rovinare per errore una formula”, non a “tenere questi dati segreti da qualcuno determinato”. Sono problemi diversi con strumenti diversi. Se ti serve riservatezza, ti serve la crittografia a livello di cartella di lavoro descritta in Output XLSX protetto con AES, che cifra davvero il pacchetto. La protezione del foglio e la crittografia della cartella di lavoro si combinano senza attriti, ma solo la seconda è un lucchetto. Tieni ben distinti questi due piani e il resto di questa pagina è solo impianto
Le quindici opzioni e la proprietà AllowOption
Ogni foglio di lavoro ora porta un insieme di TXLSXSheetProtectionOption valori che descrivono cosa l'utente può ancora fare mentre il foglio è protetto. I membri corrispondono uno a uno agli attributi OOXML e alle caselle di controllo della finestra di dialogo di Excel:
xlsxSpoEditObjects,xlsxSpoEditScenarios- modifica oggetti grafici e scenari what-ifxlsxSpoFormatCells,xlsxSpoFormatColumns,xlsxSpoFormatRows- riformatta celle, colonne e righexlsxSpoInsertColumns,xlsxSpoInsertRows,xlsxSpoInsertHyperlinks- inserisce colonne, righe e collegamentixlsxSpoDeleteColumns,xlsxSpoDeleteRows- elimina colonne e righexlsxSpoSelectLockedCells,xlsxSpoSelectUnlockedCells- sposta la selezione su celle bloccate o sbloccatexlsxSpoSort,xlsxSpoAutoFilter,xlsxSpoPivotTables- ordina intervalli, usa i menu a discesa di AutoFilter, lavora con le tabelle pivot
Leggi e scrivi i singoli bit tramite la proprietà indicizzata AllowOption su TXLSXWorksheet.AllowOption[Opt] = True significa che l'azione è consentita; impostarlo a False la vieta. L'intero insieme è accessibile anche tutto in una volta tramite SheetProtectionOptions, una TXLSXSheetProtectionOptions (un semplice Pascal set of), così puoi salvarlo, ripristinarlo o sostituirlo in blocco
Il valore predefinito conta ed è intenzionale: un foglio di lavoro appena creato parte con ogni opzione consentita. Il costruttore inizializza SheetProtectionOptions con l'intera gamma, [Low(TXLSXSheetProtectionOption)..High(TXLSXSheetProtectionOption)]. Da lì restringi il set escludendo le azioni che vuoi vietare, invece di costruire da zero un insieme di autorizzazioni. È questa scelta che fa combaciare la regola di codifica del writer, più sotto, con il comportamento di Excel
Proteggere un foglio lasciando aperti ordinamento e filtro
Ecco il caso tipico, dall'inizio alla fine: proteggi un report finito in modo che il layout non possa essere rimaneggiato, ma lascia al lettore la possibilità di ordinarlo e filtrarlo. Nota che Protect e le opzioni sono indipendenti. Protect porta il foglio nello stato protetto e salva l'hash opzionale della password; non tocca l'insieme delle opzioni. Tu modifichi AllowOption separatamente, e le impostazioni hanno effetto una volta che il foglio è protetto e salvato
var
wb: TXLSXWorkbook;
sh: TXLSXWorksheet;
begin
wb := TXLSXWorkbook.Create;
try
sh := wb.Sheets.Add('Protected');
sh.Cells[1, 1].Value := 'Region'; sh.Cells[1, 2].Value := 'Units';
sh.Cells[2, 1].Value := 'North'; sh.Cells[2, 2].Value := 120;
sh.Cells[3, 1].Value := 'South'; sh.Cells[3, 2].Value := 98;
// Protect with a password. This only sets the protected state + hash;
// the option set is left at its all-permitted default.
sh.Protect('HotXLS-2026');
// Narrow: keep sort + AutoFilter, forbid reshaping and reformatting.
sh.AllowOption[xlsxSpoSort] := True;
sh.AllowOption[xlsxSpoAutoFilter] := True;
sh.AllowOption[xlsxSpoFormatCells] := False;
sh.AllowOption[xlsxSpoFormatColumns] := False;
sh.AllowOption[xlsxSpoFormatRows] := False;
sh.AllowOption[xlsxSpoInsertRows] := False;
sh.AllowOption[xlsxSpoDeleteRows] := False;
if wb.SaveAs('protection.xlsx') <> 1 then
Writeln('SaveAs failed');
finally
wb.Free;
end;
end;
Due cose da trarre da questo frammento. Le righe Sort e AutoFilter sono scritte esplicitamente anche se entrambe predefinite a True; è documentazione per il prossimo manutentore, non un requisito funzionale. E poiché i valori predefiniti sono permissivi, le uniche righe che modificano il file di output sono quelle che impostano un'opzione a False. Non è un accidente di questa API, è il formato OOXML a farsi vedere, ed è la sezione successiva
La regola di codifica: omettere vuol dire consentire, attr=0 vuol dire vietare
Questo è l'unico fatto controintuitivo dell'intera funzione, ed è qui che un <sheetProtection> scritto a mano di solito sbaglia. In OOXML, ogni attributo per azione è un divieto flag, e la sua assenza equivale a consentire. Un attributo assente significa che l'azione è consentita. Un attributo scritto come "0" significa che l'azione è vietata mentre il foglio è protetto. Non esiste un formatCells="1" in un file ben formato per significare “la formattazione è consentita”; semplicemente lasci l'attributo fuori. (Il valore predefinito di un attributo assente è il booleano OOXML true, e questi attributi sono nominati in modo che “true” significhi che la modifica corrispondente è consentita.)
Il writer di HotXLS rispecchia esattamente questo. Emmette sheet="1" per attivare la protezione, poi percorre l'insieme delle opzioni e scrive attr="0" solo per le opzioni impostate a False. Le azioni consentite non contribuiscono all'output. Quindi la cartella di lavoro della sezione precedente si serializza in qualcosa di simile a questo, portando con sé solo le azioni vietate più l'hash della password:
// Conceptual output for the snippet above (attributes elided for brevity):
// <sheetProtection sheet="1"
// formatCells="0" formatColumns="0" formatRows="0"
// insertRows="0" deleteRows="0"
// password="...4-hex..."/>
// Note what is NOT there: no sort, no autoFilter, no selectLockedCells.
// Their absence is exactly what tells Excel those actions stay allowed.
Se arrivavi dalla vecchia stringa hard-coded e ti aspettavi di vedere ogni attributo scritto per intero, questo sembra scarno, quasi sbagliato. È corretto. Un file che elencasse sort="1" e autoFilter="1" significherebbe la stessa cosa a un lettore conforme, ma Excel stesso scrive la forma minima solo-divieti, e allinearsi ad essa mantiene piccoli i diff e noiosi i round-trip. Gli attributi objects e scenarios seguono la stessa regola: sono consentiti per default, quindi compaiono come "0" quando li vieti, che è l'inverso del vecchio objects="1" scenarios="1" emesso in modo incondizionato
Leggere la protezione all'indietro: fedeltà round-trip
A permission model that you can write but not read is a one-way door, and the usual symptom is a load-edit-save cycle that silently widens permissions. HotXLS closes that. When ParseWorksheetXml hits a <sheetProtection> element it sets the sheet protected, captures the password hash if present, and then decodes each per-action attribute back into AllowOption usando la stessa convenzione al contrario: un attributo presente ed equivalente a "0" vieta l'azione; un attributo assente lascia l'opzione al suo valore consentito predefinito
var
wb: TXLSXWorkbook;
sh: TXLSXWorksheet;
begin
wb := TXLSXWorkbook.Create;
try
wb.LoadFromFile('protection.xlsx');
sh := wb.Sheets[1]; // XLSX sheets are 1-based
if sh.IsProtected then
begin
Writeln('Protected; password hash present: ',
sh.SheetProtectHash <> '');
Writeln('Sort allowed: ', sh.AllowOption[xlsxSpoSort]);
Writeln('AutoFilter allowed: ', sh.AllowOption[xlsxSpoAutoFilter]);
Writeln('FormatCells allowed:', sh.AllowOption[xlsxSpoFormatCells]);
end;
finally
wb.Free;
end;
end;
Carichi il file prodotto dal writer e ottieni Sort e AutoFilter di nuovo come True, FormatCells, False - l'insieme che hai salvato, intatto. Questa simmetria è proprio il punto: modifica una cella in un foglio protetto e con autorizzazioni parziali, salva di nuovo, e le quattordici autorizzazioni che non hai toccato sopravvivono invece di ricadere nel vecchio default tutto-o-niente
Note pratiche e limiti
Alcune cose da sapere prima di integrare tutto questo in una pipeline di report:
- La password è debole per progetto. La protezione del foglio XLSX memorizza un hash legacy a 16 bit, lo stesso usato da Excel da decenni, conservato qui per interoperabilità. Serve a scoraggiare le modifiche accidentali; non resiste a un attaccante. Non trattarlo come un custode di segreti. Per una protezione reale, cifra la cartella di lavoro
- Impostare le opzioni prima di proteggere va benissimo.
AllowOptionpuò essere assegnato sia che il foglio sia attualmente protetto sia che non lo sia; le opzioni descrivono semplicemente ciò che la protezione consentirà una volta cheProtectè in vigore.UnProtectcancella lo stato protetto e l'hash, ma lascia l'insieme delle opzioni al suo posto per la prossima volta - Anche la semantica delle celle bloccate continua ad applicarsi. La protezione blocca solo le modifiche alle celle il cui
Lockedattributo è impostato, che è il valore predefinito della cartella di lavoro. Lasciare modificabile un'area di input è compito dello stile della cella, non di un'opzione di protezione; i due livelli si combinano come in Excel - Questo è il motore XLSX. Il modello delle opzioni rispecchia le più vecchie proprietà
Allow*del motore XLS, ma i nomi dell'enum e della proprietà qui (xlsxSpo*,AllowOptionappartengono aTXLSXWorksheetinlxHandleX. Se gestisci anche il layout di stampa sugli stessi fogli, la guida alla protezione e all'impostazione pagina spiega come queste impostazioni convivono con aree di stampa e intestazioni, e convalida dati, AutoFilter e tabelle si abbina naturalmente al lasciarexlsxSpoAutoFilteraperto su un report bloccato
Il modello di protezione granulare e il resto del motore di lettura/scrittura XLSX sono inclusi nel HotXLS Component per Delphi e C++Builder; la pagina prodotto include l'API completa del foglio di lavoro, compreso il riferimento completo alle opzioni di protezione