Warum Pakete verwenden?
Die Antwort ist einfach: wegen der Leistung 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, und Delphi selbst erledigt dies. 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 verwendet 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 Paketen 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 zum Platzieren von Komponenten, obwohl Pakete tatsächlich im modularen Anwendungsdesign verwendet werden können (und sollten).
Um zu demonstrieren, wie Sie Pakete zur Modularisierung Ihrer Anwendung verwenden, erstellen wir ein Beispiel:
1. Erstellen Sie ein neues Delphi-Programm mit zwei Formularen: Form1 und Form2;
2. Entfernen Sie Form2 aus der automatisch erstellten Formularliste (PRoject | Optionen | Formulare);
3. Platzieren Sie eine Schaltfläche auf Form1 und geben Sie den folgenden Code in den OnClick-Ereignishandler der Schaltfläche ein:
mit TForm2.Create(application) tun
beginnen
ShowModal;
Frei;
Ende;
4. Denken Sie daran, Unit2 zur use-Klausel von Unit1 hinzuzufügen.
5. Speichern Sie das Projekt und führen Sie es aus.
Wir haben eine einfache Anwendung erstellt, die ein Formular mit einer Schaltfläche anzeigt, die beim Klicken ein anderes Formular erstellt und anzeigt.
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: Bao!
Um ein Paket für Form2 zu erstellen, sind folgende Arbeiten erforderlich:
1. Öffnen Sie den Projektmanager (Ansicht | Projektmanager);
2. Klicken Sie mit der rechten Maustaste auf die Projektgruppe und wählen Sie „Neues Projekt hinzufügen …“;
3. Wählen Sie „Paket“ in der Projektliste „Neu“;
4. Jetzt sollten Sie den Paketeditor sehen können;
5. Wählen Sie den Eintrag „Enthält“ und klicken Sie auf die Schaltfläche „Hinzufügen“.
6. Klicken Sie dann auf die Schaltfläche „Durchsuchen…“ und wählen Sie „Unit2.pas“ aus.
7. Das Paket sollte nun die Unit „Unit2.pas“ enthalten;
8. Speichern und kompilieren Sie abschließend das Paket.
Jetzt haben wir das Paket fertiggestellt. In Ihrem Project/BPL-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 ist vollständig. Jetzt müssen wir den Schalter für die Paketoptionen aktivieren
und kompilieren Sie die ursprüngliche Anwendung neu.
1. Doppelklicken Sie im Projektmanager auf „Project1.exe“, um das Projekt auszuwählen.
2. Klicken Sie mit der rechten Maustaste und wählen Sie „Optionen...“ (Sie können auch Projekt | Optionen... aus dem Menü auswählen);
3. Wählen Sie die Optionsseite „Pakete“;
4. Aktivieren Sie das Kontrollkästchen „Mit Laufzeitpaketen erstellen“.
5. Bearbeiten Sie das Bearbeitungsfeld „Laufzeitpakete“: „Vcl50;Paket1“ und klicken Sie auf die Schaltfläche „OK“.
6. Hinweis: Entfernen Sie Unit2 nicht aus der Anwendung.
7. Speichern Sie die Anwendung und führen Sie sie aus.
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 führt die statische Verknüpfung von Paketen während der Kompilierung durch. (Aus diesem Grund können Sie Unit2 nicht aus dem EXE-Projekt entfernen.)
Überlegen Sie, was Sie daraus gewinnen können: Sie können ein Datenzugriffsmodul im Paket erstellen und das Paket leicht modifizieren und erneut veröffentlichen, wenn Sie die Datenzugriffsregeln ändern (z. B. von BDE-Verbindungen zu ADO-Verbindungen wechseln). 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 Laden und Entladen von Paketen
In den meisten Fällen reicht eine statisch verknüpfte DLL oder BPL aus. Was aber, wenn wir BPL nicht veröffentlichen wollen? „Die Dynamic Link Library Package1.bpl kann im angegebenen Verzeichnis nicht gefunden werden“ ist die einzige Meldung, die wir erhalten können, bevor die Anwendung beendet wird. Oder können wir in einer modularen Anwendung beliebig viele Plugins verwenden?
Wir müssen zur Laufzeit eine dynamische Verbindung zur BPL herstellen.
Für DLLs gibt es eine einfache Methode, nämlich die LoadLibrary-Funktion zu verwenden:
function LoadLibrary(lpLibFileName: Pchar): HMODULE;stdcall;
Nach dem Laden der DLL können wir die Funktion GetProcAddress verwenden, um die exportierten Funktionen und Methoden der DLL aufzurufen:
function GetProcAddress(hModule: HMODULE; lpProcName:LPCSTR): FARPROC;
Schließlich verwenden wir FreeLibrary, um die DLL zu deinstallieren:
Funktion FreeLibrary(hLibModule: HMODULE): BOOL;stdcall;
Im folgenden Beispiel laden wir dynamisch die HtmlHelp-Bibliothek von Microsoft:
function TForm1.ApplicationEvents1Help(Befehl: Word; Daten: Ganzzahl; var CallHelp: Boolean):Boolean;
Typ
TFNHtmlHelpA = function(hwndCaller: HWND; pszFile: PansiChar; uCommand: UINT;dwData: Dword): HWND;
var
HelpModule: Hmodule;
HtmlHelp: TFNHtmlHelpA;
beginnen
Ergebnis := Falsch;
HelpModule := LoadLibrary('HHCTRL.OCX');
wenn HelpModule <> 0 dann
beginnen
@HtmlHelp := GetProcAddress(HelpModule, 'HtmlHelpA');
wenn @HtmlHelp <> nil dann
Ergebnis := HtmlHelp(Application.Handle,Pchar(Application.HelpFile), Command,Data) <> 0;
FreeLibrary(HelpModule);
Ende;
CallHelp := False;
Ende;
Dynamisches Laden von BPL
Wir können die gleiche einfache Methode verwenden, um mit BPL umzugehen, oder sollte ich sagen, im Grunde die gleiche einfache Methode.
Mit der LoadPackage-Funktion können wir Pakete dynamisch laden:
function LoadPackage(const Name: string): HMODULE;
Verwenden Sie dann die GetClass-Funktion, um ein Objekt vom Typ TPersistentClass zu erstellen:
function GetClass(const AclassName: string):TPersistentClass;
Nachdem alles erledigt ist, verwenden Sie UnLoadPackage(Module:HModule);
Nehmen wir einige kleine Änderungen am Originalcode vor:
1. Wählen Sie im Projektmanager „Projekt1.exe“ aus;
2. Klicken Sie mit der rechten Maustaste darauf und wählen Sie „Optionen…“;
3. Wählen Sie die Optionsseite „Pakete“;
4. Entfernen Sie „Paket1“ aus dem Bearbeitungsfeld „Laufzeitpakete“ und klicken Sie auf die Schaltfläche „OK“.
5. Klicken Sie in der Delphi-Symbolleiste auf die Schaltfläche „Datei aus Projekt entfernen“.
6. Wählen Sie „Einheit2 | Form2“ und klicken Sie auf „OK“.
7. Entfernen Sie nun im Quellcode von „Unit1.pas“ Unit2 aus der use-Klausel;
8. Geben Sie den OnClick-Timecode von Button1 ein;
9. Fügen Sie zwei Variablen vom Typ HModule und TPersistentClass hinzu:
var
PackageModule: HModule;
AClass: TPersistentClass;
10. Verwenden Sie die LoadPackage-Funktion, um das Pacakge1-Paket zu laden:
PackageModule := LoadPackage('Package1.bpl');
11. Überprüfen Sie, ob PackageModule 0 ist.
12. Verwenden Sie die GetClass-Funktion, um einen persistenten Typ zu erstellen:
AClass := GetClass('TForm2');
13. Wenn dieser persistente Typ nicht Null ist, können wir zum vorherigen zurückkehren
Erstellen und verwenden Sie Objekte dieses Typs auf die gleiche Weise:
mit TComponentClass(AClass).Create(Application) wie TcustomForm tun
beginnen
ShowModal;
Frei;
Ende;
14. Verwenden Sie abschließend den UnloadPackage-Prozess, um das Paket zu deinstallieren:
UnloadPackage(PackageModule);
15. Speichern Sie das Projekt.
Hier ist die vollständige Liste der OnClick-Ereignishandler:
procedure TForm1.Button1Click(Sender: Tobject);
var
PackageModule: HModule;
AClass: TPersistentClass;
beginnen
PackageModule := LoadPackage('Package1.bpl');
wenn PackageModule <> 0 dann
beginnen
AClass := GetClass('TForm2');
wenn AClass <> nil dann
mit TComponentClass(AClass).Create(Application) wie TcustomForm tun
beginnen
ShowModal;
Frei;
Ende;
UnloadPackage(PackageModule);
Ende;
Ende;
Leider ist das noch nicht alles.
Das Problem besteht darin, dass die GetClass-Funktion nur nach registrierten Typen suchen kann. Formularklassen und Komponentenklassen, auf die normalerweise in einem Formular verwiesen wird, werden beim Laden des Formulars automatisch registriert. In unserem Fall kann das Formular jedoch nicht vorzeitig geladen werden. Wo registrieren wir den Typ? Die Antwort lautet: In der Tasche. Jede Einheit im Paket wird beim Laden des Pakets initialisiert und beim Entladen des Pakets bereinigt.
Nun zurück zu unserem Beispiel:
1. Doppelklicken Sie im Projektmanager auf „Package1.bpl“;
2. Klicken Sie im Abschnitt „Enthält“ auf das Pluszeichen „+“ neben „Einheit2“.
3. Doppelklicken Sie auf „Unit2.pas“, um den Unit-Quellcode-Editor zu aktivieren.
4. Fügen Sie den Initialisierungsabschnitt am Ende der Datei hinzu;
5. Verwenden Sie die RegisterClass-Prozedur, um den Formulartyp zu registrieren:
RegisterClass(TForm2);
6. Fügen Sie einen Finalisierungsabschnitt hinzu.
7. Verwenden Sie die UnRegisterClass-Prozedur, um die Registrierung des Formulartyps aufzuheben:
UnRegisterClass(TForm2);
8. Speichern und kompilieren Sie abschließend das Paket.
Jetzt können wir „Projekt1“ sicher ausführen und es wird wie zuvor funktionieren, aber jetzt können Sie die Pakete laden, wie Sie möchten.
Ende
Denken Sie daran, unabhängig davon, ob Sie Pakete statisch oder dynamisch verwenden, „Projektoptionen |“ zu aktivieren.
Denken Sie vor der Deinstallation eines Pakets daran, alle Klassenobjekte im Paket zu zerstören und die Registrierung aller registrierten Klassen aufzuheben. Der folgende Prozess kann Ihnen helfen:
procedure DoUnloadPackage(Module: HModule);
var
i: Ganzzahl;
M: TMemoryBasicInformation;
beginnen
for i := Application.ComponentCount - 1 downto 0 do
beginnen
VirtualQuery(GetClass(Application.Components[i].ClassName), M, Sizeof(M));
if (Module = 0) oder (HMODULE(M.AllocationBase) = Module) dann
Application.Components[i].Free;
Ende;
UnregisterModuleClasses(Module);
UnloadPackage(Modul);
Ende;
Vor dem Laden des Pakets muss die Anwendung die Namen aller registrierten Klassen kennen. Eine Möglichkeit, diese Situation zu verbessern, besteht darin, einen Registrierungsmechanismus zu erstellen, der der Anwendung die Namen aller vom Paket registrierten Klassen mitteilt.
Beispiel
Mehrere Pakete: Pakete unterstützen keine Zirkelverweise. Das heißt, eine Einheit kann nicht auf eine Einheit verweisen, die bereits auf diese Einheit verweist (hehe). Dies macht es schwierig, bestimmte Werte im aufrufenden Formular durch die aufgerufene Methode festzulegen.
Die Lösung für dieses Problem besteht darin, einige zusätzliche Pakete zu erstellen, auf die sowohl das aufrufende Objekt als auch die Objekte im Paket verweisen. Stellen Sie sich vor, wie wir Application zum Eigentümer aller Formulare machen? Die Variable Application wird in Forms.pas erstellt und im Paket VCL50.bpl enthalten. Möglicherweise ist Ihnen aufgefallen, dass Ihre Anwendung nicht nur VCL50.pas kompilieren muss, sondern auch VCL50 in Ihrem Paket benötigt.
In unserem dritten Beispiel entwerfen wir eine Anwendung zur Anzeige von Kundeninformationen und bei Bedarf (dynamisch) von Kundenbestellungen.
Wo können wir also anfangen? wie alle Datenbankanwendungen
Die Vorgehensweise ist die gleiche, wir müssen 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 cst_main benötigt. 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 direkt die Hauptdatenmodulinstanz der Anwendung verwenden.
Das Bild oben ist ein Funktionsdiagramm dieser Anwendung:
Austauschbare Pakete: Ein weiterer Anwendungsfall 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 des Programms veröffentlichen. Wie erreichen wir dies?
Zuerst erstellen wir ein „Splash“-Formular, normalerweise ein Bild mit dem Wort „Testversion“ darauf, und zeigen es beim Start der Anwendung an. Anschließend erstellen wir ein „Über“-Formular, das einige Informationen zur Bewerbung bereitstellt. 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 denselben Klassennamen wie die beiden vorherigen Formulare – sowie eine Testfunktion (die nichts tut) und fügen sie gekapselt in ein Paket mit denselben ein Name.
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.