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!!!
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!!!