HotXLS 文件

OnUserFunction / OnUserFunctionEx 回呼

OnUserFunction 回呼讓應用程式程式碼可以在 Calculate 期間求值自訂或其他不受支援的工作表函式。請在呼叫 Calculate 之前,把它指派到 TXLSWorkbook 或 TXLSXWorkbook 上
當回呼需要目前的公式上下文時,請使用 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 一個 0 起始的 Variant 陣列,裝著已求值的引數值。對零引數函式,這個值是 Null
Context 用於 OnUserFunctionEx,回報 SheetIndex、Row、Col,外加引數描述子:ArgCount 對應 Args 陣列長度(零引數呼叫為 0),ArgKinds 把每個引數分類為 akuValue(literal 值)、akuRangeRef(儲存格或範圍參照)或 akuArray(陣列常數)。對儲存格公式而言,工作表、列與欄是 1 起始;沒有正在求值的儲存格時,列與欄為 0。這些種類是在引數求值之前記錄的,所以 SUM 或 MAP 風格的處理器能分辨呼叫端寫的是參照,即使求值後的 Args 項目只帶值
Value 把它設成函式結果
Handled 當回呼提供了結果時設為 True。保持 False 則維持一般不支援函式的行為

回呼安全政策

傳統 XLS 與 XLSX 活頁簿的 AllowUnsafeFormulaCallbacks 預設為 False,因此保留一條公式,並不代表授權宿主應用程式程式碼去處理危險的外部或巨集風格函式名稱
預設的防護閘涵蓋 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 前置詞正規化
拒絕發生在引數求值之前,也早在活頁簿層級、處理程序層級、resolver 或事件回呼執行之前;一般自訂與不受支援的函式名稱則維持原有的回呼行為
只有在每一條公式與每個已註冊處理器都可信時,才把活頁簿屬性設為 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 項目會是一個裝著 ICalcRangeList 的 varUnknown Variant;每個被涵蓋儲存格的求值結果不會複製進 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 慣例:0 起始且含端點。想要自行傳回範圍的處理器,會先建一個 TXLSCalcRangeList,用 RangeToVariant 包裝後再指派給 Value;引擎之後會為相依公式保留這個範圍參照,而不是一份純量快照

另請參閱