Articoli DevLeap

Articoli DevLeap

Sviluppo Mobile: Costruire una propria libreria

 

di Roberto Brunetti

Roberto è un libero professionista del gruppo DevLeap (www.devleap.com) sul cui sito si trovano articoli e blog tecnici sulle tecnologie legate allo sviluppo software in .NET. E’ specializzato in ASP.NET, Sviluppo mobile, Architetture distribuite e Visual Studio Team System. E’ l’autore del libro ASP.NET Full Contact edito da Mondadori Informatica e numerose pubblicazioni su riviste del settore. Ha partecipato come speaker a numerose conferenze del settore ed è MCT da 1997. È il fondatore della community www.ThinkMobile.it dedicata allo sviluppo mobile.

Dopo vari articoli su SQLCE 3.0 (che diventa installabile anche su Desktop con SQL Compact Edition 3.1) è arrivato il momento di dare uno sguardo alla scrittura di una propria libreria di classi per facilitare lo sviluppo di applicazioni. Questo articolo vuole essere una panoramica delle potenzialità spesso ignorate di Visual Studio 2005, non tanto per la produzione automatica di righe di codice (tecnica tra l’altro pericolosa in caso di grandi volumi di dati), quanto per la versatilità nella scrittura della libreria.

Sembra un articolo introdottivo, e in parte lo è, ma i concetti che vorrei passare, soprattutto dopo la prima parte, spero servano a sviluppare più velocemente testando l’applicazione sul desktop e con strumenti evoluti piuttosto che fare F5 e testare l’applicazione su device o emulatori.

In sequenza affronteremo la scrittura da zero di una classe molto semplice per leggere file di configurazione, feature assente nel .NET Compact Framework 2.0. Questo semplicissimo scheletro ci farà da guida per affrontare le modalità con cui noi di DevLeap (www.devleap.com) sviluppiamo applicazioni mobile. Cercheremo di scrivere e testare la libreria direttamente sul desktop per poi farne una analisi con gli strumenti di Visual Studio Team System esattamente come se fosse una libreria desktop.

Non è nel mio stile fare un articolo passo passo in quanto spesso preferisco cercare di andare in profondità sugli argomenti lasciando poi al lettore la fantasia di implementazione. Questa volta farò uno strappo alla regola e partiremo da zero in modo da mettere insieme i vari pezzi. Questo articolo non vuole essere però una guida per neofiti, quanto mettere in evidenza le potenzialità di uno strumento facendo capire passo passo gli step di sviluppo che adottiamo.

“Mi suona strano iniziare così”: aprite Visual Studio e create un nuovo progetto (utilizzeremo C#, ma il tutto è facilmente adattabile a VB.NET)

Creiamo un progetto C# - Smart Device di tipo libreria: scegliere la piattaforma (Pocket PC 2003, SmartPhone 2003, Windows CE 5.0, o Windows Mobile 5.0 xxx) è un dettaglio tralasciabile; come sapete questa selezione serve solo a utilizzare un template di Visual Studio che prepara lo scheletro applicativo con le reference corrette e qualche vincolo per evitare di usare librerie che potrebbero non girare sulla piattaforma target; per esempio, se scegliete Pocket PC 2003 non si vedranno le librerie Windows Mobile 5.0 nell’elenco delle librerie sotto Add Reference. In ogni caso, il codice che viene prodotto dalla compilazione è codice IL (o CIL) e come tale può girare ovunque giri il runtime del .NET Compact Framework 2.0. Nel caso di progetti “Device Application” la scelta, che ancora una volta non è vincolante, è più utile in quanto il designer di Visual Studio visualizza i form all’interno di device aderenti, come look & feel, alla piattaforma scelta. Ripeto: la scelta non è vincolante, ma è solo una facilitazione per trovare Reference e designer “adatti” alla piattaforma. Con Visual Studio 2005 è ancora possibile modificare la piattaforma scelta per un progetto dopo la creazione: è sufficiente premere tasto destro sul progetto e scegliere – Change Target Platform; volendo si può agire anche manualmente nei file di progetto in formato XML per allargare la scelta di possibili piattaforme.

Scelgo Windows Mobile 5.0, posiziono il tutto sotto c:\temp, nomino la Solution DevLeap.Sample, e il progetto DevLeap.Library.Mobile. Solution e progetto hanno nomi diversi semplicemente perché successivamente aggiungeremo altri progetti alla solution in modo da avere un client di test e progetti desktop per testare la nostra libreria.

Naming convention a parte questa la maschera di creazione progetto:

Appena creato il progetto cancelliamo la mitica Class1.cs e creiamo un nuovo sorgente C# denonimato Utility.cs.

Il progetto, a questo punto, ha una classe Utility in cui aggiungeremo i metodi appunto utili J nelle nostre applicazioni mobile. Il namespace per default è giustamente DevLeap.Library.Mobile.

Rendiamo pubblica la classe in modo da poterla richiamare dall’esterno e, se volete, sealed in modo da non poterla derivare ulteriormente e da non doverci preoccupare nella sua definizione di eventuali politiche di inheritance.

Questo il nostro progetto:

Fino a qui niente di nuovo immagino. Aggiungiamo un metodo per leggere il classico file di configurazione. Solitamente utilizzo questa tecnica: espongo un metodo GetAppConfigString che riceve in input il nome del file config e la key da leggere. Non calcolo in automatico il nome del config, come fa il .NET Framework completo per evitare “casini” di concatenazione sul nome del file da trovare; questo evita problemi di naming sul file .config che hanno, ad esempio, creato non pochi problemi nelle vecchie versioni di OpenNETCF. Preferisco passare un parametro in più piuttosto che lasciare alla libreria l’onere di trovare il nome corretto. Se preferite calcolare il nome in automatico, i metodi GetExecutingAssembly e GetCallingAssembly della classe Assembly possono darvi una mano.

Per evitare la lettura del config da disco (pardon, da scheda SD J) ad ogni richiesta, metto in cache il documento XML alla prima lettura. Ci servono quindi una variabile statica _xmlDoc (per rispettare la naming convention) e una variabile di tipo Object su cui effettuare la sincronizzazione in caso di applicazione multithread.

Ecco la prima parte del codice che inserisce in cache un XmlDocument nel caso in cui sia null:

if (Utility._xmlDoc == null)

{

lock (_xmlDocSyncLock)

{

if (Utility._xmlDoc == null)

{

XmlDocument xmlDoc = new XmlDocument();

string xmlDocPath = Utility.GetAppPath() + "\\"
+ configName;

XmlTextReader tr = new XmlTextReader(xmlDocPath);

xmlDoc.Load(tr);

tr.Close();

_xmlDoc = xmlDoc;

}

}

}

Prima di inserire il codice nel metodo corretto due informazione: stiamo usando un pattern .NET che, in questo caso, ci serve a caricare il documento XML nella variabile statica _xmlDoc. L’utilizzo del lock e della doppia if su _xmlDoc serve a evitare che più thread chiamanti vadano in conflitto durante l’assegnazione della variabile _xmlDoc.

Per leggere il file .config in formato XML (il classico file .config di .NET) ho utilizzato un XmlDocument che viene alimentato da un XmlTextReader. Per rintracciare la path del file config si utilizza un’altra funzione da aggiungere alla nostra libreria (Utility.GetAppPath) che vedremo tra un attimo.

Una volta assegnato il documento XML alla nostra variabile statica si procede alla sua lettura; ecco il metodo completo:

using System;

using System.Collections.Generic;

using System.Text;

using System.Xml;

namespace DevLeap.Library.Mobile

{

public sealed class Utility

{

private static XmlDocument _xmlDoc;

private static Object _xmlDocSyncLock = new Object();

public static string GetAppConfigString(string configName,
string key)

{

if (Utility._xmlDoc == null)

{

lock (_xmlDocSyncLock)

{

if (Utility._xmlDoc == null)

{

XmlDocument xmlDoc = new XmlDocument();

string xmlDocPath = Utility.GetAppPath() + "\\"
+ configName;

XmlTextReader tr = new XmlTextReader(xmlDocPath);

xmlDoc.Load(tr);

tr.Close();

_xmlDoc = xmlDoc;

}

}

}

String value = "";

XmlNode node = _xmlDoc.SelectSingleNode
("//configuration//appSettings//add[@key='" +
key + "']/@value");

if (node != null)

{

value = node.Value.ToString();

}

return value;

}

}

}

Per leggere la “key” ricevuta come argomento all’interno dell’albero dei nodi ho utilizzato una espressione XPath tramite il metodo SelectSingleNode. Nel caso in cui non trovi l’elemento di configurazione ho deciso di restituire String.Empty.

Inseriamo a questo punto la funzione GetAppPath:

public static string GetAppPath()

{

#if All

return Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);

#else

return Path.GetDirectoryName(Assembly.GetCallingAssembly().GetName().CodeBase);

#endif

}

Vedremo più avanti l’utilizzo della #if (compilazione condizionale); in pratica, visto che su Windows Mobile caricare tante DLL può essere un problema, solitamente scrivo l’applicazione su diversi layer (UI, BIZ, DAL, Factory, Entity e così via) e poi li confeziono in un unico exe sui device che possono avere il problema di caricamento. Vedremo come fare più avanti. Per adesso la #if All serve a chiamare il metodo Assembly.GetExecutingAssembly nel caso in cui il progetto sia confezionato con un unico exe oppure Assembly.GetCallingAssembly per ottenere l’assembly chiamante di questa libreria. In entrambi i casi prelevo poi il CodeBase che rappresenta la directory da cui è stato letto l’assembly in questione.

Per poter usare la classe Path e la classe Assembly occorrono due using (le reference sono già corrette come le ha impostate il template di Visual Studio): System.IO per Path e System.Reflection per Assembly. Ovviamente qualunque altra tecnica (compreso passare il path completo al file .config sono ugualmente valide).

A questo punto creiamo un progetto exe che utilizza la libreria: per uniformità creiamo un progetto Device Application per Windows CE 5.0 che chiameremo Applicazione. All’interno del progetto metteremo anche il file .config che useremo dalla libreria. Ho aggiunto il progetto dalla solution con tasto destro Add New Project e poi inserito un file xml chiamato Applicazione.Config.

Ecco il risultato

Il nome del file config, per uniformità, è Applicazione.config. Se preferite mantenere la naming convention delle applicazioni desktop usate app.config: occorre però stare attenti in quanto successivamente cercheremo di utilizzare questa libreria sul desktop e così facendo ritorneremo nel problema accennato in precedenza: il calcolo del nome del file config automatico.

A questo punto si può inserire un pulsante di test sul form per provare a leggere il file di configurazione; prima di aggiungere il codice occorre ovviamente una reference dal progetto Applicazione al progetto DevLeap.Library.Mobile. Il form conterrà poi il codice per leggere il .config che, com’è intuibile, è il seguente:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using DevLeap.Library.Mobile;

namespace Applicazione

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

private void cmdReadConfig_Click(object sender, EventArgs e)

{

MessageBox.Show(Utility.GetAppConfigString
("Applicazione.config", "Test"));

}

}

}

Prima di eseguire il progetto su un emultatore o su un device occorre marcare il file Applicazione.config come Build Action = Content e come Copy To Output Directory = Always oppure Copy if Newer; queste impostazioni si scelgono dalla mascherina delle proprietà del file all’interno del progetto di Visual Studio e consentono appunto di eseguire il deploy come contenuto (non come compilazione ovviamente) del file di configurazione.

Eseguito il deploy e premuto il pulsante di test sul form (che ho chiamato cmdReadConfig) otteniamo ovviamente la stringa memorizzata nel file .config.

Proviamo quindi ad andare oltre: una volta creata la propria libreria, così come gli altri strati dell’applicazione occorre testare il tutto; testare il codice sul device è una procedura abbastanza lunga: per ogni modifica occorre rifare il deploy dell’applicazione, riavviare il debug (non c’è Edit & Continue sul device !). Debuggando il tutto sull’emulatore si va un po’ più veloci (soprattutto se collegate l’emulatore via DMA e usate la tastiera del pc per l’input), ma abbiamo sempre il problema di deploy per ogni modifica, l’impossibilità di usare Edit & Continue e soprattutto utilizziamo un “coso mobile” senza un file system comodo da visualizzare e editare.

Visual Studio Team System offre molti strumenti che facilitano il compito durante il test funzionale e il test di performance...ma…purtroppo questi strumenti non girano in ambiente Windows CE. Lo sviluppatore “accanito” però non si fa spaventare da questa limitazione della prima versione di Team System e si organizza J. Come ? Così !

Il Compact Framework è una versione ridotta del .NET Framework quindi, a parte qualche funzionalità specifica del .NET CF che non esiste su .NET FW completo (vedi IrDA ad esempio), tutto quello che funziona sul .NET CF funziona anche sul .NET FW completo. È disponibile un runtime del .NET CF 2.0 (anche della 1.0 in realtà) per processore X86 che ci consente di far girare il codice IL (o CIL) su Desktop.

Anche per SQLCE versione 3.0 è valido questo ragionamento. Si possono aprire e gestire file SDF sia da Visual Studio che da Sql Server 2005 Management Studio. Le librerie di SQLCE esistono anche per processore X86. Con SQL Compact Edition 3.0 (la 3.1 che al momento di scrittura di questo articolo – Dic 2006 – sta per uscire gira nativamente su desktop) è possibile, con poco lavoro, fare una reference da un progetto desktop alle librerie versione X86 per eseguire il codice di accesso ai dati su desktop; a tal proposito si veda il mio post di dicembre 2005; il post è su Team System, leggete l’ultima parte: http://blogs.devleap.com/rob/archive/2005/12/29/6428.aspx

Bene, abbiamo affermato che il codice si può testare sul desktop (vedremo come evitare l’utilzzo di barcode reader o librerie specifiche per l’ambiente mobile…tutto non si può avere J), vediamo come fare sul nostro semplicissimo progetto mobile composto da un exe e da una dll. Questa logica si può applicare, ed è quello che facciamo sui nostri progetti mobile reali, ad architetture più complesse seguendo sempre questi passi.

La sfiga (termine tecnico che sta a indicare generalmente una forma acuta di sfortuna) vuole che non si possa prendere il codice compilato per farlo girare sul desktop: questo non perché i compilatori siano diversi (vi garantisco che sono identici, anzi è proprio lo stesso csc.exe o vbc.exe a compilare i progetti mobile) ma perché le reference verso le librerie del framework sono diverse. Occorre quindi ricompilare il codice con le reference corrette per l’ambiente desktop.

Pronti? Iniziamo.

Per prima cosa creiamo un progetto di tipo Class Library normale (per il desktop) che per convenzione si chiamerà DevLeap.Library.Mobile.Desktop, così manteniamo i nomi allineati. Dopo la creazione togliamo tutti sorgenti all’interno e facciamo le stesse reference del progetto mobile: le reference vanno appunto fatte alle librerie del FW completo. Quindi se trovo una reference verso System.Xml nel progetto mobile farò una reference verso System.Xml del FW completo, se trovo un riferimento verso System.Windows.Form mobile farò una reference verso System.Windows.Form del FW completo.

Poi aggiungiamo i sorgenti del progetto mobile (Utility.cs nel nostro semplice caso). Nessuno di noi ha voglia di avere lo stesso codice in due posti diversi quindi non aggiungete il file normalmente, ma come LINK: questo consente di mantenere il sorgente “vero” nel progetto mobile, ma poterlo compilare, usare e modificare anche dal progetto desktop.

Dalla mascherina di Add Existing Item andate nella directory del progetto mobile (DevLeap.Library.Mobile nel nostro caso), selezionate tutti i sorgenti e poi premere il pulsantino nascosto accanto a Add: questa la maschera

Fate un Add as Link e non Add altrimenti il sorgente selezionato viene copiato nel progetto, cosa da evitare appunto per non raddoppiare i sorgenti.

Questo vale anche per i file reference.cs, per i file di configurazione (sempre che debbano essere identici), per i file che rappresentano i form (basta linkare il Formxxx.cs, non occorre prendere anche .designer.cs e .resx) e per i controlli custom (anche in questo caso prendere solo il Controxxx.cs; VS si preoccupa di linkare anche i file designer e resx).

Date una compilata al progetto Desktop per vedere se abbiamo tutte le reference corrette.

Proseguiamo poi con la creazione del progetto exe allineato all’eseguibile mobile: creare un Windows Application Project e referenziare DevLeap.Library.Mobile.Desktop (occorre prendere il progetto desktop ovviamente). Cancellare Form1.cs e Program.cs e poi aggiungere con la stessa tecnica dell’Add As Link Form1.cs, Applicazione.config e Program.cs dal progetto Applicazione. Per uniformità chiamerei il progetto desktop Applicazione.Desktop.

Questa la situazione attuale:

Come si nota i progetti desktop hanno i sorgenti dei rispettivi progetti mobile come Link (c’è un simbolino accanto al file) e le rispettive Reference. Ultima cosa: sul file Applicazione.config nel progetto desktop occorre impostare (come avevamo fatto per il progetto mobile) Build Action = Content e Copy to Output Directory = Copy Always o Copy If Newer.

Curiosi ? Avviate il progetto Applicazione.Desktop (fate tasto destro sul progetto, Debug, Start new instance) e come per magia ecco un form su XP (o Vista) con il form Mobile !!!

Questa tecnica funziona per qualunque controllo mobile supportato anche sul desktop. Ad esempio aggiungiamo un menù sul form mobile per capire cosa succede. Un consiglio: anche se si possono aprire i sorgenti linkati dal progetto .Desktop vi consiglio di editare i form sempre partendo dal progetto mobile; questo perché altrimenti la Toolbox di Visual Studio 2005 vi presenta tutti i controlli Windows.Form dell’ambiente desktop (giustamente) molti dei quali non esistono sul .NET CF. Aprendo invece i form dal progetto mobile avrete la toolbox corretta. Non preoccupatevi se ogni tanto riceverete il messaggio di errore “This document is opened by another project”: indica semplicemente che avete aperto un sorgente da un progetto e poi avete provato ad aprirlo da un altro progetto; è solo un warning che giustamente vi avverte che lavorate su un sorgente già aperto.

Per esempio: ho inserito sul form, dal progetto mobile, un po’ di controlli e un menù mobile. Questo il risultato sul desktop

Ovviamente il menù esce stile XP e non stile mobile e non ci sono le due soft key per rappresentare il menù.

Con il progetto desktop si può, solo per citare alcuni punti:

1) Lavorare con l’interfaccia

2) Testare il codice

3) Fare Edit & Continue durante il debug

4) Se l’applicazione lavora con il file system si può controllare il lavoro direttamente nella directory bin sul file system della macchina di sviluppo

5) Se l’applicazione scrive e legge file si possono editare dal file system del PC senza dover usare ActiveSync per copiare i file da e verso il device.

Ci sono una serie di cose che non si possono fare. Ad esempio

1) Testare librerie specifiche mobile come ad esempio BarCode Reader

2) Testare IrDA

3) Testare librerie Windows Mobile per Pocket Outlook

In questi casi uso la sequente tecnica: inserisco una direttiva di compilazione sul progetto Desktop oppure sfrutto le direttive #Pocket PC già inserite da Visual Studio sui progetti mobile per eliminare le righe che non funzionano sul desktop.

Ad esempio, in un applicativo che stiamo scrivendo adesso e che fa uso di librerie per lettori di codice a barre, questo è il codice per eliminare le parti non ammesse sul desktop. Siamo nel load di un form nello strato di User Interface che è divisa dall’eseguibile per essere sfruttata da più risoluzioni video.

private void MainForm_Load(object sender, EventArgs e)

{

UI.DummyCalls();

#if PocketPc

UI.CreateBrowserForm(new BrowserForm());

#endif

UI.ChangeState(ComeurOnline.Maui.UI.Common.BaseUI.FlowState.Login, txtRequest, lblError, lblRequest, treeLog, SendActionStepOutputMessageResponseStatus.Information, null, null, lblUserId, txtUserId, lblUserPassword, txtUserPassword, cmdLogin, cmdLogout);

#if PocketPc

// If we can initialize the Reader

if (BarCodeReader.InitReader())

{

// Create a new delegate to handle scan notifications

this._barCodeEventHandler = new
EventHandler(BarCodeReader_ReadNotify);

// Set the event handler of the Scanning class to our delegate

BarCodeReader.BarCodeEventHandler = this._barCodeEventHandler;

// Attach to activate and deactivate events

// this.Activated += new EventHandler(ReaderForm_Activated);

// Start a read on the reader

BarCodeReader.StartRead();

}

else

{

// Error

UI.ChangeState(ComeurOnline.Maui.UI.Common.BaseUI.FlowState.CommunicationError, txtRequest, lblError, lblRequest, treeLog, SendActionStepOutputMessageResponseStatus.Error, null, "BarCode Reader Error", lblUserId, txtUserId, lblUserPassword, txtUserPassword, cmdLogin, cmdLogout);

return;

}

#endif

}

Come si nota, Visual Studio “dipinge” di grigio il codice sotto compilazione condizionale. In questo caso, semplicemente “attiviamo” la nostra libreria per la lettura del codice a barre nel caso Pocket Pc: questa direttiva di compilazione è già inserita da VS 2005 in tutti i progetti mobile.

Anche se non è argomento di questo articolo, avere un progetto desktop per ogni progetto mobile della solution (un lavoro che costa circa 1 oretta di lavoro su un progetto reale: cronometrati 1 ore e 15 minuti sul progetto che stiamo seguendo adesso composto da 9 layer e quindi 9 progetti in cascata sul client mobile) consente poi di usare gli strumenti di Visual Studio Team System come Unit Testing, Code Coverage e Performance Session.

Update: Visual Studio 2008 consente la creazione e eseguzione di Unit Test direttamente dalla versione Professional. Gli unit test sono eseguibili anche su device e emulatore. In ogni caso mancano gli strumenti integrati di code coverage e performance session.

Per completezza riporto gli screenshot in cui dovrebbe essere facilmente leggibile anche il codice di Unit Test sul metodo GetAppConfigString, il risultato del Code Coverage del test e la maschera di solo sommario della Performance Session basata sull Unit Test.

Unit Test su GetAppConfigString (UtilityGetAppConfigStringTest):

 

Code Coverage su Unit Test UtilityGettAppConfigStringTest


Performance Session su Unit Test UtilityGettAppConfigStringTest

Per avere un’idea del carico sotto stress si può simulare l’accesso allo unit test da parte di uno o più utenti (simulazione valida anche per più thread) per un tempo stabilito. In VSTS questa tecnica implica l’utilizzo di un Load Test basato sullo Unit Test; sono 3 click di mouse per creare la versione base:

In un solo minuto abbiamo fatto 36.023 chiamate alla nostra funzione e direi che dai risultati (che non posso incollare in questo articolo per motivi di spazio, ma che potete analizzare voi stessi scaricando gli esempio) il tutto gira molto bene...e ci mancherebbe visto che abbiamo scritto una sola funzione J Applicate il ragionamento ad un progetto reale e tutto il tempo che risparmiate nello sviluppo su desktop potete investirlo in test più accurati.

N.B. Nel Load Test ovviamente otteniamo dei warning (39 threshold violation per l’esattezza) sul processore in quanto spesso il processore della macchina va oltre il 95%: la situazione è più che normale visto che “stressante e stressato” (Test e applicazione J) sono sulla stessa macchina.

Con pochi passaggi, avendo un progetto desktop, siamo in grado di testare il codice, di vedere cosa implica l’esecuzione del codice e possiamo mettere sotto stress il tutto per capire cosa succede nell’utilizzo reale dell’applicazione. Ovviamente i dati di performance riguardano l’ambiente desktop, ma facendo gli opportuni “scaling” si può come girerà nell’ambiente mobile.

Il codice completo, compreso il semplice Unit Test, il Load Test per testare il carico, una Performance Session per capire l’allocazione di memoria e i tempi di chiamate, e l’analisi dinamica del codice con Code Coverage, sono disponibili su www.thinkmobile.it/files: il file da scricare, previa registrazione al sito, è Articolo Infomedia Lib Mobile.zip.

Sperando di avervi semplificato la vita durante lo sviluppo delle parti core di una applicazione mobile, dandovi la possiblità con un semplice “lavoretto” di testare il 90% della soluzione e lasciando sul device o emulatore (l’emulatore comunque non avrebbe BarCode Reader ad esempio) solo la parte finale dei test vi rimando alla nostra conferenza http://devcon2007.devleap.com dove avremmo modo di analizzare in varie sessioni anche lo sviluppo mobile oltre a .NET 3.0 per Desktop e Server.

Reference:

OpenNETCF: Libreria Shared Source scaricabile da www.opennetcf.org

Articolo su Progetti desktop/mobile in VSTS : http://blogs.devleap.com/rob/archive/2005/12/29/6428.aspx

Esempi di codice completi che ripercorrono l’articolo

www.thinkmobile.it/files