なぜバッグを使うのですか?
答えは簡単です。パッケージの強力な機能のためです。設計時パッケージはカスタム コンポーネントのリリースとインストールを簡素化し、実行時パッケージは従来のプログラミングに新たな力を注入します。再利用可能なコードをランタイム ライブラリにコンパイルすると、それを複数のアプリケーション間で共有できます。すべてのアプリケーションは、パッケージを通じて標準コンポーネントにアクセスできます。これは、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, 'HtmlHelpA'); 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('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;
不幸的是,并不是这样就万事大吉了。
问题在于,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 のパッケージを機能させることだけです。