Creating and Using DLLs with
Exported Classes
Advanced
DLL's - exporting a class & its methods from a DLL:
- as seen in previous DLL topics, using functions & procedures within a
DLL is easy. Classes and objects require a slightly different approach.
- the secrete here is to create an interfaced class unit in both the
application and the DLL which are identical and contain the abstract
class, then in the DLL create an additional unit that implements a derived
class ("concrete" class) from the abstract class.
- what is needed to achieve this:
- a common identical interfaced abstract unit that is referenced both by
the DLL and the application
- an routine in the application that accesses and version checks this
abstract unit
- routines in the application which load & unload the DLL, create
the class objects and access the class methods
- a "concrete" implementation unit of the abstracted class in
the DLL
Example 1:
- an example from DI
Oct 1996 SpellChecker designed to demonstrate this technique
rather than be a good spellchecker:
- the interfaced abstract class unit:
- this has no implementation section as abstract classes cannot be
implemented.
- eg.
- unit SpellChk;
- ....
- uses virtinf; //this Delphi unit defines TInterface and has
the Release and GetVersion methods;
- const ExpectedVersion = 2; //this allows you to check that
both the application & the DLL have the same version
- type
- TSuggestProc = procedure(Word: PChar) of object; stdcall;
- TISpellChecker = class
- public
- function OpenDictionary(Path:PChar): Boolean;
virtual; export; stdcall; abstract;
- procedure ClearDictionary; virtual; export; stdcall;
abstract;
- function CheckSpelling(Word:PChar; Suggest:
TSuggestProc):Boolean; virtual; export; stdcall;
abstract;
- function GetVersion: Integer; virtual; export;
stdcall; abstract;
- end;
- function GetSpellChecker: TISpellChecker;
- begin
- Result := InitSpellChecker;
- if Result.GetVersion <> ExpectedVersion
- then raise Exception.create('Incorrect version of
SPELCHECK.DLL');
- end;
- create a unit in your application to access and version check the
DLL:
- unit SpelIntf;
- { Interface unit for the spelling checker DLL (SpelChek). To use the spelling checker, add this unit to the application's
USES statement. Call the InitSpellChecker function to obtain the spell checker object. Call DoneSpellChecker when the spell
checker object is no longer needed. To ensure that the DLL implements the correct version of the TISpellChecker interface class, the InitSpellChecker routine calls the GetVersion method and compares the value with
ExpectedVersion. They should be identical. }
- interface
- uses SpellChk;
- function InitSpellChecker: TISpellChecker;
- procedure DoneSpellChecker(Checker: TISpellChecker);
- implementation
- uses WinTypes, WinProcs, Classes, SysUtils, Forms;
- { Do not load the spell checker DLL until it is needed. Most of the time, it probably is not needed, which is
why it is implemented in a DLL in the first place. }
- var
- type
- TInitSpellChecker = function: TISpellChecker;
- function InitSpellChecker: TISpellChecker;
- var InitProc: TInitSpellChecker;
- begin
- DllHandle := LoadLibrary('SpelChek.dll');
- if DllHandle = 0 then raise Exception.Create('Cannot load
SpelChek.dll');
- @InitProc := GetProcAddress(DllHandle,
InitSpellCheckerName);
- if not Assigned(InitProc) then raise Exception.Create('Invalid DLL,
SpelChek.dll');
- Result := InitProc;
- if Result = nil then raise Exception.Create('Error initializing spelling checker');
- if Result.GetVersion <> ExpectedVersion then raise Exception.Create('Incorrect version of
SpelChek.dll');
- end;
- procedure DoneSpellChecker(Checker: TISpellChecker);
- begin
- Checker.Free;
- FreeLibrary(DllHandle);
- DllHandle := 0;
- end;
- end;
- in your application you need to call the DLL as follows:
- in the uses clause:
- in the private section:
- { Reference to the spelling checker interface. }
- fSpellChecker: TISpellChecker;
- { Callback for suggested corrections. }
- procedure SuggestCallback(Word: PChar); export;
- in implementation section:
- eg. in FormCreate method:
- fSpellChecker := InitSpellChecker;
- if (fSpellChecker <> nil) and not
fSpellChecker.OpenDictionary('TestDict.txt') then
- begin
- DoneSpellChecker(fSpellChecker);
- fSpellChecker := nil;
- end;
- eg. in FormDestroy method:
- if fSpellChecker <> nil then
DoneSpellChecker(fSpellChecker);
- procedure TDemoForm.SuggestCallback(Word: PChar);
- begin
- SpellDlg.SuggestionList.Items.Add(StrPas(Word));
- end;
- procedure TDemoForm.EditCheckSpellingClick(Sender: TObject);
- { Check the spelling of the current selection. Trim non-alphabetic characters,
convert to a PChar, and call the spelling checker. If the word is not in
the dictionary, then open a modal dialog where the user can
enter a correction. }
- var Word: string; SelStart: Integer;
- begin
- SpellDlg.SuggestionList.Clear;
- Word := Trim(Memo.SelText);
- if fSpellChecker.CheckSpelling(StrToPChar(Word),
SuggestCallback) then ShowMessage('Spelling OK')
- else
- begin
- { Display the spelling correction dialog. }
- SpellDlg.WordEdit.Text := Memo.SelText;
- SetParentForm(SpellDlg.Handle, Handle);
- if SpellDlg.ShowModal = mrOK then
- begin { Replace the selection with the corrected spelling. }
- SelStart := Memo.SelStart;
- Memo.SelText := SpellDlg.CorrectEdit.Text;
- { Select the newly corrected text. }
- Memo.SelStart := SelStart;
- Memo.SelLength :=
Length(SpellDlg.CorrectEdit.Text);
- { This could be smarter about preserving spaces & punctuation... }
- end;
- end;
- end;
- the concrete implementation unit in the DLL (see SpelChek.dpr
in DI Oct 1996):
- type
- { Keep the list of words in a TDictionary. }
- TDictionary = class(TStringList)
- public
- constructor Create;
- function Add(const S: string): Integer; override;
- end;
- TSpellChecker = class(TISpellChecker)
- private
- protected
- constructor Create;
- procedure LookupSuggestions(const Word: string; Suggest:
TSuggestProc);
- function IsSimilar(const TestWord, DictionaryWord: string): Boolean;
- property WordList: TDictionary read fWordList;
- public
- function OpenDictionary(Path:PChar): Boolean; override;
- procedure ClearDictionary; override;
- function CheckSpelling(Word:PChar; Suggest:
TSuggestProc):Boolean; override;
- function GetVersion: Integer; override;
- end;
- function InitSpellChecker: TISpellChecker; export; stdcall;
- begin
- try Result := TSpellChecker.Create; except Result := nil; end;
- end;
- exports
- InitSpellChecker name InitSpellCheckerName;
- function TSpellChecker.GetVersion: Integer;
- begin
- Result := ExpectedVersion;
- end;
- other functions & procedure implementations as declared above