在我的库中,我在特定条件下调用方法,这需要stdcall调用约定。目前我正在使用编译器静态解析,实现为相当大的众所周知的方法签名列表和我子例程的相应重载版本。这有效,但看起来很脏,并没有100%覆盖所有可能的方法。我想增加使用泛型方法指针的可能性,并通过询问RTTI来断言正确的调用约定。在这里我被卡住了,请指教。
Input: code/data pair of pointers as in TMethod
Output: boolean indicator, true if method is stdcall
我最好使用“经典”RTTI来创建更少的版本依赖项,但是我无法在“经典”RTTI中找到任何调用约定指示符...
注意:此问题与导入外部功能无关
答案 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视图中。