Results 1 to 8 of 8

Thread: Exporting classes from DLLs

  1. #1

    Exporting classes from DLLs

    I am looking for a way to export classes from a Delphi-dll.

    The only thing I found was in the content of "The Delphi Magazine" from July 1995.
    My question: Does anybody have this issue and can send me a pdf-copy (or anything similiar) or do you know the solution for my problem?

    thanx,
    Christoph
    wolfnight.org - Wolfnight Game Development

  2. #2

    Exporting classes from DLLs

    Hope this helps..

    First we need an abstract class which your application uses to 'know' how to use the class in the DLL..

    [pascal]
    type
    TBaseTest = class
    public
    function GetValue : Integer; virtual; abstract;
    end;
    [/pascal]

    This is pretty useless as a class, returning the number 1234 through a DLL has no use whatsoever, but its the principle

    You only have to declare the public section in our abstract class, and in it all procs/funcs must be virtual and abstract. Public variables are declared in the normal way, private variables don't have to be declared.

    Next, in the DLL, we create the implementation of this class, eg:

    [pascal]
    type
    TBaseTestImp = class(TBaseTest)
    private
    DummyValue : integer;
    public
    function GetValue: integer; override;
    end;
    [/pascal]

    and the GetValue function...

    [pascal]
    function TBaseTestImp.GetValue : integer;
    begin
    Result := 1234;
    end;
    [/pascal]

    Now, create another unit of the DLL and create this function..

    [pascal]
    function CreateNewObject : TBaseTest;
    begin
    CreateNewObject:= TBaseTestImp.Create;
    end;
    [/pascal]

    This creates a new instance and returns its reference. This function must be added to the exports list in the DLL.

    Compile the DLL, and now on to the EXE

    [pascal]
    procedure TForm1.FormCreate(Sender : TObject);
    var
    a : TBaseTest;
    begin
    a := CreateNewObject;
    ShowMessage('The number is :' + IntToStr(a.GetValue));
    a.Free;
    end;
    [/pascal]

    and for completeness, the DLL function declaration

    [pascal]
    function CreateNewObject : TBaseTest; stdcall; external 'MyDll.dll';
    [/pascal]

    .Zanthos

  3. #3

    Exporting classes from DLLs

    Thank you!
    wolfnight.org - Wolfnight Game Development

  4. #4

    Exporting classes from DLLs

    I think the proper way is to use interfaces (like in DirectX). I think its basically as described above, but your class will look like this:
    [pascal]TMyClass = class(TObject, IMyInterface)[/pascal]

    I have never used interfaces, though, so I don't really know anything about it except what I read in Delphi in a Nutshell.
    [size=10px][ Join us in #pgd on irc.freenode.net ] [ Sign the Petition for a Software Patent Free Europe ][/size]

  5. #5

    Exporting classes from DLLs

    Yep, you can use interfaces in place of abstract virtual procedures. An interface is pretty much that - a guarantee of what procedures/functions will be available in any class implementing the interface. This lets you call a procedure on a class with a given interface - without worrying whether the classes are descended from a base class. E.g.

    [pascal]type
    IMyInterface = Interface
    procedure SomeProc;
    end;

    TClassOne = class(TInterfacedObject, IMyInterface)
    public
    procedure SomeProc;
    end;

    TClassTwo = class(SomeOtherClass, IMyInterface)
    public
    procedure SomeProc;
    end;

    procedure DoIt(const Thing: IMyInterface);
    begin
    Thing.SomeProc;
    end;[/pascal]

    In the above code, the two classes will implement the IMyInterface interface, which guarantees they'll have a given procedure ("SomeProc") with the correct signature. Of course, the procs can be implemented in any way that's fitting. Note that they're not necessarily inheriting the functionality from a base class - which means the interface "inheritance" goes across, rather than down the inheritance tree.

    If you're using an interface, you add the interface in a class' declaration after the base class - you can implement many, which is another neat feature of interfaces ("TMyClass = class(TInterfacedObject, IOne, ITwo, IThree)").

    Interfaces are reference counted. This means that they can die when out of scope, which is nice and handy. Any class implementing an interface, though, has to implement AddRef, Release and QueryInterface. These are responsible for the reference counting. If you don't want to bother with that, you can use TInterfacedObject (Delphi 6+) as a base class, which includes implementations for those functions.

    Here's a quick example of how an interface could be used. It may be totally wrong, so caveat emptor (this is my first ever dll ):

    DLL Code:
    [pascal]library dlltest;

    {$R *.res}

    uses
    Dialogs;

    type
    IMyInterface = Interface
    ['{0806339A-4EDD-48A5-A60A-54E6E9AF94E1}']
    procedure DoIt;
    end;

    type
    TMyClass = class(TInterfacedObject, IMyInterface)
    private
    protected
    public
    destructor Destroy; override;
    procedure DoIt;
    end;

    procedure TMyClass.DoIt;
    begin
    ShowMessage('Doing something');
    end;

    destructor TMyClass.Destroy;
    begin
    ShowMessage('I am dying!');
    inherited;
    end;

    function GetObj: IMyInterface; stdcall;
    begin
    Result := TMyClass.Create;
    end;

    exports
    GetObj;

    begin
    end.[/pascal]

    Test code in unit using DLL (assumes a button placed on form):
    [pascal]unit dll_test_main;

    interface

    uses
    Windows, Classes, Controls, Forms, StdCtrls;

    type
    IMyInterface = Interface
    ['{0806339A-4EDD-48A5-A60A-54E6E9AF94E1}']
    procedure DoIt;
    end;

    TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    Form1: TForm1;

    function GetObj: IMyInterface; stdcall; external 'dlltest.dll';

    implementation

    {$R *.dfm}

    procedure TForm1.Button1Click(Sender: TObject);
    var
    a: IMyInterface;
    begin
    a := GetObj;
    a.DoIt;
    end; // object dies here

    end.[/pascal]
    Notice that you don't need to create or free the class now! It's lifetime is managed by the interface - when the interface goes out of scope, it will kill the object (as can be seen in the destructor). That's a lot less hassle.


    Meh. I'm bored, sigh. If I messed up any of the above, let me know please.

    [Edit: Hmm, I should probably declare the exported func as stdcall so that other languages have an easier time. The change has been made!]
    "All paid jobs absorb and degrade the mind."
    <br />-- Aristotle

  6. #6

    Exporting classes from DLLs

    Well, one last question: How to access variables? When I create a D3DDevice via the dll then it says it's nil to the application but it's not (little bit strange, but the Device was created).
    wolfnight.org - Wolfnight Game Development

  7. #7

    Exporting classes from DLLs

    Okay, I've done a little workaround: I've just written a function which gives back the device.

    thanks (I love this forums)
    wolfnight.org - Wolfnight Game Development

  8. #8

    Exporting classes from DLLs

    The forums love you too, Snape!

    A quick tip for interfaces: you can insert a GUID (globally unique ID) after the '= interface' bit using CTRL-SHIFT-G. That's what the scary long number is in the above code. You don't specifically need a GUID, but it can give you more options later in using the interface IIRC.
    "All paid jobs absorb and degrade the mind."
    <br />-- Aristotle

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •