توثيق HotXLS

معاودة الاتصال OnUserFunction / OnUserFunctionEx

تتيح معاودة الاتصال OnUserFunction لكود التطبيق تقييم دوال ورقة العمل المخصصة أو غير المدعومة أثناء Calculate. عيِّنها على TXLSWorkbook أو TXLSXWorkbook قبل استدعاء Calculate
استخدم OnUserFunctionEx عندما تحتاج معاودة الاتصال إلى سياق الصيغة الحالي. يُبلِّغ السياق عن فهرس الورقة القائم على 1 بالإضافة إلى صف وعمود خلية الصيغة القائمَين على 1؛ تُبلِّغ استدعاءات Calculate على مستوى المصنف التي لا تُقيِّم خلية بصف وعمود = 0

البنية

type
  TXLSUserArgKind = (akuValue, akuRangeRef, akuArray);

  TXLSUserFunctionContext = record
    SheetIndex: Integer;
    Row: Integer;
    Col: Integer;
    ArgCount: Integer;
    ArgKinds: array of TXLSUserArgKind;
  end;

  TXLSUserFunctionEvent = procedure(
    Sender: TObject;
    const FunctionName: WideString;
    const Args: Variant;
    var Value: Variant;
    var Handled: Boolean) of object;

  TXLSUserFunctionExEvent = procedure(
    Sender: TObject;
    const FunctionName: WideString;
    const Args: Variant;
    const Context: TXLSUserFunctionContext;
    var Value: Variant;
    var Handled: Boolean) of object;

property OnUserFunction: TXLSUserFunctionEvent;
property OnUserFunctionEx: TXLSUserFunctionExEvent;
property AllowUnsafeFormulaCallbacks: Boolean;

المتغيرات

FunctionName اسم الدالة كما ورد في نص الصيغة
Args مصفوفة Variant ذات فهرس صفري تحمل قيم الوسائط بعد تقييمها. في الدوال بلا وسائط تكون هذه القيمة Null
Context في OnUserFunctionEx، يُبلّغ عن SheetIndex وRow وCol، إضافة إلى واصف الوسائط: ArgCount يطابق طول مصفوفة Args (0 في الاستدعاءات بلا وسائط)، بينما يصنّف ArgKinds كل وسيط على أنه akuValue (قيمة literal) أو akuRangeRef (إشارة إلى خلية أو نطاق) أو akuArray (ثابت مصفوفة). قيم الورقة والصف والعمود قائمة على 1 في صيغ الخلايا؛ ويكون الصف والعمود 0 عندما لا تجري عملية تقييم لأي خلية. تُلتقط هذه الأنواع قبل تقييم الوسائط، بحيث يستطيع المعالج ذو الأسلوب الشبيه بـ SUM أو MAP أن يميّز أن مستدعي الصيغة كتب إشارة مرجعية حتى لو حمل إدخال Args بعد التقييم القيمة فقط
Value أسند إليها نتيجة الدالة
Handled أسندها إلى True عندما توفّر معاودة الاتصال نتيجة. واتركها False للاحتفاظ بالسلوك المعتاد تجاه الدوال غير المدعومة

سياسة أمان معاودة الاتصال

قيمة AllowUnsafeFormulaCallbacks الافتراضية هي False في مصنفات Classic XLS وXLSX، فالحفاظ على صيغة لا يخوّل أبدًا كود التطبيق المضيف معالجة اسم دالة خارجية خطرة أو من النمط الماكروي
تغطي البوابة الافتراضية DDE وCALL وREGISTER وREGISTER.ID وWEBSERVICE وRTD وSQL.REQUEST وEXEC وRUN وCREATE.OBJECT وAPP.ACTIVATE وSEND.KEYS وOPEN وSAVE وSAVE.AS وFOPEN وFWRITE وFWRITELN وFCLOSE وFILE.DELETE بعد توحيد أي بادئة future-function معروفة
يحدث الرفض قبل تقييم الوسائط وقبل تشغيل معاودات الاتصال المحلية على مستوى المصنف أو العملية أو الخاصة بالمحلِّل أو بالأحداث؛ أما أسماء الدوال المخصصة وغير المدعومة العادية فتبقي على سلوك معاودة الاتصال الحالي لديها
اضبط خاصية المصنف على True فقط عندما تكون كل الصيغ والمعالجات المسجلة موثوقة؛ وبدلًا من ذلك يمكن لتقييم سياقي معيّن أن يفعّل الخيار لاستدعاء واحد عبر TXLSFormulaEvaluationOptions.AllowUnsafeFormulaCallbacks، في حين يتجاوز تمرير False صراحةً أي تفعيل على مستوى المصنف بأكمله

مثال

procedure TForm1.WorkbookUserFunction(Sender: TObject;
  const FunctionName: WideString; const Args: Variant;
  var Value: Variant; var Handled: Boolean);
begin
  if SameText(FunctionName, 'DOUBLEPLUS') then
  begin
    Value := Double(Args[0]) * 2 + Double(Args[1]);
    Handled := True;
  end;
end;

Workbook.OnUserFunction := WorkbookUserFunction;
Value := Workbook.Calculate('=DOUBLEPLUS(A1;5)');

مثال مُدرك للسياق

procedure TForm1.WorkbookUserFunctionEx(Sender: TObject;
  const FunctionName: WideString; const Args: Variant;
  const Context: TXLSUserFunctionContext;
  var Value: Variant; var Handled: Boolean);
begin
  if SameText(FunctionName, 'ROWBONUS') then
  begin
    Value := Double(Args[0]) + Context.Row + Context.Col;
    Handled := True;
  end;
end;

Workbook.OnUserFunctionEx := WorkbookUserFunctionEx;
Value := Worksheet.Calculate('=C2');

استقبال وسائط النطاقات

عندما يُقيَّم وسيط إلى نطاق من ورقة العمل، يكون إدخال Args هو Variant من النوع varUnknown يحمل ICalcRangeList؛ ولا تُنسخ القيمة المقَّيمة لكل خلية تغطيها النطاقات إلى الـ Variant. اختبره واستخرجه باستخدام التحميل الزائد من VarIsRange الذي يعيد القائمة، ثم اقرأ المستطيلات المحلولة عبر TXLSCalcRange
type
  TXLSCalcRange = class
    property row1: Integer;   { 0-based, inclusive }
    property col1: Integer;   { 0-based, inclusive }
    property row2: Integer;
    property col2: Integer;
    property SheetIndex: Integer;
  end;

  ICalcRangeList = interface
    property Count: Integer;
    property Item[Index: Integer]: TXLSCalcRange;
  end;

function VarIsRange(Value: Variant): Boolean; overload;
function VarIsRange(Value: Variant; var Int: ICalcRangeList): Boolean; overload;
function RangeToVariant(Value: TXLSCalcRangeList): Variant;
    
تتبع الإحداثيات اصطلاح TXLSGetValue نفسه: قائمة على الصفر وشاملة للطرفين. والمعالج الذي يريد أن يعيد نطاقًا بنفسه يبني TXLSCalcRangeList ويغلّفه بـ RangeToVariant قبل إسناد Value؛ يحتفظ عندها المحرك بالإشارة إلى النطاق للصيغ التابعة بدل لقطة عددية واحدة

انظر أيضاً