Esame 70-536 http://blogs.ugidotnet.org/idamiani/category/Esame 70-536.aspx Serie di post sull'esame 70-536 per conseguire la certificazione MCPD it-IT Igor Damiani Subtext Version 2.6.0.0 [70-536, #33] Passato! http://blogs.ugidotnet.org/idamiani/archive/2006/07/26/45355.aspx <FONT face=Verdana size=2> <P>Innanzitutto chiedo scusa a tutti per essere stato così poco produttivo nella mia serie di post relativi all'<A title="" href="http://blogs.ugidotnet.org/idamiani/category/1704.aspx" target=_new name="">esame 70-536</A>. Purtroppo ho avuto francamente poca voglia di scrivere (e studiare), e quando l'ho fatto è stato nei rari momenti in cui mi sentivo ispirato. Ho studiato&nbsp;molto meno per questo esame, ma sono rimasto contento perchè se l'ho superato devo ringraziare&nbsp;soprattutto la mia esperienza.</P> <P>L'unico rammarico è che avrei potuto prendere un punteggio nettamente superiore al <STRONG><FONT color=red>718/1000</FONT></STRONG> che ho miseramente conquistato. Ho&nbsp;sfiorato i punteggi massimi in quasi tutte le categorie (<EM><FONT color=#008040>Developing applications that use types and applications, Implementing service processes, threading and application domains in a .NET Framework application, Implementing serialization and input/output functionality in a .NET Framework application, Improving the security of .NET Framework applications by using the .NET Framework 2.0 security features, Implementing globalization, drawing and text manipulation functionality in a .NET Framework application</FONT></EM>), ma sono letteralmente caduto a picco nelle ultime due categorie rimaste <EM>(<FONT color=#008040>Embedding configuration, diagnostic, management and installation features into a .NET Framework application, Implementing interoperability, reflection and mailing functionality in a .NET Framework application</FONT></EM>). Se avrò voglia questa sera scansiono la parte del report e la metto on-line, così me la guarderò tutte le mattine e mi do le martellate sulla testa. <IMG height=19 src="http://www.imhoproject.org/files/serio.gif" width=19 align=absMiddle border=0>&nbsp;Devo dire che più che la teoria studiata da MSDN, questa volta ho sfruttato molto tutto quello che ho imparato lavorando con .NET - un'esperienza molto maggiore rispetto all'ottobre scorso.</P> <P>Comunque mi sento un pollo questa mattina, perchè me ne sarei potuto tornare a casa con un punteggio altissimo. <IMG height=19 src="http://www.imhoproject.org/files/deluso.gif" width=19 align=absMiddle border=0>&nbsp;Beh, insomma, pazienza, ormai è andata! Adesso non voglio più pensare ad esami per un bel pezzo, prima devo tornare ricaricato dalle vacanze che mi aspettano!</P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.2</FONT></A></P></FONT><!-- Powered by IMHO Instant Blogger Copyright (c) 2004 A.Boschin - http://www.elite.boschin.it --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/45355.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/07/26/45355.aspx Wed, 26 Jul 2006 13:26:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/07/26/45355.aspx#feedback 28 http://blogs.ugidotnet.org/idamiani/comments/commentRss/45355.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/45355.aspx [70-536, #32] L'attributo DllImport nei dettagli http://blogs.ugidotnet.org/idamiani/archive/2006/06/12/42703.aspx <FONT face=Verdana size=2> <P><STRONG>I parametri dell'attributo DllImport</STRONG><BR><A title="" href="http://blogs.ugidotnet.org/idamiani/archive/2006/06/07/42435.aspx" target=_new name="">Nell'ultimo post</A> riguardante l'esame 70-536, abbiamo visto come utilizzare l'attributo <STRONG>DllImport</STRONG> per poter utilizzare una funzione esportata da una libreria <EM>unmanaged</EM>. Nella sua forma base, <STRONG>DllImport</STRONG> richiede un solo parametro: una stringa che contiene il nome del file DLL che esporta la funzione che ci interessa utilizzare nella nostra applicazione <EM>managed</EM>. L'ultima volta avevamo fatto un piccolo esempio, considerando la funzione GetTempPath, esportata da kernel32.dll. la dichiarazione nella nostra classe <EM>managed</EM>era:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">[DllImport("kernel32.dll")]<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">private&nbsp;static&nbsp;extern&nbsp;uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">GetTempPath(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">nBufferLength,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Out]&nbsp;StringBuilder&nbsp;lpBuffer);<BR></DIV></SPAN> <P>L'attributo <STRONG>DllImport</STRONG> può avere qualche parametro aggiuntivo. Ad esempio, nel codice qui sopra il nome della funzione GetTempPath corrisponde al nome della funzione contenuta in kernel32.dll. Se per qualche motivo il nome della funzione <EM>managed</EM> contenuta nel prototipo differisce dal nome della funzione contenuta nella libreria <EM>unmanaged</EM>, dobbiamo fornire al framework l'<STRONG>entrypoint</STRONG> corretto. Ad esempio:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">[DllImport("kernel32.dll",&nbsp;EntryPoint&nbsp;=&nbsp;"GetTempPath")]<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">private&nbsp;static&nbsp;extern&nbsp;uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">GetTemporaryPath(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">nBufferLength,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Out]&nbsp;StringBuilder&nbsp;lpBuffer);<BR></DIV></SPAN> <P>Il parametro <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.entrypoint.aspx" target=_new name=""><STRONG>EntryPoint</STRONG></A></STRONG> di <STRONG>DllImport</STRONG> nella dichiarazione qui sopra dice semplicemente che il nome della funzione in <EM>managed</EM> code è GetTemporaryPath, che&nbsp;punta all'entrypoint GetTempPath della libreria kernel32.dll. Un secondo possibile parametro è <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.charset.aspx" target=_new name=""><STRONG>CharSet</STRONG></A></STRONG>, che riveste un'importanza fondamentale nel caso in cui dobbiamo lavorare con le stringhe: impostando questo parametro su <STRONG>CharSet.Auto</STRONG>, ci assicuriamo che le stringhe vengono passate correttamente dal mondo <EM>managed</EM> al mondo <EM>unmanaged</EM>, e viceversa. Per il marshaling, il CLR si basa sul sistema operativo su cui sta girando l'assembly.</P> <P><STRONG>Il default marshaling e l'attributo MarshalAsAttribute</STRONG><BR>Il default marshaling è il comportamento di default che .NET assume ogni volta che deve passare oggetti tra il mondo <EM>managed</EM> ed il mondo <EM>unmanaged</EM>. Tecnicamente, è espresso da una serie di tabelle che esprimono come un tipo <EM>managed</EM> viene convertito nel tipo <EM>unmanaged</EM>. Ad esempio, <A title="" href="http://msdn2.microsoft.com/en-us/library/0t2cwe11.aspx" target=_new name="">questa tabella</A> considera i value-typed di .NET.&nbsp;Tener presente&nbsp;l'attributo <A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.marshalasattribute.aspx" target=_new name=""><STRONG>MarshalAs</STRONG></A>, grazie al quale&nbsp;possiamo modificare questo comportamento, comunicando esplicitamente al CLR come effettuare il marshaling in un particolare frangente. Caso tipico sono le stringhe, che in base al tipo <EM>unmanaged</EM> (LPStr, BStr, LPWStr, etc.) possono subire il marshaling verso il contesto <EM>managed</EM> secondo diverse tipi di destinazione.</P> <P><STRONG>Fare il marshaling di strutture<BR></STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/awbckfbz.aspx" target=_new name="">Esiste una pagina su MSDN</A> che descrive accuratamente questo scenario. Fino ad adesso, abbiamo trattato con tipi di dato semplici, ad esclusione delle stringhe dove abbiamo a che fare innanzitutto con il CharSet, in secondo luogo con stringhe a lunghezza fissa o variabile (dove quindi dobbiamo usare il tipo <FONT color=blue>string</FONT> oppure la classe <FONT color=blue>StringBuilder</FONT>).</P> <P>Cosa succede se dobbiamo invece trasferire intere strutture dati? Ne dobbiamo fare ovviamente il marshaling, ma come? Dobbiamo capire&nbsp;essenzialmente che il codice del mondo&nbsp;<EM>managed</EM> ed il codice del&nbsp;mondo <EM>unmanaged</EM> formattano i dati in memoria in modo diverso. Dobbiamo trovare quindi una strada per dire al CLR di non mappare i dati in memoria come vorrebbe lui, ma di rispettare determinate logiche. Questa strada è disponibile, ancora una volta, con&nbsp;un attributo .NET dedicato: <A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx" target=_new name=""><STRONG>StructLayout</STRONG></A>. Questo attributo prende come parametro un valore dell'enum <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.layoutkind.aspx" target=_new name=""><STRONG>LayoutKind</STRONG></A></STRONG>, che esprime la modalità con cui i campi della nostra struttura vengono mappati in memoria. I valori di questo enum più interessanti sono principalmente due:</P> <OL> <LI><STRONG>Explicit</STRONG>: siamo noi che indichiamo al CLR come mappare (uno ad uno) i campi della struttura. Attraverso l'attributo <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.fieldoffsetattribute.aspx" target=_new name=""><STRONG>FieldOffset</STRONG></A></STRONG>, esprimiamo in bytes la posizione fisica di un campo all'interno di una classe o di una struttura. Questo vale ovviamente sia nel mondo <EM>managed</EM>, che in <EM>unmanaged</EM>.</LI> <LI><STRONG>Sequential</STRONG>: i campi vengono mappati in memoria sequenzialmente, nello stesso ordine con cui questi servono dal lato <EM>unmanaged</EM> della nostra applicazione. I campi possono essere non contigui</LI></OL> <P><STRONG>Funzioni di callback<BR></STRONG>In altri scenari serve una funzione di callback, ovvero una funzione scritta in <EM>managed</EM> code che viene eseguita al termine dell'esecuzione di una funzione <EM>unmanaged</EM>. In altre parole, dal nostro codice .NET eseguiamo una funzione nativa attraverso P/Invoke, la quale deve sapere cosa eseguire quando la sua esecuzione termina. In realtà, come vedremo fra poco, non è detto che la callback serva solamente al termine dell'esecuzione: la funzione <EM>unmanaged</EM> potrebbe utilizzare la callback quando necessario, magari per avvisare il mondo .NET. Anche in questo caso, possiamo fare riferimento <A title="" href="http://msdn2.microsoft.com/en-us/library/d186xcf0.aspx" target=_new name="">a questa pagina su MSDN</A> per maggiori dettagli. La tecnica consiste nell'utilizzare un delegate, che non è nient'altro che un puntatore a funzione in ambiente .NET. <A title="" href="http://msdn2.microsoft.com/en-us/library/843s5s5x.aspx" target=_new name="">L'esempio su MSDN</A> tratta un caso molto particolare: attraverso P/Invoke, utilizziamo la funzione <STRONG>EnumWindows</STRONG>, la quale richiede una callback, che viene eseguita ogni volta che la funzione <EM>unmanaged</EM> lo richiede. <STRONG><FONT color=green>Come facciamo a capire quando una funzione <EM>unmanaged</EM> richiede una callback?</FONT></STRONG> Bisogna leggere con attenzione il prototipo della funzione stessa, perchè dal tipo e dal nome dei parametri possiamo arrivare a capirlo. Il prototipo C++ della funzione <STRONG>EnumWindows</STRONG> è il seguente:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">BOOL&nbsp;EnumWindows(WNDENUMPROC&nbsp;lpEnumFunc,&nbsp;LPARAM&nbsp;lParam)<BR></DIV></SPAN> <P>Ciò significa che se vogliamo usare questa funzione <EM>unmanaged</EM> dobbiamo semplicemente convertire il primo parametro in un delegate:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">public&nbsp;delegate&nbsp;bool&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">CallBack(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">int&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">hwnd,&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">int&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">lParam);<BR></DIV></SPAN> <P>e conseguente utilizzare questo delegate come funzione di callback.</P> <P> </FONT> <FONT face=Verdana size=2><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.2</FONT></A></P></FONT><!-- Powered by IMHO Instant Blogger Copyright (c) 2004 A.Boschin - http://www.elite.boschin.it --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/42703.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/06/12/42703.aspx Mon, 12 Jun 2006 16:08:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/06/12/42703.aspx#feedback http://blogs.ugidotnet.org/idamiani/comments/commentRss/42703.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/42703.aspx [70-536, #31] P/Invoke e l'attributo DllImport http://blogs.ugidotnet.org/idamiani/archive/2006/06/07/42435.aspx <FONT face=Verdana size=2> <P><STRONG>Introduzione</STRONG><BR>P/Invoke è l'abbreviazione di <STRONG>Platform Invoke</STRONG> &nbsp;e consiste nella capacità da parte del nostro <EM>managed code</EM> di utilizzare DLL di sistema ed eseguire le funzioni contenute. Ho trovato di grande comodità il sito <A href="http://www.pinvoke.net/">www.pinvoke.net</A>, che ci aiuta con una grande raccolta di tutte le librerie di sistema e come poterle importare in una classe managed per rendere accessibile da C#, VB.NET e da tutti gli altri linguaggi della famiglia .NET. Anche in questo caso, come nel mio ultimo post, <STRONG><FONT color=#008040>abbiamo a che fare con una classe wrapper</FONT></STRONG>: sarà questa che il codice <EM>managed</EM> eseguirà. Tale classe deve essere decorata dall'attributo <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.aspx" target=_new name=""><STRONG>DllImport</STRONG></A></STRONG>, specificando il nome del file DLL che si vuole utilizzare in questa chiamata. Il namespace di riferimento per tutto quello che riguarda P/Invoke è <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.aspx" target=_new name=""><STRONG>System.Runtime.InteropServices</STRONG></A></STRONG>.</P> <P><STRONG>Una chiamata semplice a GetTempPath</STRONG><BR>La funzione GetTempPath è funzione contenuta nel file kernel32.dll di Windows. Partendo da questo presupposto, in C# possiamo dichiarare una classe statica PInvoke ed inserire un metodo definito così:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">static&nbsp;class&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">PInvoke<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;[DllImport("kernel32.dll")]<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">public&nbsp;static&nbsp;extern&nbsp;uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">GetTempPath(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">nBufferLength,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Out]&nbsp;StringBuilder&nbsp;lpBuffer);<BR>}<BR></DIV></SPAN> <P>Il metodo è marcato come statico e come <STRONG><A title="" href="http://lab.msdn.microsoft.com/search/Redirect.aspx?title=" target=_new name="" ?><STRONG>extern</STRONG></A></STRONG>, che esplicita il fatto che il metodo è implementato esternamente rispetto all'assembly. Il secondo parametro è un oggetto <A title="" href="http://lab.msdn.microsoft.com/search/Redirect.aspx?title=" target=_new name="" ?>StringBuilder</A> che verrà restituito (notare la keyword <STRONG>out</STRONG> )&nbsp;e conterrà il path per i files temporanei restituito dall'OS. Questo è un caso estremamente semplice. L'unica verrà difficoltà è alla fin fine mappare correttamente i tipi tra il mondo <EM>managed</EM> ed il mondo <EM>unmanaged</EM>. Per questo risorse on-line come <A href="http://www.pinvoke.net/">www.pinvoke.net</A> sono estremamente importanti ed utili, perchè hanno fatto gran parte del lavoro e ci basta copiare &amp; incollare.</P> <P>Torniamo a noi. Una volta dichiarata la classe ed il metodo, chiamarlo non è per nulla diverso a qualsiasi altro metodo statico scritto in C#.</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">StringBuilder&nbsp;bld&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">StringBuilder();<BR>PInvoke.GetTempPath(255,&nbsp;bld);<BR></SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">string&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">tempPath&nbsp;=&nbsp;bld.ToString();<BR></DIV></SPAN> <P><STRONG><FONT color=#008040>Quando ha senso usare P/Invoke?</FONT></STRONG> In primo luogo, <STRONG><FONT color=#008040>quando una certa funzionalità non è esposta dal Framework</FONT></STRONG> . Ad esempio, la libreria <EM>kernel32.dll</EM> espone la funzione <A title="" href="http://www.pinvoke.net/default.aspx/kernel32.CopyFileEx" target=_new name="">CopyFileEx</A>, che però è direttamente utilizzabile da codice <EM>managed</EM>. P/Invoke è molto utile, ripeto,&nbsp;se vogliamo interagire con componenti COM che al contrario non sono disponibili in .NET. <A title="" href="http://www.codeproject.com/csharp/wmp_pinvoke.asp?print=true" target=_new name="">Questo articolo</A> su CodeProject ad esempio dimostra come usare P/Invoke con Windows Media Player, cosa che può essere fatta tra l'altro con le tecniche viste ieri (ovvero aggiungere come riferimento il file WMP.dll ed accedere così all'object model).</P> <P>Ovviamente P/Invoke non funziona solo con le librerie di sistema, ma con&nbsp;qualsiasi libreria COM scritta in C++, comprese quindi le nostre.</P> <P><STRONG>Usiamo&nbsp;P/Invoke, ma usiamolo pulito!<BR></STRONG>In alcune occasioni, P/Invoke è davvero necessario, per cui siamo obbligati per forza di cose a servirci delle sue potenzialità. Però dobbiamo pensare bene alle complicazioni ed agire di conseguenza.</P> <P>Le Windows API ritornano le condizioni di errori come banali costanti. Questo è in netta contrapposizione con la logica delle Exception messa in campo da .NET e dal framework. Per ovviare a questo inconveniente, è opportuno costruire una classe wrapper che converta un HRESULT restituito da Windows nella Exception più opportuna. Questa classe wrapper può inoltre <STRONG>nascondere l'accesso al mondo unmanaged</STRONG>: in pratica, una best practice da applicare è rendere privata la dichiarazione del metodo extern e creare invece un metodo public di buffer che sarà quello che useremo realmente nel nostro codice.</P> <P>Vediamo di applicare nel concreto queste best practices. Vogliamo sempre utilizzare la funzione API GetTempPath esportata dalla libreria <STRONG>kernel32.dll</STRONG>. Ho creato una classe PInvoke, il cui codice è:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: black 1px solid; WIDTH: 668px; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; HEIGHT: 138px; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">sealed&nbsp;class&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">PInvoke<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">public&nbsp;static&nbsp;string&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">GetTemporaryPath()<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;bld&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">StringBuilder();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">ret&nbsp;=&nbsp;&nbsp;GetTempPath(255,&nbsp;bld);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">return</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">(bld.ToString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;[DllImport("kernel32.dll")]<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">private&nbsp;static&nbsp;extern&nbsp;uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">GetTempPath(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">uint&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">nBufferLength,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Out]&nbsp;StringBuilder&nbsp;lpBuffer);<BR>}<BR></DIV> <P></SPAN>Ho&nbsp;riportato&nbsp;due metodi. Un metodo pubblico <STRONG>GetTemporaryPath</STRONG>, scritto completamente in codice <EM>managed</EM>. Un metodo privato <STRONG>GetTempPath</STRONG> che invece è extern, ed è in <EM>unmanaged code</EM>. Tale classe sarà accessibile dal chiamante solo attraverso il metodo pubblico, come è normale che sia. Questo <EM>modus operandi</EM> rende possibili alcune migliorie al codice che abbiamo scritto:</P> <OL> <LI>quando il Framework renderà eventualmente disponibile la funzionalità alla quale adesso accediamo attraverso P/Invoke, ci basterà modificare l'implementazione del metodo pubblico <STRONG>GetTemporaryPath</STRONG> ed il gioco è fatto.</LI> <LI>così facendo, isoliamo&nbsp;tutto quello che riguarda&nbsp;P/Invoke. Questo ci permette di limitare le porzioni di codice interessate ad eventuali crash o a problemi dovuti ad Interop.</LI> <LI>come dicevo prima (ed è domanda di esame) possiamo mascherare gli errori ritornati da Win32 in opportune exception. Nel codice qui sopra, per esempio, memorizza nella variabile ret di tipo <STRONG>uint</STRONG> il valore ritornato dalla chiamata: mi basterebbe implementare un costrutto switch per intercettare eventuali errori ed agire di conseguenza.</LI></OL> <P><STRONG>Come usare DllImport<BR></STRONG>L'attributo DllImport ci permette di indicare in quale libreria viene esportata una certa funzione. Questo attributo ha diverse proprietà importanti. <STRONG>EntryPoint</STRONG> ci permette di specificare il nome della funzione contenuta nella libreria, nel caso in cui il nome sia diverso rispetto a quello che indichiamo noi nella dichiarazione del metodo. <STRONG>CharSet</STRONG> va impostato su CharSet.Auto, e ha importanza solo se la funzione esportata utilizza le stringhe; questo permette al CLR di utilizzare il set di caratteri corretto in base all'OS su cui si sta eseguendo il codice.</P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.2</FONT></A></P></FONT><!-- Powered by IMHO Instant Blogger Copyright (c) 2004 A.Boschin - http://www.elite.boschin.it --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/42435.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/06/07/42435.aspx Wed, 07 Jun 2006 12:42:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/06/07/42435.aspx#feedback 2 http://blogs.ugidotnet.org/idamiani/comments/commentRss/42435.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/42435.aspx [70-536, #30] Primi passi con il mondo Interop http://blogs.ugidotnet.org/idamiani/archive/2006/06/05/42185.aspx <FONT face=Verdana size=2> <P><STRONG>Introduzione</STRONG><BR>Fino ad oggi abbiamo sempre discusso con un mondo completamente <EM>managed</EM>, ovvero il cui codice è scritto al 100% in linguaggi previsti dal FX (VB.NET e C# in primo luogo), e che quindi&nbsp;gira all'interno del CLR. Possiamo però andare oltre, e permettere al nostro codice di oltrepassare le normali barriere ed avere a che fare con il managed <EM>unmanaged</EM>, ovvero tutto quello che riguarda codice Win32, mondo COM e via dicendo.</P> <P>Ovviamente, il fatto che sia possibile non vuol dire che sia una cosa semplice, nè tantomeno che siamo esenti da rischi. La difficoltà principale è saper mappare in memoria&nbsp;gli oggetti managed in modo tale che siano utilizzabili dalle funzioni native Win32, e viceversa. Tale operazione è nota con il termine di <EM>marshaling</EM>: vi rimando <A title="" href="http://www.microsoft.com/indonesia/msdn/pinvoke.aspx#docum_topic3" target=_new name="">alla tabella in fondo a questo articolo</A> per tutti i dettagli del caso. Oggi cominciamo con le cose più semplici: vediamo infatti come poter aggiungere ad un nostro progetto .NET un riferimento ad un componente COM, sia esso un OCX, una DLL ActiveX o una type library.</P> <P><STRONG>Un riferimento ai componenti di Microsoft Word</STRONG><BR>Il concetto che ruota a queste problematiche è che .NET crea una classe&nbsp;<EM>managed</EM> che il nostro codice utilizza come wrapper per tutte le chiamate al mondo <EM>unmanaged</EM>. Ad esempio, se apriamo un qualsiasi progetto .NET con Visual Studio 2005, clicchiamo con il pulsanto destro sul nome del progetto e selezioniamo la voce <STRONG>Add Reference</STRONG>, l'IDE ci propone una finestra di dialogo dalla quale possiamo selezionare qualsiasi cosa ci passi per la testa: oggetti .NET, oggetti COM, altri progetti inseriti nella nostra solution, e via dicendo. Il tab <STRONG>Browse</STRONG> ci permette di sfogliare il contenuto del nostro hard-disk alla ricerca di files DLL, TLB, OLB, OCX ed EXE che vogliamo mettere nei riferimenti del nostro progetto.</P> <P>Supponiamo per esempio di voler <EM>interoperare</EM> con Microsoft Word all'interno della nostra applicazione.&nbsp;Una delle possibili soluzioni&nbsp;consiste nell'aggiungere come riferimento il file MSWORD.OLB, seguendo il procedimento indicato prima. Questa semplice operazione si traduce in:</P> <UL> <LI>la creazione di un nuovo assembly in <EM>managed code</EM>, ottenuto partendo dal file MSWORD.OLB</LI> <LI>l'inserimento del nuovo assembly nella GAC</LI> <LI>l'aggiunta del nuovo assembly come riferimento al nostro progetto .NET</LI></UL> <P>Così facendo, otteniamo in tutto e per tutto un riferimento valido all'object model di Word, utilizzabile quindi direttamente nel nostro codice attraverso la classe wrapper. Tale wrapper viene inserito in un namespace dedicato, che va quindi referenziato tramite la solita <STRONG>using</STRONG>. Per esempio:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">Microsoft.Office.Interop.Word.Application&nbsp;appWord&nbsp;=<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">Microsoft.Office.Interop.Word.Application();<BR>Document&nbsp;doc&nbsp;=&nbsp;appWord.Documents.Open(&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #008000; FONT-FAMILY: Courier New">/*Parametri*/&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">);<BR></DIV></SPAN> <P>Il metodo <STRONG>Open</STRONG> esposto dall'oggetto <STRONG>Documents</STRONG> richiede un gran numero di parametri obbligatori, cosa che invece non accade per esempio quando facciamo la stessa con VB6. Il motivo è che molti dei parametri sono opzionali: in .NET questo comportamento si attua sfruttando l'overloading dei metodi. Questo esempio vale solo a scopo didattico, per cui ho deciso comunque di mantenerlo.</P> <P><STRONG>Il tool Tlbimp.exe<BR></STRONG>Il meccanismo di creazione della classe wrapper in managed code è disponibile anche attraverso il tool <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/tt0cf3sx.aspx" target=_new name=""><STRONG>tlbimp.exe</STRONG></A></STRONG> fornito dal FX2.0. Questo tool richiede essenzialmente il nome del file DLL di cui si vuole creare il wrapper. Rispetto alle funzionalità esposte dall'IDE, abbiamo diverse marce in più:</P> <UL> <LI>l'opzione <STRONG>/namespace:</STRONG> permette di specificare il namespace attraverso il quale sarà possibile raggiungere la nuova classe wrapper</LI> <LI>l'opzione <STRONG>/out:</STRONG> permette di specificare il nome del file generato</LI> <LI>l'opzione <STRONG>/keyfile:</STRONG> permette di specificare un file SNK per firmare con uno strong name l'assembly (tale file viene generato dal tool <STRONG>sn.exe</STRONG>, <A title="" href="http://msdn2.microsoft.com/en-us/library/k5b5tt23.aspx" target=_new name="">cliccare qui per maggiori informazioni</A>)</LI></UL> <P>Questo tool, quindi, permette di generare la classe wrapper al di fuori dell'IDE ed in modo completamente indipendente. L'unico parametro davvero necessario è il nome del file (<EM>tlbFile</EM>), che deve contenere obbligatoriamente una COM type library.</P> <P><STRONG>Compilare un progetto che fa uso di Interop<BR></STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/538aes2a.aspx" target=_new name="">Esiste una pagina su MSDN</A> che descrive come compilare un progetto .NET che usa InterOp per interagire con il mondo <EM>unmanaged</EM>. Se compiliamo dall'IDE, non cambia nulla: la classe wrapper nasconde il mondo <EM>unmanaged</EM> e siamo a posto. Se compiliamo da command-line, assicuriamoci di utilizzare il parametro <STRONG>/reference:</STRONG> del compilatore per referenziare l'assembly contenente la classe wrapper che ci serve.</P> <P><STRONG>Deploy di un'applicazione che fa uso di InterOp<BR></STRONG>Se stiamo facendo il deploy di un'applicazione InterOp, dobbiamo considerare se i nostri assembly wrapper devono essere condivisi fra più applicazioni. La <A title="" href="http://msdn2.microsoft.com/en-us/library/tc0204w0.aspx" target=_new name="">pagina dedicata su MSDN</A> è piuttosto chiara. Se gli assembly wrapper sono condivisi, vale la pena firmarli con uno strong name, inserirli nella GAC (che alla fin fine è un <EM>centralized repository</EM> fatto apposta per questo scopo)&nbsp;ed il gioco è fatto. Se gli assembly sono privati per una certa applicazione, il deploy deve essere fatto nella stessa directory dove si trova l'applicazione.</P> <P><STRONG>Dietro le quinte: come fa Tlbimp a convertire?<BR></STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/k83zzh38(vs.80).aspx" target=_new name="">A partire da questa pagina su MSDN</A>, vengono descritte quali sono le logiche che Tlbimp.exe mette in campo per convertire dal mondo COM al mondo <EM>managed</EM>. Si parla quindi di Imported Member Conversion, Imported Library Conversion, Imported Module Conversion e via dicendo.</P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.2</FONT></A></P></FONT><!-- Powered by IMHO Instant Blogger Copyright (c) 2004 A.Boschin - http://www.elite.boschin.it --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/42185.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/06/05/42185.aspx Mon, 05 Jun 2006 14:32:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/06/05/42185.aspx#feedback 1 http://blogs.ugidotnet.org/idamiani/comments/commentRss/42185.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/42185.aspx Manca un mese al mio esame 70-536 http://blogs.ugidotnet.org/idamiani/archive/2006/06/04/42155.aspx <FONT face=Verdana size=2> <P>In molti, giustamente, mi hanno detto che i miei post per <A title="" href="http://blogs.ugidotnet.org/idamiani/category/1704.aspx" target=_new name="">l'esame 70-536</A> sono più discontinui rispetto alla mia prima serie per <A title="" href="http://blogs.ugidotnet.org/idamiani/category/1246.aspx" target=_new name="">l'esame 70-316</A>. Effettivamente, i miei impegni di lavoro mi hanno portato via più tempo del previsto e anche se trovo il tempo per studiare un po' (ho giusto giusto&nbsp;del materiale&nbsp;su P/Invoke che mi aspetta sul letto), ho sicuramente meno tempo per dedicarmi alla scrittura di post.</P> <P>Spero davvero di poter riprendere a parlare come una volta, perlomeno sugli argomenti più importanti come P/Invoke che citavo prima. Rimane da vedere qualcosa sulla sicurezza ed altro ancora: avevo fissato l'esame il 1° Giugno, ma ho dovuto per forza spostarlo in là di un mese per essere sicuro di studiare tutto e per bene. Tra l'altro, non avere software di&nbsp;self-test come i <STRONG>Boson</STRONG> che avevo usato per il 70-316, mi incita ad arrivare più preparato rispetto al solito. <EM>Sperem...</EM></P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.3</FONT></A></P></FONT><!-- Powered by IMHO 1.3 (IT) Instant Blogger Copyright (c) 2005 A.Boschin - http://www.imhoproject.org --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/42155.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/06/04/42155.aspx Sun, 04 Jun 2006 23:42:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/06/04/42155.aspx#feedback 2 http://blogs.ugidotnet.org/idamiani/comments/commentRss/42155.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/42155.aspx [70-536, #29] Qualche dettaglio in pi&#249; sulla gestione dei thread http://blogs.ugidotnet.org/idamiani/archive/2006/05/04/40019.aspx <FONT face=Verdana size=2> <P><STRONG>Introduzione</STRONG><BR>Nel post precedente abbiamo visto come utilizzare la classe <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.thread.aspx" target=_new name=""><STRONG>Thread</STRONG></A></STRONG> in modo basilare. In breve: come creare un nuovo thread, utilizzando le classi <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.threadstart.aspx" target=_new name=""><STRONG>ThreadStart</STRONG></A></STRONG> e <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx" target=_new name=""><STRONG>ParameterizedThreadStart</STRONG></A></STRONG>, a seconda del fatto che il metodo da eseguire in modo asincrono abbiamo parametri oppure no.</P> <P>Quello che vediamo oggi va un po' più in là. Sarà un po' tutto teorico, perchè francamente non ho mai avuto bisogno di scendere così nel dettaglio. Innanzitutto il Framework ci mette a disposizione la classe <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.threadpool(VS.80).aspx" target=_new name=""><STRONG>ThreadPool</STRONG></A></STRONG>. Questa classe è definita come static, di conseguenza non abbiamo bisogno di istanziarla per poterla utilizzare. Come dice la pagina su MSDN, la classe ThreadPooo...</P> <P><EM>Provides a pool of threads that can be used to post work items, process asynchronous I/O, wait on behalf of other threads, and process timers.</EM></P> <P>Direi che è tutto abbastanza chiaro. Attraverso il metodo <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.threadpool.queueuserworkitem.aspx" target=_new name=""><STRONG>QueueUserWorkItem</STRONG></A></STRONG>, possiamo richiedere al pool di thread la creazione di un nuovo thread che - ovviamente - giri in modo asincrono. Tutti i thread gestiti all'interno di ThreadPool hanno la property <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.thread.isbackground.aspx" target=_new name=""><STRONG>IsBackground</STRONG></A></STRONG> = <FONT color=blue>true</FONT>. Una cosa importante da sottolineare è che la chiamata al metodo QueueUserWorkItem ritorna <FONT color=blue>true</FONT> se il thread è stato accodato al pool, oppure <FONT color=blue>false</FONT> se qualcosa non è andato bene (in tal caso, verrà sollevata un eccezione di tipo <A title="" href="http://msdn2.microsoft.com/en-us/library/system.applicationexception.aspx" target=_new name="">ApplicationException</A>). Il metodo specificato nel costruttore inoltre non viene eseguito immediatamente, ma viene eseguito quando diventa disponibile il pool.</P> <P><A title="" href="http://blogs.ugidotnet.org/idamiani/archive/2006/04/27/39545.aspx" target=_new name="">Nell'ultimo post</A> avevo parlato di un'applicazione creata apposta per studiare questo argomento. In questa applicazione avevo implementato un semplice metodo <EM>aggiungiRecord</EM> per inserire qualche migliaio di record dentro una tabella di SQL Server, e lo avevo fatto girare in modo asincrono facendomi avvisare alla fine. Vediamo come ottenere lo stesso risultato con il pool di thread.</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">WaitCallback&nbsp;clk&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">WaitCallback(aggiungiRecord);<BR></SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">bool&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">ret&nbsp;=&nbsp;ThreadPool.QueueUserWorkItem(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">WaitCallback(aggiungiRecord));<BR></DIV></SPAN> <P>Il codice qui sopra istanzia un oggetto <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.waitcallback.aspx" target=_new name=""><STRONG>WaitCallback</STRONG></A></STRONG>, da usare nella chiamata al metodo <STRONG>QueueUserWorkItem</STRONG>. La classe WaitCallback espone due proprietà interessanti: <STRONG>Method</STRONG> e <STRONG>Target</STRONG>. La prima è un'istanza della classe <STRONG><A title="" href="http://lab.msdn.microsoft.com/search/Redirect.aspx?title=" target=_new name="" ?><STRONG>MethodInfo</STRONG></A></STRONG> che ci permette (tramite Reflection) di analizzare a run-time il metodo che sta per essere eseguito in modo asincrono nel ThreadPool. La seconda proprietà invece è un riferimento all'object nel quale ci troviamo: nel mio caso, trattandosi di un Button, tale object è la Windows Forms relativa.<BR>La seconda riga di codice non fa altro che richiedere l'inserimento all'interno del ThreadPool dell'oggetto WaitCallback. Salvo il risultato in una variabile bool per controllare che tutto sia andato regolarmente.</P> <P><STRONG>Metodi interessanti del&nbsp;ThreadPool</STRONG><BR>La classe ThreadPool espone alcuni metodi interessanti. Il metodo <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.threadpool.getavailablethreads.aspx" target=_new name=""><STRONG>GetAvaiableThreads</STRONG></A></STRONG> ritorna il numero di thread disponibili all'interno del thread.&nbsp; Esiste un pool di thread per ogni processo. Per default, il numero di thread disponibili per ogni pool è 25. E' possibile cambiare il numero di thread attraverso i metodi <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.threadpool.setmaxthreads.aspx" target=_new name=""><STRONG>SetMaxThreads</STRONG></A></STRONG> e <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.threading.threadpool.setminthreads.aspx" target=_new name=""><STRONG>SetMinThreads</STRONG></A></STRONG>.</P> <P>Mi sento di consigliare inoltre la lettura di <A title="" href="http://msdn2.microsoft.com/en-us/library/0ka9477y.aspx" target=_new name="">questo articolo su MSDN</A> che tratta in modo esauriente questo argomento.</P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.3</FONT></A></P></FONT><!-- Powered by IMHO 1.3 (IT) Instant Blogger Copyright (c) 2005 A.Boschin - http://www.imhoproject.org --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/40019.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/05/04/40019.aspx Thu, 04 May 2006 11:19:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/05/04/40019.aspx#feedback 1 http://blogs.ugidotnet.org/idamiani/comments/commentRss/40019.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/40019.aspx Ho fissato la data dell'esame http://blogs.ugidotnet.org/idamiani/archive/2006/04/27/39548.aspx <FONT face=Verdana size=2> <P>Il <STRONG>1° Giugno</STRONG>, alle ore <STRONG>10:00</STRONG>, andrò a&nbsp;sostenere l'<STRONG>esame 70-536</STRONG>. In tempo per riprendere a lavorare, per ripassare un po' di roba, per vedere le cose che mancano, per farmi venire un po' d'ansia.</P> <P>E, credetemi, se lo passo, io salto dalla gioia, e ve lo&nbsp;dico anche,&nbsp;<A title="" href="http://blogs.ugidotnet.org/lbarbieri/archive/2006/04/26/39442.aspx" target=_new name="">alla faccia delle polemiche</A>&nbsp;che ogni tanto imperversano sul blog.&nbsp;<IMG height=19 src="http://www.imhoproject.org/files/complice.gif" width=19 align=absMiddle border=0>&nbsp;Su questo discorso, mi allineo con <A title="" href="http://blogs.ugidotnet.org/lbarbieri" target=_new name="">Lorenzo</A>, sinceramente: quando posto sul mio blog, so di rivolgermi ad un mio gruppo di amici e quindi mi viene naturale raccontare le cose che mi succedono, sia belle che brutte. Tra queste cose, ci sono anche i miei esami, per cui...Posso però capire chi magari ha fallito un esame, e sente gli altri che magari lo passano...</P> <P>Che dire...che il byte sia con me, e con tutti voi. <IMG height=19 src="http://www.imhoproject.org/files/allegro.gif" width=19 align=absMiddle border=0></P> <P> </FONT> <FONT face=Verdana size=2><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.3</FONT></A></P></FONT><!-- Powered by IMHO 1.3 (IT) Instant Blogger Copyright (c) 2005 A.Boschin - http://www.imhoproject.org --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/39548.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/04/27/39548.aspx Thu, 27 Apr 2006 19:27:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/04/27/39548.aspx#feedback 27 http://blogs.ugidotnet.org/idamiani/comments/commentRss/39548.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/39548.aspx [70-536, #28] Esecuzione asincrona? Thread! http://blogs.ugidotnet.org/idamiani/archive/2006/04/27/39545.aspx <FONT face=Verdana size=2> <P>Ve l'avevo promesso che avrei ricominciato. Dopo questa lunga pausa, dovuta un po' a motivi di lavoro e salute, vediamo oggi di riprendere la mia serie di post per l'esame 70-536.</P> <P>Questa ripresa dei lavori vede come protagonista la class <STRONG><A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.aspx" target=_new name=""><STRONG>Thread</STRONG></A></STRONG> ed affini, ovvero tutte quelle classi definite nel namespace <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.aspx" target=_new name="">System.Threading</A> che ci permettono di eseguire operazioni in modo asincrono. Ne avevamo già parlato in passato quando avevamo trattato il nuovo componente <STRONG><A title="" href="http://blogs.ugidotnet.org/idamiani/archive/2006/02/28/35763.aspx" target=_new name=""><STRONG>BackgroundWorker</STRONG></A></STRONG>: con nuovo, intendo con il FX2.0. Il BackgroundWorker internamente fa ovviamente uso di thread secondari, e risolve la maggior parte delle problematiche riguardanti la gestione della UI. Come sappiamo, infatti, non possiamo manipolare i controlli sulla Windows Forms al di fuori del thread che le ha create: se abbiamo un task che dura un po' di tempo, e vogliamo ogni tanto notificare qualcosa su una Label, per esempio, non possiamo farlo in modo diretto come faremmo di solito, ma dobbiamo avere qualche accorgimento in più. Il <STRONG>BackgroundWorker</STRONG> semplifica e gestisce direttamente questi accorgimenti.</P> <P>Se abbiamo bisogno di qualcosa di più performante, oppure non abbiamo UI da gestire (magari stiamo sviluppando un web-services), possiamo lavorare con la classe <STRONG>Thread</STRONG>. Vediamo come.</P> <P><STRONG>Introduzione alla classe Thread</STRONG><BR>Non mi capita tutti i giorni di sviluppare software che abbiano bisogno di thread secondari. Per questo motivo, ho sviluppato una piccola applicazione di test. In pratica, una semplice Windows Forms che si connette ad un database SQL Server, chiede quanti record si vogliono inserire e poi comincia a lanciare INSERT INTO su una tabella. La tabella non è importante (ha un campo identity ed un varchar(50)): ci basta sapere che per aggiungere 20.000 records, ci metto circa 20 secondi.</P> <P>All'interno della form, ho creato un metodo privato aggiungiRecord:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">private&nbsp;void&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">aggiungiRecord()<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #008000; FONT-FAMILY: Courier New">//&nbsp;codice<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">for&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">int&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">cycle&nbsp;=&nbsp;0;&nbsp;cycle&nbsp;&lt;=&nbsp;20000;&nbsp;cycle++)<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg&nbsp;=&nbsp;"Marca&nbsp;"&nbsp;+&nbsp;cycle.ToString();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd.Parameters[0].Value&nbsp;=&nbsp;msg;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd.ExecuteNonQuery();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateUI.BeginInvoke(msg,&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">null</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">,&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">null</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #008000; FONT-FAMILY: Courier New">//&nbsp;codice<BR></SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">}<BR></DIV></SPAN> <P>Ho eliminato le parti di codice non interessanti. Sul click di un pulsante, vogliamo eseguire il metodo qui sopra in un thread separato, cosicchè questo comincia a fare 20.000 inserimenti senza interrompere il nostro lavoro. Oltre alla classe Thread vera e propria, abbiamo bisogno di una delle due classi: <STRONG><A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.threadstart.aspx" target=_new name=""><STRONG>ThreadStart</STRONG></A></STRONG> oppure <STRONG><A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.parameterizedthreadstart.aspx" target=_new name=""><STRONG>ParameterizedThreadStart</STRONG></A></STRONG>. La prima se il metodo che vogliamo eseguire non ha parametri di input (come nel nostro caso), la seconda se invece c'è qualche parametro.<BR>Quindi, per esempio:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">ThreadStart&nbsp;start&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">ThreadStart(aggiungiRecord);<BR>_th&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">Thread(start);<BR><FONT size=2>_th.Start();</FONT><BR></DIV></SPAN> <P>Questo è la tecnica assolutamente più semplice: al click del pulsante, il thread parte ed esegue il codice. Il thread non ha nome, non ha priorità. Se il metodo aggiungiRecord avesse un parametro, dobbiamo usare un altro modalità:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">int&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">howMany;<BR></SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">int</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">.TryParse(txtHowManyRecords.Text,&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">out&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">howMany);<BR><BR>ParameterizedThreadStart&nbsp;start&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">ParameterizedThreadStart(aggiungiRecord);<BR>_th&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">new&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">Thread(start);<BR>_th.Start(howMany);<BR></DIV></SPAN> <P>Ho aggiunto sulla form una TextBox chiamata txtHowManyRecords&nbsp;che l'utente può usare per esprimere quanti record vuole inserire in tabella. Utilizzo il metodo statico <STRONG><A title="" href="http://lab.msdn.microsoft.com/search/Redirect.aspx?title=" target=_new name="" ?><STRONG>TryParse</STRONG></A></STRONG> della classe int: il metodo ritorna un bool, e in howMany finisce il numero richiesto. Il thread viene inizializzato con la classe ParameterizedThreadStart: per farlo, ho dovuto ovviamente modificare la firma di aggiungiRecord in questo modo:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">private&nbsp;void&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">aggiungiRecord(</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">object&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">o)<BR>{&nbsp;}<BR></DIV></SPAN> <P><STRONG>Il thread sta girando...e allora?<BR></STRONG>Giunti a questo punto, il thread sta girando. Il thread secondario sta eseguendo il codice del metodo aggiungiRecord. Probabilmente, il ciclo for inserito occuperà gran parte del tempo. All'interno di questo ciclo, possiamo notificare sulla UI qualsiasi cosa all'utente, ma con le dovute attenzioni che - credo - esulano un po' dallo scopo di questo post. Basta giocherellare con delegate, con <A title="" href="http://lab.msdn.microsoft.com/search/Redirect.aspx?title=" target=_new name="" ?>BeginInvoke</A>, <A title="" href="http://lab.msdn.microsoft.com/search/Redirect.aspx?title=" target=_new name="" ?>InvokeRequired</A> e dintorni, ed il gioco è fatto. Personalmente, avevo studiato il capitolo 14 "Multithreaded User Interfaces" di "Windows Forms Programming in C#", scritto da Chris Sells: credo il miglior capitolo di quel libro.</P> <P><STRONG>Maggior controllo sul thread<BR></STRONG>La classe Thread ha alcune caratteristiche molto interessanti, che consentono al nostro codice di controllarla, magari via UI. Innanzitutto, il thread secondario può girare con una cultura differente da quella standard (proprietà <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.currentculture.aspx" target=_new name="">CurrentCulture</A> e <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.currentuiculture.aspx" target=_new name="">CurrentUICulture</A>). Possiamo dargli un nome (proprietà <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.name.aspx" target=_new name="">Name</A>)&nbsp;e capire in quale stato si trova (<A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.threadstate.aspx" target=_new name="">ThreadState</A>, che è <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.threadstate.aspx" target=_new name="">un'enum</A>). Possiamo usare il metodo <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.suspend.aspx" target=_new name="">Suspend</A> per interrompere l'esecuzione e il metodo <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.resume.aspx" target=_new name="">Resume</A> per riprenderla. Il metodo <A title="" href="http://msdn2.microsoft.com/en-US/library/system.threading.thread.abort.aspx" target=_new name="">Abort</A> blocca del tutto l'esecuzione del thread.</P> <P>Vedremo nel prossimo post qualche dettaglio in più, e il download dell'applicazione di esempio che ho creato.</P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.3</FONT></A></P></FONT><!-- Powered by IMHO 1.3 (IT) Instant Blogger Copyright (c) 2005 A.Boschin - http://www.imhoproject.org --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/39545.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/04/27/39545.aspx Thu, 27 Apr 2006 19:01:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/04/27/39545.aspx#feedback 2 http://blogs.ugidotnet.org/idamiani/comments/commentRss/39545.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/39545.aspx [70-536, #27] Le classi Installer, InstallContext ed altre classi http://blogs.ugidotnet.org/idamiani/archive/2006/03/28/37966.aspx <FONT face=Verdana size=2> <P>Della classe Installer abbiamo <A title="" href="http://blogs.ugidotnet.org/idamiani/archive/2005/09/27/27129.aspx" target=_new name="">già parlato molto tempo fa</A>. Era il 27 settembre, e mi stavo preparando per l'esame 70-316. Quel giorno abbiamo visto come aggiungere alla nostra soluzione un nuovo progetto&nbsp;<STRONG>Class Library</STRONG>, come implementare una classe custom che eredita da <STRONG>Installer</STRONG>. Tale classe viene consumata da Windows Installer durante il setup della nostra applicazione. Possiamo in pratica fare l'overloading degli eventi che accadono durante il processo di installazione, quindi gestire manualmente la Commit, provocare il Rollback, e via dicendo. Ma non solo: ci eravamo spinti un po' più in là. Avevamo aggiunto Custom Dialog, richiedendo alcune informazioni all'utente, come il suo indirizzo e-mail, oppure altre informazioni più semplici tramite CheckButton o RadioButton. Se volete implementare qualcosa di simile, consiglio caldamente di vedere il mio vecchio post, perchè io stesso ho avuto qualche difficoltà che sono riuscito a risolvere <EM>consultando me stesso</EM> indietro nel tempo. Questo è un paradosso temporale, ma lascio che siano Doc e Marty a tirarmi fuori dai guai senza provocare qualche casino nello spazio-tempo. <IMG height=19 src="http://www.imhoproject.org/files/risate.gif" width=19 align=absMiddle border=0></P> <P>In questo post, darò per assodate un sacco di cose, quelle cioè descritte nel post a cui mi riferivo più sopra. In questi giorni ho ripreso in mano l'argomento, perchè ho voluto preparare un setup un po' particolare al mio software di fatturazione. Quindi, mi sono complicato la vita, giusto per approfondire la questione. Nulla di trascendentale, sia chiaro, ma è comunque qualcosa di interessante. Vediamo perchè.</P> <P><STRONG>La classe InstallContext<BR></STRONG>Dal punto di vista prettamente teorico, la classe <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.installcontext(VS.80).aspx" target=_new name=""><STRONG>InstallContext</STRONG></A></STRONG> ci fornisce informazioni sull'Installer corrente. Essa si trova nel namespace <A title="" href="http://msdn2.microsoft.com/en-us/library/1yece858(VS.80).aspx" target=_new name="">System.Configuration.Install</A>. Involontariamente, ne abbiamo già parlato. Quando ad esempio <EM>overloadiamo</EM> l'evento <STRONG>Commit</STRONG>, possiamo scrivere quanto segue:</P> <DIV style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: black 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: black 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: gainsboro"><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">public&nbsp;override&nbsp;void&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">Commit(System.Collections.IDictionary&nbsp;savedState)<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">base</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">.Commit(savedState);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;InstallContext&nbsp;ctx&nbsp;=&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">this</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">.Context;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">string&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">db&nbsp;=&nbsp;ctx.Parameters["DB"];<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">string&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">cognomeNome&nbsp;=&nbsp;ctx.Parameters["COGNOME_NOME"];<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">string&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">provincia&nbsp;=&nbsp;ctx.Parameters["PROVINCIA"];<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #0000ff; FONT-FAMILY: Courier New">string&nbsp;</SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Courier New">email&nbsp;=&nbsp;ctx.Parameters["EMAIL"];<BR>}<BR></DIV></SPAN> <P>Nel codice qui sopra, non facciamo altro che usare un'istanza di InstallContext, che ci permette di reperire le informazioni inserite dall'utente nella prima fase del setup. La proprietà <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.installcontext.parameters(VS.80).aspx" target=_new name=""><STRONG>Parameters</STRONG></A></STRONG> serve proprio a questo: nell'esempio qui sopra, DB è un CheckButton (che può essere recuperato anche dal metodo <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.installcontext.isparametertrue(VS.80).aspx" target=_new name=""><STRONG>IsParameterTrue</STRONG></A></STRONG>), mentre le altre COGNOME_NOME, PROVINCIA ed EMAIL sono normali TextBox che l'utente ha inputato. L'istanza è ritornata dalla property <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.installer.context(VS.80).aspx" target=_new name=""><STRONG>Context</STRONG></A></STRONG> della classe <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.installer(VS.80).aspx" target=_new name=""><STRONG>Installer</STRONG></A></STRONG>, che rappresenta per l'appunto l'engine di Windows Installer che sta girando in questo momento. Interessante l'uso del metodo <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.installcontext.logmessage(VS.80).aspx" target=_new name=""><STRONG>LogMessage</STRONG></A></STRONG> che ci permette di scrivere sul file di log dedicato per questo setup, utile magari per capire cosa ha inserito l'utente e come si è comportato di conseguenza l'installer.</P> <P><STRONG>Altre classi che riguardano il setup<BR></STRONG>Il namespace System.Configuration.Install contiene altre classi, alcune interessanti, altre un po' meno. La classe <A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.managedinstallerclass(VS.80).aspx" target=_new name="">ManagedInstaller</A>, per esempio, non deve essere usata direttamente nei nostri Installer: fa parte integrante dell'engine del FX, e quindi viene utilizzata solo internamente. Stesso discorso per l'interfaccia <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.imanagedinstaller(VS.80).aspx" target=_new name=""><STRONG>IManagedInstaller</STRONG></A></STRONG>.</P> <P>La classe <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.transactedinstaller(VS.80).aspx" target=_new name=""><STRONG>TransactedInstaller</STRONG></A></STRONG> invece è più interessante. Essa permette di installare una serie di assembly in un'unica transazione: o tutto avviene con successo, oppure in presenza di qualche errore, il tutto viene <EM>rollbackato</EM>. Questa classe espone la proprietà <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.installer.installers(VS.80).aspx" target=_new name=""><STRONG>Installers</STRONG></A></STRONG>, a cui vanno aggiunti a mano gli installer che vogliamo inserire in transazione. Dal momento che non è inserita nell'esame 70-536, non ne parlerò, ma è interessante sapere che c'è e su che principio funziona. <A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.transactedinstaller_members(VS.80).aspx" target=_new name="">Date un'occhiata all'interfaccia esposta</A> da questa classe e buona lettura! <IMG height=19 src="http://www.imhoproject.org/files/allegro.gif" width=19 align=absMiddle border=0></P> <P>Le classi <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.assemblyinstaller(VS.80).aspx" target=_new name=""><STRONG>AssemblyInstaller</STRONG></A></STRONG> e <STRONG><A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.componentinstaller(VS.80).aspx" target=_new name=""><STRONG>ComponentInstaller</STRONG></A></STRONG> ereditano entrambe da Installer. La prima, in particolare, ci permette di caricare un assembly - specificato attraverso il costruttore - e di far partire il setup contenuto. Ovviamente, l'assembly deve derivare da Installer. <A title="" href="http://msdn2.microsoft.com/en-us/library/system.configuration.install.assemblyinstaller_members(VS.80).aspx" target=_new name="">L'interfaccia esposta</A> ci permette di fare tutto quanto necessario per agevolare il lavoro.</P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.2</FONT></A></P></FONT><!-- Powered by IMHO Instant Blogger Copyright (c) 2004 A.Boschin - http://www.elite.boschin.it --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/37966.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/03/28/37966.aspx Tue, 28 Mar 2006 17:46:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/03/28/37966.aspx#feedback 16 http://blogs.ugidotnet.org/idamiani/comments/commentRss/37966.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/37966.aspx [70-536, #26] Punto della situazione e rallentamenti http://blogs.ugidotnet.org/idamiani/archive/2006/03/24/37806.aspx <FONT face=Verdana size=2> <P>Ultimamente ho un po' rallentato&nbsp;la pubblicazione dei miei post <A title="" href="http://blogs.ugidotnet.org/idamiani/category/1704.aspx?Show=All" target=_new name="">sull'esame 70-536</A>. In questo periodo sono un po' stra-carico, in tutti i casi per motivi di lavoro. Ho impegni che riguardano il mio software di fatturazione, ho impegni con la società con la quale lavoro, ho impegni personali, etc. etc. Insomma, il tempo è davvero poco. Gli argomenti da affrontare per coprire tutta la <A title="" href="http://www.microsoft.com/learning/exams/70-536.asp" target=_new name="">Preparation Guide</A> dell'esame per ottenere la <A title="" href="http://www.microsoft.com/learning/mcp/mcpd/windev/" target=_new name="">MCPD : Windows Developer</A>&nbsp;invece sono ancora tanti.</P> <P>Bisogna parlare di Thread ed affini, bisogna parlare di ServiceBase e dintorni, qualche concetto teorico su Application Domain, la classe Configuration e le sue relative interfacce. Altri argomenti invece sono bene o male gli stessi già affrontati in passato per il 70-316: mi sto riferendo in particolare alla classe Installer (per customizzare il setup della nostra applicazione), le classi Debug e Trace e a qualcosa inerente la globalizzazione (CultureInfo, prima fra tutte, ad esempio). C'è molta carne al fuoco anche per quanto riguarda P/Invoke ed Interop, c'è tutta una parte dedicata al System.Drawing: Brush, Pen, Color, TextureBrush, Font, Graphics, Point, etc. etc. Altre cose invece le salterò a piedi uniti con un solo balzo senza pensarci troppo: le classi File, Directory, DriveInfo, Path, EventArgs. Non che non siano importanti, ma ritengo che se uno si vuole certificare, debba sapere il minimo necessario e, onestamente, preferisco approfondire altri argomenti più succulenti.</P> <P>Nonostante io continui a studiare per conto mio, ho veramente poco tempo per aprire IMHO e scrivere i miei soliti post. Continuerò a scrivere, questo è ovvio, ma sicuramente sarò un po' più rallentato rispetto ai miei soliti ritmi con i quali vi ho "viziato" fino ad oggi. Pazienza!</P> </FONT> <FONT face=Verdana size=2><P><A href="http://imhoproject.org/"><FONT face=Verdana size=1>powered by IMHO 1.2</FONT></A></P></FONT><!-- Powered by IMHO Instant Blogger Copyright (c) 2004 A.Boschin - http://www.elite.boschin.it --> <img src="http://blogs.ugidotnet.org/idamiani/aggbug/37806.aspx" width="1" height="1" /> Igor Damiani http://blogs.ugidotnet.org/idamiani/archive/2006/03/24/37806.aspx Fri, 24 Mar 2006 18:33:00 GMT http://blogs.ugidotnet.org/idamiani/archive/2006/03/24/37806.aspx#feedback 4 http://blogs.ugidotnet.org/idamiani/comments/commentRss/37806.aspx http://blogs.ugidotnet.org/idamiani/services/trackbacks/37806.aspx