مقال تقني

HotXLS Delphi Component: VBA payload and external link preservation في Delphi

تفرّق النسخة المحدثة بين حفظ VBA وتنفيذه، وبين payload المشروع وخريطة external links. وتوضح مسار إعادة كتابة XLSX وملفات XLS القديمة، وأن الخريطة قد تبقى بينما لا تبقى القيم المخزنة، مع التحقق من الملف الذي كُتب فعلاً

تخيّل مهمة لا تفعل شيئا تقريبا: افتح مصنفا شهريا، اكتب تاريخ اليوم في خلية واحدة، احفظه مرة أخرى شغّل ذلك عبر خدمة بما يكفي من التكرار وستصل شكوى مع ذلك الوحدات الماكرو اختفت، أو أن أسعار الصرف المرتبطة الآن تقرأ #REF!، وفريق العمليات مقتنع بأن كودك حذفها لم يحذف شيئا ما حدث عادة هو أن مصنفا مفعّل الماكرو خرج باسم .xlsx عادي، وأطاع Excel قواعد نوع المحتوى في ECMA-376: حزمة يعلن نوع محتواها عدم وجود VBA لا يمكنها تحميل مشروع VBA، بغض النظر عن كون البايتات موجودة فعليا هناك الملف لم ينكسر أعيدت تسميته إلى حالة يُطلب فيها من Excel تجاهل جزء منه

الوحدات الماكرو وروابط المصنفات الخارجية هما الشيئان اللذان تفقدهما الأتمتة بأكبر موثوقية، لنفس السبب الكامن كلاهما يعيش خارج شبكة الخلايا التي يلمسها كود التحرير فعليا، لذا الكود الذي يفكر بمصطلحات الصفوف والأعمدة سيسقطهما دون أن يصدر حذفا أبدا HotXLS مكتبة Delphi وC++Builder أصلية تقرأ وتكتب XLS وXLSX دون تثبيت Excel، وتعامل كلا الأصلين كحمولات تحملها عمدا لا كبيانات تصادف نسخها ما يلي هو ما يحتاجه كل منهما من مسار الحفظ لديك، وأين تتوقف الضمانات

لماذا يتصرف هذان الأصلان بشكل مختلف تحت إعادة الكتابة

مشروع VBA هو ثنائي معتم واحد في حزمة OOXML هو الملف vbaProject.bin؛ وفي ملف BIFF القديم هو تخزين OLE هناك طريقتان بالضبط لفقدانه: الكاتب لا ينسخه أبدا إلى الخرج، أو يحصل الخرج على نوع ملف يحظره أي فشل منهما كلي وصامت المشروع إما موجود أو غير موجود

مخطط مقارنة بين كتلة مشروع VBA والأجزاء الثلاثة لرابط مصنف خارجي يحملها HotXLS عبر إعادة كتابة في Delphi
ينجو مشروع VBA من إعادة كتابة حملاً ثنائياً كل شيء أو لا شيء، بينما الرابط الخارجي مخطط صغير يمكن حفظ هدفه وأسماء أوراقه وقيمه المخزنة أو إسقاطها باستقلال

الرابط الخارجي ليس كتلة بيانات على الإطلاق إنه رسم بياني صغير من العلاقات: مسار هدف أو رابط يشير إلى مصنف آخر، وقائمة أسماء الأوراق التي يعرضها ذلك الهدف، وذاكرة تخزين مؤقت اختيارية للقيم التي شُوهدت آخر مرة في تلك الأوراق كي يستطيع Excel عرض شيء حين يكون الهدف غير متصل تلك الأجزاء الثلاثة لها أعمار مختلفة تحت إعادة الكتابة، ويمكن لمكتبة أن تحافظ بأمانة على بعضها بينما تُسقط أخرى بصمت هذا التفاوت هو الجزء الذي يستحق الدقة بشأنه، لأن لا شيء في كود تحرير الخلايا سيظهره

حمل مشروع VBA عبر إعادة كتابة XLSX

في جانب XLSX، تحتفظ TXLSXWorkbook بحمولة الماكرو حرفيا خاصية VbaProject تحمل بايتات vbaProject.bin الخام داخل AnsiString، وسلسلة فارغة هي كيف يقول النموذج إنه لا توجد ماكرو حولها ثلاث عمليات: HasVbaProject تجيب هل يوجد مشروع، وClearVbaProject تزيله عمدا، وLoadVbaProjectFromFile تحقن واحدا مستخرجا من قالب ذلك الاستدعاء الأخير يستحق أكثر مما يبدو يتيح للمصنفات المولَّدة اكتساب مشروع ماكرو قياسي دون سحب ملف قالب كامل عبر خط الأنابيب

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Data');
    Sheet.Cells[1, 1].Value := 'Refreshed ' + DateTimeToStr(Now);

    Book.LoadVbaProjectFromFile('macros\vbaProject.bin');
    if not Book.HasVbaProject then
      raise Exception.Create('VBA payload failed to load');

    // امتداد .xlsm ليس تجميليا: إنه يختار
    // نوع المحتوى المفعّل للماكرو داخل الحزمة
    Book.SaveAs('monthly-report.xlsm');
  finally
    Book.Free;
  end;
end;

سطر الحفظ هو حيث تتحول المشكلة بأكملها مصنف يحمل مشروع VBA يجب أن يُكتب بدلالات مفعّلة للماكرو، وتطبّقها HotXLS حين ينتهي اسم الهدف بـ.xlsm سلّمه .xlsx بدلا من ذلك وسيرفض Excel الماكرو، رغم أن البايتات موجودة فعليا في الحزمة وستُحلَّل بلا مشكلة الامتداد ليس زخرفة؛ إنه يختار نوع المحتوى الذي يخبر Excel أنه يُسمَح بوجود مشروع VBA معظم الوقت تحتاج فقط إلى حمل الحمولة عندما تحتاج إلى القراءة داخلها، لنقل لسرد أسماء الوحدات لتقرير تدقيق، تعرض ParsedVBAProject نموذج وحدات مُحلَّلا بينما تبقى VbaProject البايتات الأصلية غير الملموسة

إعادة استخدام الماكرو من مصنفات XLS القديمة

واجهة BIFF تعكس مجموعة الأدوات تلك بخطوة إضافية واحدة HasVBAProject تفحص ملفا محمَّلا، وSaveVBAProjectToFile تكتب تخزين المشروع إلى القرص، وLoadVBAProjectFromFile تقرأ واحدا مرة أخرى إلى مصنف آخر الالتفافة عبر ملف تجعل مهمة تحديث شائعة مباشرة: ارفع الماكرو من نموذج من حقبة 2003 وازرعها في خرج XLS مولَّد حديثا، دون الحاجة إلى القالب الأصلي وقت التشغيل

var
  Src, Dst: IXLSWorkbook;   // مراجع واجهة: لا Free يدوي
begin
  Src := TXLSWorkbook.Create;
  if Src.Open('legacy-model.xls') <= 0 then
    raise Exception.Create('Cannot open legacy model');
  if Src.HasVBAProject then
    Src.SaveVBAProjectToFile('extracted-vba.bin');

  Dst := TXLSWorkbook.Create;
  Dst.Sheets.Add.Name := 'Report2026';
  Dst.LoadVBAProjectFromFile('extracted-vba.bin');
  Dst.SaveAs('report-with-macros.xls');
end;

نموذج الذاكرة هو الفخ هنا، ويسير عكس صنف XLSX TXLSWorkbook يُمسَك عبر واجهة IXLSWorkbook المعدودة المراجع، لذا لا تحرره أبدا يدويا؛ بينما TXLSXWorkbook في XLSX كائن عادي يجب أن تلفّه في try..finally وتحرره امزج العرفين في وحدة واحدة وستتبع ذلك انهيارات تحرير مزدوج حد آخر يستحق الاحترام: أبقِ الاستخراج والحقن داخل صيغة ملف واحدة تخزين مشروع BIFF وملف vbaProject.bin في OOXML قريبان، لا نفس الحاوية، وخط أنابيب يحتاج إلى إصدار الماكرو في كلتا الصيغتين ينبغي أن يحتفظ بقالب ماكرو منفصل لكل منهما

الروابط الخارجية: الخريطة تنجو، القيم المخزنة مؤقتا لا تنجو

لمصنفات XLSX، تعرض HotXLS الروابط الخارجية عبر مجموعة ExternalLinks كل TXLSXExternalLink يحمل Target، مسار أو رابط المصنف البعيد، إضافة إلى قائمة SheetNames تسمّي الأوراق التي يشير إليها كلاهما ينجو من دورة فتح وحفظ سليما، ويمكنك أيضا بناء رابط من الصفر

مخطط تدفق لاستدعاء حفظ في Delphi حيث يختار امتداد .xlsm نوع المحتوى الممكن بالماكرو ويرفض .xlsx الماكرو بصمت في Excel
يحمل HotXLS بايتات vbaProject.bin الخام عبر الحفظ، وامتداد .xlsm هو ما ينتقي نوع المحتوى الممكّن للماكرو الذي تتطلبه Excel
var
  Link: TXLSXExternalLink;
begin
  Link := Book.ExternalLinks.Add('\\fileserver\finance\fx-rates-2026.xlsx');
  Link.SheetNames.Add('FX');

  if Book.ExternalLinks.Count > 0 then
    Writeln(Format('%d external link(s): delivery requires reachable targets',
      [Book.ExternalLinks.Count]));
end;

الحد يقع مستوى أعمق من قائمة الأهداف HotXLS ينقل خريطة الرابط ذهابا وإيابا، أي الهدف وأسماء الأوراق، لكنه لا يحلل أو يعيد كتابة قيم الخلايا المخزنة مؤقتا التي يحتفظ بها OOXML في عنصر sheetDataSet الخاص بالرابط تلك الذاكرة المؤقتة هي ما يسمح لـExcel بعرض آخر رقم معروف حين يكون ملف المصدر غير متصل، ومصنف مولَّد يُشحَن دونها العاقبة تقع على المستلم، لا عليك افتح ملفا كهذا حيث الهدف غير قابل للوصول، حاسوب محمول خارج VPN أو مشاركة أُعيدت تسميتها، وستُحل الصيغ المعتمدة على الرابط إلى #REF! أو تتوقف خلف مطالبة تحديث فقاعدتان تنتجان عن هذا لا تعِد بأن مصنفا مولَّدا سيعرض قيمه المرتبطة خارجيا دون اتصال واقرأ ExternalLinks.Count غير الصفري كشرط مسبق للتسليم لا كميزة: كل هدف يجب أن يكون قابلا للوصول من أينما سيُفتَح الملف فعليا

مخطط لأي أجزاء من رابط مصنف خارجي في HotXLS تنجو من إعادة كتابة وما يحدث عندما تغيب القيم المخزنة خلف sheetDataSet دون اتصال
يُجري HotXLS رحلة ذهاب وإياب لهدف الرابط وأسماء أوراقه، لكن القيم المخزنة للخلايا خلف sheetDataSet لا تُحمل إلى الملف المولَّد

ما تحفظه قارئة XLS حرفا بحرف

بالنسبة إلى البنى التي لا تنمذجها، جانب BIFF لديه إجابة مختلفة: اتركها بالضبط كما وُجدت ذاكرات وعروض الجداول المحورية المؤقتة (عائلة سجلات SX*)، وتعريفات QueryTable، واتصالات البيانات الخارجية، والعروض المخصصة، وصور الترويسة، وسجلات السمات كلها تمر عبر دورة فتح وحفظ ككتل سجلات خام، غير مُحلَّلة وغير معدَّلة المراجع الخارجية نفسها تنتقل ذهابا وإيابا عبر سجلات EXTERNSHEET وSupBook الأساسية لا توجد واجهة إنشاء مكتوبة النوع لها في جانب XLS، لكن رابطا موجودا ينجو من التحرير دون لمس

الحفظ حرفا بحرف ضمان حقيقي بحافة حادة لأنه لا شيء يقرأ بنية محفوظة، لا يمكن لتعديلاتك إتلافها للسبب نفسه، لا شيء يحدّثها أيضا أدخل صفوفا عبر منطقة تشير إليها ذاكرة جدول محوري محفوظة أو جدول استعلام، وستحتفظ البنية بإحداثياتها الأصلية بينما تنزاح البيانات تحتها الملف لا يزال XML أو BIFF صالحا؛ المعنى انحرف بصمت عن المحاذاة، ولا يُطلَق خطأ ليخبرك التخطيط القابل للدفاع عنه هو إبقاء التعديلات المولَّدة على أوراق لا تحمل بنى محفوظة، وهو نفس الانضباط الذي يحمي الأوراق المقفلة والمهيّأة للطباعة في مقالتنا عن حماية أوراق العمل وإعداد الصفحة

التحقق من الملف الذي كتبته فعليا

كلا نمطي الفشل صامت وقت الكتابة، لذا التأكيد المهم يتم بإعادة فتح الخرج لا بالوثوق بالكود الذي أنتجه ثلاثة فحوص تغطي كل شيء تقريبا أعد فتح الملف وتأكد أن HasVbaProject لا تزال تعيد صحيحا كلما كانت الماكرو متوقعة، وهذا يلتقط حمولة أُسقطت وامتدادا خاطئا في اختبار واحد اقرأ ExternalLinks.Count وقارنه بالعدد من قبل إعادة الكتابة ثم افتح الملف مرة واحدة في Excel مع تعطيل الماكرو، لأن التحقق من نوع المحتوى في Excel أصرم من أي مكتبة، وExcel هو البرنامج الذي سيحكم عملاؤك على الملف من خلاله

لا شيء من ذلك يتطلب تحليلا كاملا في طريق الدخول حين تصل المصنفات بكميات وتحتاج فقط إلى فرز أيها يحمل محتوى محكوما، الفحص الخفيف في مقالتنا عن سرد الأوراق والفحص الخفيف للمصنفات يتيح لك توجيه الملفات الحاملة للماكرو والمرتبطة إلى خط أنابيب أصرم قبل أن تعمل أول إعادة كتابة على الإطلاق

بضعة أسئلة تتكرر بما يكفي للإجابة عنها مباشرة HotXLS لا تنفّذ أبدا الماكرو التي تحفظها: لا يوجد وقت تشغيل VBA في المكتبة، فقط الآلية لتخزين المشروع ونسخه واستخراجه وحقنه كبيانات على خادم، هذه خاصية أمان تستحق الذكر، لأن ماكرو معادية تمر عبر خط الأنابيب تبقى خاملة حتى يفتح Excel سطح المكتب الملف ويمكّن مستخدم المحتوى تحويل .xlsm إلى .xlsx مع الاحتفاظ بالماكرو غير ممكن، وذلك قاعدة الصيغة لا قيد المكتبة: نوع محتوى .xlsx يعلن مصنفا خاليا من الماكرو، لذا النتائج الصادقة الوحيدة هي البقاء على .xlsm أو استدعاء ClearVbaProject وشحن ملف لا يحمل أيا منها فعلا إعادة التسمية الصامتة هي الخيار الوحيد الذي لا يرضي أحدا وحين تُظهر الخلايا المرتبطة #REF! بعد إعادة كتابة، السبب هو ذاكرة القيم المفقودة الموضحة أعلاه: الملف الجديد يحمل الهدف لكن ليس الأرقام المخزنة مؤقتا، لذا يجب على Excel حل المصدر وقت الفتح، ومسار غير قابل للوصول أو نسبي للبيئة يهزمه إما اضمن أن الهدف قابل للوصول أو اكتب قيما محسوبة في الخلايا قبل التسليم وأسقط الاعتمادية كليا

تحرير مصنفات الآخرين هو في معظمه عمل الحفاظ على أشياء لم تكتبها ولا تفهمها تماما مرافق نقل VBA والروابط الخارجية ذهابا وإيابا الموصوفة هنا تأتي مع HotXLS Delphi Component لـDelphi وC++Builder، إلى جانب خصائص التدقيق التي تتيح لك كشف المحتوى المحكوم في اللحظة التي يصل فيها ملف