У меня проблема с параметрами 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 проходит по значению, хотя необходимо было передать адрес