1. Technology

Your suggestion is on its way!

An email with a link to:

http://delphi.about.com/library/weekly/aa020805a.htm

was emailed to:

Thanks for sharing About.com with others!

Storing and Calling an MDI Child Form from a DLL
Need to modularize your application? Learn how to place a Delphi MDI child form into a dynamic link library (DLL) and how to display the child form inside an MDI parent window. Surprise: this is only possible when using run-time packages!
 More of this Feature
• Download Source CODE
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• MDI development
• Creating and using DLL's
• Understanding and creating Packages
• DLL vs. Package

It's quite common, when developing complex applications, to "break" the application into modules (application partitioning), with each module providing special functionality. A module can be built as a dynamic link library, and added (loaded) by the application as needed.

If you are creating an MDI application, and want to break it into modules, one natural question pops up: "how to store an MDI child form into a DLL, and let the main (MDI parent) application load the child form as needed?"

If you know how to store a non - MDI child form into a DLL and call it from an application, you might think your problem is solved ... unfortunately it is not!

How to tell the MDI child form located in a DLL which form is its parent (so that it actually appears as a child form)? The Application object used to create an MDI child form (in the MDI parent) is NOT the same Application object you have in a DLL! What's more, the Screen object (cursor control) is also different.

MDI child in a DLL? No way!
As you might guess from the title, there is NO way to place an MDI child form into a DLL and call it from some MDI type application. Even though you might locate some articles on the Internet that describe how to "send" the Application object to the DLL (in order for the child window to be created inside the correct MDI parent), this approach will not work (at least will not work with Delphi > 5 versions)!

But, what's the solution?
If you want to group MDI children into a DLL you have to build the host MDI application and the DLL with run-time packages. This ensures that all the modules and the main application use the same Application and Screen objects as well as the same instance of the RTL and VCL. To be 100% sure, you should use Packages instead of DLLs (why mess with DLLs when those MDI child forms make sense only in your Delphi application)!

MDI child inside a Package - the only true solution!
A Delphi Package is a special type of DLL, designed only to be used by Delphi applications. If your modules are developed as packages and not as DLLs, all modules will share the same memory manager, the VCL globals like Application and Screen, the same copy of the RTL and VCL code in memory!

Enough theory, let's get to code...

MDI child packed in a Package, an example
Let's build one simple MDI application and a simple package holding one child form.

Note: both projects are contained in one project group, full source can be downloaded.

First, we'll create an MDI type of application:

Here are the steps:

  1. Create a new MDI application. You can even use the MDI Application wizard (File - New - Other - Projects - MDI Application).
  2. Make sure FormStyle property of the main form is set to fsMDIForm.
  3. Add a main menu to the application. Let it have one item - the one that loads the MDI child from the Package.

    MDI module parent

  4. Make sure you build the application using run-time packages. Go to Project-Options, select the Packages tab, and check the "Build with run-time packages". You should at least use the rtl and the vcl package.

    Project-Options - built with packages

Before writing some actual Delphi code, let's first build the package and add one MDI child form to it.
  1. Start by creating a run-time package.
  2. Add a TForm object to the package. Make sure FormStyle property is set to fsMDIChild.
  3. Add one exported procedure to create an instance of the child form:

procedure TPackageMDIChildForm.FormClose
  (Sender: TObject; 
   var Action: TCloseAction);
begin
  //since this is an MDI child, make sure 
  //it gets closed when the user 
  //clicks the x button.
  Action := caFree;
end;

procedure ExecuteChild;
begin
  TPackageMDIChildForm.Create(Application);
end;

exports
  //NOTE!! The export name 
  //is CASE SENSITIVE
  ExecuteChild;
  
end.

Going back to the MDI host application...

Here's the entire code in the main MDI form:

type
  //signature of the "ExecuteChild" 
  //procedure from the Package
  TExecuteChild = procedure;

  TMainForm = class(TForm)
   ...
  private
    PackageModule : HModule;
    ExecuteChild : TExecuteChild;
    procedure PackageLoad;
  end;

var
  MainForm: TMainForm;

implementation
{$R *.dfm}

procedure TMainForm.PackageLoad;
begin
  //try loading the package 
  //(let's presume it's in the same 
  //folder, where the main app. exe is)
  PackageModule := LoadPackage('MDIPackage.bpl');

  //if loaded, try locating 
  //the ExecuteChild procedure
  if PackageModule <> 0 then
  try
    @ExecuteChild := GetProcAddress(PackageModule,
                                    'ExecuteChild');
  except
    //display an error message if we fail
    ShowMessage ('Package not found');
  end;
end;

//menu click
procedure TMainForm.mnuCallFromDLLClick
(Sender: TObject);
begin
  //lazzy load package
  if PackageModule = 0 then PackageLoad;

  //if the ExecuteChild procedure
  //was found in the package, call it
  if Assigned(ExecuteChild) then ExecuteChild;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  //if the package was loaded, 
  //make sure to free the resources
  if PackageModule <> 0 then 
    UnloadPackage(PackageModule);
end;

The above code, dynamically loads (PackageLoad procedure) the package as needed (when menu item is selected) and unloads the package when the application terminates. If you need help with the code, check the "Dynamic Form in Dynamic Package" article.

Finally, at run time, we have an MDI child form loaded from a package and working happily inside an MDI parent form:

Packaged MDI Child form inside an MDI parent

One final note: when modularizing applications using run-time packages, you have to redistribute the required packages along with the application's exe file.

That's it! As simple as it can be.

©2014 About.com. All rights reserved.