我有一个函数返回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是方法名称列表中的索引。
答案 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)