Warum Pakete verwenden?
Die Antwort ist einfach: wegen der leistungsstarken Funktionen des Pakets. Entwurfszeitpakete vereinfachen die Veröffentlichung und Installation benutzerdefinierter Komponenten; Laufzeitpakete verleihen der herkömmlichen Programmierung neue Kraft. Sobald Sie wiederverwendbaren Code in einer Laufzeitbibliothek kompiliert haben, können Sie ihn für mehrere Anwendungen freigeben. Alle Anwendungen können über Pakete auf Standardkomponenten zugreifen, so wie es auch Delphi selbst tut. Da die Anwendung keine separate Komponentenbibliothek in die ausführbare Datei kopieren muss, werden Systemressourcen und Speicherplatz erheblich gespart. Darüber hinaus reduzieren Pakete den Zeitaufwand für die Kompilierung, da Sie nur anwendungsspezifischen Code kompilieren müssen.
Wenn Pakete dynamisch genutzt werden können, können wir mehr Vorteile erzielen. Pakete bieten einen neuartigen modularen Ansatz für die Entwicklung von Anwendungen. Manchmal möchten Sie möglicherweise bestimmte Module zu optionalen Komponenten der Anwendung machen, beispielsweise ein Buchhaltungssystem, das mit einem optionalen HR-Modul geliefert wird. In einigen Fällen müssen Sie nur die Basisanwendung installieren, während Sie in anderen Fällen möglicherweise zusätzliche HR-Module installieren müssen. Dieser modulare Ansatz lässt sich einfach durch Pakettechnologie umsetzen. In der Vergangenheit konnte dies nur durch dynamisches Laden einer DLL erreicht werden. Mithilfe der Paketierungstechnologie von Delphi können Sie jedoch jeden Modultyp der Anwendung in Bundles „verpacken“. Insbesondere sind aus einem Paket erstellte Klassenobjekte Eigentum der Anwendung und können daher mit Objekten in der Anwendung interagieren.
Laufzeitpakete und -anwendungen
Viele Entwickler betrachten Delphi-Pakete nur als einen Ort, an dem Komponenten platziert werden können. Tatsächlich können (und sollten) Pakete im modularen Anwendungsdesign verwendet werden.
Um zu demonstrieren, wie Sie Pakete zur Modularisierung Ihrer Anwendung verwenden, erstellen wir ein Beispiel:
1. Erstellen Sie ein neues Delphi-Programm mit zwei Formen: Form1 und Form2; . Entfernen Sie Form2 aus der automatisch erstellten Formularliste (Projekt | Optionen | Formulare);
3. Platzieren Sie eine Schaltfläche auf Form1 und geben Sie Folgendes in den OnClick-Ereignishandlercode ein:
4. Denken Sie daran, Unit2 zur Verwendungsklausel von Unit1 hinzuzufügen.with TForm2.Create(Application) do begin ShowModal; Free; End;
5.
Wir haben eine einfache Anwendung erstellt, die ein Formular mit einer Schaltfläche anzeigt. Durch Klicken auf diese Schaltfläche wird ein weiteres Formular erstellt und angezeigt.
Aber was sollen wir tun, wenn wir Form2 im obigen Beispiel in ein wiederverwendbares Modul einbinden und dafür sorgen möchten, dass es weiterhin normal funktioniert?
Die Antwort lautet: garantiert!
Um ein Paket für Form2 zu erstellen, sind folgende Arbeiten erforderlich:
Öffnen Sie den Projektmanager (Ansicht | Projektmanager).
2 Projektgruppe und wählen Sie „Neues Projekt hinzufügen …“; wählen Sie „Paket“ in der Projektliste „Neu“ aus.
;
5. Wählen Sie das Projekt „Enthält“ aus und klicken Sie dann auf die Schaltfläche „Hinzufügen“. ";
7. Jetzt sollte das Paket die Einheit „Unit2.pas" enthalten;
8. Abschließend speichern und kompilieren Sie das Paket.
Jetzt haben wir das Paket fertiggestellt. In Ihrem ProjectBPL-Verzeichnis sollte sich eine Datei mit dem Namen „package1.bpl“ befinden. (BPL ist die Abkürzung für Borland Package Library und DCP ist die Abkürzung für Delphi CompiledPackage.)
Dieses Paket wurde fertiggestellt. Jetzt müssen wir den Paketoptionsschalter
aktivieren und die ursprüngliche Anwendung neu kompilieren.
1. Doppelklicken Sie im Projektmanager auf „Projekt1.exe“, um das Projekt auszuwählen.
2. Klicken Sie mit der rechten Maustaste und wählen Sie „Optionen…“. das Menü Projekt |. Optionen...)
3 >5. Bearbeiten Sie das Bearbeitungsfeld „Laufzeitpakete“: „Vcl50;Paket1“ und klicken Sie auf die Schaltfläche „OK“
6 . App speichern und ausführen.
Die Anwendung wird wie zuvor ausgeführt, der Unterschied ist jedoch in der Dateigröße erkennbar.
Project1.exe ist jetzt nur noch 14 KB groß, im Vergleich zu 293 KB zuvor. Wenn Sie den Ressourcenbrowser verwenden, um den Inhalt der EXE- und BPL-Dateien anzuzeigen, werden Sie feststellen, dass Form2 DFM und Code jetzt im Paket gespeichert sind.
Delphi schließt die statische Verknüpfung von Paketen während der Kompilierung ab. (Aus diesem Grund können Sie Unit2 nicht aus dem EXE-Projekt entfernen.)
Überlegen Sie, was Sie daraus ziehen können: Sie können ein Datenzugriffsmodul im Paket erstellen und beim Ändern der Datenzugriffsregeln ( (z. B. Konvertieren von BDE-Verbindung zu ADO-Verbindung), ändern Sie es leicht und veröffentlichen Sie das Paket erneut. Alternativ können Sie in einem Paket ein Formular erstellen, das die Meldung „Diese Option ist in der aktuellen Version nicht verfügbar“ anzeigt, und dann in einem anderen Paket ein voll funktionsfähiges Formular mit demselben Namen erstellen. Jetzt haben wir das Produkt ohne Aufwand in den Versionen „Pro“ und „Enterprise“.
Dynamisches Be- und Entladen von Paketen
在大多数情况下,静态连接的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。
In unserem dritten Beispiel entwerfen wir eine Anwendung zur Anzeige von Kundeninformationen und (dynamisch) zur Anzeige von Kundenbestellungen bei Bedarf.
Wo fangen wir also an? Wie bei allen Datenbankanwendungen müssen wir eine Verbindung herstellen. Wir erstellen ein Hauptdatenmodul, das eine TDataBase-Verbindung enthält. Anschließend kapseln wir dieses Datenmodul in ein Paket (cst_main).
Jetzt erstellen wir in der Anwendung ein Kundenformular und verweisen auf DataModuleMain (wir verknüpfen statisch VCL50 und cst_main).
Dann erstellen wir ein neues Paket (cst_ordr), das das Kundenbestellformular enthält, und benötigen cst_main. Jetzt können wir cst_ordr dynamisch in die Anwendung laden. Da das Hauptdatenmodul bereits vorhanden ist, bevor das dynamische Paket geladen wird, kann cst_ordr die Hauptdatenmodulinstanz der Anwendung direkt verwenden.
Das obige Bild ist ein Funktionsdiagramm dieser Anwendung:
Austauschbare Pakete: Ein weiteres Anwendungsbeispiel für Pakete ist die Erstellung austauschbarer Pakete. Für die Implementierung dieser Funktionalität sind die dynamischen Ladefunktionen des Pakets nicht erforderlich. Angenommen, wir möchten eine zeitlich begrenzte Testversion eines Programms veröffentlichen.
Zuerst erstellen wir ein „Splash“-Formular, normalerweise ein Bild mit dem Wort „Testversion“, und zeigen es während des Startvorgangs der Anwendung an. Anschließend erstellen wir ein „Über“-Formular, um einige Informationen zur Bewerbung bereitzustellen. Abschließend erstellen wir eine Funktion, die testet, ob die Software veraltet ist. Wir kapseln diese beiden Formen und diese Funktion in einem Paket und veröffentlichen es mit der Testversion der Software.
Für die kostenpflichtige Version erstellen wir außerdem ein „Splash“-Formular und ein „About“-Formular – mit den gleichen Klassennamen wie die beiden vorherigen Formulare – und eine Testfunktion (nichts tun) und kapseln sie in ein Paket mit demselben Namen.
Was was? Ist das nützlich, fragen Sie? Nun, wir können eine Testversion der Software der Öffentlichkeit zugänglich machen. Wenn ein Kunde die App kauft, müssen wir nur das Nicht-Testpaket senden. Dies vereinfacht den Software-Release-Prozess erheblich, da nur eine Installation und ein Registrierungspaket-Upgrade erforderlich sind. Das
-Paket öffnet eine weitere Tür zum modularen Design für die Entwicklungsgemeinschaften Delphi und C Builder. Mit Paketen müssen Sie keine Fensterhandles mehr weitergeben, keine Callback-Funktionen mehr, keine andere DLL-Technologie mehr. Dies verkürzt auch den Entwicklungszyklus der modularen Programmierung. Alles, was wir tun müssen, ist, die Pakete von Delphi für uns arbeiten zu lassen.