Plug-ins Explained - An example
(Page 4 of 4 )
Let's write an example program to demonstrate how to link a package to an application. Create a new application by going to File|New|Application and save the project as "Host" in a folder. Add a button to the form. Our application is simply going to call a function that is stored in a package that is dynamically linked to it. Our package is going to have a simple routine called "sayhello." The routine will simply say "Hello World." Create the package and add a unit. In the unit add the following code:
unit Unit1;
interface
implementation
uses windows,dialogs;
procedure sayhello;
begin
showmessage('Hello world!');
end;
exports sayhello;
end.
Before you save the package and unit, on the package editor, click on the options button, then select the "Directories/Conditionals" tab and enter the output directory. Make sure it is in the same directory as the host application. This is where the .BPL file is going to be located when you eventually compile the package. Save the package as package1. We now need to export the procedure; as with DLL files, we use the "exports" keyword. This keyword tells the compiler to make the procedure available to other programs that want to use it.
exports sayhello;
The "uses" clause is included in the code because I'm using the "showmessage" dialog, which uses the "Dialogs" Pascal unit. And that's it. Now all you need to do is save and compile the package and a .BPL file will be created. If you haven't saved and compiled the package already, then do so and exit.
All that remains for us to do now is to call the sayhello procedure that we defined in our package, from our host application that we created earlier. To load the package into our host application we use the loadpackage() function:
Loadpackage('package1.bpl');
You are responsible for unloading the package once it is loaded. The Loadpackage() function returns a handle for the package that it loaded. The handle is of type HModule and is user defined. You can find it in the Windows.pas unit, which will also show that it resolves to Pascal native longword type. This handle is what you should use to unload the package by calling the Unloadpackage() function:
Package:=LoadPackage('package1.bpl');
Unloadpackage(package);
As a matter of good coding practice, it would probably be best to first check whether the package exists before trying to load it. So try writing a function that checks whether the package exists. I usually place my plug-ins in a particular folder, to make it easy when searching for packages.
Now that we know how to load and unload packages, the next step is to call the functions and procedures that are contained within it. This is a bit tricky, since the VCL does not offer any help to us. Most programmers use the Windows API. It has a function called GetProcAddress(), that takes the dynamically loaded package handle and the function or procedure that we want to call, as in:
Var
Package:HModule;
Procptr:Pointer;
Begin
Procptr:=getprocaddress(package,'sayhello');
If assigned(procptr) then
...
Notice that our "sayhello" function is in quotes. This is because we are calling it indirectly. The getprocaddress() function returns a pointer to the procedure. The compiler does not know that procptr is a pointer, so we need to let it know what it is. Declare the following just above the implementation section:
type
Procptrtype:procedure
And call the procedure or function like so:
Procptrtype(procptr);
Our procedure does not take any parameters and therefore does not have a return type. So how would you use a function that takes parameters? Well, you do exactly what you did above, just declare the procedural type with the correct parameter type and typecast to that. For example, if we have a procedure that takes a number, we would do this:
Type
Ptrtakesnumber:procedure(num:integer);
And then call the procedure like so:
Num:=5;
Ptrtakesnumber(procptr)(num);
To put everything we've discussed so far into practice, let's return to our host application that we created earlier and do the following:
- Add the following to the forms "var" declaration:
packModule : HModule;
procptraddr : Pointer;
Type
ProcPtr = Procedure;
- Double click on the button and add the following:
procptraddr := GetProcAddress(packModule, 'sayhello' );
ProcPtr(procptraddr);
- On the OnCreate procedure add the following:
packModule := LoadPackage('Package1.Bpl');
When you run the application, and click on the button you should get a message saying "Hello World."
| DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware. |