Ver Mensaje Individual
  #3  
Antiguo 31-01-2009
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Reputación: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Smile

¡Chanfle!

Ese tipo de cosas siempre han sido mis mayores motivaciones para usar nuevas versiones de Delphi (que ribbon controls, ni qué ocho cuartos ). Nada más de pensar que podría estar palpando esa nueva unidad System.pas...

No he probado la función GetVirtualMethodCount que pusiste, pero dudo que pueda funcionar en Delphi 7. Ayer estuve haciendo algunas pruebas con ésta versión y no hay un marcador seguro que señale la terminación de la VMT. Por cierto, esta es la documentación oficial que viene en la propia ayuda en esa y otras versiones anteriores:

Cita:
Delphi Language Reference
Class types

Topic Groups See Also

A class-type value is stored as a 32-bit pointer to an instance of the class, which is called an object. The internal data format of an object resembles that of a record. The object's fields are stored in order of declaration as a sequence of contiguous variables. Fields are always aligned, corresponding to an unpacked record type. Any fields inherited from an ancestor class are stored before the new fields defined in the descendant class.

The first 4-byte field of every object is a pointer to the virtual method table (VMT) of the class. There is exactly one VMT per class (not one per object); distinct class types, no matter how similar, never share a VMT. VMT's are built automatically by the compiler, and are never directly manipulated by a program. Pointers to VMT's, which are automatically stored by constructor methods in the objects they create, are also never directly manipulated by a program.

The layout of a VMT is shown in the following table. At positive offsets, a VMT consists of a list of 32-bit method pointers--one per user-defined virtual method in the class type--in order of declaration. Each slot contains the address of the corresponding virtual method's entry point. This layout is compatible with a C++ v-table and with COM. At negative offsets, a VMT contains a number of fields that are internal to Delphi's implementation. Applications should use the methods defined in TObject to query this information, since the layout is likely to change in future implementations of the Delphi language.

Virtual method table layout
Offset Type Description
-76 Pointer pointer to virtual method table (or nil)
-72 Pointer pointer to interface table (or nil)
-68 Pointer pointer to Automation information table (or nil)
-64 Pointer pointer to instance initialization table (or nil)
-60 Pointer pointer to type information table (or nil)
-56 Pointer pointer to field definition table (or nil)
-52 Pointer pointer to method definition table (or nil)
-48 Pointer pointer to dynamic method table (or nil)
-44 Pointer pointer to short string containing class name
-40 Cardinal instance size in bytes
-36 Pointer pointer to a pointer to ancestor class (or nil)
-32 Pointer pointer to entry point of SafecallException method (or nil)
-28 Pointer entry point of AfterConstruction method
-24 Pointer entry point of BeforeDestruction method
-20 Pointer entry point of Dispatch method
-16 Pointer entry point of DefaultHandler method
-12 Pointer entry point of NewInstance method
-8 Pointer entry point of FreeInstance method
-4 Pointer entry point of Destroy destructor
0 Pointer entry point of first user-defined virtual method
4 Pointer entry point of second user-defined virtual method
... ... ...
Aunque la System.pas de Delphi 7 presenta cuatro entradas estándares más, las cuales sólo se utilizan (si mal no recuerdo) cuando la clase implementa alguna interfaz (fueron introducidas cuando Delphi comenzó a soportar interfaces):
Código Delphi [-]
  vmtQueryInterface    = 0 deprecated;
  vmtAddRef            = 4 deprecated;
  vmtRelease           = 8 deprecated;
  vmtCreateObject      = 12 deprecated;
(NOTA: La directiva deprecated señala que esas constantes están cayendo en desuso, más no las entradas de VMT que representan).

Cita:
Empezado por poyo Ver Mensaje
A propósito, los métodos virtuales y abstractos (sin implementación por decirlo de algún modo), apuntan (tal como lo decía Al), a la función _AbstractError pero indirectamente... o sea que se puede hookear.
El puntero a interceptar está (como no podía ser de otra manera) en el System.pas:

AbstractErrorProc: procedure; { Abstract method error handler }

Allí, entonces, podremos colgar nuestra propia rutina para manejar el error, e inclusive llamar desde allí a la original.

sólo bastará declarar la función...

procedure CustomAbastractError;
begin
raise EAbstractError.Create('my custom abstract error');
end;

y asignarla:

AbstractErrorProc := CustomAbastractError;
Es lo que encontré ayer. Apuntan (pero directamente ) a la función _AbstractError, mas es el procedimiento indicado por la variable AbstractErrorProc el que define lo que hay que hacer, como bien señalas.

Algo curioso es que todos los métodos abstractos cuentan con su código máquina particular, que es simplemente un salto a la función _AbstractError. O sea que cada método abstracto tiene una dirección de memoria única (como cualquier otro tipo de rutina). Sin embargo, para estos casos, esas direcciones no son guardadas en las entradas de la VMT, sino que éstas almacenan la dirección de memoria de _AbstractError (una VMT con tres métodos abstractos contiene tres entradas de valor idéntico: la dirección de _AbstractError). ¿Entonces por qué genera el compilador código máquina particular con cada método abstracto? Tal parece que sólo para que tengan "identidad propia" a la hora de usarlos como valores procedimentales (@TClase.Metodo).

Esto de las entradas puntero a _AbstractError es lo que puede ayudar a saber, VMT mediante, si un método es abstracto o no, apoyándonos con ensamblador y el operador especial VMTOFFSET.

Cita:
Empezado por Ayuda de Delphi 7 (Assembly directives)
VMTOFFSET retrieves the offset in bytes of the virtual method pointer table entry of the virtual method argument from the beginning of the virtual method table (VMT). This directive needs a fully specified class name with a method name as a parameter (for example, TExample.VirtualMethod), or an interface name and an interface method name.
Pero, volviendo a la pregunta que originó este hilo (el cual es de agradecerse a Poyo), al menos en Delphi 7 no parece haber una forma segura de saber cuántos métodos virtuales tiene una clase hurgando en las entradas de la VMT. ¿O acaso sí?

Y aún más, ahora que recordé el asunto de las interfaces, creo que los métodos con que una clase las implementa son guardados también como entradas en desplazamientos positivos de la VMT. Según la propia ayuda, el operador VMTOFFSET también sirve con métodos de interfaces implementadas por la clase. Habría que hacer algunas pruebas para ver si esto último complicaría más el asunto en Delphi 7.

Nos vemos pronto, un saludo.

Al González.

Última edición por Al González fecha: 31-01-2009 a las 04:25:41.
Responder Con Cita