开发者

Bug with RTTI TRttiMethod.Invoke, stdcall and const parameters

开发者 https://www.devze.com 2023-03-14 18:55 出处:网络
I have a problem with RTTI TRttiMethod.Invoke, stdcall and const parameters: obj := TClassRecordTest.Create;

I have a problem with RTTI TRttiMethod.Invoke, stdcall and const parameters:

    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 it is:

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;

and functions 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;

The result of this is:

>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                                                                                                                                                                                                                                                                                    

This error occurs only when used as parameters const and stdcall.

If 开发者_运维技巧i change Test3 and 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;

The result is:

>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

It turns out that TRttiMethod.Invoke const passes by value, although it was necessary to pass the address


You ran into the same problem like me. Let me quote what Barry said:

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.

So in order to pass const, out and var arguments you need to use TValue.From<Pointer>()


If you think you've found a bug, then you should submit it to Embarcadero's QC system:

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

That is the proper place for bug reports and it is also the only place where submitting a bug report has any chance of resulting in a bug fix.

0

精彩评论

暂无评论...
验证码 换一张
取 消