将TArgList参数转换为TValue

时间:2017-02-23 09:45:26

标签: delphi rtti

我有一个函数返回TArray< TValue>给出一个TArgList。

function GetParameters(Args: TArgList): TArray<TValue>;
var
  Parameters: TArray<TValue>;
  I: Integer;

    function ArgParam(Index: Integer): OleVariant;
    begin
        Result := OleVariant(Args.Arguments[Args.Count - Index - 1]);
    end;

begin
  Parameters := nil;
  for I := 0 to Args.Count - 1 do
  begin
    SetLength(Parameters, Length(Parameters) + 1);
    Parameters[High(Parameters)] := TValue.FromVariant(ArgParam(I));
  end;
  Result := Parameters;
end;

然后传递此函数的结果以调用RTTI方法。

procedure ExecMethod(MethodName: string; Args: TArray<TValue>);
var
  Method: TRttiMethod;
  Ctx: TRttiContext;
  vType: TRttiType;
  I: Integer;
begin
  vType := nil;
  Ctx := TRttiContext.Create;
  try
    vType := Ctx.GetType(FControl.ClassInfo);

    for Method in vType.GetMethods do
    begin
      if SameText(Method.Name, MethodName) then
            break;
    end;

    if Assigned(Method) then
      Method.Invoke(FControl, Args);
  finally
    Ctx.Free;
    vType.Free;
  end;
end;

这适用于简单类型,例如int,string或boolean等 但是,当其中一个参数是对象/组件时。那就是我撞墙的地方。不知何故,如果方法需要对象/组件,我需要转换或输入一个参数。

Parameters[High(Parameters)] := TValue.FromVariant(ArgParam(Args, I));

如何将其转换为TValue?尝试TValue.From&lt; TObject&gt;,TValue.From&lt; Pointer&gt;但我仍然得到无效的类型转换或访问冲突。

感谢您的回复。

附录

TArgList = record
   Arguments: PVariantArgList;
   Count: Integer;
end;

PVariantArgList在ActiveX

中声明

Invoke签名是:

function Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  ArgErr: Pointer): HResult;
var
    DispParams: PDispParams;
  **ArgList: TArgList;**
begin
    DispParams := @Params;
    ArgList.Count := DispParams.cArgs;
    ArgList.Arguments := DispParams.rgvarg;
    Result := DISP_E_BADINDEX;
    try
      if Flags and DISPATCH_METHOD = DISPATCH_METHOD then
      begin
        Dec(DispId, BaseMethodDispid);
        if (DispId > -1) and (DispId < FMethods.Count) then
        begin
          Result := S_OK;
          ExecMethod(FMethods[DispId], GetParameters(ArgList));
        end
      end
    .
    .
    .

FMethods只是一个方法的字符串列表。其中DispID是方法名称列表中的索引。

1 个答案:

答案 0 :(得分:0)

假设Args.Arguments指向一个有效的变体数组,并且变体带有OLE自动化类型,那么你的GetParameters函数应该没有任何问题。

所以,Args.Arguments可能没有指向正确的位置,因为它的派生方式看起来很可疑:

function Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  ArgErr: Pointer): HResult;
var
    DispParams: PDispParams;
  **ArgList: TArgList;**
begin
    DispParams := @Params;
    ArgList.Count := DispParams.cArgs;
    ArgList.Arguments := DispParams.rgvarg;

&#34; var Params&#34;是无类型的,您使用&#34; @ Params&#34;制作指向TDispParams的指针。所以,ArgList.Arguments完全依赖于传递给&#34; Invoke&#34; (你没有透露给我们。)

现在,也许Args.Arguments指向有效的变体列表。你说,给你带来问题的变量参数包含一个&#34; IDispatch&#34;,这是一个有效的OLE自动化类型,所以你的函数应该处理它们;但是你展示了一个例子,你试图使用TValue.From&lt; TObject&gt;来施放olevariant。这将失败,因为olevariant不能携带对象实例,只能携带接口(通常是IUnknown或IDispatch)。我假设你得到了#34;无效的变体类型&#34;来自&#34; TValue.FromVariant(ArgParam(I))的例外;&#34;。 我怀疑那些不是olevariants而是包含对象(不是接口)的Delphi变体。如果是这种情况,您可以尝试这样的事情:

function GetParameters_usingVariantCracking(Args: TArgList): TArray<TValue>;

var
  Parameters: TArray<TValue>;
  I: Integer;

    function ArgParam(Index: Integer) : TValue;

    var
       ArgumentType : TVarType;
       AnObject : TObject;

    begin
       ArgumentType := TVarData(Args.Arguments[Args.Count - Index - 1]).VType;
       if (ArgumentType = varObject) then
          Result := TObject(TVarData(Args.Arguments[Args.Count - Index - 1]).VPointer)
       else
          Result := TValue.FromVariant(Args.Arguments[Args.Count - Index - 1])
    end;

begin
  Parameters := nil;
  SetLength(Parameters, Args.Count);
  for I := 0 to Args.Count - 1 do
    Parameters[High(Parameters)] := ArgParam(I);

  Result := Parameters;
end;

详细了解olevariant和variant之间的区别: Variant Types (Delphi)