Forms in DLLs
Thursday, April 29, 2004 02:42 PM
Every now and then, a question comes up on the Delphi newsgroups regarding forms in a DLL. The questions vary, but the principle is always the same. The developer writes an application using Delphi and the VCL, the creates a separate DLL using containing VCL forms. For some mysterious reason, forms in the DLL do not behave as well as forms in the main executable.
What's mysterious to me, actually, is that Delphi has been using the same VCL architecture for over nine years now, and people are still surprised by this. The reason forms in DLLs behave differently is simple - the VCL was not designed to support applications that are split into DLLs.
When Delphi was initially released, its main competitor was Visual Basic. Back then, Delphi had two main advantages over VB: first, it produced natively compiled code, which ran much faster than VB's interpreted code. And second, a Delphi executable was completely self-contained - no runtime libraries had to be distributed. That's because when Delphi compiles a module (EXE or DLL), it compiles links all the necessary code into that module - including the runtime library and the VCL (Delphi's smart linker removes any unused code from the final executable).
And herein lies the problem. A VCL application starts by creating the Application object, an instance of the TApplication class. TApplication descends from TComponent, the base component class for the VCL. TComponent descends from TPersistent, the VCL's base class that supports streaming, which in turn descends directly from TObject - the Delphi language's base class type. The code and symbols for these classes are compiled into every executable module created by Delphi.
Now, suppose we build two modules - an executable application and a DLL. Each of these modules, as far as the compiler is concerned, is completely independent. Each of them initializes its own Application object, and forms created in each module are only aware of that module's Application object. Moreover - each module actually implements its own, completely separate object model. It therefore makes very little sense to try and share forms between modules - they may appear to be similar, but in fact they have nothing in common.
Still, it's perfectly reasonable to split large applications into several modules, and DLLs seem like the perfect solution. Over the years, seeing how many people kept trying to split VCL applications using DLLs, Borland has added some support for this. But the real problem is at the base of the VCL's architecture.
Fortunately, there's a solution: don't use DLLs - use packages instead. A DLL knows nothing about object models. Externally, it can only export functions. A package, on the other hand, is a special kind of DLL that contains information about objects. It can export classes, and refer to classes in other packages. When an application uses packages, each class is only defined once. There's only one copy of TObject, for example, and only one Application object.
There are many articles and code samples available on the web, in books, and in Delphi's online help about creating and using packages. The point here is that if you want to split your application into several modules, you have a simple solution in packages. Trying to split your program into DLLs is an exercise in futility - the VCL just wasn't built for that.