2005. július 6., szerda

Export functions and methods from DLL


Problem/Question/Abstract:

Some time ago I have created a program using Delphi3. Now I want to adapt it to Delphi7. The problem is that creating the program I have used component (TComponent descendent) without source files and I can not install it into Delphi7.

Actually I use only one function and one method (event) which returns the progress. I thought maybe I could add that component into DLL using D3 and use this DLL with D7. The function of the component exports successfully from DLL but how to export the Method (Event) of it?

Answer:

Well, if there is no D7 version of this component a DLL build with D3 is indeed the best solution to your problem. It would have helped if you had posted the declarations of the function you need to call and the event you want to handle, though. Without that at hand i can only give you some general guidance.

What you need to do is to build a set of exported functions for the DLL that gives you access to the components functionality. Since DLL and host EXE are build with different Delphi versions they cannot safely share a memory manager via the ShareMem unit, so you cannot pass data types like AnsiString to the DLL functions or receive such parameters in the event handler. You have to write the DLL interface like a set of Windows API methods, using only types that do not require a shared memory manager. Since the original component may not fit this requirements you need a layer of insulation between the DLLs exported functions and the component, best implemented as a class since you will need an object method to handle the components event anyway.

There is also the question of how to manage the lifetime of the DLL component.You can create it easily the first time the DLLs exported function is called to get the component do some work. But where to destroy it again? The usage pattern your post implies is not synchronous, the component seems to be doing something in a secondary thread after its mystery method has been called, delivering progress events while at work. The best option seems to be to provide another exported function the host EXE can call to get the DLL to destroy the component when it is no longer needed.

OK, let's try to code a DLL interface as a wrapper for this hypothetical component:

type
  TProgressEvent = procedure(PercentDone: Integer) of object;
  TMysteryComponent = class(TComponent)
    {....}
  public
    function ProcessData(const Data: string): Boolean;
  published
    property OnProgress: TProgressEvent
      read FProgressEvent write FProgressEvent;
  end;
    
The import unit for the DLL used in your D7 program would then look like this:

unit MysteryComponentWrapper;

interface

type
  TWrapperProgressEvent = procedure(PercentDone: Integer) of object;

function WrapperProcessData(Data: Pchar;
  ProgressCallback: TWrapperProgressEvent): Boolean;

function DestroyWrapper: Boolean;

implementation

function WrapperProcessData(Data: Pchar;
  ProgressCallback: TWrapperProgressEvent): Boolean;
  external 'MystComp.DLL';

function DestroyWrapper: Boolean;
  external 'MystComp.DLL';

end.

The DLL project file would look like this:

library MystComp;

uses
  WrapperU;

exports
  WrapperProcessData, DestroyWrapper;
begin
end.

The meat is in the WrapperU unit:

unit WrapperU;

interface
type
  TWrapperProgressEvent = procedure(PercentDone: Integer) of object;

function WrapperProcessData(Data: Pchar;
  ProgressCallback: TWrapperProgressEvent): Boolean;

function DestroyWrapper: Boolean;

implementation

uses Sysutils, MysteryComponentU;

type
  TWrapper = class
  private
    FProgressEvent: TWrapperProgressEvent;
    FMysteryComponent: TMysteryComponent;

    procedure ProgressHandler(PercentDone: Integer);
  public
    constructor Create;
    destructor Destroy; override;

    function ProcessData(Data: Pchar;
      ProgressCallback: TWrapperProgressEvent): Boolean;
  end;

var
  Wrapper: TWrapper; //starts out as Nil

function WrapperProcessData(Data: Pchar;
  ProgressCallback: TWrapperProgressEvent): Boolean;
begin
  try
    if not Assigned(Wrapper) then
      Wrapper := TWrapper.Create;
    Result := Wrapper.ProcessData(Data, ProgressCallback);
  except
    Result := false;
  end;
end;

function DestroyWrapper: Boolean;
begin
  Wrapper.Free;
  Wrapper := nil;
end;

procedure TWrapper.ProgressHandler(PercentDone: Integer);
begin
  if Assigned(FProgressEvent) then
    FProgressEvent(PercentDone);
end.

constructor TWrapper.Create;
begin
  inherited;
  FMysteryComponent := TMysteryComponent.Create(nil);
  FMysteryComponent.OnProgress := ProgressHandler;
end;

destructor TWrapper.Destroy;
begin
  FMysteryComponent.Free;
  inherited;
end;

function TWrapper.ProcessData(Data: Pchar;
  ProgressCallback: TWrapperProgressEvent): Boolean;
begin
  FProgressEvent := ProgressCallback;
  Result := FMysteryComponent.ProcessData(Data);
end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése