5,388
社区成员
发帖
与我相关
我的任务
分享
主要是这部分
Dynamically loaded BPLs
BPLs are just as simple. Well almost.
We dynamically load the package by using the LoadPackage function.
function LoadPackage(const Name: string): HMODULE;
We create TPersistentClass of the class we wish to instantiate by using the GetClass function.
function GetClass(const AClassName: string):
TPersistentClass;
Instantiate an object of the loaded class and use it.
And when we are done, unload the package using the UnloadPackage procedure.
procedure UnloadPackage(Module: HMODULE);
Let us go back to our example and make a few changes:
1. Select "Project1.exe" from the project manager.
2. Right-click and select "Options..."
3. Select the "Packages" tab.
4. Remove "Package1" from the "Runtime packages" edit-box section and OK the options.
5. On Delphi's toolbar, click on the "Remove file from project" button.
6. Select "Unit2 | Form2" from the list and then "OK."
7. Now go to the "Unit1.pas" source and remove Unit2 from its uses clause. (These steps are required to remove any link to Unit2 and the package we wish to load dynamically.)
8. Go to the source of Button1's OnClick event.
9. Add two variables of type HModule and TPersistentClass.
var
PackageModule: HModule;
AClass: TPersistentClass;
10. Load the package Package1 by using the LoadPackage function.
PackageModule := LoadPackage('Package1.bpl');
11. Check that the Package Module is not 0 (zero).
12. Create a persistent class using the GetClass function, passing it the name of the form within the package as its parameter:
AClass := GetClass('TForm2');
13. If the persistent class is not nil, create and use an instance of the class just a before.
with TComponentClass(AClass).Create(Application)
as TCustomForm do
begin
ShowModal;
Free;
end;
14. Finally, unload the package using the UnloadPackage procedure:
UnloadPackage(PackageModule);
15. Save the project.
Here is the complete listing of the OnClick event:
procedure TForm1.Button1Click(Sender: TObject);
var
PackageModule: HModule;
AClass: TPersistentClass;
begin
PackageModule := LoadPackage('Package1.bpl');
if PackageModule <> 0 then
begin
AClass := GetClass('TForm2');
if AClass <> nil then
with TComponentClass(AClass).Create(Application)
as TCustomForm do
begin
ShowModal;
Free;
end;
UnloadPackage(PackageModule);
end;
end;
Unfortunately that's not the end of it.
The problem is that the GetClass function requires the class to be registered before the function can find it. Usually form classes and component classes that are referenced in a form declaration (instance variables) are automatically registered when the form is loaded. But the form isn't loaded yet. So where should we register the class? The answer: in the package. Each unit in the package is initialized when the package is loaded and finalized when the package is unloaded.
Let's return to our example and make a few changes:
1. Double-click on "Package1.bpl" in the project manager; this will activate the package editor.
2. Click on the + symbol next to "Unit2" in the "Contains" section. This will expand the unit tree.
3. Double-click on "Unit2.pas" to activate the unit's source code.
4. Scroll down to the end of the file and add an initialization section.
5. Register the form's class using the RegisterClass procedure:
RegisterClass(TForm2);
6. Add a finalization section.
7. Un-register the form's class using the UnRegisterClass procedure:
UnRegisterClass(TForm2);
8. Finally, save and compile the package.
Now we can safely run the "Project1" application - it will function just as before, but with the added benefit of being able to load the package when you want to.
Finally
Make sure you compile any project that uses packages (static or dynamic) with runtime packages turned on: "Project | Options | Packages | Build with runtime packages."
You must be careful that when you unload a package you destroy any objects using those classes and un-register any classes that were registered.
This procedure may help:
procedure DoUnloadPackage(Module: HModule);
var
i: Integer;
M: TMemoryBasicInformation;
begin
{ Make sure there aren't any instances of any
of the classes from Module instantiated, if
so then free them. (This assumes that the
classes are owned by the application) }
for i := Application.ComponentCount - 1 downto 0 do
begin
VirtualQuery(
GetClass(Application.Components[i].ClassName),
M, SizeOf(M));
if (Module = 0) or
(HMODULE(M.AllocationBase) = Module) then
Application.Components[i].Free;
end;
UnRegisterModuleClasses(Module);
UnLoadPackage(Module);
end;
An application requires "knowledge" of the registered class names prior to loading the package. One way to improve this would be to create a registration mechanism to inform the application of all the class names registered by the package.