TPP2 - De versie voor thumbdrives

Door AtleX op donderdag 27 augustus 2009 13:00 - Reacties (6)
Categorieën: Development & code, Tools, Views: 5.261

Een vaak geuite wens voor zowel T.net Photo Poster 1 als T.net Photo Poster 2 is het kunnen installeren van het programma op een USB-stick. Dit omdat veel gebruikers op kantoor of op school geen rechten hebben om programma's te kunnen installeren. Aangezien ik een groot fan ben van portable applicaties heb ik besloten hier wat tijd aan te besteden.

Er zijn verschillende manieren om portable applicaties te 'installeren'. Vaak is er een speciale installer die de applicatie op een USB-stick of ander removable storage device kan installeren. Daarnaast worden sommige applicaties in een zip-file verspreid die na het uitpakken op de USB-stick een werkend programma oplevert. Deze laatste optie heeft een aantal voordelen:
  • Geen installer, dus de gebruiker heeft vrijwel nooit onvoldoende rechten
  • Weinig stappen benodigd voor installatie
  • Minder werk voor de ontwikkelaar, er hoeft immers geen aparte installer onderhouden te worden
In eerste instantie ging ik voor de luie optie. Simpelweg een Zip maken van alle DLL's en de executable, die uploaden en klaar is Kees AtleX!.

Nou, niet dus. Er komt wel iets meer bij kijken kwam ik achter. Als eerste, de settings file. De instellingen van TPP2 worden opgeslagen in een XML-bestand dat wordt opgeslagen in de AppData-folder van de user. Bij een normale release wordt een initiele versie van het bestand daar neergezet en die is daarna dus bruikbaar door de applicatie. Als het bestand niet gevonden kan worden draait TPP wel, maar instellingen worden niet opgeslagen.

Ook de image cache, waarover ik in een later artikel een stuk meer ga vertellen, wordt in AppData opgeslagen. Zonder cache is het alleen maar mogelijk om files te uploaden in TPP2 en alle overige functionaliteit werkt niet. Deze twee grote problemen riepen om een oplossing, die gelukkig snel gevonden was.

TPP2 kijkt namelijk niet altijd in AppData voor de settings en de image cache. Bij een debug-build (Debug configuration) wordt in Visual Studio altijd een DEBUG constant gedefinieerd. Deze gebruikte ik al om bij het debuggen de settings en imagecache in dezelfde folder te zoeken als de executable. Hierdoor is het mogelijk te debuggen zonder eerst de installer een keer gedraaid te moeten hebben en kan ik de image cache en instellingen van debug runs gescheiden houden van eventuele geïnstalleerde versies van TPP2.

Het checken hierop is makkelijk:

C#:
1
2
3
4
5
6
7
8
// Alleen in AppData opslaan als er niet wordt gedebugged
#if (!DEBUG)
            string appdataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "T.net Photo Poster 2");
            this.m_Settingsfile = Path.Combine(appdataDir, "settings.xml");
#else
            this.m_Settingsfile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "settings.xml");

#endif


Ik heb een losse configuration aangemaakt voor portable builds, genaamd "Release_USB" waarbij de constant "USB" gedefinieerd wordt. Daardoor kon ik bovenstaande code aanpassen naar:

C#:
1
2
3
4
5
6
7
8
// Alleen in AppData opslaan als er niet wordt gedebugged of vanaf een thumbdrive gedraaid wordt
#if (!DEBUG && !USB)
            string appdataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "T.net Photo Poster 2");
            this.m_Settingsfile = Path.Combine(appdataDir, "settings.xml");
#else
            this.m_Settingsfile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "settings.xml");

#endif


En voila, bij een build met de "Release_USB" config, die verder een kopie van de al aanwezige "Release" configuratie is, wordt de config ook uit dezelfde folder als waar de executable staat gelezen. Hetzelfde trucje haal ik natuurlijk ook uit bij het bepalen van de locatie van de image cache.

Dit ging een tijdje goed, al bleef er een nadeel over. Ik test de daily builds vanaf een oude trage USB-stick met een opslagcapaciteit van wel 128MB. Dat ding kan nog aardig snel sequentieel lezen, maar random reads gaan niet zo snel. TPP2 bestaat op het moment van schrijven uit 6 DLL's, 1 executable en 1 XML-bestand voor de instellingen. Op mijn bejaarde USB-stick duurde het al gauw 1-1,5 seconde voor TPP gestart was. Wat meten en debuggen toonde aan dat het inladen van de DLL's de oorzaak was.

Gelukkig is ook daar een oplossing voor: ILMerge. Hiermee kunnen meerdere .NET assemblies (DLL's, executables) gemerged worden tot één assembly. Ik gebruik het al een jaar om bijvoorbeeld SendMail.exe (een tooltje om mailtjes te sturen vanuit bijvoorbeeld batch-files) als single executable te kunnen releasen.

Automatisch een enkele executable maken van alle losse bestanden (op de XML-file na natuurlijk) is heel eenvoudig te doen door middel van een Post-build event in Visual Studio:

code:
1
2
3
4
5
6
7
8
9
10
11
12
if $(ConfigurationName)==Debug goto :exit
if $(ConfigurationName)==Release goto :exit
if $(ConfigurationName)==Release_USB goto :ilmerge

:ilmerge
cd $(SolutionDir)
if Exist ILMerge cd ILMerge
if Exist ILMerge.exe ILMerge.exe /wildcards /target:winexe /out:"$(TargetDir)tpp_usb.exe" "$(TargetPath)"  "$(TargetDir)*.dll"
cd $(TargetDir)

:exit
exit


Deze code wordt uitgevoerd na het builden en controleert voert ILMerge uit als de "Release_USB" configuratie is gekozen. De "if Exists" controles zijn noodzakelijk omdat ILMerge niet geherdistribueerd mag worden en aangezien opnemen in SVN (zodat elke developer 'm binnenkrijgt) valt daar waarschijnlijk ook onder. De check voorkomt een foutmelding als een developer niet over ILMerge beschikt.

Het resultaat is uiteindelijk één enkele executable die samen met de instellingen gedistribueerd wordt in een Zip archive. Het opstarten van TPP2 op mijn antieke USB-stick duurt nu nog slechts 200-300 miliseconden, een behoorlijk tijdwinst dus.

Buiten deze kleine wijzigingen is de thumbdrive versie exact gelijk aan de normale. De image cache bestaat dus nog steeds uit veel kleine bestanden en dat maakt de applicatie nog steeds niet flitsend snel als 'ie vanaf een USB-stick gedraaid wordt. Ik heb een poging gedaan om een in-memory cache te maken maar heeft meer na- dan voordelen. Zo is een in-memory cache natuurlijk weg zodra de applicatie gesloten wordt en elke keer als TPP2 gebruikt wordt moet dus een volledig sync gedaan worden. Bij een gemiddeld fotoalbum duurt dat al gauw 5 minuten. Ook moet ik dan twee caching systemen gaan onderhouden en daar heb ik begrijpelijkerwijs geen zin in.

Zodra TPP2 'af' is zal de portable variant ook direct beschikbaar zijn. Daarnaast is 'ie al onderdeel van mijn daily builds dus de gelukkigen die over een paar weken/maanden (nee, nog steeds geen release date ;)) mogen gaan bèta-testen kunnen er dan ook over beschikken. :)

Volgende: TPP2 -  De caches uitgelegd 09-'09 TPP2 - De caches uitgelegd
Volgende: Negeer de 'vervuiling'! 08-'09 Negeer de 'vervuiling'!

Reacties



Door Tweakers user AtleX, donderdag 27 augustus 2009 13:40

himlims_: Da's leuk, maar een applicatie moet er wel geschikt voor zijn. De standaard versie van TPP2 is absoluut niet geschikt te krijgen voor PortableApps en daarom heb ik een portable versie gemaakt die je wel op USB-sticks kan zetten (en desnoods een PortableApps versie ervan maken).

Door Tweakers user aegis, donderdag 27 augustus 2009 13:42

voor dat caching waarom gebruik je niet in-mem cache tijdens het gebruik en dat als TPP2 gesloten word dat het naar een file geschreven word. en bij opstarten dat die die file weer inleest?

Door Tweakers user AtleX, donderdag 27 augustus 2009 13:50

@aegis: Dat was mijn eerste caching systeem en dat heeft een aantal nadelen:
  • Enorme memory usage, bij mijn fotoalbum algauw 500-600MB
  • Het afsluiten wordt enorm traag, ik moet dan honderden MB's gaan wegschrijven bij een beetje leuk fotoalbum en da's niet zo snel.
  • Bij het starten van de applicatie moet ik hoe dan ook een cache sync doen, dan kan ik 'm net zo goed direct wegschrijven naar disk. Dat levert amper een performance-loss op aangezien de ene thread naar een in-memory buffer download en een andere thread consumed die buffer en schrijft 'm weg.
  • Als TPP openstaat als Windows afgesloten wordt is de kans heel groot dat ik mijn cache niet compleet kan wegschrijven. Er blijft dan een corrupted cache over en dat zorgt voor een full resync bij de volgende keer dat TPP gebruikt wordt.
Al met al denk ik niet dat het een goede oplossing is. :)

Door mengoboii, donderdag 27 augustus 2009 19:38

Mischien is kijken naar applicatie virtualisatie zoals vmware thinapp.

Door Tweakers user AtleX, donderdag 27 augustus 2009 19:56

@Mengoboii: Da's vast heel leuk voor beheerders, maar hoe gaat mij dat helpen bij de ontwikkeling en verspreiding van TPP2? Het was prettig geweest als je een beetje de post had gelezen. :)

Reageren is niet meer mogelijk