Ver Mensaje Individual
  #4  
Antiguo 31-01-2009
poyo poyo is offline
Miembro
 
Registrado: ene 2009
Posts: 47
Reputación: 0
poyo Va por buen camino
Gracias Al por el pedazo de documentación. Siempre había tenido la sospecha (aunque nunca la había confirmado) de que las instancias comparten la VMT y NO poseen una copia para sí.

Cita:
Empezado por Al González Ver Mensaje
vmtQueryInterface = 0 deprecated;
vmtAddRef = 4 deprecated;
vmtRelease = 8 deprecated;
vmtCreateObject = 12 deprecated;
ahhh! claro! son de la implementación TInterfacedObject, IInterface...


Cita:
Empezado por Al González Ver Mensaje
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.

...

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í?
En la versión 2009 (y creo que en ninguna otra) tampoco hay nada indicador de la terminación de la vmt. Lo que la función es pura aritmética de puntero. saca cuentas con las direcciones de memoria para deducir el final (hace una diferencia y lo divide por 4 (size of pointer en realidad).
Según lo que he visto, todos lo resuelven así. No lo he visto implementado de ninguna otra manera... ni se me ocurre como.
Y sí... es un poco turbio, pero al menos a mí me funciona. Creo que debería funcionar en versiones anteriores también, pues se viene usando desde hace mucho.

--------

Con respecto a _AbstractError, tienes absoluta razón! Apuntan allí y desde allí se fija si está asignado AbstractErrorProc y, si es así, la llama, sino, sigue con "_RunError", rutina famosa que muestra los cartelos "Runtime Error" y luego termina en la función Halt0 (que se encarga de terminar la aplicación).

debo ser la falta de sueño, de ansiolíticos o el exceso de horas en la máquina! (o la suma de todo. jejejeje)

Estuve investigando algo más... algo de eso ya me había cruzado.
En en INITIALIZATION del SysUtils.pas se llama (entre otras cosas) a el procedure InitExceptions que se encarga de inicializar la variable AbstractErrorProc del System.pas (y así también de otros errores como ser assert).

El manejador de errores de Abstracción es sencillo:

procedure AbstractErrorHandler;
begin
raise EAbstractError.CreateRes(@SAbstractError);
end;

la asignación también pero lo que me llama la atención son unos comentarios que hay alrededor de tal asignación y el IFNDEF:

{$IFNDEF PC_MAPPED_EXCEPTIONS}
// We don't hook this under PC mapped exceptions, because
// we have no idea what the parameters were to the procedure
// in question. Hence we cannot hope to unwind the stack in
// our handler. Since we just throw an exception from our
// handler, that pretty much rules out using this without
// exorbitant compiler support. If you do hook AbstractErrorProc,
// you must make sure that you never throw an exception from
// your handler if PC_MAPPED_EXCEPTIONS is defined.
AbstractErrorProc := @AbstractErrorHandler;
{$ENDIF}

Supongo que PC_MAPPED_EXCEPTIONS se trata de algo heredado del kylix y su compatibilidad con linux...

Cita:
Empezado por Al González Ver Mensaje
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).
No entiendo bien lo que planteas aquí.
A ver si comprendo: una entrada de una función virtual en una VMT, si esta es abtracta, apuntará a _AbstractError. Cada Método Abstracto tiene un dirección de memoria particular (corresponfiende al Offset dentro de un array de punteros a métodos, no?).
La pregunta es por qué hay un elementos en la el array que no están implementados? (es decir, apuntan a _AbstractError)?

Siguiendo con la experimentación, querer reemplazar el puntero de un método virtúal de la VMT de la suguiente manera:

vmt^.UserDefinedVirtuals[x] := @TForm1.MyMethod;

me di cuenta de que no se podía por algo... ARROJABA UNA EXCEPCION!
Tras de mirar y mirar si estaba haciendo algo mal, caí en que los Administradores de Memoria (memory managers) de los sistemas operativos que trabajan con microprocesadores que operan en modo protegido, marcan a las páginas de memoria con atributos especiales haciendo que estas se puedan (o no) leer, escribir y/o ejecutar. Claro que todo esto depende del microprocesador y/o sistema operativo... en la actualidad creo que ya todos los microsprocesadores lo soportan y el windows lo viene soportando desde hace rato... no tengo idea.

La cuestión es que implementé una función que se encarga de desproteger la vmt y otra que vuelve a protegerla, para dejarla todo como estaba... bueno, casi

Otra duda surge de acá: Qué pasaría si el DEP esta activado?

Nota: Data Execution Prevention, es algo que se agregó a partir del SP2 del WinXP. Claro que el micro lo tiene que soportar.

pd: urgar va con H... lo puse en el título y está mal. Se podrá cambiar?

pd2: Adjunto RttiUtils.pas
Archivos Adjuntos
Tipo de Archivo: zip RttiUtils.zip (2,3 KB, 4 visitas)
Responder Con Cita