如何断言给定的方法指针使用stdcall调用约定?

时间:2011-05-22 00:14:39

标签: delphi rtti

在我的库中,我在特定条件下调用方法,这需要stdcall调用约定。目前我正在使用编译器静态解析,实现为相当大的众所周知的方法签名列表和我子例程的相应重载版本。这有效,但看起来很脏,并没有100%覆盖所有可能的方法。我想增加使用泛型方法指针的可能性,并通过询问RTTI来断言正确的调用约定。在这里我被卡住了,请指教。

Input: code/data pair of pointers as in TMethod 
Output: boolean indicator, true if method is stdcall

我最好使用“经典”RTTI来创建更少的版本依赖项,但是我无法在“经典”RTTI中找到任何调用约定指示符...


注意:此问题与导入外部功能无关

3 个答案:

答案 0 :(得分:3)

您可以从扩展的RTTI中提取调用约定信息(自Delphi 2010起可用)。

uses RTTI, TypInfo;

function GetMethCallConv(AMeth: TMethod; out Conv: TCallConv): Boolean;
var
  Ctx: TRttiContext;
  Meth: TRttiMethod;
  Typ: TRttiType;

begin
  Ctx:= TRttiContext.Create;
  try
    Typ:= Ctx.GetType(TObject(AMeth.Data).ClassType);
    for Meth in Typ.GetMethods do begin
      if Meth.CodeAddress = AMeth.Code then begin
        Conv:= Meth.CallingConvention;
        Exit(True);
      end;
    end;
    Exit(False);
  finally
    Ctx.Free;
  end;
end;

//test

type
  TMyObj = class
  public
    procedure MyMeth(I: Integer); stdcall;
  end;

procedure TMyObj.MyMeth(I: Integer);
begin
  ShowMessage(IntToStr(I));
end;
procedure TForm2.Button2Click(Sender: TObject);
var
  Conv: TCallConv;
  Meth: TMethod;
  MyObj: TMyObj;

begin
  MyObj:= TMyObj.Create;
  Meth.Code:= @TMyObj.MyMeth;
  Meth.Data:= MyObj;
  if GetMethCallConv(Meth, Conv) then begin
    case Conv of
      ccReg: ShowMessage('Register');
      ccCdecl: ShowMessage('cdecl');
      ccPascal: ShowMessage('Pascal');
      ccStdCall: ShowMessage('StdCall');
      ccSafeCall: ShowMessage('SafeCall');
    end;
  end;
  MyObj.Free;
end;

<强>更新

对于“经典”RTTI阅读Sertac答案;以下在Delphi 2010上正常工作:

uses ObjAuto;

function GetMethCallConv2(AMeth: TMethod; out Conv: TCallingConvention): Boolean;
var
  Methods: TMethodInfoArray;
  I: Integer;
  P: PMethodInfoHeader;

begin
  Result:= False;
  Methods:= GetMethods(TObject(AMeth.Data).ClassType);
  if not Assigned(Methods) then Exit;

  for I:= Low(Methods) to High(Methods) do begin
    P:= Methods[I];
    if P^.Addr = AMeth.Code then begin
      Inc(Integer(P), SizeOf(TMethodInfoHeader) - SizeOf(ShortString) + 1 +
        Length(PMethodInfoHeader(P)^.Name));
      Conv:= PReturnInfo(P).CallingConvention;
      Result:= True;
      Exit;
    end;
  end;
end;

{$TYPEINFO ON}
{$METHODINFO ON}
type
  TMyObj = class
  public
    procedure MyMeth(I: Integer);
  end;

procedure TMyObj.MyMeth(I: Integer);
begin
  ShowMessage(IntToStr(I));
end;

procedure TForm2.Button3Click(Sender: TObject);
var
  Conv: TCallingConvention;
  Meth: TMethod;
  MyObj: TMyObj;

begin
  MyObj:= TMyObj.Create;
  Meth.Code:= @TMyObj.MyMeth;
  Meth.Data:= MyObj;
  if GetMethCallConv2(Meth, Conv) then begin
    case Conv of
      ccRegister: ShowMessage('Register');
      ccCdecl: ShowMessage('cdecl');
      ccPascal: ShowMessage('Pascal');
      ccStdCall: ShowMessage('StdCall');
      ccSafeCall: ShowMessage('SafeCall');
    end;
  end;
  MyObj.Free;
end;

答案 1 :(得分:3)

包括Delphi 7及更高版本,当启用METHODINFO指令时,运行时生成有关的信息,至少具有公共可见性,方法参数和返回类型以及调用约定(TYPEINFO也应该打开)。

不确定下面的示例是否会直接帮助您,因为它可以处理实例和方法的名称而不是它的地址,但也许您可以事先为方法的名称地址构建查找表。

type
{$METHODINFO ON}
  TSomeClass = class
  public
    procedure Proc1(i: Integer; d: Double); stdcall;
    procedure Proc2;
  end;
{$METHODINFO OFF}

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    FSomeClass: TSomeClass;

  ..

uses
  objauto;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FSomeClass := TSomeClass.Create;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Info: Pointer;
begin
  Info := GetMethodInfo(FSomeClass, 'Proc1');
  if Assigned(Info) then begin
    Inc(Integer(Info), SizeOf(TMethodInfoHeader) - SizeOf(ShortString) + 1 +
      Length(PMethodInfoHeader(Info).Name));
    if PReturnInfo(Info).CallingConvention = ccStdCall then
      // ...

  end;


小心并做一些测试,但在D2007上进行了测试,工作有些不可预测。例如,如果上面的'Proc1'更改为procedure Proc1(i: Pointer; d: Double);,则不会生成详细的RTTI。

答案 2 :(得分:1)

请参阅此处了解如何查找:

http://rvelthuis.de/articles/articles-convert.html#cconvs

IOW,你可以简单地尝试它是否有效,或者你看一下导出的名字(_name @ 17或类似名称),或者你看一下反汇编,例如在CPU视图中。