Подавате готов workbook на колега и му казвате да го филтрира, не да го пренаписва. Затова защитавате sheet-а. В по-старите HotXLS builds този жест записваше едно нещо във файла: <sheetProtection sheet="1" objects="1" scenarios="1"/>, hard-coded, всеки път. Sheet-ът се заключваше, password hash-ът се добавяше и потребителят не можеше да прави нищо, дори sort и filter, които всъщност искахте да оставите отворени. Собственият диалог "Protect Sheet" на Excel има петнадесет отметки точно по тази причина, а engine-ът не можеше да изрази нито една от тях. Тази празнина затваря protection моделът във v2.91.0
HotXLS е нативен VCL spreadsheet компонент за Delphi и C++Builder, който чете и записва XLS и XLSX без да автоматизира самия Excel. Тази статия е за XLSX страната на worksheet protection: новия TXLSXSheetProtectionOption enum, AllowOption property-то, което превключва всяко право, и правилото за OOXML encoding, което хваща всеки, който пише <sheetProtection> елемент на ръка
Какво всъщност защитава worksheet protection
Първо, границата, защото тя решава колко от всичко това трябва да ви вдъхва доверие. Worksheet protection в OOXML spreadsheet format (ECMA-376) е политика за взаимодействие, не encryption. Тя казва на съвместимото приложение кои edits да откаже, докато sheet-ът е защитен. Cell стойностите все още стоят в xl/worksheets/sheetN.xml на plain text; разархивирайте .xlsx и те са си там. Опционалният password се съхранява като кратък legacy hash, не като key, който разбърква нещо. Всеки, който преименува файла, отвори part-а и махне <sheetProtection> реда, може да чете и редактира всичко
Така че protection отговаря на „спри колегата ми да съсипе случайно една формула“, не на „скрий тези данни от някой мотивиран“. Това са различни проблеми с различни инструменти. Ако ви трябва confidentiality, искате workbook-level encryption, описано в AES-protected XLSX output, което наистина encipher-ва пакета. Sheet protection и workbook encryption се комбинират чисто, но само второто е lock. Дръжте тази линия ясна и останалото на тази страница е просто plumbing
Петнадесетте опции и AllowOption property-то
Всеки worksheet вече носи набор от TXLSXSheetProtectionOption values, описващи какво потребителят още може да прави, докато sheet-ът е protected. Членовете map-ват едно към едно към OOXML attributes и към Excel dialog checkboxes:
xlsxSpoEditObjects,xlsxSpoEditScenarios— редактиране на drawing objects и what-if scenariosxlsxSpoFormatCells,xlsxSpoFormatColumns,xlsxSpoFormatRows— форматиране на клетки, колони и редовеxlsxSpoInsertColumns,xlsxSpoInsertRows,xlsxSpoInsertHyperlinks— вмъкване на колони, редове и linksxlsxSpoDeleteColumns,xlsxSpoDeleteRows— изтриване на колони и редовеxlsxSpoSelectLockedCells,xlsxSpoSelectUnlockedCells— преместване на селекцията върху locked или unlocked клеткиxlsxSpoSort,xlsxSpoAutoFilter,xlsxSpoPivotTables— сортиране на ranges, използване на AutoFilter dropdowns и работа с PivotTables
Четете и записвате отделните bits през индексираното AllowOption property на TXLSXWorksheet. AllowOption[Opt] = True означава, че действието е разрешено; задаването му на False го забранява. Целият набор е достъпен и наведнъж през SheetProtectionOptions, TXLSXSheetProtectionOptions (обикновен Pascal set of), така че можете да го запишете, възстановите или замените изцяло
Подразбиращото се поведение е важно и е умишлено: току-що създаден worksheet започва с позволена всяка опция. Конструкторът seed-ва SheetProtectionOptions с пълния диапазон, [Low(TXLSXSheetProtectionOption)..High(TXLSXSheetProtectionOption)]. Оттам стеснявате, като изключвате действията, които искате да забраните, вместо да строите permission set от нулата. Този избор е това, което кара правилото за encoding по-долу да съвпада с поведението на Excel
Защита на sheet, но оставяне на sort и filter отворени
Ето често срещаният случай от край до край: защитете завършен report, така че оформлението да не може да бъде пренаредено, но оставете читателя да го сортира и филтрира. Забележете, че Protect и опциите са независими. Protect превключва sheet-а в protected state и записва optional password hash-а; не пипа option set-а. Настройвате AllowOption отделно, а toggles-ите влизат в сила, когато sheet-ът е защитен и записан
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;
Две неща да прочетете от този snippet. Sort и AutoFilter линиите се записват изрично, въпреки че и двете по подразбиране са True; това е документация за следващия maintainer, не функционално изискване. И понеже defaults-ите са permissive, единствените линии, които променят output файла, са тези, които задават опция на False. Това не е случайност на този API, а OOXML wire format-ът, който прозира, и това води към следващия section
Правилото за encoding: omit означава allow, attr=0 означава forbid
Това е единственият counter-intuitive факт в цялата функция и именно тук ръчно написаният <sheetProtection> обикновено греши. В OOXML всеки per-action attribute е forbid flag, а липсата му е permission. Attribute, който липсва, означава, че действието е allowed. Attribute, записан като "0", означава, че действието е forbidden, докато sheet-ът е protected. Няма formatCells="1" в добре оформен файл, който да означава "formatting is allowed"; просто не включвате атрибута. (Подразбирането за липсващ attribute е OOXML boolean default true, а тези атрибути са именувани така, че "true" да означава, че съответното edit е permitted.)
Writer-ът на HotXLS отразява това точно. Той изписва sheet="1" за да включи защитата, после обхожда option set-а и пише attr="0" само за опциите, които сте задали на False. Permitted действията не допринасят нищо към output-а. Така workbook-ът от предишния section се сериализира до нещо подобно, носейки само forbidden действията плюс password hash-а:
// 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.
Ако идвате от стария hard-coded string и очаквате да видите всеки attribute изписан, това изглежда оскъдно, почти грешно. Но е правилно. Файл, който изброява sort="1" и autoFilter="1" би означавал същото за съвместим reader, но самият Excel пише минималната forbid-only форма и това държи diff-овете малки, а round-trip-овете скучни. objects и scenarios атрибутите следват идентичното правило: те са default-permitted, така че се появяват само като "0" когато ги забраните, което е обратното на стария objects="1" scenarios="1" който се изписваше безусловно
Четене на protection обратно: round-trip fidelity
Permission модел, който можете да запишете, но не и да прочетете, е еднопосочна врата, а обичайният симптом е load-edit-save цикъл, който тихо разширява разрешенията. HotXLS затваря това. Когато ParseWorksheetXml срещне <sheetProtection> елемент, той маркира sheet-а protected, прихваща password hash-а, ако има такъв, и после декодира всеки per-action attribute обратно в AllowOption с една и съща конвенция наобратно: attribute, който присъства и е равен на "0", забранява действието; липсващ attribute оставя опцията в нейното permitted default състояние
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;
Заредете файла, който writer-ът е произвел, и получавате Sort и AutoFilter обратно като True, FormatCells като False - наборът, който сте записали, непокътнат. Тази симетрия е цялата идея: редактирате една клетка в защитен, частично permitted sheet и записвате отново, а четиринадесетте permissions, които не сте пипнали, оцеляват вместо да се сринат обратно към стария all-or-nothing default
Практически бележки и ограничения
Няколко неща, които си струва да знаете, преди да включите това в report pipeline:
- Password-ът е weak по дизайн. XLSX worksheet protection съхранява 16-битов legacy hash (същия, който Excel използва от десетилетия), пазен тук за interoperability. Той възпира случайни edits; не се противопоставя на attacker. Не го третирайте като secret-keeper. За истинска защита encrypt-нете workbook-а
- Задаването на options преди защитата е наред.
AllowOptionможе да се задава независимо дали sheet-ът в момента е protected; toggles-ите просто описват какво protection ще позволи, когатоProtectе в сила.UnProtectизчиства protected state-а и hash-а, но оставя option set-а на място за следващия път - Locked-cell semantics все още важат. Protection блокира само edits към клетки, чийто
Lockedattribute е зададен (workbook default-ът). Да оставите input region editable е работа на cell-style-а, не на protection option; двете layers се комбинират по същия начин, както в Excel - Това е XLSX engine-ът. Option model-ът отразява по-старите
Allow*properties на XLS engine-а, но enum и property имената тук (xlsxSpo*,AllowOption) принадлежат наTXLSXWorksheetвlxHandleX. Ако управлявате и печатното оформление на същите sheet-ове, protection and page-setup walkthrough показва как тези settings стоят до print areas и headers, а data validation, AutoFilter and tables се съчетава естествено с оставянето наxlsxSpoAutoFilterотворен върху заключен report
Fine-grained protection model-ът и останалата XLSX read/write engine част идват в HotXLS Component за Delphi и C++Builder; product page-ът носи пълния worksheet API, включително пълната reference за protection options