Documentação do HotXLS

Callback OnUserFunction / OnUserFunctionEx

O callback OnUserFunction permite que o código da aplicação avalie funções de planilha personalizadas ou não suportadas durante Calculate. Atribua-o em TXLSWorkbook ou TXLSXWorkbook antes de chamar Calculate
Use OnUserFunctionEx quando o callback precisar do contexto da fórmula atual. O contexto reporta o índice da planilha (base 1) mais a linha e a coluna (base 1) da célula da fórmula; chamadas de Calculate no nível da pasta de trabalho que não estão avaliando uma célula reportam linha e coluna como 0

Sintaxe

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;

Argumentos

FunctionName O nome da função extraído do texto da fórmula
Args Um array de Variant com base 0 contendo os valores avaliados dos argumentos. Para funções sem argumentos, esse valor é Null
Context Para OnUserFunctionEx, reporta SheetIndex, Row e Col, além do descritor de argumentos: ArgCount corresponde ao comprimento do array Args (0 para chamadas sem argumentos) e ArgKinds classifica cada argumento como akuValue (valor literal), akuRangeRef (referência a célula ou intervalo) ou akuArray (constante de matriz). Os valores de planilha, linha e coluna são base 1 para fórmulas de célula; linha e coluna são 0 quando nenhuma célula está sendo avaliada. Os tipos são capturados antes que os argumentos sejam avaliados, então um handler no estilo SUM ou MAP pode saber que o chamador escreveu uma referência mesmo que a entrada Args avaliada carregue apenas o valor
Value Defina isto como o resultado da função
Handled Defina como True quando o callback fornecer um resultado. Deixe False para manter o comportamento normal de função não suportada

Política de segurança de Callback

O AllowUnsafeFormulaCallbacks tem padrão False nas pastas de trabalho XLS clássicas e XLSX, então preservar uma fórmula nunca autoriza o código da aplicação host a tratar um nome de função perigosa externa ou no estilo macro
A barreira padrão cobre 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 e FILE.DELETE depois que qualquer prefixo reconhecido de future-function é normalizado
A negação acontece antes da avaliação dos argumentos e antes de callbacks locais da pasta de trabalho, de processo, de resolver ou de evento rodarem; nomes comuns de funções personalizadas e não suportadas mantêm o comportamento de callback existente
Defina a propriedade da pasta de trabalho como True apenas quando toda fórmula e handler registrado for confiável; a avaliação contextual pode, em vez disso, habilitar isso para uma única chamada por meio de TXLSFormulaEvaluationOptions.AllowUnsafeFormulaCallbacks, enquanto um False fornecido explicitamente sobrepõe uma habilitação no nível da pasta de trabalho

Exemplo

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

Exemplo com contexto

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

Recebendo argumentos de intervalo

Quando um argumento avalia para um intervalo de planilha, a entrada Args é um Variant varUnknown que contém um ICalcRangeList; o valor avaliado de cada célula coberta não é copiado para o Variant. Teste e extraia com o overload de VarIsRange que retorna a lista, depois leia os retângulos resolvidos por meio de 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;
    
As coordenadas seguem a convenção de TXLSGetValue: base 0 e inclusivas. Um handler que quiser retornar um intervalo ele próprio constrói um TXLSCalcRangeList e o embrulha com RangeToVariant antes de atribuir Value; o motor então mantém a referência do intervalo para fórmulas dependentes em vez de um snapshot escalar

Veja também