Friday, June 23, 2006

 

Example added


In order to finalize this series of articles related to interfacing objects with Dll's i've added an example (as promised on my last post) you can download from this url.

Feel free to post comments about the example and/or propose alternatives.

Tuesday, June 20, 2006

 

Virtual-Abstract objects as Interfaces


In my last post i exposed a method to share objects between Dll's and applications. As i wrote in that post, the compiler doesn't know the type of received object (it assumes it's a pointer), so you must cast it to the correct type. In the code below, i casted it to TObject only to be sure it compiles correctly:
(* Initialize exported class *)
myObj := TObject(_EC).create;
(* work with exported class
:
:
... and free exported class *)
myObj.free;
This aproach allows you to create objects inside Dlls (in some cases it's all you need), but compiler can't access public methods and properties of the object because it doesn't know the object's type.

Delphi & Kylix developers can argue (with reason) that if you use Packages (bpl's) instead of Dll's you can share objects and the compiler will intelligently know wich is the type of shared objects. But if applications must compile in FreePascal in addition to that compilers you have to devise a workaround.

Well, the workaround is to create an interface who'll allow the compiler to access objects methods and properties. Object Pascal knows two Interface types, COM Interfaces and CORBA interfaces wich i'm not going to explain here, and a third one that isn't really an Interface type but works the same way is called Virtual-Abstract Objects, it will help you with code portability.

To define a Virtual-Abstract Object you have to create a class with all of it's methods virtual and abstract as this code shows:
Type
TAgeCalculator = Class
public
function GetAge: Integer; virtual; abstract;
procedure SetAge; virtual; abstract;
end;
Abstract means that the implementation of the object doesn't have to be in the same level of inheritance of the interface, but inherited to be implemented. Virtual means methods can be oberriden.

As an example, if you need to share objects between Dll's and must access to methods and properties, you have to include the class definition (like TAgeCalculator) in both units, the one wich implements the methods and the one wich access to the instance.

In my next post i'll include a complete example of this.

Tuesday, June 13, 2006

 

How to import an Object from a dll?


Some time ago, a boss of a company that i used to work gave a course of COM & DCOM technologies because he thought packages (bpl's) wasn't working as one might expect (actually the problem wasn't packages at all, but that's another story). He was trying to change packages to COM interfaces as a way to share application's objects with Dll's (dynamic link libraries). Thanks god we solved the problem without changing the more than 50 packages the application uses.

Well, there's a way to export objects (and classes) from dll's. The first thing you have to do is to code a Unit containing the classes you want to export:
{ MyClasses.pas }

unit MyClasses;

interface

uses
Classes;

type
TMyClass = class
public
constructor create;
(* Here you can create the methods you want *)
end;

implementation

constructor TMyClass.create;
begin
writeln('Create!!!');
end;

end.
The second thing to do is to create the the library project and include the recent unit:
{ interfaces.dpr }

library interfaces;

uses
Classes,
MyClasses;

function GetMyExportedClass: Pointer; cdecl;
begin
Result := GetClass('TMyClass');
end;

exports
GetMyExportedClass;

end.
After this you have to create a program that loads the dll and execute the exported function (GetMyExportedClass) to create TMyClass objects:

{ test.dpr }

program test;

{$APPTYPE CONSOLE}

uses
{$ifdef fpc}
dynlibs,
{$endif}
SysUtils,
Classes;

var
{$ifdef fpc}
mHandle: TLibHandle;
{$else}
mHandle: THandle;
{$endif}

var
myObj: TObject;
_EC: function : Pointer; cdecl;

begin
(* load shared library *)
if (pointer(mHandle) = nil) then
begin
(* Try to load shared library *)
mHandle := LoadLibrary('lib.dll');
if (pointer(mHandle) <> nil) then
begin
writeln('Loaded!');
(* instantiate a TMyExportedClass object *)
_EC := GetProcAddress(mHandle, 'GetMyExportedClass');
if (pointer(@_EC) <> nil) then
begin
(* Initialize exported class *)
myObj := TObject(_EC).create;
(* work with exported class
:
:
... and free exported class *)
myObj.free;
end;
(* unload shared library *)
{$ifdef fpc}
UnLoadLibrary(mHandle);
{$else}
FreeLibrary(mHandle);
{$endif}
end;
end;
end.
The code above exports an object from a dll, but as the object must be type casted to TObject, you can't view it's methods. In the next post, i'll show you how to create interfaces that allows to view the object's metods.
 

First blog!


Hi everybody, my name is Leonardo M. Ramé, i'm software developer from Córdoba, Argentina and started this blog to help the Object Pascal community with some hacks useful to me (and you).

This page is powered by Blogger. Isn't yours?