PDA

Ver la Versión Completa : Clases encapsuladas en DLL y herencia


bucanero
30-05-2018, 12:04:13
Hola a todos

Hasta ahora en las DLL almacenaba funciones o procedimientos sueltos que posteriormente se utilizaban en el programa. Ahora estoy experimentando con la idea de encapsular clases completas para utilizarla después y ya he conseguido la parte del encapsulado que realizo de la siguiente forma:

Uso una unidad que comparten tanto la DLL como el proyecto y es donde solo se definen las declaraciones abstractas e interfaces publicas de la clase a encapsular (aquí no hay nada en la implementación):


unit UMyObjectInterface;

interface

type
// se define una interface con los procedimientos/funciontes
IMyInterface = interface(IInterface)
['{ECEF5846-F1B3-4ECE-9113-16B2C7962F04}']
function algoMas: integer;
procedure mostrar;
end;

// se define una Objeto abstracto con los metodos de la interface
TMyAbstractObject = class abstract (TInterfacedObject, IMyInterface)
function algoMas: integer; virtual; abstract;
procedure mostrar; virtual; abstract;
end;

// se define el puntero a la clase abstracta del objeto
TClassMyObject = class of TMyAbstractObject;

// se define la funcion para la importacion/exportacion de la clase
TGetMyExportedObject = function: TClassMyObject; cdecl;

implementation

end.



Y para la DLL declaro una clase que hereda de la clase abstracta y es en donde se implementan los procedimientos y funciones necesarios


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes,
DateUtils, UMyObjectInterface;

type
TMyObject = class(TMyAbstractObject)
function algoMas: integer; override;
procedure mostrar; override;
end;

function GetMyExportedClass: TClassMyObject; cdecl;

exports
GetMyExportedClass;

implementation

uses
Vcl.Dialogs;

// función que devuelve el puntero a la clase del objeto
function GetMyExportedClass: TClassMyObject; cdecl;
begin
Result := TMyObject;
end;


{ TMyClass }
function TMyObject.algoMas: integer;
begin
Result := 123456;
end;

procedure TMyObject.mostrar;
begin
inherited;
MessageDlg('Hola mundo!!!', mtInformation, [mbOK], 0);
end;

end.


Compilo, generando la DLL y listo, ya tengo mi clase de prueba encapsulada dentro de la DLL

Ahora en la aplicación, cargo la DLL de forma dinámica y declaro una variable que apunta a la clase del objeto exportado

unit UMyObjectInDLL;

interface

uses UMyObjectInterface;

var
TMyObjectDLL: TClassMyObject = Nil;

implementation

uses
{$ifdef fpc}
dynlibs,
{$endif}
Winapi.Windows, System.SysUtils;


var
{$ifdef fpc}
mHandle: TLibHandle = NIL;
{$else}
mHandle: THandle = 0;
{$endif}
_EC: TGetMyExportedObject = Nil;

initialization
(* load shared library *)
if (pointer(mHandle) = nil) then begin
(* Try to load shared library *)
mHandle := LoadLibrary('MiClassDLL.dll');
if (pointer(mHandle) = nil) then
raise Exception.Create('No se ha podido cargar la DLL')
else begin
(* instantiate a TMyExportedClass object *)
_EC := GetProcAddress(mHandle, 'GetMyExportedClass');
if (pointer(@_EC) = nil) then
raise Exception.Create('No se ha podido cargar la funcion GetMyExportedClass')
else
TMyObjectDLL := _EC;
end;
end;

finalization
(* unload shared library *)
{$ifdef fpc}
UnLoadLibrary(mHandle);
{$else}
FreeLibrary(mHandle);
{$endif}
end.


y para utilizar dicha clase lo hago así:


...
uses
UMyObjectInterface,
UMyObjectInDLL;

procedure TMainForm.Button1Click(Sender: TObject);
var
myObj: TMyAbstractObject;
begin
/// Inicializar la clase exportada
myObj := TMyObjectDLL.create;
try
MessageDlg(IntToStr(myObj.AlgoMas), mtInformation, [mbOK], 0);
myObj.mostrar;
finally
(*... and free exported class *)
myObj.free;
end;
end;


Hasta aquí todo perfecto y funciona correctamente.

Ahora lo que pretendo es desde la aplicación generar una clase que herede de la clase almacenada en la DLL, con un código similar a esto pero obtengo el error E2005 'TMyObjectDLL' is not a type identifier


uses UMyObjectInDLL, UMyObjectInterface;

type
TOtraMyClass= class(TMyObjectDll)
procedure Procedimiento2;
end;


Realmente no se si esto que estoy intentando realizar se puede hacer de alguna otra manera o no es posible...

saludos!!!

engranaje
02-06-2018, 16:42:44
En el código anterior tienes esto

var TMyObjectDLL: TClassMyObject = Nil;


Más adelante dices que te funciona esto:

myObj := TMyObjectDLL.create;


y finalmente el código que te falla:

TOtraMyClass= class(TMyObjectDll)


Si no me he perdido en todo esto TMyObjectDLL no es mas que una instancia de la clase TClassMyObject. Pero al final no deja de ser mas que una variable,
no puedes hacer que TOtraMyClass sea una clase hija de TMyObjectDll porque como te dice el mensaje TMyObjectDll no es un TYPE. Podrías hacer:

TOtraMyClass= class(TClassMyObject )

Como digo espero no haberme perdido pero a priori parece que te has despistado, igual por haberle puesto de nombre TMyObjectDLL a una instancia del objeto TClassMyObject. Puede colaborar con el error que funcione el código:

myObj := TMyObjectDLL.create;

pero despues de todo TMyobjectdll aunque sea un instancia tiene un metodo create que devuelve un objeto del tipo TMyAbstractObject, aunque si he seguido bien todo tu código igual te serviría:

myObj := TClassMyObject.create;

bucanero
04-06-2018, 11:17:55
Gracias engranaje por responder




Si no me he perdido en todo esto TMyObjectDLL no es mas que una instancia de la clase TClassMyObject. Pero al final no deja de ser mas que una variable,
no puedes hacer que TOtraMyClass sea una clase hija de TMyObjectDll porque como te dice el mensaje TMyObjectDll no es un TYPE. Podrías hacer:

TOtraMyClass= class(TClassMyObject )

[/DELPHI]

Quizas no me explique bien en mi post anterior..., y si como bien dices TMyObjectDLL es una instancia de la clase TClassMyObject, el tema es que la clase TClassMyObject esta solamente definida en la DLL y no en la aplicacion. A priori la aplicación solo sabe que es una clase descendiente de TMyAbstractObject pero no sabe mas.

Aquí dejo un enlace a la estructura del programa
https://image.ibb.co/dPSNoT/Diagrama_de_datos.jpg

y aquí dejo el código fuente por si alguien quiere hacer pruebas:
https://www.megaupload.us/10ip/MiEjemplo.rar

La idea original que pretendo es cargar una clase desde una DLL, Al igual que cuando se utiliza una función definida en una DLL el programa solo sabe el nombre de la función y los parámetros que utiliza, sin conocer mas nada sobre dicha función y se carga de esta manera

function MiFuncion : string; stdcall; external 'MiDll.dll';

Para utilizar una clase la idea sería algo similar, como poder hacer algo así:

TMyObjectDLL = class stdcall; external 'MiDll.dll';

Aunque evidentemente esto no esta soportado, ni lo acepta el compilador, por eso ando buscando alternativas...

Como una clase es bastante mas compleja que una simple función, he pretendido utilizar clases predefinidas donde las funciones genéricas al menos estén definidas ya en interfaces y donde el programa que pretende utilizar dicha clase, tenga ya de antemano alguna información sobre la clase

Neftali [Germán.Estévez]
05-06-2018, 08:30:24
y aquí dejo el código fuente por si alguien quiere hacer pruebas:
https://www.megaupload.us/10ip/MiEjemplo.rar


¿Puedes subir el fichero al FTP del club o algún otro sitio "normal"?
Después de unas cuantas extensiones que me ha intentado instalar esta web, varias ventanas de publicidad, pedirme que quite el AdBloqcker y unas cuantas esperas, he decidido desistir... :(:(

bucanero
05-06-2018, 09:55:02
Gracias Neftali por responder

;526837']
Después de unas cuantas extensiones que me ha intentado instalar esta web, varias ventanas de publicidad, pedirme que quite el AdBloqcker y unas cuantas esperas, he decidido desistir... :(:(

Ahora que lo intente, es cierto que es una pesadilla... No suelo usar habitualmente este tipo de paginas para subir ficheros... y por ser MEGAUPLOAD de las mas conocidas pensé que funcionaria mejor :mad:

Aquí esta el enlace al fichero en el FTP del ClubDelphi Leer_Clase_Desde_DLL.rar (http://terawiki.clubdelphi.com/Varios/?download=Leer_Clase_Desde_DLL.rar)