HotXLS-Dokumentation

OnUserFunction / OnUserFunctionEx-Rückruf

Der OnUserFunction-Rückruf ermöglicht es Anwendungscode, benutzerdefinierte oder anderweitig nicht unterstützte Tabellenblattfunktionen während Calculate auszuwerten. Weisen Sie ihn TXLSWorkbook oder TXLSXWorkbook vor dem Aufrufen von Calculate zu
Verwenden Sie OnUserFunctionEx, wenn der Rückruf den aktuellen Formelkontext benötigt. Der Kontext meldet den 1-basierten Blattindex sowie die 1-basierte Zeile und Spalte der Formelzelle; Aufrufe von Calculate auf Arbeitsmappenebene, die keine Zelle auswerten, melden Zeile und Spalte als 0

Syntax

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;

Argumente

FunctionName Der Funktionsname aus dem Formeltext
Args Ein nullbasiertes Variant-Array mit den ausgewerteten Argumentwerten. Für Funktionen ohne Argumente ist dieser Wert Null
Context Bei OnUserFunctionEx meldet er SheetIndex, Row und Col plus den Argument-Deskriptor: ArgCount entspricht der Länge des Args-Arrays (0 bei Aufrufen ohne Argumente), und ArgKinds klassifiziert jedes Argument als akuValue (Literalwert), akuRangeRef (Zell- oder Bereichsreferenz) oder akuArray (Array-Konstante). Blatt-, Zeilen- und Spaltenwerte sind bei Zellformeln 1-basiert; Zeile und Spalte sind 0, wenn keine Zelle ausgewertet wird. Die Kinds werden erfasst, bevor die Argumente ausgewertet werden — ein Handler im Stil von SUM oder MAP erkennt also, dass der Aufrufer eine Referenz geschrieben hat, obwohl der ausgewertete Args-Eintrag nur den Wert trägt
Value Hier setzen Sie das Funktionsergebnis
Handled Auf True setzen, wenn der Callback ein Ergebnis liefert. Auf False lassen, um das übliche Verhalten für nicht unterstützte Funktionen beizubehalten

Sicherheitsrichtlinie für Callbacks

AllowUnsafeFormulaCallbacks steht bei Classic-XLS- und XLSX-Arbeitsmappen standardmäßig auf False — das Erhalten einer Formel autorisiert also nie, dass Code der Hostanwendung einen gefährlichen externen oder makroartigen Funktionsnamen behandelt
Die Standard-Sperre umfasst 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 und FILE.DELETE, nachdem ein erkanntes Future-Functions-Präfix normalisiert wurde
Die Ablehnung passiert vor der Argumentauswertung und bevor arbeitsmappenlokale, prozessweite, Resolver- oder Ereignis-Callbacks laufen; gewöhnliche benutzerdefinierte und nicht unterstützte Funktionsnamen behalten ihr bisheriges Callback-Verhalten
Setzen Sie die Arbeitsmappen-Eigenschaft nur dann auf True, wenn jede Formel und jeder registrierte Handler vertrauenswürdig ist; die kontextbezogene Auswertung kann sich stattdessen für einen einzelnen Aufruf über TXLSFormulaEvaluationOptions.AllowUnsafeFormulaCallbacks anmelden, während ein explizit gesetztes False eine arbeitsmappenweite Anmeldung überschreibt

Beispiel

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)');

Kontextbezogenes Beispiel

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');

Bereichsargumente entgegennehmen

Lautet ein Argument auf einen Arbeitsblattbereich, ist der Args-Eintrag ein varUnknown-Variant, der eine ICalcRangeList hält; der ausgewertete Wert jeder abgedeckten Zelle wird nicht in den Variant kopiert. Prüfen und extrahieren Sie ihn mit der VarIsRange-Überladung, die die Liste zurückgibt, und lesen Sie dann die aufgelösten Rechtecke über 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;
    
Die Koordinaten folgen der TXLSGetValue-Konvention: nullbasiert und inklusiv. Ein Handler, der selbst einen Bereich zurückgeben will, baut eine TXLSCalcRangeList und wickelt sie mit RangeToVariant ein, bevor er Value zuweist; die Engine behält dann die Bereichsreferenz für abhängige Formeln, statt eines skalaren Schnappschusses

Siehe auch