2011-05-22 6 views
5

En mi biblioteca estoy invocando métodos bajo condiciones específicas, lo que requiere la convención de llamadas stdcall. Actualmente estoy usando la resolución estática del compilador, implementada como una lista bastante grande de firmas de métodos bien conocidos y las correspondientes versiones sobrecargadas de mi subrutina. Esto funciona pero parece bastante fugitivo y no cubre al 100% todos los métodos posibles. Me gustaría agregar la posibilidad de trabajar con un puntero de método genérico y afirmar la convención de llamada adecuada preguntando a RTTI. Y aquí estoy atascado, por favor asesorar.¿Cómo afirmar que el puntero del método dado utiliza la convención de llamadas stdcall?

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

preferible que haría uso de "clásico" de RTTI para crear dependencias de la versión menos, sin embargo no puedo encontrar ningún indicador dentro de convención de llamada "clásica" RTTI ...


NB: Esta cuestión no está relacionado con la importación de funciones externas

Respuesta

3

Puede extraer la información de la convención de llamadas de RTTI extendido (disponible desde 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; 

actualización

Por "clásico" de RTTI lea la respuesta Sertac; las siguientes obras OK en 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; 
+0

Gracias! ¿Qué tal RTTI "clásico"? –

+0

no hay oportunidad con rtti clásico. –

1

ver aquí en la manera de averiguar:

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

IOW, simplemente puede intentar si funciona, o echa un vistazo al nombre exportado (_name @ 17 o similar) o echa un vistazo a un desmontaje, p. en la vista de CPU

+1

Por favor, resalte las partes poco claras en mi pregunta, porque no se trata de importar encabezados en absoluto. Por cierto, el método de prueba y error es un enfoque muy malo en cualquier caso, con algo de suerte, la subrutina invocada con una convención de llamadas incorrecta puede no bloquearse de inmediato y provocar un comportamiento indefinido en otra parte. –

+4

bienvenido a stackoverflow. :) – RRUZ

+0

@Serg entonces probablemente no entendí bien. –

3

Incluyendo Delphi 7 y hasta, cuando METHODINFO directiva está activada, tiempo de ejecución genera información sobre, al menos, tener visibilidad pública, los parámetros del método y tipos de retorno y la convención de llamada (TYPEINFO también debería estar encendido).

No estoy seguro de si la muestra siguiente podría ayudarlo directamente ya que funciona en una instancia y método y no en su dirección, pero quizás pueda construir una tabla de búsqueda para la dirección de nombre de los métodos de antemano.

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; 


Cuidado con y hacer algunas pruebas, sin embargo, probado en D2007 el funcionamiento es algo impredecible. Por ejemplo, si el 'Proc1' anterior se cambia a procedure Proc1(i: Pointer; d: Double); no se genera RTTI detallado.

+0

Gracias por mencionar METHODINFO, presente en el compilador D7, pero falta en la documentación. –

+0

@user - ¡De nada! Si necesita entrar en más detalles de * classic * RTTI, puedo recomendarlo [blog de Hallvard Vassbotn] (http://hallvards.blogspot.com/). –

+0

autor muy interesante, gracias una vez más. –

Cuestiones relacionadas