2011-07-19 22 views
8

Estoy tratando de encontrar una forma segura/determinista para liberar una interfaz que está encapsulada en un OleVariant.¿Cuál es la forma correcta de liberar una interfaz detrás de un OleVariant?

AFAICS Delphi publica referencias de interfaz al final de un procedimiento, pero en mi caso tengo que hacerlo antes, porque tengo que cerrar COM.

procedure Test; 
var 
    LLibrary: OleVariant; 
begin 
    CoInitialize(nil); 
    try 
    LLibrary := Null; 
    try 
     LLibrary := CreateOleObject(LibraryName); 
    finally 
     LLibrary := Unassigned; // <-- I would like to release the interface here 
    end; 
    finally 
    CoUninitialize; // <-- Shutdown of COM 
    end; 
end; // <-- The compiler releases the interface here 

pensé en poner el OleVariant en una instancia de clase extra que puedo liberar antes de que llame CoUninitialize.

procedure Test; 
var 
    Container: TLibraryContainer; // Holds the OleVariant 
begin 
    CoInitialize(nil); 
    try 
    Container := TLibraryContainer.Create; 
    try 
     {...} 
    finally 
     Container.Free; 
    end; 
    finally 
    CoUninitialize; 
    end; 
end; 

¿Es segura esta solución o hay una solución mejor que he pasado por alto?

Respuesta

9

El compilador usa claramente una variable de interfaz local implícita para el valor de retorno de CreateOleObject. Esto luego se libera al final de la rutina, demasiado tarde para ti.

Hay un par de maneras de vencer esto. En primer lugar, podría ser explícito sobre la referencia de interfaz IDispatch devuelta por CreateOleObject. Esto le permite controlar su vida útil.

procedure Test; 
var 
    intf: IDispatch; 
    LLibrary: OleVariant; 
begin 
    CoInitialize(nil); 
    try 
    intf := CreateOleObject(LibraryName); 
    try 
     LLibrary := intf; 
    finally 
     VarClear(LLibrary); 
     intf := nil; 
    end; 
    finally 
    CoUninitialize; 
    end; 
end; 

Una alternativa sería mover el código que llama CreateOleObject en una rutina independiente con su propio alcance.

procedure DoWork; 
var 
    LLibrary: OleVariant; 
begin 
    LLibrary := CreateOleObject(LibraryName); 
    //do stuff with LLibrary 
end; 

procedure Test; 
begin 
    CoInitialize(nil); 
    try 
    DoWork; 
    finally 
    CoUninitialize; 
    end; 
end; 

Dado que la referencia local implícita está dentro del alcance de DoWork se libera al final de DoWork y por lo tanto antes de ejecutar CoUninitialize.

Mi recomendación es usar la segunda opción, que es más limpia y obliga al compilador a hacer el trabajo en su nombre.

+2

La única opción real es usar la rutina secundaria, a menos que disfrute de contar las llamadas a funciones e inspeccionar el código ensamblador generado para asegurarse de que no haya variables locales ocultas. +1 para llamar a su rutina 'DoWork' ...' DoWork', vea mi respuesta eliminada para más detalles. –

+0

@ Cosmin Acepto que la segunda opción es mejor. –

+0

En realidad, mi código real es un poco más sofisticado y creo que tendré que usar su primera variante, pero intentaré refactorizar el código para poder usar el segundo. :) –

Cuestiones relacionadas