Delphi で動的パッケージを使用する理由

伊谢尔伦
リリース: 2016-11-21 11:34:44
オリジナル
1983 人が閲覧しました

なぜバッグを使うのですか?

答えは簡単です。パッケージの強力な機能のためです。設計時パッケージはカスタム コンポーネントのリリースとインストールを簡素化し、実行時パッケージは従来のプログラミングに新たな力を注入します。再利用可能なコードをランタイム ライブラリにコンパイルすると、それを複数のアプリケーション間で共有できます。すべてのアプリケーションは、パッケージを通じて標準コンポーネントにアクセスできます。これは、Delphi 自体が行う方法です。アプリケーションは実行可能ファイル内の別のコンポーネント ライブラリをコピーする必要がないため、システム リソースとディスク領域が大幅に節約されます。さらに、パッケージを使用すると、アプリケーション固有のコードのみをコンパイルするだけで済むため、コンパイルにかかる時間が短縮されます。

パッケージを動的に使用できれば、より多くのメリットを得ることができます。パッケージは、アプリケーション開発に対する新しいモジュール式アプローチを提供します。場合によっては、オプションの HR モジュールが付属する会計システムなど、特定のモジュールをアプリケーションのオプション コンポーネントにしたい場合があります。基本アプリケーションのみをインストールする必要がある場合もあれば、追加の HR モジュールをインストールする必要がある場合もあります。このモジュール式のアプローチは、パッケージ技術を通じて簡単に実装できます。これまでは、DLL を動的にロードすることによってのみこれを実現できましたが、Delphi のパッケージング テクノロジを使用すると、アプリケーションの各モジュール タイプをバンドルに「パッケージ化」することができます。特に、パッケージから作成されたクラス オブジェクトはアプリケーションによって所有されるため、アプリケーション内のオブジェクトと対話できます。

ランタイム パッケージとアプリケーション

多くの開発者は、Delphi パッケージをコンポーネントを配置する場所としてのみ考えています。実際、パッケージはモジュール型アプリケーション設計で使用できます (またそうすべきです)。

パッケージを使用してアプリケーションをモジュール化する方法を示すために、次の例を作成します。

1. Form1 と Form2 の新しい Delphi プログラムを作成します。

2. Form2 からフォームを自動的に作成します。プロジェクト | オプション | フォーム);

3. ボタンの OnClick イベント ハンドラーに次のコードを入力します。

4. Unit2 を Unit1 の子に追加します。 5. プロジェクトを保存して実行します。

ボタン付きのフォームを表示するシンプルなアプリケーションを作成しました。このボタンをクリックすると、別のフォームが作成されて表示されます。

しかし、上記の例の Form2 を再利用可能なモジュールに組み込み、引き続き正常に動作するようにしたい場合は、どうすればよいでしょうか?

答えは「バオ」です!

Form2 のパッケージを作成するには、次の作業が必要です:

1. プロジェクト マネージャーを開きます ([表示]、[プロジェクト マネージャー])

2. プロジェクト グループを右クリックし、[新しいプロジェクトの追加...] を選択します。

3. [新しい] プロジェクト リストで、[パッケージ] を選択します。

5. [含む] プロジェクトを選択し、[追加] ボタンをクリックします。 ;

6. [参照] ボタンをクリックして、[Unit2.pas] を選択します

8. 最後に、パッケージを保存してコンパイルします。

これでパッケージが完成しました。 ProjectBPL ディレクトリに「package1.bpl」という名前のファイルがあるはずです。 (BPLはBorland Package Libraryの略、DCPはDelphi CompiledPackageの略です。)

このパッケージは完成しました。次に、パッケージ オプション

をオンにして、元のアプリケーションを再コンパイルする必要があります。

1. プロジェクトマネージャーで「Project1.exe」をダブルクリックしてプロジェクトを選択します。

2. 右クリックして「オプション...」を選択します (メニューから「プロジェクト」→「オプション...」を選択することもできます)。 );

3. [パッケージ] オプション ページを選択します。

4. [ランタイム パッケージを使用してビルド] チェック ボックスを選択し、[Vcl50;Package1] をクリックします。 「OK」ボタン

6. 注: Unit2 をアプリケーションから削除しないでください。

7.

アプリケーションは以前と同じように実行されますが、違いはファイルサイズに見られます。

Project1.exe のサイズは、以前は 293K でしたが、現在はわずか 14K です。リソース ブラウザを使用して EXE ファイルと BPL ファイルの内容を表示すると、Form2 DFM とコードがパッケージに保存されていることがわかります。

Delphi はコンパイル中にパッケージの静的リンクを完了します。 (これが、EXE プロジェクトから Unit2 を削除できない理由です。)

これから何が得られるかを考えてください: パッケージ内にデータ アクセス モジュールを作成でき、データ アクセス ルールを変更するとき (たとえば、からの切り替えなど) ADO 接続用の BDE 接続)を少し変更して、このパッケージを再公開します。あるいは、「このオプションは現在のバージョンでは使用できません」というメッセージを表示するフォームを 1 つのパッケージ内に作成し、完全に機能するフォームを同じ名前の別のパッケージ内に作成することもできます。今では、手間をかけずに製品を「プロ」バージョンと「エンタープライズ」バージョンで利用できるようになりました。

パッケージの動的なロードとアンロード

在大多数情况下,静态连接的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;
ログイン後にコピー

动态装载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;
ログイン後にコピー

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;
ログイン後にコピー

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

问题在于,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;
ログイン後にコピー

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

实例

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

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

3 番目の例では、顧客情報と顧客の注文をオンデマンドで (動的に) 表示するアプリケーションを設計します。

では、どこから始めればよいでしょうか?すべてのデータベース アプリケーション

プログラムと同様に、接続する必要があります。 TDataBase 接続を含むメイン データ モジュールを作成します。次に、このデータ モジュールをパッケージ (cst_main) にカプセル化します。

今度はアプリケーションで顧客フォームを作成し、DataModuleMain を参照します (VCL50 と cst_main を静的にリンクします)。

次に、顧客注文フォームを含む新しいパッケージ (cst_ordr) を作成し、cst_main を要求します。これで、アプリケーションに cst_ordr を動的にロードできるようになりました。メイン データ モジュールはダイナミック パッケージがロードされる前にすでに存在しているため、cst_ordr はアプリケーションのメイン データ モジュール インスタンスを直接使用できます。

上の写真は、このアプリケーションの機能図です:

交換可能パック: パッケージのもう 1 つの応用例は、交換可能パッケージを作成することです。この機能の実装には、パッケージの動的読み込み機能は必要ありません。プログラムの期間限定の試用版をリリースしたいとします。これを実現するにはどうすればよいでしょうか。

まず、「スプラッシュ」フォーム (通常は「トライアル」という単語が含まれた画像) を作成し、アプリケーションの起動プロセス中に表示します。次に、アプリケーションに関する情報を提供する「About」フォームを作成します。最後に、ソフトウェアが古いかどうかをテストする関数を作成します。これら 2 つの形式とこの機能をパッケージにカプセル化し、ソフトウェアの試用版とともにリリースします。

ソフトウェアの有料版では、前の 2 つのフォームと同じクラス名を持つ「スプラッシュ」フォームと「About」フォームも作成し、テスト関数 (何も実行しません) を作成し、それらを同じ名前のパッケージ。

何、何?これは便利ですか?さて、ソフトウェアの試用版を一般公開できます。顧客がアプリを購入した場合は、試用版以外のパッケージを送信するだけで済みます。これにより、インストールと登録パッケージのアップグレードが 1 回だけ必要となるため、ソフトウェア リリース プロセスが大幅に簡素化されます。

パッケージは、Delphi および C++ Builder 開発コミュニティにモジュラー設計への新たな扉を開きます。パッケージを使用すると、ウィンドウ ハンドルを渡す必要がなく、コールバック関数や他の DLL テクノロジも必要ありません。これにより、モジュール型プログラミングの開発サイクルも短縮されます。私たちがしなければならないのは、Delphi のパッケージを機能させることだけです。


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!