Вы передаете коллеге готовую книгу и просите его фильтровать ее, а не переписывать. Поэтому вы защищаете лист. В старых сборках HotXLS этот жест записывал в файл одну и ту же строку: <sheetProtection sheet="1" objects="1" scenarios="1"/> , жестко зашитую всегда. Лист блокировался, хеш пароля прикреплялся, а пользователь не мог делать вообще ничего, даже сортировку и фильтрацию, которые вы как раз хотели оставить доступными. В собственном диалоге Excel "Protect Sheet" для этого существует пятнадцать флажков, а движок не умел выразить ни один из них. Именно этот пробел и закрывает модель защиты из v2.91.0
HotXLS - это нативный VCL spreadsheet component для Delphi и C++Builder, который читает и пишет XLS и XLSX без установленного Excel. Эта статья посвящена стороне XLSX у защиты worksheet: новому enum TXLSXSheetProtectionOption , свойству AllowOption , которое переключает каждое разрешение, и одному правилу кодирования OOXML, на котором спотыкается почти каждый, кто пишет элемент <sheetProtection> вручную
Что на самом деле охраняет защита листа
Сначала граница применимости, потому что именно она определяет, насколько вообще этому можно доверять. Защита worksheet в формате OOXML spreadsheet, ECMA-376, является политикой взаимодействия, а не шифрованием. Она сообщает совместимому приложению, какие правки нужно отклонять, пока лист защищен. Значения ячеек по-прежнему лежат в xl/worksheets/sheetN.xml в открытом виде. Распакуйте .xlsx , и они окажутся прямо перед вами. Необязательный пароль хранится как короткий устаревший хеш, а не как ключ, который что-либо шифрует. Любой, кто переименует файл, откроет part и удалит строку <sheetProtection> , сможет читать и редактировать все
Поэтому защита отвечает на вопрос "не дай коллеге случайно испортить формулу", а не на вопрос "спрячь эти данные от мотивированного человека". Это разные задачи с разными инструментами. Если вам нужна конфиденциальность, вам нужна workbook-level encryption, описанная в AES-protected XLSX output , которая действительно шифрует пакет. Защита листа и шифрование книги хорошо сочетаются, но замком является только второе. Держите эту границу в уме, и все остальное на странице сводится к аккуратной plumbing
Пятнадцать опций и свойство AllowOption
Теперь каждый worksheet несет набор значений TXLSXSheetProtectionOption , описывающий, что пользователь все еще может делать, пока лист защищен. Элементы набора один к одному соответствуют атрибутам OOXML и флажкам в диалоге Excel:
xlsxSpoEditObjects,xlsxSpoEditScenarios- редактирование drawing object и сценариев what-ifxlsxSpoFormatCells,xlsxSpoFormatColumns,xlsxSpoFormatRows- изменение форматирования ячеек, столбцов и строкxlsxSpoInsertColumns,xlsxSpoInsertRows,xlsxSpoInsertHyperlinks- вставка столбцов, строк и ссылокxlsxSpoDeleteColumns,xlsxSpoDeleteRows- удаление столбцов и строкxlsxSpoSelectLockedCells,xlsxSpoSelectUnlockedCells- перемещение выделения на заблокированные или незаблокированные ячейкиxlsxSpoSort,xlsxSpoAutoFilter,xlsxSpoPivotTables- сортировка диапазонов, использование выпадающих списков AutoFilter и работа с PivotTable
Отдельные биты читаются и записываются через индексированное свойство AllowOption у TXLSXWorksheet .AllowOption[Opt] = True Значение False означает, что действие разрешено. Установка в SheetProtectionOptions запрещает его. Весь набор также доступен целиком через TXLSXSheetProtectionOptions , то есть set of , обычный Pascal
. Поэтому его можно сохранить, восстановить или заменить полностью.SheetProtectionOptionsЗначение по умолчанию важно, и оно выбрано намеренно: свежесозданный worksheet стартует с разрешением всех опций. Конструктор заполняет [Low(TXLSXSheetProtectionOption)..High(TXLSXSheetProtectionOption)] полным диапазоном,
. Дальше вы сужаете этот набор, исключая действия, которые хотите запретить, а не собираете систему разрешений с нуля. Именно это решение и делает правило кодирования, описанное ниже, согласованным с поведением Excel
Защищаем лист, но оставляем сортировку и фильтрацию открытымиProtectВот типичный сценарий целиком: защитить готовый отчет так, чтобы макет нельзя было перекроить, но при этом позволить читателю сортировать и фильтровать его. Обратите внимание, что Protect и набор опций независимы. AllowOption переводит лист в защищенное состояние и сохраняет необязательный хеш пароля. Сам набор опций при этом не меняется. Вы отдельно настраиваете
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;
, и переключатели начинают действовать после защиты и сохранения листа.SortИз этого фрагмента нужно считать две вещи. Строки AutoFilter и True записаны явно, хотя обе и так по умолчанию равны False . Это документация для следующего сопровождающего, а не функциональная необходимость. А поскольку значения по умолчанию разрешительные, единственными строками, которые реально меняют выходной файл, остаются те, где опция переводится в
. Это не случайность данного API, а прямое проявление формата OOXML, о чем и говорится в следующем разделе
Правило кодирования: отсутствие означает разрешено, attr=0 означает запрещено<sheetProtection>Это единственный по-настоящему контринтуитивный факт во всей функции и именно то место, где вручную написанный запрет для "0" обычно ломается. В OOXML каждый атрибут для отдельного действия является флагом formatCells="1" . Если атрибут отсутствует, действие разрешено. Если атрибут записан как
, действие запрещено, пока лист защищен. Корректный файл не содержит sheet="1" в смысле "formatting allowed". Вы просто не пишете этот атрибут вообще. Значение по умолчанию для отсутствующего атрибута равно булевому default OOXML true, а сами атрибуты названы так, что true означает разрешение соответствующего редактирования.attr="0"Writer в HotXLS отражает это правило буквально. Он выводит False , чтобы включить защиту, затем проходит по набору опций и записывает
// 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.
только для тех опций, которые вы установили в sort="1" . Разрешенные действия вообще ничего не добавляют в output. Поэтому workbook из предыдущего раздела сериализуется примерно так, неся лишь запрещенные действия плюс хеш пароля:autoFilter="1"Если вы пришли от старой жестко зашитой строки и ожидали увидеть в явном виде каждый атрибут, такой результат покажется чересчур разреженным и почти неправильным. На самом деле он корректен. Файл, который перечислил бы objects и scenarios , значил бы для совместимого reader то же самое, но сам Excel записывает минимальную форму только с запретами, а совпадение с этим поведением делает diff небольшими, а round-trip скучными и надежными. Атрибуты "0" и objects="1" scenarios="1" следуют тому же правилу: они по умолчанию разрешены, поэтому появляются только как
, когда вы их запрещаете. Это обратная логика по сравнению со старой строкой
, которая раньше выводилась безусловно.ParseWorksheetXmlЧтение защиты обратно: точность round-trip<sheetProtection>Модель разрешений, которую можно записать, но нельзя прочитать обратно, - это билет в одну сторону. Типичный симптом здесь состоит в том, что цикл load-edit-save молча расширяет разрешения. HotXLS закрывает и эту дыру. Когда AllowOption встречает элемент "0" , он помечает лист как защищенный, захватывает хеш пароля, если тот присутствует, а затем декодирует каждый атрибут отдельного действия обратно в
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;
по тому же соглашению, но в обратную сторону: присутствующий атрибут со значением Sort запрещает действие, а отсутствие атрибута оставляет опцию в разрешенном состоянии по умолчанию.AutoFilterЗагрузите файл, который записал writer, и вы получите True и FormatCells обратно как False , а
как
- тот самый набор, который вы сохранили. В этом и состоит вся ценность симметрии: вы меняете одну ячейку на защищенном листе с частично разрешенными действиями, пересохраняете файл, и четырнадцать разрешений, которых вы не касались, не схлопываются обратно к старому all-or-nothing поведению
- Практические замечания и ограниченияНесколько вещей полезно знать, прежде чем включать это в pipeline отчетов:
- Пароль по замыслу слабый.
AllowOptionЗащита worksheet в XLSX хранит 16-битный устаревший хеш, тот самый, который Excel использует десятилетиями. Он нужен для совместимости. Он удерживает от случайных правок, но не сопротивляется атакующему. Не считайте его хранителем секретов. Для настоящей защиты шифруйте workbook.ProtectНастраивать опции до включения защиты нормально.UnProtectЗначение - можно назначать вне зависимости от того, защищен лист сейчас или нет. Эти переключатели всего лишь описывают, что будет разрешено после того, как вступит в силу.
Lockedочищает флаг защищенности и хеш, но оставляет набор опций на месте для следующего раза - Семантика locked cell по-прежнему действует. Защита блокирует только изменения в тех ячейках, у которых атрибут
Allow*установлен, а это поведение workbook по умолчанию. Оставить область ввода редактируемой - задача стиля ячейки, а не опции защиты. Эти два слоя сочетаются точно так же, как в Excel.xlsxSpo*Это относится к движку XLSX.AllowOptionМодель опций повторяет более старые свойстваTXLSXWorksheetу движка XLS, но enum и имена свойств здесь,lxHandleX, , принадлежат в . Если вы теми же листами управляете и с точки зрения печати, то разбор protection и page setupxlsxSpoAutoFilterпоказывает, как эти настройки живут рядом с print area и header, а
data validation, AutoFilter and tables естественно сочетается с тем, чтобы оставлять открытым на заблокированном отчете