2011-06-21 7 views
6

私はRTTI TRttiMethod.Invoke、stdcall規約およびconstのパラメータに問題がある: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として使用された場合にのみ発生します。

iは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は、値を通過することが判明

+0

感謝します。 – Mielofon

答えて

11

あなたは私と同じ問題に遭遇しました。バリーが言ったことを私に言わせてください:

これは仕様です。 Rtti.Invoke関数はスタックのレベルが低すぎるため、参照渡しと値渡しのどちらで引数を渡すべきかを指定できるtypeinfoへのアクセス権はありません。必要に応じてby-refパラメータをポインタに変換するなど、すべてのパラメータが正しい型に変換されることを期待しています。それは、必要に応じて値をレジスタやスタックに埋め込み、呼び出し、適切な場所からの戻り値(存在する場合)を取得することだけです。

アウトのconstに合格するために、そう

、そしてあなたがTValue.From <ポインタ>を(使用する必要があるのvar引数)

+0

ありがとうございます。それはよい考えです。 – Mielofon

0

バグを見つけたと思われる場合は、エンバカデロのQCシステムに送信してください。

バグレポートのための適切な場所であり、それはまた、バグレポートを提出するとバグ修正が生じの 任意のチャンスを持っている唯一の場所である

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

+0

数年前に古い[QualityCentral](http://qc.embarcadero.com)が[品質ポータル](http://quality.embarcadero.com)に置き換えられました。また、[QualityCentralがシャットダウンされました](https://community.embarcadero.com/blogs/entry/quality-keeps-moving-forward)、 'qc.embarcadero.com'リンクにはもうアクセスできません。 –

関連する問題