Dynamische applicatiesGeplaatst door Baldo op 16-04-03Erik Stok (aka Baldo)
Er zijn op NL Delphi al veel vragen gesteld over het concept dynamische applicaties.
Hoewel veel mensen, bewust of onbewust, een soort dynamische applicatie willen
bouwen is er opvallend weinig literatuur over te vinden. En werkende voorbeelden
zijn al helemaal schaars. Bij deze een poging daar verandering in aan te brengen...
Definitie
De meeste artikelen die ik over het onderwerp dynamische applicaties las vergaten
eenvoudigweg te vertellen wat een dynamische applicatie eigenlijk is. Een dynamische
applicatie is een applicatie die run-time qua samenstelling van functionaliteit
kan wijzigen.
Een goed voorbeeld hiervan is de verkenner van Windows. Winzip breidt de functionaliteit
van de verkenner uit door zich in het pop-up menu van de verkenner te plaatsen.
De verkenner biedt faciliteiten om programmas van derde partijen in te
laten haken, de derde partij draagt zorg voor de implementatie van de code die
de verkenner van informatie voorziet hoe en waar dit dynamische gedeelte dient
te verschijnen.

Natuurlijk is het concept veel breder dan de manier waarop de verkenner werkt.
Je kunt zelf in Delphi applicaties bouwen waarvan bijvoorbeeld delen van het
menu dynamisch worden gevuld afhankelijk van wat de gebruiker aan functionaliteit
heeft geïnstalleerd. Of je kunt bijvoorbeeld de lijst van rapporten bepalen
op basis van de rapporten die geladen zijn.
Schematisch is een dynamische applicatie als volgt voor te stellen:

De voordelen van dynamische applicaties zijn duidelijk. Je kunt je applicatie
in delen uitrollen. Gebruikers installeren alleen dat wat nodig is of dat waar
ze een licentie voor hebben.
Een ander voordeel is dat je je applicatie kunt uitbreiden en alleen het dynamisch
toe te voegen deel hoeft uit te rollen. Hetzelfde geldt voor eventuele patches
in de dynamische onderdelen. Je hoeft alleen het aangepaste dynamische onderdeel
opnieuw uit te rollen, niet de complete applicatie.
Een ander mogelijk voordeel is dat je derde partijen toestaat uitbreidingen
op jouw applicatie te bouwen. Natuurlijk moet je goed afspreken wat jij aanbiedt
aan de derde partij en wat jij van hem verwacht dat hij implementeert. WinAmp
is een goed voorbeeld van een dynamische applicatie waar derde partijen veel
voor implementeren.
Er zijn natuurlijk ook nadelen aan het maken van dynamische applicaties. Je
zult goed in de gaten moeten houden wat er allemaal geïnstalleerd moet
worden om de applicatie te laten werken.
Je zult ook een goede scheiding moeten maken tussen de verschillende onderdelen
van je applicatie, want het ene onderdeel van de applicatie moet goed blijven
functioneren als het andere onderdeel er niet is. Dat is iets waar je met statische
applicaties wat slordiger mee om mag gaan.
Het veranderen van de afspraken wat een dynamisch onderdeel moet doen en wat
jouw applicatie doet voor een dynamisch onderdeel is ook een risicovolle onderneming.
Je zult zorg moeten dragen dat alleen dynamische onderdelen geladen worden die
voldoen aan de afspraken die gelden voor deze versie van de applicatie.
Tevens moet er toezicht gehouden worden op de beveiliging van de applicatie.
Als jij een dynamisch onderdeel voor je applicatie kan bouwen, dan kan een ander
dat ook. En waar heeft die programmeur allemaal toegang toe als zijn onderdeel
geladen wordt?
Technieken
Er zijn verschillende technische oplossingen mogelijk om dynamische applicaties
te maken met Delphi. De drie meest besproken zijn dynamische applicaties op
basis van dlls, op basis van com en op basis van Delphi packages.
Het spreekt voor zich dat het statische deel van de applicatie een reguliere
Delphi applicatie is. Delphi biedt gelukkig faciliteiten om dlls, com-objecten
en Delphi packages te maken, dus ook het dynamische deel van de applicatie kan
in Delphi worden gebouwd.
Bij dynamische applicaties op basis van dlls wordt het dynamische gedeelte
van de applicatie in dlls geïmplementeerd. Het voordeel van een dll
oplossing is dat iedere programmeur die in staat is een dll te bouwen jouw applicatie
kan uitbreiden, dus ook C++ programmeur kan uitbreidingen bouwen. Tevens kun
je in een dll allerlei code insluiten zonder je zorgen te hoeven maken over
conflicten met andere delen van de applicatie (unit1 in dll A heeft geen last
van het feit dat er ook een unit1 in dll B bestaat).
Een groot nadeel van een dll oplossing vind ik persoonlijk dat ik rekening
moet gaan houden met memory management zodra ik strings, complex types of objecten
wilde doorgeven als parameters aan functies.
Tevens is het gebruik van singletons een stuk lastiger, omdat bij mijn implementatie
van het singleton pattern iedere dll zijn eigen instantie maakte van de singleton.
En dat is natuurlijk niet de bedoeling.
Ook ben ik niet in staat instanties te maken van classes gedefinieerd in dlls
buiten de dll waarin ze gedefinieerd zijn. De nadelen wegen me iets zwaarder
dan de voordelen, dus voor dlls kies ik in ieder geval niet.
Bij dynamische applicaties op basis van com wordt het dynamische gedeelte van
de applicatie gebouwd in (in process) com-objecten. Bij communicatie met com-objecten
liep ik al vrij snel tegen het probleem aan dat ik aan de com datatypes gebonden
ben, tenzij ik mijn eigen datatypes bij com registreer. Dat vind ik wat te ver
gaan.
Tevens is het registratieproces van com een vervelende bijkomstigheid. Bij
een installatie moet ik er zorg voor dragen dat het com object geregistreerd
raakt. En bij de-installatie moet alles weer worden teruggedraaid. En wat me
aan het hele registratieproces echt tegenstaat is dat ik een applicatie-specifiek
onderdeel voor heel Windows zou registreren. De scope van mijn com object
zou zich moeten beperken tot mijn eigen applicatie en niet verder!
Ook com leek mij dus geen geschikte kandidaat voor mijn dynamische applicatie,
aangezien ik geen voordelen zie ten opzichte van dlls en die hebben al
niet mijn voorkeur.
De dynamische applicatie oplossing met Delphi packages lijkt veel op die met
dlls, alleen in plaats van dlls wordt er gebruik gemaakt van bpls
(feitelijk is een bpl een luxe dll). Bij het gebruik van packages worden de
nadelen van de dlls echter opgeheven. Er is dan geen memory management,
er kan wel gebruik gemaakt worden van singletons en classes kunnen wel over
package-grenzen heen worden aangemaakt. Een bijkomend voordeel is dat de dynamische
onderdelen van de applicatie gebruik maken van dezelfde Delphi packages als
de applicatie zelf, dus de uiteindelijke executable en dlls worden relatief
kleiner.
Er is echter wel een nadeel aan packages. Je kunt niet in package A een unit1
en in package B ook een unit1 opnemen. Aangezien het toch een goede gewoonte
is om units unieke namen te geven is dat voor mij geen beperkend nadeel. Bij
het toestaan van dynamische onderdelen van derden is het natuurlijk wel een
beperking om serieus rekening mee te houden.
Een ander nadeel van Delphi packages is de lastige deployment. Het uitrollen
van een applicatie wordt een complexe taak omdat je wel alle noodzakelijke run-time
packages moet uitrollen, zowel die van jezelf als die van Delphi. En het is
geen eenvoudige taak om erachter te komen welke packages allemaal nodig zijn,
want de foutmelding volgt vaak pas bij het opstarten van de functionaliteit.
Al met al kleven overal nadelen aan, maar de nadelen van de Delphi packages
oplossing zijn voor mij het meest gemakkelijk op te lossen. Unieke naamgeving
en een slimme dependency tool lossen deze nadelen op.
Het voorbeeld
Als voorbeeld van een dynamische applicatie
gaan we de volgende applicatie maken:

DynamicApp is de applicatie die dynamisch kan worden uitgebreid qua
functionaliteit. De message plugin is een dynamisch applicatie onderdeel
dat een stuk functionaliteit implementeert dat een booschap toont. De form
plugin is een dynamisch applicatie onderdeel dat twee stukken functionaliteit
implementeert: het tonen van een modaal form en het tonen van één
of meer niet modale forms.
De applicatie
Zoals al eerder gezegd is het de taak van de applicatie om faciliteiten te
bieden om dynamische onderdelen zich in te laten haken. In feite sluit het statische
gedeelte van de applicatie een contract met potentiële dynamische onderdelen
waarin staat zo kun je inhaken. Het is een goede zaak om een dergelijk
contract vast te leggen en interfaces zijn precies daarvoor bedoeld.
De applicatie zal het volgende interface gaan implementeren:
IApplication = interface(IInterface) procedure RegisterMenuItem(Action: TAction); procedure UnregisterMenuItem(Action: TAction); end;
De RegisterMenuItem method stelt dynamische applicatie onderdelen in
staat één of meer actions te registreren waarvan de functionaliteit
in de OnExecute zal worden gestart. De applicatie draagt zorg voor de
toegang naar deze actions, in het geval van het voorbeeld via het menu.
Natuurlijk zal bij het uit de lucht halen van de dynamische functionaliteit
ook het item weer uit het menu moeten worden gehaald. Daar dient de UnregisterMenuItem
method voor.
Een willekeurig object in de applicatie kan het IApplication interface implementeren.
In het voorbeeld is ervoor gekozen om het main form zorg te laten dragen voor
deze implementatie, want het main form is altijd beschikbaar gedurende het bestaan
van de applicatie en tevens is het menu direct bij de hand.
TFrmMainForm = class(TForm, IApplication)
... public // De implementatie van IApplication procedure RegisterMenuItem(Action: TAction); procedure UnregisterMenuItem(Action: TAction); end;
In het voorbeeld wordt voor elke action die zich registreert een nieuw menu
item aangemaakt onder het menu item Plugins.
procedure TFrmMainForm.RegisterMenuItem(Action: TAction); var Mni : TMenuItem; begin // Maak een nieuw menu item aan Mni := TMenuItem.Create(mnuMain);
// Zet de action ervan naar de binnengekomen action Mni.Action := Action;
// Voeg het nieuwe item toe aan het plugins menu mniPlugins.Add(Mni); end;
Voor elk item dat zich de-registreert wordt het menu item weer opgeruimd.
procedure TFrmMainForm.UnregisterMenuItem(Action: TAction); var i : Integer; begin // Loop door het plugins menu for i := 0 to mniPlugins.Count - 1 do begin
// Kijk of het huidige menu item de action aan zich gekoppeld heeft if mniPlugins.Items[i].Action = Action then begin // Zo ja, verwijder het menu item dan mniPlugins.Items[i].Free;
// En stop de loop Break; end;
end; end;
De applicatie is ook verantwoordelijk voor het laden van de dynamische onderdelen
van de applicatie. Vaak zal bij het opstarten van de applicatie het laden van
de dynamische onderdelen plaatsvinden, en bij het afsluiten worden de dynamische
onderdelen weer uit de lucht gehaald. In het geval van het voorbeeld worden
in de FormCreate de packages geladen en in de FormDestroy worden de packages
weer uit de lucht gehaald.
procedure TFrmMainForm.FormCreate(Sender: TObject); begin // Registreer deze applicatie als de applicatie waar packages geregistreerd // moeten worden PackageManager.SetApplication(Self);
// Geef opdracht tot het loaden van de packages PackageManager.LoadPackages(ChangeFileExt(Application.ExeName, '.txt')); end;
procedure TFrmMainForm.FormDestroy(Sender: TObject); begin // Geef opdracht tot het unloaden van de packages PackageManager.UnloadPackages;
// Geef aan dat er geen applicatie meer is PackageManager.SetApplication(nil); end;
Packages
Zoals ook al eerder gezegd draagt het dynamische onderdeel zorg voor de implementatie
van de functionaliteit en hoe dit dynamische gedeelte dient te verschijnen.
In het geval van het voorbeeld zal de OnExecute van ieder geregistreerde
action de implementatie van de functionaliteit bevatten. De applicatie biedt
maar één manier hoe de functionaliteit zich kan tonen, namelijk
via de RegisterMenuItemmethod. Impliciete afspraak hierbij is dat de
caption van de action zal bepalen welke tekst er in het menu van de applicatie
zal worden getoond.
Als het package bij het laden zijn actions registreert bij de applicatie, en
zijn actions bij het uit de lucht gaan weer de-registreert, dan hebben we een
werkende dynamische applicatie. Het probleem is alleen: het package weet niets
van de applicatie waarin hij geladen wordt! Het (de)registreren van de actions
bij de applicatie kan dus niet in het package worden gebouwd tenzij het package
kennis heeft van de applicatie.
De package manager
Hier komt het principe van de package manager om de hoek kijken. Als er een
class zou zijn die kennis heeft van zowel de applicatie als de packages, dan
zouden we via een instance van die class de communicatie tussen beiden op kunnen
zetten. De package manager is zon class.
Ten behoeve van de application heeft de package manager drie methods geimplementeerd.
De eerste method is SetApplication. Via deze method kan de applicatie
zich registreren bij de package manager zodat de package manager in staat is
om op de applicatie invloed uit te oefenen. De tweede method is LoadPackages.
De LoadPackages method zorgt ervoor dat de packages geladen worden die
in het via de parameter doorgegeven bestand staan opgegeven. De derde method
is UnloadPackages. Zoals de naam al doet vermoeden zorgt deze method
dat alle packages weer uit de lucht gehaald worden.
Ten behoeve van packages voorziet de package manager in twee methods: RegisterPackage
en UnregisterPackage. Zodra een package zich registreert bij de packagemanager
zal deze, gebruik makend van de methods die het package moet implementeren,
er zorg voor dragen dat alle actions uit het package geregistreerd raken bij
de application. Dit neemt de noodzaak van kennis van de applicatie bij de packages
weg. De UnregisterPackage method doet precies het tegenovergestelde van
RegisterPackage.
In de declaratie van RegisterPackage en UnregisterPackage is
te zien dat gebruik gemaakt wordt van een package interface: IPackage. Het package
interface is het contract dat het package afsluit met de applicatie
waarop hij dynamische functionaliteit vormt. Het package garandeert dat het
dit interface implementeert. Ook hier geldt dat elke willekeurig object in het
package dit interface kan implementeren. In het voorbeeld is gekozen voor een
datamodule, want daar kan makkelijk een actionlist in geplaatst worden.
IPackage = interface(IInterface) function ActionCount: Integer; function Action(Index: Integer): TAction; end;
Met de package manager is nog een ander probleem opgelost.
Als packages kennis moeten hebben van de applicatie, dan zouden ze dus kennis
moeten hebben van het IApplication interface. De enige manier waarop dit het
geval kan zijn is de iApplicationIntf unit op te nemen in de uses clause van
een unit in het package. Dat veroorzaakt een probleem als de applicatie deze
ook al in de uses clause van het main form heeft staan. Je kunt met packages
immers niet tweemaal dezelfde unit in een package en/of applicatie gebruiken.
Door de unit met het IApplication interface op te nemen in het package manager
package is dit probleem uit de wereld geholpen. Zowel de applicatie als alle
packages hebben een afhankelijkheid naar het package manager package en daarmee
is er geen probleem meer van het dubbel gebruiken van units.
De packages zorgen ervoor dat bij het laden van het package een registratie
plaatsvindt. Dit gebeurt door gebruik te maken van de initialization sectie
van een unit. De initialization vindt plaats zodra een onderdeel geladen wordt
waar de unit in de uses clause staat, in dit geval de package project unit.
initialization // Maak de datamodule aan die het package interface implementeert DtmPluginMessage := TDtmPluginMessage.Create(nil);
Zodra de functionaliteit implementerende datamodule aangemaakt
wordt vindt de registratie plaats bij de package manager.
procedure TDtmPluginMessage.DataModuleCreate(Sender: TObject); begin // Als dit package wordt geladen, dan wordt in de initialization een instance // van deze class gemaakt. Bij het aanmaken zal deze insance zich moeten // registreren bij de package manager. De communicatie tussen het package // en de package manager verloopt dan via de datamodule instance PackageManager.RegisterPackage(Self); end;
Zodra het package uit de lucht gehaald wordt vindt de finalization
van de unit plaats. Deze finalization draagt zorg voor het opruimen van de datamodule
die de dynamische funtionaliteit implementeert.
finalization // Ruim de datamodule op FreeAndNil(DtmPluginMessage);
In de destructor van de datamodule wordt zorg gedragen voor
de-registratie bij de package manager.
procedure TDtmPluginMessage.DataModuleDestroy(Sender: TObject); begin // Als deze datamodule weer wordt opgeruimd moet hij zich ook weer // de-registreren bij de package manager. Deze datamodule instance wordt // opgeruimd in de finalization (dus bij het uit de lucht halen van het // package) PackageManager.UnregisterPackage(Self); end;
Compileren
Voordat een applicatie met dergelijke afhankelijkheden gecompileerd kan worden
zal er nogal wat ingesteld moeten worden voor de compiler. Er zijn zeker wat
instellingen die moeten worden gedaan om alles goed te laten werken.
Het is verstandig om de applicatie onderdelen in verschillende mappen onder
te verdelen. In het voorbeeld is daar ook gebruik van gemaakt.
Zorg ervoor dat de dcp van de package manager naar een map gecompileerd worden
waar de dynamische onderdelen van de applicatie ook bij kunnen. In het geval
van het voorbeeld wordt er voor alle packages gecompileerd naar de ../../bin
map en de dcps worden naar de ../../dcp map gecompileerd. Door in de packages
met dynamische onderdelen ../../dcp op te nemen in het search path, zal de .dcp
van de package manager gevonden worden.

Bij de dynamische packages kan het package manager package dan opgenomen worden
in de lijst van required packages. Dit zorgt ervoor dat alle units uit het package
manager package die in een uses clause staan van een dynamisch package niet
in het dynamische package gecompileerd worden, maar dat er voor deze units een
verwijzing naar het package manager gebruikt wordt.
Omdat men naar mijn gevoel bij Borland denkt dat het goed kunnen werken met
packages niet echt belangrijk is (de updates voor packages verschijnen altijd
later of nooit) is aan de package editor weinig aandacht besteed. Als je voor
het eerst met deze editor werkt hou dan rekening met het volgende: een unit
opnemen in het package kun je doen door gebruik te maken van de Add
knop, maar voor het toevoegen van een requirement van een ander package wordt
dezelfde Add knop gebruikt. Het is echter niet zo dat je bij het
toevoegen kunt kiezen welke van deze twee acties je wilt doen. Het is afhankelijk
van wat je hebt geselecteerd in de treeview met de package inhoud. Heb je de
Contains node (of iets wat daar onder hangt) geselecteerd, dan voeg
je met Add een unit toe. Heb je de Requires node (of
iets wat daar onder hangt) geselecteerd, dan voeg je een requirement toe. Grappig
detail hierbij: voor het verwijderen wordt weer geen onderscheid gemaakt.
Om de requirement naar het package manager package op te nemen in een dynamisch
package, moet dus eerst de Requires node geselecteerd worden. Vervolgens
kan de PackageManagerPkg.dcp uit de map dcp geselecteerd worden.

De applicatie maakt ook gebruik van units uit de package manager. Deze moeten
ook niet in de applicatie worden meegecompileerd om dezelfde reden als bij de
packages. De applicatie zal dan ook gecompileerd worden met de optie Build
with runtime packages.
Deze optie kan worden ingesteld door de applicatie te selecteren in de project
group en via het menu Project - Options de tab Packages te selecteren.

Als een applicatie gecompileerd wordt met runtime packages, dan wordt daarmee
bedoeld dat units die in de applicatie worden gebruikt die afkomstig zijn uit
één van de packages genoemd in de opgegeven lijst van runtime
packages niet worden meegecompileerd in de applicatie. In plaats daarvan wordt
een verwijzing naar het overeenkomstige package opgenomen.
Om de hoeveelheid packages die uitgeleverd moeten worden te reduceren was mijn
eerste gedachte om hier alleen het package manager package op te nemen. Dit
kan echter niet, want de dynamische packages hebben requirements naar bepaalde
runtime packages. Als daar units in zitten die ook in de applicatie gecompileerd
worden dan is daar weer het eerder genoemde unit conflict.
Strikt genomen is het voldoende om de runtime package list van de applicatie
op te bouwen uit alleen die packages die gebruikt worden vanuit de dynamische
onderdelen. Let hierbij op dat als het package manager package required is in
een bepaald dynamisch package, en in het package manager package is het vcl
package required, dan is het vcl package dus ook impliciet required voor dat
dynamische package.
Dat laatste brengt ons even op een zijspoor. Delphi gaat niet echt handig om
met het aangeven welke packages required zijn als je een package compileert.
Delphi geeft dan namelijk een overzicht van alle required packages, ook de impliciete
required packages. Vaak is het voldoende om maar een aantal van de genoemde
required packages op te nemen in de lijst van required packages en wordt de
rest impliciet voldaan. Het is aan te raden goed te kijken welke packages expliciet
nodig zijn om de lijst van required packages zo klein mogelijk te houden. Dit
vergroot de overzichtelijkheid en indien een requirement vervalt worden ook
eventuele implicitiete requirements opgeheven.
Even een simpel voorbeeld hiervan. Maak een nieuw package aan. Maak daar een
nieuwe unit in aan. Zet in de uses clause van deze unit de unit Spin (de unit
die hoort bij de spinedit van de samples tab). Als je het package compileert,
dan zegt Delphi dat er required packages missen, namelijk Vcl en VclSmp. Vcl
is echter alleen required omdat VclSmp required is. Annuleer de waarschuwing
en voeg handmatig in de requires lijst het VclSmp package toe (te vinden in
C:\Program Files\Borland\Delphi6\Lib). Als er nu wordt gecompileerd is er geen
melding van het feit dat Vcl mist.
Maar nu weer terug naar de lijst van runtime packages voor de applicatie. Om
het voorbeeld te laten werken voor zowel Delphi professional als Delphi enterprise
(er is voor dit voorbeeld gebruik gemaakt van Delphi 6), bestaat de lijst van
runtime packages uit de standaard lijst van runtime packages voor Delphi 6 professional.
Het enige standaard package wat niet in de lijst is opgenomen is het office
package omdat dit per installatie kan verschillen (office 97 of office 2000).
Aan de lijst is voor deze applicatie het package manager package toegevoegd
(aan het einde van de lijst). Delphi heeft nog steeds geen fatsoenlijke editor
voor deze lijst, wat aansluit bij het gevoel dat Borland het bouwen met packages
niet belangrijk vindt. Toevoegen kan door gebruik te maken van de Browse
knop of door aan het einde van de lijst een puntkomma toe te voegen en de naam
van het package daar achter op te nemen.
Installatie
We hebben nu een applicatie die compileert en werkt. Maar wat moet er nu precies
bij de gebruiker worden geïnstalleerd om de applicatie te laten werken?
Het antwoord op deze vraag is eenvoudig: de executable en alle noodzakelijke
packages. Dat zijn dus de Delphi packages, eventuele third party component packages,
applicatie packages (zoals de package manager) en de dynamische packages.
Het is redelijk eenvoudig om te achterhalen welke packages expliciet required
zijn en dus welke er moeten worden uitgerold. Deze packages zijn immers terug
te vinden in de requires lijst van de packages. Voor packages waarvoor een impliciete
requirement geldt is dat echter een stuk lastiger. Eigenlijk is het nalopen
van alle applicatiefunctionaliteit en kijken naar eventuele foutmeldingen het
enige wat Delphi hiervoor standaard als oplossing biedt.
Er is echter een schaars aanbod van tools op het internet te vinden die helpen
bij het bepalen van een lijst van uit te rollen packages. Ik vond persoonlijk
alles wat ik tegenkwam onder de maat, dus heb ik zelf iets gemaakt. Deze tool
genaamd Re-depend is terug te vinden op de Delphi
sectie van mijn homepage. Door de executable en alle dynamische packages
op te nemen in de scanlijst kan worden bepaald welke packages required zijn.
Het is de bedoeling door te blijven gaan met uitbreiden van de lijst totdat
alle noodzakelijke packages gevonden zijn.
Nadat bekend is welke bestanden er bij de gebruiker terecht moeten komen is
er natuurlijk de vraag waar deze geïnstalleerd moeten worden. De meeste
eenvoudige manier van installeren is alles in de applicatie map installeren.
Dan kunnen altijd alle packages gevonden worden en is er nooit een probleem
met verschillende versies van packages.
Een nadeel aan deze oplossing is dat voor alle applicaties die packages gebruiken
een kopie van de Delphi packages in de applicatie map staan. Dat is een beetje
zonde van de ruimte. Een optie is dan om de packages waar meerdere applicaties
gebruik van maken te installeren in een map die toegankelijk is voor al deze
applicaties. Vaak wordt de windows system map hiervoor gebruikt. Let er wel
op dat deze manier van installeren alleen verstandig is voor packages waarvoor
de kans op wijzigingen zeer gering is. Als er een nieuwe versie van een package
geïnstalleerd wordt, dan kan dat gevolgen hebben voor alle applicaties
die gebruik maken van dat package.
Conclusie
Delphi biedt alle faciliteiten om dynamische applicaties te bouwen. De Delphi
packages bieden duidelijk de meest krachtige oplossing voor dynamische applicaties.
Er zijn wat zaken waar rekening mee gehouden dient te worden alvorens dynamische
applicaties kunnen worden gebouwd, maar common Delphi knowledge
is voldoende om een succesvolle Delphi implementatie te maken. Sta wel stil
bij de complexiteit die je jezelf op de hals haalt op het gebied van installatie
en updates. Een goede versie administratie en een helder plan voor eventuele
updates zijn echt een noodzaak om dynamische applicaties goed te laten werken. |