مقال تقني

حماية أوراق عمل XLSX في دلفي: 15 خيار سماح

تقوم بتسليم مصنف عمل منتهٍ إلى زميل وتطلب منه تصفيته، وليس إعادة كتابته. لذا تقوم بحماية الورقة. في بنيات HotXLS الأقدم، كتبت تلك الإيماءة شيئاً واحداً في الملف: <sheetProtection sheet="1" objects="1" scenarios="1"/>، مرمزاً بشكل ثابت، في كل مرة. تم قفل الورقة، وإرفاق تجزئة كلمة المرور، ولم يتمكن المستخدم من فعل أي شيء على الإطلاق، ولا حتى الفرز والتصفية اللذين أردت بالفعل تركهما مفتوحين. يحتوي مربع حوار "حماية الورقة" الخاص بـ Excel على خمسة عشر خانة اختيار لهذا السبب بالضبط، ولم يتمكن المحرك من التعبير عن أي منها. تلك الفجوة هي ما يغلقه نموذج الحماية v2.91.0

إن HotXLS هو مكون جدول بيانات VCL أصلي لدلفي وC++Builder يقرأ ويكتب XLS وXLSX بدون تثبيت Excel. تدور هذه المقالة حول جانب XLSX لحماية ورقة العمل: تعداد TXLSXSheetProtectionOption الجديد، وخاصية AllowOption التي تبدل كل إذن، وقاعدة ترميز OOXML واحدة تعيق كل من يكتب عنصر <sheetProtection> يدوياً

ما الذي تحميه حماية ورقة العمل بالفعل

تعتبر حماية ورقة العمل في تنسيق جدول بيانات OOXML (وهو ECMA-376) سياسة تفاعل، وليس تشفيراً. فهي تخبر التطبيق المتوافق بالتعديلات التي يجب رفضها أثناء حماية الورقة. لا تزال قيم الخلايا موجودة في xl/worksheets/sheetN.xml بنص عادي؛ قم بفك ضغط ملف .xlsx وستجدها هناك مباشرة. ويتم تخزين كلمة المرور الاختيارية كتجزئة قديمة قصيرة، وليس كمفتاح يشوش أي شيء. وأي شخص يقوم بإعادة تسمية الملف، وفتح الجزء، وتجريد سطر <sheetProtection> سيقرأ ويحرر كل شيء

لذا تجيب الحماية على "منع زميلي في العمل من تخريب صيغة بالخطأ"، وليس "الحفاظ على سرية هذه البيانات من شخص متحمس". فهذه مشاكل مختلفة بأدوات مختلفة. إذا كنت بحاجة إلى السرية، فأنت تريد التشفير على مستوى مصنف العمل المغطى في مخرجات XLSX المحمية بـ AES، والذي يشفر الحزمة بالفعل. وتتألف حماية الورقة وتشفير مصنف العمل بشكل نظيف، ولكن الثاني فقط هو القفل. حافظ على استقامة هذا الخط والباقي في هذه الصفحة مجرد سباكة

الخيارات الخمسة عشر وخاصية AllowOption

تحمل كل ورقة عمل الآن مجموعة من قيم TXLSXSheetProtectionOption التي تصف ما لا يزال بإمكان المستخدم فعله أثناء حماية الورقة. وتتطابق الأعضاء واحداً لواحد مع سمات OOXML ومع خانات اختيار مربع حوار Excel:

  • xlsxSpoEditObjects، وxlsxSpoEditScenarios — تحرير كائنات الرسم وسيناريوهات ماذا لو
  • xlsxSpoFormatCells، وxlsxSpoFormatColumns، وxlsxSpoFormatRows — إعادة تنسيق الخلايا والأعمدة والصفوف
  • xlsxSpoInsertColumns، وxlsxSpoInsertRows، وxlsxSpoInsertHyperlinks — إدراج الأعمدة والصفوف والروابط
  • xlsxSpoDeleteColumns، وxlsxSpoDeleteRows — حذف الأعمدة والصفوف
  • xlsxSpoSelectLockedCells، وxlsxSpoSelectUnlockedCells — نقل التحديد إلى الخلايا المغلقة أو غير المغلقة
  • xlsxSpoSort، وxlsxSpoAutoFilter، وxlsxSpoPivotTables — فرز النطاقات، واستخدام قوائم التصفية التلقائية، والعمل مع جداول PivotTable

تقرأ وتكتب البتات الفردية من خلال خاصية AllowOption المفهرسة على TXLSXWorksheet. وتعني AllowOption[Opt] = True أن الإجراء مسموح به؛ بينما إعداده False يمنعه. ويمكن أيضاً الوصول إلى المجموعة بأكملها مرة واحدة من خلال SheetProtectionOptions، وهي TXLSXSheetProtectionOptions (وهي set of في Pascal)، بحيث يمكنك حفظها أو استعادتها أو استبدالها بالكامل

الافتراضي مهم وهو متعمد: تبدأ ورقة العمل المنشأة حديثاً بالسماح بكل الخيارات. يبذر المنشئ SheetProtectionOptions بالنطاق الكامل، [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. هذا ليس حادثاً لواجهة برمجة التطبيقات هذه، بل هو تنسيق سلك OOXML الذي يظهر، وهو القسم التالي

قاعدة الترميز: الحذف يعني السماح، السمة=0 تعني المنع

هذه هي الحقيقة الوحيدة المخالفة للبديهة في الميزة بأكملها، وهي المكان الذي تخطئ فيه عادة كتابة <sheetProtection> اليدوية. ففي OOXML، تعتبر كل سمة لكل إجراء علامة منع، وغيابها هو إذن بالسماح. وتعني السمة المفقودة أن الإجراء مسموح به. وتعني السمة المكتوبة كـ "0" أن الإجراء ممنوع أثناء حماية الورقة. لا يوجد formatCells="1" في ملف جيد التنسيق ليعني "التنسيق مسموح به"؛ بل ببساطة تحذف السمة. (الافتراضي للسمة الغائبة هو الافتراضي المنطقي لـ OOXML وهو true، وتم تسمية هذه السمات بحيث تعني "true" أن التحرير المقابل مسموح به)

يعكس كاتب HotXLS ذلك تماماً. فهو يصدر sheet="1" لتشغيل الحماية، ثم يتصفح مجموعة الخيارات ويكتب attr="0" فقط للخيارات التي عينتها إلى 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" وautoFilter="1" سيعني الشيء نفسه لقارئ متوافق، ولكن Excel نفسه يكتب نموذج المنع الأدنى فقط، ويحافظ على تطابقه في جعل الاختلافات صغيرة والرحلات الدائرية مملة. تتبع سمتا objects وscenarios القاعدة المتطابقة نفسها: فهما مسموحتان افتراضياً، لذا تظهران فقط كـ "0" عندما تمنعهما، وهو عكس objects="1" scenarios="1" القديم الذي تم إصداره دون قيد أو شرط

قراءة الحماية رجوعاً: أمانة الرحلة الدائرية

نموذج الصلاحيات الذي يمكنك كتابته ولكن لا يمكنك قراءته هو باب ذو اتجاه واحد، والعَرَض المعتاد هو دورة تحميل-تعديل-حفظ توسع الصلاحيات بصمت. يغلق HotXLS ذلك. عندما يضرب ParseWorksheetXml عنصر <sheetProtection> فإنه يعين الورقة محمية، ويلتقط تجزئة كلمة المرور إذا كانت موجودة، ثم يفك تشفير كل سمة لكل إجراء رجوعاً إلى 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 كـ True، وFormatCells كـ False — المجموعة التي حفظتها، سليمة. ذلك التماثل هو الهدف بأكمله: قم بتحرير خلية واحدة في ورقة محمية ومسموح بها جزئياً وأعد الحفظ، وتنجو الأذونات الأربعة عشر التي لم تلمسها بدلاً من الانهيار إلى الافتراضي القديم المتمثل في الكل أو لا شيء

ملاحظات وحدود عملية

بضعة أشياء يجدر معرفتها قبل توصيل هذا في خط أنابيب التقارير:

  • كلمة المرور ضعيفة بالتصميم. تخزن حماية ورقة عمل XLSX تجزئة قديمة بقيمة 16 بتة (وهي التجزئة نفسها التي استخدمها Excel لعقود)، وهي محفوظة هنا للتوافقية. فهي تردع التعديلات العشوائية؛ ولا تقاوم المهاجم. لا تعاملها كحارس أسرار. وللحصول على حماية حقيقية، قم بتشفر مصنف العمل
  • ضبط الخيارات قبل الحماية أمر جيد. يمكن تعيين AllowOption سواء كانت الورقة محمية حالياً أم لا؛ حيث تصف المفاتيح ببساطة ما ستسمح به الحماية بمجرد دخول Protect حيز التنفيذ. وتقوم UnProtect بمسح حالة الحماية والتجزئة ولكن تترك مجموعة الخيارات الخاصة بك في مكانها للمرة القادمة
  • لا تزال دلالات الخلايا المغلقة تنطبق. تمنع الحماية فقط التعديلات على الخلايا التي تم تعيين سمة Locked الخاصة بها (الافتراضي لمصنف العمل). وترك منطقة إدخال قابلة للتحرير هي وظيفة نمط الخلية، وليس خيار حماية؛ وتندمج الطبقتان بالطريقة نفسها التي تندمجان بها في Excel
  • هذا هو محرك XLSX. يعكس نموذج الخيارات خصائص Allow* الأقدم لمشغلات محرك XLS، ولكن أسماء التعداد والخصائص هنا (وهي xlsxSpo*، وAllowOption) تنتمي إلى TXLSXWorksheet في lxHandleX. وإذا كنت تدير أيضاً تخطيط الطباعة على الأوراق نفسها، فإن دليل الحماية وإعداد الصفحة يغطي كيفية وضع هذه الإعدادات جنباً إلى جنب مع مناطق الطباعة والرؤوس، وتقترن تصفية البيانات والتصفية التلقائية والجداول بشكل طبيعي مع ترك xlsxSpoAutoFilter مفتوحاً في تقرير مغلق

يُشحن نموذج الحماية دقيق الحبيبات وبقية محرك القراءة/الكتابة لـ XLSX في مكون HotXLS Component لدلفي وC++Builder؛ وتحمل صفحة المنتج واجهة برمجة تطبيقات ورقة العمل الكاملة بما في ذلك مرجع خيارات الحماية الكامل