|
Dynamische applicaties
Erik 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 RegisterMenuItem
method. 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.
|