Friday, July 4, 2008

Delphi plugin by example

One of the top asked questions in Delphi newsgroups is how to place a form contained in a Dll into our application forms. This method allows to create pluggable applications, easier to maintain and customize.

To accomplish our task, first of all we'll create a Dll using File -> New -> Other -> Dll Wizard. Delete the comment aboud memory managment then Save the Dll as "dllform.dpr".

Now create a form inside the Dll with File -> New -> Form. Go to the Object Inspector and change the name of the new form to MyDllForm then add some controls like buttons, labels and everything you want, then remove the "var Form1: TForm1;" reference, you don't need it. Save the form as "myform".

Go back to the Dll source by going to Project -> View Source, and look at the uses clause, a reference to the recently created form unit must be there. Try to compile by hitting Ctrl + F9, if anything fails, re-check the previous paragraphs.

Adding exportable code

As you can't export a Form directly, you must create an exportable function who can export your form class.

Go to your form's source and add just above the "implementation" section, this code:
  // To get a reference of your form's class
TMyFormClass = class of TMyDllForm;
// To be able to export the form class.
function MyFormClass: TFormClass; stdcall; export;
Then create the body of MyFormClass function, go below the "implementation" section and write this:
function MyFormClass: TFormClass; stdcall;
begin
Result := TMyDllForm;
end;
Now, you must tell your library what functions to export. This is easy, just go to Project -> View Source and add this before the "end.":
exports
MyFormClass;
Before compiling be sure to activate "Build with Runtime Packages" in Project -> Options -> Packages. When you click the checkbox, a ton of packages separated by a comma appears just below, leave only the "vcl" package.

The main form

Create a new application by going to File -> New -> VCL Forms Application. This creates a new application with a main form called Form1. Go to the form and add a TButton and a TPanel.

Why a TPanel?, whell, the TPanel will contain the form we'll load from the Dll. You can use a TPageControl with a TTabSheet instead, or any other container.

Now we'll add a couple of private fields in the TForm1 class:

private
FLoadedForm: TForm;
FLibHandle: Cardinal;
Now we'll implement a method to dynamically load the Dll. Go to the Form1, then to the Object Inspector -> Events and double click on "OnCreate" and "OnDestroy" to create these two events, then write this code inside them:

procedure TForm5.FormCreate(Sender: TObject);
begin
FLibHandle := LoadLibrary('dllform.dll');
end;

procedure TForm5.FormDestroy(Sender: TObject);

begin
FLoadedForm.Free;
FreeLibrary(FLibHandle);
end;

After that, double click on the TButton to add the code needed to call the exported function in the Dll and create an instance of TMyForm:

procedure TForm1.Button1Click(Sender: TObject);
type
TMyFormClass = function: TFormClass; stdcall;

var
lMyFormClass: TMyFormClass;
begin
if not Assigned(FLoadedForm) then
begin
lMyFormClass := GetProcAddress(FLibHandle, 'MyFormClass');
if @lMyFormClass <> nil then
begin
// Create a TMyForm instance (not visible)
FLoadedForm := lMyFormClass.Create(nil);
// Place the Panel from TMyForm in Panel1
FLoadedForm.Controls[0].Parent := Panel1;
end;
end;
end;
Again, before compiling remember to enable "Build with Runtime Packages", as you did when compiling the Dll. If you don't do this, an "Cannot assign a TFont to a TFont" error will be raised when you click Button1.

You can download a sample project from here.

No comments: