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

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