delphi – 带有RTTI TRttiMethod.Invoke,stdcall和const参数的Bug

前端之家收集整理的这篇文章主要介绍了delphi – 带有RTTI TRttiMethod.Invoke,stdcall和const参数的Bug前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有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',@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',@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',@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按值传递,尽管有必要传递地址

解决方法

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

This is by design; the Rtti.Invoke function is at too low a level on the stack,and has no access to any typeinfo that could tell it whether to pass arguments by reference or by value. It expects all parameters to be converted to the correct type,including any by-ref parameters being converted to pointers as needed. All it does is stuff the values into registers and/or stack as required,invoke,and retrieve the return value (if any) from the appropriate location.

所以为了传递const,out和var参数,你需要使用TValue.From< Pointer>()

猜你在找的Delphi相关文章