Why use dynamic packages in Delphi

伊谢尔伦
Release: 2016-11-21 11:34:44
Original
1952 people have browsed it

Why use bags?

The answer is simple: because of the powerful functions of the package. Design-time packages simplify the release and installation of custom components; run-time packages inject fresh power into traditional programming. Once you compile reusable code into a runtime library, you can share it among multiple applications. All applications can access standard components through packages, which is how Delphi itself does it. Because the application does not have to copy a separate component library in the executable file, this greatly saves system resources and disk space. Additionally, packages reduce the time spent on compilation because you only need to compile application-specific code.

If the package can be used dynamically, then we can get more benefits. Packages provide a novel modular approach to developing applications. Sometimes you may want to make certain modules optional components of the application, such as an accounting system that comes with an optional HR module. In some cases, you only need to install the basic application, while in other cases you may need to install additional HR modules. This modular approach can be easily implemented through package technology. In the past, this could only be achieved by dynamically loading a DLL, but using Delphi's packaging technology, you can "package" each module type of the application into bundles. In particular, class objects created from packages are owned by the application and can therefore interact with objects in the application.

Runtime Packages and Applications

Many developers only think of Delphi packages as a place to put components. In fact, packages can (and should) be used in modular application design.

To demonstrate how to use packages to modularize your application, we create an example:

1. Create a new Delphi program with two forms: Form1 and Form2;

2. Automatically create a form from Form2 Remove from the list (Project | Options | Forms);

3. Place a button on Form1, and enter the following code in the button's OnClick event handler:

with TForm2.Create(Application) do
begin
ShowModal;
Free;
End;
Copy after login

4. Remember to add Unit2 to the uses child of Unit1 In the sentence;

5. Save and run the project.

We created a simple application that displays a form with a button. Clicking this button will create and display another form.

But if we want to include Form2 in the above example into a reusable module and make it still work normally, what should we do?

The answer is: Bao!

To create a package for Form2, the following work is required:

1. Open the Project Manager (View | Project Manager);

2. Right-click the Project Group and select "Add NewProject...";

3. In " New" project list, select "Package";

4. Now you should be able to see the package editor;

5. Select the "Contains" project, and then click the "Add" button;

6. Then click "Browse. .." button and select "Unit2.pas";

7. The package should now contain the "Unit2.pas" unit;

8. Finally save and compile the package.

Now we have completed the package. There should be a file named "package1.bpl" in your ProjectBPL directory. (BPL is the abbreviation of Borland Package Library, and DCP is the abbreviation of Delphi CompiledPackage.)

This package has been completed. Now we need to turn on the package options

and recompile the original application.

1. Double-click "Project1.exe" in the project manager to select the project;

2. Right-click and select "Options..." (you can also select Project | Options... from the menu);

3. Select the "Packages" option page;

4. Select the "Build with runtime packages" check box;

5. Edit the "Runtime packages" edit box: "Vcl50;Package1", and click the "OK" button;

6. Note: Do not remove Unit2 from the application;

7. Save and run the application.

The application will run as before, but the difference can be seen in the file size.

Project1.exe is now only 14K in size, compared with 293K before. If you use the Resource Browser to view the contents of the EXE and BPL files, you will see that the Form2 DFM and code are now saved in the package.

Delphi completes static linking of packages during compilation. (This is why you can't remove Unit2 from the EXE project.)

Think about what you can gain from this: you can create a data access module in the package, and when changing the data access rules (such as switching from BDE connection for ADO connections), modify it slightly and republish this package. Alternatively, you could create a form in one package that displays the message "This option is not available in the current version" and then create a fully functional form in another package with the same name. Now we have the product in “Pro” and “Enterprise” versions without any effort.

Dynamic loading and unloading of packages

在大多数情况下,静态连接的DLL或BPL已经可以满足要求了。但是如果我们不想发布BPL呢? “在指定目录中找不到动态链接库Package1.bpl”,这是在应用程序终止前,我们所能得到 的唯一消息。或者,在模块化应用程序程序中,我们是否可以使用任意数量的插件?

我们需要在运行期动态连接到BPL。

对于DLL 来说,有一个简单的方法,就是使用LoadLibrary函数:

function LoadLibrary(lpLibFileName: Pchar): HMODULE;stdcall;

装载了DLL之后,我们可以使用GetProcAddress函数来调用DLL的导出函 数和方法:

function GetProcAddress(hModule: HMODULE; lpProcName:LPCSTR): FARPROC; stdcall;

最后,我们使用FreeLibrary卸载DLL:

function FreeLibrary(hLibModule: HMODULE): BOOL;stdcall;

下面这个例子中我们动态装载Microsoft的HtmlHelp库:

function TForm1.ApplicationEvents1Help(Command: Word; Data: Integer; var CallHelp: Boolean):Boolean;
type
TFNHtmlHelpA = function(hwndCaller: HWND; pszFile: PansiChar; uCommand: UINT;dwData: Dword): HWND; stdcall;
var
HelpModule: Hmodule;
HtmlHelp: TFNHtmlHelpA;
begin
Result := False;
HelpModule := LoadLibrary('HHCTRL.OCX');
if HelpModule <> 0 then
begin
@HtmlHelp := GetProcAddress(HelpModule, &#39;HtmlHelpA&#39;);
if @HtmlHelp <> nil then
Result := HtmlHelp(Application.Handle,Pchar(Application.HelpFile), Command,Data) <> 0;
FreeLibrary(HelpModule);
end;
CallHelp := False;
end;
Copy after login

动态装载BPL

我们可以用同样简单的方法来对付BPL,或者应该说基本上同 样简单。

我们可以使用LoadPackage函数动态装载包:

function LoadPackage(const Name: string): HMODULE;

然后使用GetClass 函数创建一个TPersistentClass类型对象:

function GetClass(const AclassName: string):TPersistentClass;

完成所有操作后,使用UnLoadPackage(Module:HModule);

让我们对原来的代码作一些小小的改动:

1、 在工程管理器中选中“Project1.exe”;

2、 右击之并选择“Options...”;

3、 选中“Packages”选项页;

4 、 从“Runtime packages”编辑框中移除“Package1”,并点击OK按钮;

5、 在Delphi的工具栏中,点击“Remove file from project”按钮;

6、 选择“Unit2 | Form2”,并点击OK;

7、 现在在“Unit1.pas”的源代码中,从uses子句中移除Unit2;

8、 进入Button1 的OnClick时间代码中;

9、 添加两个HModule和TPersistentClass类型的变量:

var

PackageModule: HModule;

AClass: TPersistentClass;

10、使用LoadPackage 函数装载Pacakge1包:

PackageModule := LoadPackage('Package1.bpl');

11、检查PackageModule是否为0;

12、使用GetClass函数创建一个持久类型:

AClass := GetClass('TForm2');

13、如果这个持久类型不为nil,我们就可以向从前

一样创建并使用该类型的对象了:

with TComponentClass(AClass).Create(Application) as TcustomForm do
begin
ShowModal;
Free;
end;
Copy after login

14、最后,使用UnloadPackage 过程卸载包:

UnloadPackage(PackageModule);

15、保存工程。

下面是OnClick事件处理器的完整清单:

procedure TForm1.Button1Click(Sender: Tobject);
var
PackageModule: HModule;
AClass: TPersistentClass;
begin
PackageModule := LoadPackage(&#39;Package1.bpl&#39;);
if PackageModule <> 0 then
begin
AClass := GetClass(&#39;TForm2&#39;);
if AClass <> nil then
with TComponentClass(AClass).Create(Application) as TcustomForm do
begin
ShowModal;
Free;
end;
UnloadPackage(PackageModule);
end;
end;
Copy after login

不幸的是,并不是这样就万事大吉了。

问题在于,GetClass函数只能搜索到已经注册的类型。 通常在窗体中引用的窗体类和组件类会在窗体装载时自动注册。但是在我们的例子中,窗体无法提前装载。那么我们在哪里注册类型呢?答案是,在包中。包中的每 个单元都会在包装载的时候初始化,并在包卸载时清理。

现在回到我们的例子中:

1、 在工程管理器双击“Package1.bpl”;

2、 点击“Contains”部分“Unit2”旁的+号;

3、 双击“Unit2.pas”激活单元源代码编辑器;

4、 在文件的最后加入initialization部分;

5、 使用RegisterClass过程注册窗体的类型:

RegisterClass(TForm2);

6、 添加一个finalization部分;

7、 使用UnRegisterClass过程反注册窗体的类 型:

UnRegisterClass(TForm2);

8、 最后,保存并编译包。

现在我们可以安全的运行“Project1”,它还会像从前 一样工作,但是现在你可以随心所欲的装载包了。

尾声

记住,无论你是静态还是动态的使用包,都要打开Project | Options | Packages | Build with runtime packages 选项。

在你卸载一个包之前,记得销毁所有该包中的类对象,并反注册所有已注册的类。下面的过程可能会对你有所帮助:

procedure DoUnloadPackage(Module: HModule);
var
i: Integer;
M: TMemoryBasicInformation;
begin
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;
Copy after login

在装载包之前,应用程序需要知道所有已注册类的名字。改善这一情况的方法是建立一个注册机制,以便告诉应用程序所有 由包注册的类的名字。

实例

多重包:包不支持循环引用。也就是说,一个单元不能引用一个已经引用了该单元的单元(嘿嘿)。这使得调用窗体中的某 些值难以由被调用的方法设置。

解决这个问题的方法是,创建一些额外的包,这些包同时由调用对象和包中的对象引用。设想一下我们如何使Application成为所有窗体的拥有者?变量Application创 建于Forms.pas 中,并包含在VCL50.bpl包 中。你大概注意到了你的应用程序既要将VCL50.pas编译进来,也同时你的包也需要(require) VCL50。

In our third example, we design an application to display customer information and customer orders (dynamically) on demand.

So where can we start? Like all database applications

programs, we need to connect. We create a main data module containing a TDataBase connection. Then we encapsulate this data module in a package (cst_main).

Now in the application, we create a customer form and reference DataModuleMain (we statically link VCL50 and cst_main).

Then we create a new package (cst_ordr), which contains the customer order form, and require cst_main. Now we can dynamically load cst_ordr in the application. Since the main data module already exists before dynamic packaging is loaded, cst_ordr can directly use the application's main data module instance.

The picture above is a functional diagram of this application:

Interchangeable Package: Another application example of packages is the creation of interchangeable packages. Implementing this functionality does not require the dynamic loading capabilities of the package. Suppose we want to release a time-limited trial version of a program. How do we achieve this?

First we create a "Splash" form, usually a picture with the word "Trial", and display it during the application startup process. Then we create an "About" form to provide some information about the application. Finally, we create a function that tests whether the software is out of date. We encapsulate these two forms and this function into a package and release it with the trial version of the software.

For the paid version of the software, we also create a "Splash" form and an "About" form - with the same class names as the previous two forms - and a test function (does nothing), and Encapsulate them into a package with the same name.

What what? Is this useful, you ask? Well, we can release a trial version of the software to the public. If a customer purchases the app, we only need to send the non-trial package. This greatly simplifies the software release process, as only one installation and one registration package upgrade are required. The

package opens another door to modular design for the Delphi and C++ Builder development communities. With packages you no longer need to pass window handles around, no more callback functions, no more other DLL technology. This also shortens the development cycle of modular programming. All we have to do is let Delphi's packages work for us.


Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Recommendations
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!