Delphi RTTI TRttiMethod.Invoke、stdcall和const参数存在错误
我对RTTI TRttiMethod有问题。Invoke、stdcall和const参数:Delphi RTTI TRttiMethod.Invoke、stdcall和const参数存在错误,delphi,delphi-xe,rtti,Delphi,Delphi Xe,Rtti,我对RTTI TRttiMethod有问题。Invoke、stdcall和const参数: obj := TClassRecordTest.Create; try b.a := 10; b.b := 100; a.a := 1; a.b := 2; writeln('b.a='+IntToStr(b.a)+' b.b='+IntToStr(b.b)); writeln; writeln('call test1');
obj := TClassRecordTest.Create;
try
b.a := 10; b.b := 100;
a.a := 1; a.b := 2;
writeln('b.a='+IntToStr(b.a)+' b.b='+IntToStr(b.b));
writeln;
writeln('call test1');
writeln('a.a='+IntToStr(a.a)+' a.b='+IntToStr(a.b));
r := VToRec(RTTICall(obj, 'Test1', @a, @b));
writeln('test1 r.a='+IntToStr(r.a)+' r.b='+IntToStr(r.b));
a.a := 2; a.b := 3;
writeln('call test2');
writeln('a.a='+IntToStr(a.a)+' a.b='+IntToStr(a.b));
r := VToRec(RTTICall(obj, 'Test2', @a, @b));
writeln('test3 r.a='+IntToStr(r.a)+' r.b='+IntToStr(r.b));
a.a := 3; a.b := 4;
writeln('call test3');
writeln('a.a='+IntToStr(a.a)+' a.b='+IntToStr(a.b));
r := VToRec(RTTICall(obj, 'Test3', @a, @b));
writeln('test3 r.a='+IntToStr(r.a)+' r.b='+IntToStr(r.b));
a.a := 4; a.b := 5;
writeln('call test4');
writeln('a.a='+IntToStr(a.a)+' a.b='+IntToStr(a.b));
r := VToRec(RTTICall(obj, 'Test4', @a, @b));
writeln('test4 r.a='+IntToStr(r.a)+' r.b='+IntToStr(r.b));
finally
obj.Destroy;
end;
RTTICall它是:
function RTTICall(aObj: TObject; MethodName: string; a, b: pointer): TValue;
var
RttiContext: TRttiContext;
ClassType: TRttiType;
Methods: TMethodList;
Method: TRttiMethod;
Params: TParamList;
Args: TArgList;
begin
RttiContext := TRttiContext.Create;
try
ClassType := FindFirstClassTypeByName(RttiContext, aObj.ClassName);
if ClassType <> nil then
begin
Methods := ClassType.GetDeclaredMethods;
for Method in Methods
do begin
if SameText(Method.Name, MethodName) then
begin
Params := Method.GetParameters;
SetLength(Args, Length(Params));
TValue.Make(nil, Params[0].ParamType.Handle, Args[0]);
move(a^, Args[0].GetReferenceToRawData^, Params[0].ParamType.TypeSize);
TValue.Make(nil, Params[1].ParamType.Handle, Args[1]);
move(b^, Args[1].GetReferenceToRawData^, Params[1].ParamType.TypeSize);
Result := Method.Invoke(TObject(aObj), Args);
exit;
end;
end;
end;
finally
// FreeAndNil(aObj);
end;
end;
其结果是:
>Project7.exe
b.a=10 b.b=100
call test1
a.a=1 a.b=2
test1 r.a=11 r.b=102
call test2
a.a=2 a.b=3
test3 r.a=12 r.b=103
call test3
a.a=3 a.b=4
test3 r.a=13 r.b=104
call test4
a.a=4 a.b=5
EAccessViolation: Access violation at address 0047A65A in module 'Project7.exe'. Read of address 00000004
此错误仅在用作参数const和stdcall时发生
如果我更改Test3和Test4:
function TClassRecordTest.Test3(const a, b: TRecordTest): TRecordTest;
begin
writeLn('@a='+IntToStr(integer(@a))+' @b='+IntToStr(integer(@a)));
result.a := a.a+b.a;
result.b := a.b+b.b;
end;
function TClassRecordTest.Test4(const a, b: TRecordTest): TRecordTest;
begin
writeLn('@a='+IntToStr(integer(@a))+' @b='+IntToStr(integer(@a)));
result.a := a.a+b.a;
result.b := a.b+b.b;
end;
结果是:
>Project7.exe
b.a=10 b.b=100
call test1
a.a=1 a.b=2
test1 r.a=11 r.b=102
call test2
a.a=2 a.b=3
test3 r.a=12 r.b=103
call test3
a.a=3 a.b=4
@a=31301448 @b=31301448
test3 r.a=13 r.b=104
call test4
a.a=4 a.b=5
@a=4 @b=4
EAccessViolation: Access violation at address 0047A76C in module 'Project7.exe'. Read of address 00000004
事实证明,TRttiMethod.Invoke const是按值传递的,尽管传递地址是必要的,但如果您认为自己发现了错误,则应将其提交给Embarcadero的QC系统:
这是错误报告的适当地点,也是提交错误报告有可能导致错误修复的唯一地点。您遇到了与我相同的问题。让我引用巴里的话: 这是故意的;Invoke函数在堆栈上的级别太低,无法访问任何可以告诉它是通过引用还是通过值传递参数的typeinfo。它希望所有参数都转换为正确的类型,包括根据需要转换为指针的任何by ref参数。它所做的只是根据需要将值填充到寄存器和/或堆栈中,调用并从适当的位置检索返回值(如果有)
因此,为了传递const、out和var参数,您需要使用TValue.From()谢谢。我会设法解决;-)旧的几年前就被替换了。还要注意,这样您就不能再访问
qc.embarcadero.com
链接了。
>Project7.exe
b.a=10 b.b=100
call test1
a.a=1 a.b=2
test1 r.a=11 r.b=102
call test2
a.a=2 a.b=3
test3 r.a=12 r.b=103
call test3
a.a=3 a.b=4
@a=31301448 @b=31301448
test3 r.a=13 r.b=104
call test4
a.a=4 a.b=5
@a=4 @b=4
EAccessViolation: Access violation at address 0047A76C in module 'Project7.exe'. Read of address 00000004