RTTI TRttiMethod.Invoke,stdcall和const参数的错误

时间:2011-06-21 07:17:36

标签: 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');
      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;

和函数TestN:

function TClassRecordTest.Test1(a, b: TRecordTest): TRecordTest;
begin
  result.a := a.a+b.a;
  result.b := a.b+b.b;
end;

function TClassRecordTest.Test2(var a, b: TRecordTest): TRecordTest;
begin
  result.a := a.a+b.a;
  result.b := a.b+b.b;
end;

function TClassRecordTest.Test3(const a, b: TRecordTest): TRecordTest;
begin
  result.a := a.a+b.a;
  result.b := a.b+b.b;
end;

function TClassRecordTest.Test4(const  a, b: TRecordTest): TRecordTest;
begin
  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
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按值传递,尽管有必要传递地址

2 个答案:

答案 0 :(得分:11)

你遇到了和我一样的问题。让我引用巴里所说的话:

  

这是设计上的; Rtti.Invoke函数在堆栈上的级别太低,并且无法访问任何可以通过引用或值传递参数的typeinfo。它期望将所有参数转换为正确的类型,包括根据需要将任何by-ref参数转换为指针。它所做的就是根据需要将值填充到寄存器和/或堆栈中,从适当的位置调用和检索返回值(如果有的话)。

因此,为了传递const,out和var参数,您需要使用TValue.From&lt; Pointer&gt;()

答案 1 :(得分:0)

如果您认为自己发现了错误,那么您应该将其提交给Embarcadero的质量控制系统:

http://qc.embarcadero.com/wc/qcmain.aspx

这是错误报告的正确位置,它也是唯一提交错误报告任何导致错误修复的机会的地方。