2011-03-27 11 views
6

Para un marco de plugin del lado del servidor Me gustaría implementar DLL que expongan un método RegisterPlugin que devuelve una referencia de clase (TInterfacedClass).Gestión de memoria para un marco de plugin Delphi basado en TInterfacedClass

La aplicación de host luego crea la (s) instancia (s) de esta clase, y las instancias se ejecutarán en el contexto de la (s) cadena (s) de host. (Esto es diferente, por ejemplo, del marco de plugin Jedi VCL, que instancia el plugin en el DLL o BPL y devuelve la instancia al host.)

Las primeras pruebas no mostraron problemas hasta el momento. Sin embargo, ¿hay problemas ocultos con la gestión de memoria de los que debería tener conocimiento? Como uso Delphi 2009 para este proyecto, FastMM4 es el administrador de memoria predeterminado.

aquí un bosquejo del proyecto de plugin DLL:

library ExamplePlugin; 
uses 
    ... 
type 
    TPluginOne = class(TInterfacedObject, ...) 
    ... 
    end; 

function RegisterPlugin: TInterfacedClass; stdcall; 
begin 
    Result := TPluginOne; 
end; 

exports 
    RegisterPlugin; 

{ TPluginOne } 
// ... plugin class implementation 

begin 
end. 

Respuesta

7

No hay problemas con el administrador de memoria, porque FastMM funciona como un administrador de memoria compartida entre el EXE y el DLL. Pero realmente no me siento cómodo con el concepto de pasar objetos puros, o (peores) metaclases entre el DLL y el EXE. El problema es que TInterfacedObject desde el EXE no es lo mismo que TInterfacedObject desde el DLL! Claro, pueden parecer exactamente iguales, ¡pero no lo son! Y si alguna vez actualiza la versión de Delphi para el EXE o para cualquiera de los DLL, tendrá que reconstruir todo (perdiendo así cualquier ventaja que haya obtenido al implementar un marco de Plugin).

Una solución mucho más portátil sería devolver un "Interfaz de fábrica", algo a lo largo de las líneas de:

IFactoryInterface = interface 
[GUId-goes-here] 
    function MakeWhateverInterfaceYouNeed: IUnknownDerivate 
end; 

luego exportar una función con esta firma:

function RegisterPlugin: IFactoryInterface; 
+0

La interfaz de fábrica funcionaría con todas las versiones de Delphi (siempre que la interfaz asociada con el GUID sea la misma en el host y en la DLL? – mjn

+0

Esto es COM, ¿no? –

+0

@mjin, sí, las interfaces tienen una interfaz bien definida interfaz binaria. Se espera que las interfaces con un GUID dado (y de confianza por el compilador) tengan un cierto VMT. * Ejercicio: * Defina la interfaz 'ITestIntf' en la unidad A. Copie y pegue la misma definición en la unidad B. Si intenta hacer 'var i1: A.ITestIntf: = B.ITestIntf' el compilador se quejará porque el' A.ITestIntf' no es compatible con 'B.ITestIntf'. Si lo hace' var i1: A.ITestIntf: = B.ITestIntf as A.ITestIntf' la asignación funciona como se espera porque ambas declaraciones tienen el mismo GUID. –

2

Su código es incompleta, sino de lo que ha incluido hay una falla obvia.

Parece que está exportando una clase (TInterfacedClass) desde una DLL. Esto causará problemas cuando los clientes intenten consumir su clase con una versión diferente de Delphi. Lo que es más, los dejará indefensos si quieren escribir complementos en un idioma diferente.

Personalmente, elegiría una interfaz COM que permita a los autores de complementos crear complementos en cualquier idioma. Este es, de hecho, el mismo problema que COM fue inventado para resolver.

Si está contento de tener que usar el mismo compilador para complementos y aplicaciones de host, y prefiere exponer las clases a las interfaces COM, entonces debe asegurarse de que todas las desasignaciones se realicen con el mismo administrador de memoria como asignado la memoria. La forma más sencilla es usar ShareMem y entonces estarás a salvo.

ACTUALIZACIÓN

Cosmin señala en un comentario de otro defecto con la exportación de clases a través de límites de módulos. Básicamente es algo que no deberías hacer. COM fue diseñado para este propósito y aún debe ser la primera opción para usted. interfaces de Delphi que son COM compatible para que pueda obtener los mismos beneficios de la interoperabilidad binaria sin tener que crear servidores, registro de CLSID etc.

Creo que el plug-in debería tener este aspecto:

library ExamplePlugin; 

type 
    TPluginOne = class(TInterfacedObject, IPlugin) 
    [GUID] 
    public 
    constructor Create(const Host: THostApp); 
    end; 

function RegisterPlugin(const Host: IHostApp): IPlugin; stdcall; 
begin 
    Result := TPluginOne.Create(Host); 
end; 

exports 
    RegisterPlugin; 

begin 
end. 
+0

Incluso si el archivo DLL y el EXE está construido con la misma versión de Delphi, si tienes un 'TObjectDerivate' del DLL y tratas de hacer' O is TObjectDerivate' en el EXE obtendrás FALSE, porque el 'es' y' como 'los operadores confían en el valor real de' Class': DLL y EXE van a tener diferentes metac de 'Clase' ¡lasses para 'TObjectDerivate'! –

+0

En la versión actual del código, el servidor instancia un complemento usando 'Inst: = AComponentClass.Create;' y desde este punto solo accede a los métodos de interfaz usando 'if Admite (Inst, ISomeInterface, X)' - Así que con Delphi Interfaces COM compatibles, ¿existe el riesgo de devolver una referencia de clase de la DLL? – mjn

+1

@mjn Por lo que vale, mi respuesta y la de Cosmin son esencialmente idénticas. Su código 'Inst: = AComponentClass.Create;' ya es un problema. En cambio, su complemento debería exponer una función global que devuelve una interfaz. En el complemento, se implementaría llamando a un constructor. Pero va a entrar en un mundo de dolor si llama constructores y métodos en clases declaradas en un módulo diferente. Aunque parece que estás muy cerca. Solo necesita su complemento para exportar este método de fábrica. –