2010-04-13 10 views
6

Necesito ejecutar mi código después de la finalización de la unidad SysUtils.Orden de finalización de la unidad para la aplicación, compilada con paquetes de tiempo de ejecución?

he puesto mi código en la unidad separada y la incluyó por primera vez en la cláusula de usos DPR-archivo, así:

project Project1; 

uses 
    MyUnit, // <- my separate unit 
    SysUtils, 
    Classes, 
    SomeOtherUnits; 

procedure Test; 
begin 
    // 
end; 

begin 
    SetProc(Test); 
end. 

MyUnit se ve así:

unit MyUnit; 

interface 

procedure SetProc(AProc: TProcedure); 

implementation 

var 
    Test: TProcedure; 

procedure SetProc(AProc: TProcedure); 
begin 
    Test := AProc; 
end; 

initialization 

finalization 
    Test; 
end. 

Tenga en cuenta que MyUnit no tiene ningún uso.

Este es el habitual Windows exe, sin consola, sin formularios y compilado con paquetes predeterminados de tiempo de ejecución. MyUnit no es parte de ningún paquete (pero he intentado usarlo también desde el paquete).

Espero que la sección de finalización de MyUnit se ejecute después de la sección de finalización de SysUtils. Esto es lo que la ayuda de Delphi me dice.

Sin embargo, este no es siempre el caso.

Tengo 2 aplicaciones de prueba, que difieren un poco por código en Probar rutina/archivo dpr y unidades, enumeradas en los usos. MyUnit, sin embargo, aparece primero en todos los casos.

Una aplicación se ejecuta como se esperaba: Halt0 -> FinalizeUnits -> ... otras unidades ... -> finalización de SysUtils -> finalización de MyUnit -> ... otras unidades ...

Pero el segundo no es La finalización de MyUnit se invoca antes de Finalización de SysUtils. La cadena de llamadas actual se ve así: Halt0 -> FinalizeUnits -> ... otras unidades ... -> Finalización de SysUtils (omitida) -> Finalización de MyUnit -> ... otras unidades ... -> Finalización de SysUtils (ejecutada)

Ambos proyectos tienen configuraciones muy similares. Intenté eliminar/minimizar sus diferencias, pero todavía no veo una razón para este comportamiento.

He intentado depurar esto y descubrí que: parece que cada unidad tiene algún tipo de recuento de referencias. Y parece que InitTable contiene múltiples referencias a la misma unidad. Cuando se llama a la sección de finalización de SysUtils por primera vez, cambia el contador de referencia y no hace nada. Luego se ejecuta la finalización de MyUnit. Y luego SysUtils se llama de nuevo, pero esta vez ref-cuenta llega a cero y sección de finalización se ejecuta:

Finalization: // SysUtils' finalization 
5003B3F0 55    push ebp   // here and below is some form of stub 
5003B3F1 8BEC    mov ebp,esp 
5003B3F3 33C0    xor eax,eax 
5003B3F5 55    push ebp 
5003B3F6 688EB50350  push $5003b58e 
5003B3FB 64FF30   push dword ptr fs:[eax] 
5003B3FE 648920   mov fs:[eax],esp 
5003B401 FF05DCAD1150  inc dword ptr [$5011addc] // here: some sort of reference counter 
5003B407 0F8573010000  jnz $5003b580  // <- this jump skips execution of finalization for first call 
5003B40D B8CC4D0350  mov eax,$50034dcc // here and below is actual SysUtils' finalization section 
... 

Puede alguien puede triturar luz sobre esta cuestión? ¿Me estoy perdiendo de algo?

Respuesta

1

que fue capaz de encontrar una razón y me siento un poco estúpido ahora :)

Mi segunda aplicación de prueba tienen una referencia estática a DLL, que fue compilado con RTL.bpl (que está vacío, a excepción de las referencias a SysUtils y teniendo 1 rutina simple). Entonces, dado que DLL está enlazado estáticamente, se inicializa antes de que se ejecute cualquier código de exe.

Eso es todo:

Sistema de DLL -> de DLL SysUtils ->Sistema de exe (omitido) ->MyUnit - SysUtils> exe (omitidos) -> etc

Las finalizaciones están en orden inverso, lo que lleva a la ejecución de MyUnit antes de SysUtils.

Solución: requieren incluir MyUnit primero en todos los proyectos.

(oh, cómo me gustaría tener una máquina del tiempo para viajar en el tiempo y forzar a alguien para agregar evento OnBeforeMMShutdown: D)

0

No le falta nada. Eso es exactamente lo que está sucediendo.

Cuando su programa carga un paquete, inicializará todas las unidades utilizadas en ese paquete. Cuando descarga el paquete, tiene que finalizar todas las unidades. Pero la finalización a menudo implica liberar variables. Recuerde que los dobleces son una mala cosa, especialmente si ocurren durante la finalización cuando ciertas funciones de manejo de excepciones pueden haberse descargado, lo que hace que la depuración sea muy difícil. Por lo tanto, pone un contador de referencia en las inicializaciones de la unidad para que no se finalicen hasta que todo lo que estaba usando se haya hecho con ellos.

¿Hay alguna razón particular por la que tu MyUnit necesita finalizar después de SysUtils?

+0

> ¿Hay alguna razón en particular por su MyUnit necesita para finalizar después SysUtils? Sí. Son cheques que pierden memoria. – Alex

+0

@Alexander: ¿Se está haciendo algo especial que no se puede obtener con FastMM en FullDebugMode? –

+0

> ¿Está haciendo algo especial que no puede obtener con FastMM en FullDebugMode? Esa no es la pregunta, que estoy preguntando, así que dejémosla a un lado. FastMM no funcionó en mi segunda aplicación exactamente por la misma razón: se llama demasiado pronto. – Alex

10

Las unidades se finalizan en el orden inverso de inicialización. El orden de inicialización está determinado por un recorrido posterior a la orden no cíclico (es decir, nunca desciende a una unidad ya visitada) de la unidad que utiliza un gráfico, comenzando por la cláusula de uso principal (en el programa o la biblioteca). SysInit es normalmente la primera unidad en inicializarse, seguida de System.

La carga dinámica de paquetes complica las cosas, porque el principal EXE o DLL consigue especificar el orden de inicialización de las unidades utilizadas por la imagen principal. Entonces, cuando un paquete se carga dinámicamente, ejecutará lo que cree que debería ser el orden de inicialización, pero las unidades ya inicializadas se omitirán; cuando el paquete se descarga dinámicamente, esto ocurre al revés.

Las reglas generales:

cosas
  • de nivel inferior deben ser inicializadas antes que las cosas de mayor nivel
  • finalización debe estar en orden inverso al de inicialización

Estas reglas casi siempre tienen sentido. Las inicializaciones de unidades de alto nivel a menudo se basan en servicios proporcionados por unidades de nivel inferior. Por ejemplo, sin SysUtils, no hay soporte de excepción en Delphi. La finalización del pedido inverso tiene sentido por la misma razón: las finalizaciones de alto nivel dependen de los servicios proporcionados por las unidades de nivel inferior, por lo que deben ejecutarse antes de las finalizaciones de las unidades de nivel inferior.

Dicho todo esto, con respecto a su problema, parece que puede haber un error en alguna parte del compilador o RTL, si lo que dice es cierto: que el EXE principal utiliza MyUnit primero, y MyUnit utiliza ninguna otra unidad en su interfaz o implementación, y no hay negocios graciosos con paquetes cargados dinámicamente. Todo lo que puedo sugerir es seguir minimizando el proyecto con un comportamiento extraño hasta que tenga una muestra mínima de reproducción; en ese punto, debe quedar claro exactamente qué está causando el problema.

+0

Todos los paquetes están enlazados estáticos y no hay carga dinámica, eso es seguro. Gracias por la respuesta, pero tenía miedo de eso :(Espero que alguien pueda dar algunas pistas para buscar cosas específicas, porque no sé dónde buscar más. Lo intentaré algunas veces más. .. – Alex

0

No estoy seguro pero todavía no existe la buena y antigua variable ExitProc de la fama de Turbo/BorlandPascal? Si es así, esto podría resolver su problema.

+0

Sí, lo es. Desafortunadamente, esta rutina se llama ** antes ** llamando a cualquier sección de finalización. Por lo tanto, este es demasiado pronto. También hay un evento ExitProcess, pero se llama justo antes de la finalización del proceso. demasiado tarde. (No escribo el administrador de memoria - solo instalo un filtro, por lo que es importante para mí ejecutar mi código antes de la finalización del sistema cuando el administrador de memoria predeterminado puede liberar toda la memoria) Diablos, estoy pensando en instalar enganchar en la finalización del sistema: (Es una broma. – Alex

+0

@Alexander: Tal vez un gancho en la finalización de * SysUtils * no sea una mala idea si no puede resolverlo de otra manera. – dummzeuch

Cuestiones relacionadas