The Unofficial Newsletter of Delphi Users - by Robert Vivrette




Inside the Delphi 3 Package (*.dpl)

by Wei Bao - wei.bao@bj.col.com.cn

Editors Note:  This is a very interesting technique, but I can't vouch for its safety. If you have an adventurous streak and don't mind digging into the bowels of compiled code, then this technique might be useful to you.

One of the most important improvements of Delphi 3 is the introduction of packages. Now the components are registered into individual packages. And you can dynamically load different packages in the IDE for individual projects. The best example is the IDE, and I think every Delphi expert wants to know how the IDE does this. In this article, I'll open the secret.

If you have written a component yourself, you know that in the unit of your component you have a procedure named Register. One for each component's unit. But you never call it in your application. And you even can't find the machine code for the procedure! Who needs it? or who calls it?

The answer is in fact that the component is bound into the package you've defined for Delphi. After Delphi compiles the package's source, the package's binary format (*.dpl) is loaded by the IDE through the LoadPackage procedure call.

If you look at the source of LoadPackage(in sysutils.pas), you'll see that there's a call to Windows API LoadLibrary. LoadLibrary? This function is used to load DLL files?

Well, let's look at the DPL file...

If you run the command 'tdump -e XXX.DPL', you may see that the DPL file is DLL indeed. In its export section, you may find something like following:

And near the LoadPackage, you'll find a function called GetPackageInfo(in sysutils.pas). With this function, you can get the source file of a DPL within several lines:

Yes!

Now you can load the package into your application space, but you can't use any components in it. If the package is written by you, you can solve this with the following method:

In the Initialization section of your component's unit, call registerClass(yourcomponent). The Initialization procedure is called after call to LoadLibrary in LoadPackage(see sysutils.pas). After doing this, you may create an instance of your component:

But if the DPL is from the third party, you won't have an opportunity to do this.

Now let's go back to the dump of DPL, look at the line begins with '*' . You see the DPL exports a function named 'QRNew.Register@51F89FF7'. It's obviously that the 'QRNEW' is the unit name.  What's '51F89FF7'? Is it an address of the Register procedure? No, after you look through the total output of the dump, you'll find that they are always '51F89FF7'. So we can call it a magic number. It's a const for Delphi 3.0 at least.

If we want to use the component in the DPL, we should call the Register ourselves.

So we come to the exciting part:

We should know the unit name to call 'unitname.Register@51F89FF7'. As I've given before, we can get the unit name in procedure EveryUnit . The rest thing is familiar to those who've been working with Windows APIs for a long time.

The new version of EveryUnit is :

Now all these work fine? No, you'll see a error messagebox 'register component ..'. Why? Everything is here. Now let's look at the procedure RegisterComponents you always call in procedure Register(in classes.pas). A pointer to a procedure is checked, and it always equal nil in our application! Of course, you can see that the solution is simple, write our procedure and set the pointer to it.

Every thing is well ? No, if you compile the project without runtime package, you'll get the same error messagebox. Yes, to use DPLs dynamically you can only compile your project as a runtime package.

If you try to load DPLs that ship with Delphi 3.0, you'll meet other problems, try to solve them! And you can contact the author at wei.bao@bj.col.com.cn or wei.bao@usa.net. I'm very interested to talk about these technologies with any one!

If you want to take a look at the source code I used for these examples, you can download it here...