Home

 
 
 
 
 



 
 
 
 

 
 
 

 
 
 
 
 









Blog2theMax
Il blog del team di Code Architects

Fino a pochi anni fa per generare o modificare un file Word o Excel era necessario operare sul modello a oggetti della stessa applicazione (e quindi avere a disposizione una licenza di Office), oppure acquistare un componente di terze parti in grado di generare tali file. In entrambi i casi si tratta di opzioni che hanno un costo, e inoltre la prima opzione era improponibile per le applicazioni lato-server (che generano file da inviare ai browser sul client) per i problemi di scalabilità che comporta.

La soluzione è arrivata con Office 2007 e il suo formato Open XML, che permette quindi di salvare, rileggere, modificare e interrogare un file Word o Excel come se fosse un semplice file di testo XML. Se lavorate in .NET, alla fine si riduce a saper usare la classe XmlDocument per leggere/scrivere, conoscere la grammatica di XPath per le query, e poco altro. Quello che realmente occorre è comprendere la sintassi e le sottigliezze di Open XML, ma per quello da qualche tempo c'è un bel libro (in italiano!): Open XML Guida allo Sviluppo. L'autore Gianni Giaccagliani è una vecchia conoscenza per chi lavora in questo settore da tempo nonchè un mio amico dai tempi delle gloriose riviste BIT (che non esiste più già da un paio di decenni) e PC Magazine.

In questo agile libretto (250 pagine) Gianni introduce il DOM di Open XML e mostra come manipolarlo usando direttamente il VBA degli stessi Word o Excel, quindi il libro è alla portata anche di coloro che non hanno installato Visual Studio, anche se la conversione in VB.NET è immediata, tanto più se si utilizza VSTO. Ci sono esempi approfonditi sia con Word che Excel, e una buona introduzione a XPath, il tutto nello stile semplice e "fluido" di Gianni, che ben conoscono i suoi lettori (incluso il sottoscritto). Che volete di più?

 

 

 

 

10/28/2008 2:19:38 PM (GMT Standard Time, UTC+00:00) #  | Comments [1] | 

 Ecco una bella notizia che mi ha fatto cominciare l'anno nel migliore dei modi.

Microsoft Corp. ha confermato Giuseppe Dimauro e il sottoscritto come gli unici due Regional Director per l'Italia, per gli anni 2008-09. E' una grande soddisfazione far parte di una elite di soli 140 esperti in tutto il mondo - a differenza di altri gruppi come gli MVP, il numero degl RD è "contingentato", tipicamente 1 o 2 per ciascuna nazione e per ciascuno dei 50 stati degli USA. Negli ultimi tempi il Regional Director Program si è arricchito di un sito tutto nostro, che funge anche da aggregatore dei blog. Data la qualiltà del materiale che si può trovare, è quasi meglio di una rivista tecnica.

1/11/2008 10:00:24 AM (GMT Standard Time, UTC+00:00) #  | Comments [2] | 

In questo periodo sto facendo molta consulenza per aziende italiane alle prese con la migrazione da VB6, usando ovviamente la versione preliminare di VB Migration Partner. Oramai la beta è stabile e si comporta davvero molto bene. In un solo giorno, ad esempio, abbiamo migrato una applicazione di circa 20mila righe che lavora in modo intensivo con ADO in circa mezza giornata, e tutto funziona a meraviglia!

Anche se la migrazione sta andando bene, a volte i programmi migrati sono più lenti del codice VB6 originario. Per esperienza le cause sono principalmente due: (1) l'uso di COM Interop per accedere agli oggetti ADO, (2) un uso massiccio di concatenazione di stringhe. Per il primo problema non c'è molto da fare, purtroppo: occorre migrare il codice di accesso al database da ADO a ADO.NET, un compito più semplice a dirsi che a farsi. Per il secondo problema ci sarebbe invece una soluzione semplice: basterebbe sostituire la variabile stringa con una variabile StringBuilder, e le concatenazioni sarebbero automaticamente più veloci. Peccato che questa sostituzione comporti una revisione completa del codice, perchè richiede di trasformare tutti gli operatori & (concatenazione) con il metodo Append, per non parlare dei casi in cui la variabile stringa viene usata come argomento a funzioni come Trim o Left. Ad esempio, il seguente codice richiede ben 17 secondi sul mio sistema a 3GHz:

Dim s As String = ""
Dim sw As Stopwatch = Stopwatch.StartNew()
For i As Integer = 1 To 100000
    s = s + "*"
Next
MsgBox(sw.Elapsed.ToString)

Per fortuna la soluzione è davvero semplice: basta creare una classe che utilizzi internamente un oggetto StringBuilder, che ridefinisca gli operatori & e +, e che supporti la conversione implicita da-a stringa. Il codice di questa classe StringBuilder6 si scrive davvero in pochi minuti:

Imports System.Text

' a wrapper for the StringBuilder object, with support for + and & operators

Public Class StringBuilder6

    Private buffer As New StringBuilder

    ' return the inner string

    Public Overrides Function ToString() As String
       
Return buffer.ToString()
    End Function

    Public Shared Operator +(ByVal op1 As StringBuilder6, ByVal op2 As String) As StringBuilder6
        op1.buffer.Append(op2)
        Return op1
    End Operator

    Public Shared Operator &(ByVal op1 As StringBuilder6, ByVal op2 As String) As StringBuilder6
        op1.buffer.Append(op2)
        Return op1
    End Operator

    ' convert to string

    Public Shared Widening Operator CType(ByVal op As StringBuilder6) As String
       
Return op.ToString()
    End Operator

    ' convert from string

    Public Shared Widening Operator CType(ByVal str As String) As StringBuilder6
        Dim op As New StringBuilder6()
        op.buffer.Append(str)
        Return op
    End Operator

End Class

A questo punto per velocizzare il codice visto prima è sufficiente modificare UNA SOLA ISTRUZIONE, ovvero la dichiarazione della variabile stringa:

Dim s As StringBuilder6 = ""

Dopo questa sostituzione, il ciclo visto prima viene eseguito in 8 millesimi di secondo, ovvero circa 2000 volte più velocemente!!! Non male, per una fix così semplice :-)

Indipendentemente dal fatto che state migrando codice da VB6 o se avete scritto codice VB.NET (o C#) da zero: se trovate dei punti in cui fate uso massiccio di concatenazione di stringhe e che pensate possano rallentare l'esecuzione, provate a sostituire la variabile stringa con un oggetto StringBuilder6: in alcuni casi il programma girerà molto ma molto più velocemente.

 

12/16/2007 11:44:27 AM (GMT Standard Time, UTC+00:00) #  | Comments [2] | 

L'altro giorno un nostro amico-cliente ci chiama per un problema con un controllo WinForm wordprocessor-like. Nella loro applicazione usano questo controllo anche per la capacità di importare HTML, solo che a un certo punto scoprono che il controllo va in crash se il test HTML contiene un tag IMG con attributo ALIGN e che punta a una immagine "remota", ovvero il cui URL comincia per http:// , come in questo esempio:

   <IMG SRC="http://www.somesite.com/image.jpg" ALIGN=left WIDTH=100>

Con un po' di tentativi scoprono che il problema è la presenza dell'attributo ALIGN e che il crash si può evitare se si trasforma l'HTML in questo modo:

   <DIV style="float:left"><IMG SRC="http://www.somesite.com/image.jpg" WIDTH=100></DIV>

in altre parole, occorre eliminare l'attributo ALIGN dall'interno del tag IMG e renderlo come style di un blocco DIV che racchiude il tag IMG. Questa sostituzione va fatta un attimo prima di caricare il testo HTML nel controllo, ma solo se il tag IMG contiene un attributo ALIGN e solo se l'URL dell'immagine comincia con i caratteri "http://". Il tutto è complicato dal fatto che il valore dei vari attributi può essere o non essere racchiuso tra virgolette. Insomma, un bel problemino.

Ovviamente, la soluzione sarebbe stata basata su qualche regex, ma era la prima volta che mi imbattevo in un caso di questo tipo. I problemi da risolvere erano principalmente due: come agire solo sui tag IMG contenenti un SRC remoto e come fare per "spostare" solo l'attributo ALIGN, senza toccare tutti gli altri attributi che compaiono nel tag IMG. Il primo problema lo si risolve con le cosiddette positive look-ahed assertion, ovvero il costrutto (?=...), che permette di controllare che esista una certa sottostringa a destra della regex principale, ma senza consumare caratteri per il match. Il secondo problema si risolve facilmente se si pensa a suddividere l'intero contenuto del tag IMG in tre gruppi: il gruppo pre contiene tutti i caratteri prima dell'attributo ALIGN, il gruppo align contiene il valore dell'attributo ALIGN, e il gruppo post contiene tutti i caratteri dopo l'attributo align. Ecco la soluzione in VB e C#:

   Dim search As String = "<img(?=.*src=\""?http://.*?>)(?=.*\balign=.*?>)(?<pre>[^>]*)\balign=\""?(?<align>[^ \""]+)\""?(?<post>[^>]*>)"
   Dim replace As String = "<div style=""float:${align}""><img ${pre} ${post}</div>"
   Dim result As String = Regex.Replace(htmlText, search, replace, RegexOptions.IgnoreCase Or RegexOptions.Multiline)

   string pattern = @"<img(?=.*src=\""?http://.*?>)(?=.*\balign=.*?>)(?<pre>[^>]*)\balign=\""?(?<align>[^ \""]+)\""?(?<post>[^>]*>)";
   string replace = @"<div style=""float:${align}""><img ${pre} ${post}</div>";
   string result = Regex.Replace(htmlText, search, replace, RegexOptions.IgnoreCase | RegexOptions.Multiline);

La cosa notevole non è tanto che questo problema è risolvibile con le regex - lo davo per scontato, in effetti - ma che possa essere risolto senza implementare un loop o un metodo di callback per il replace.

C# | Regex
12/8/2007 1:46:40 PM (GMT Standard Time, UTC+00:00) #  | Comments [3] | 

VB Migration Partner - il progetto a cui ho lavorato praticamente a tempo pieno negli ultimi 22 mesi è finalmente in beta!

Cosa faccia questo tool lo dice il suo nome: è un convertitore di applicazioni da VB6 a VB.NET. Immagino bene che a questo punto molti di voi scrolleranno le spalle e penseranno "Ancora VB6?" oppure "Ecco un altro che ci tenta!", ma la verità è che VB6 continua a imperversare tra le software house, anche e soprattutto perchè non ci sono strumenti decenti per convertire centinaia di migliaia di righe di codice VB6 in VB.NET, in modo veloce e (almeno relativamente) indolore. Davanti alla prospettiva di sprecare diversi anni uomo di lavoro sulla migrazione di un gestionale VB6 complesso, e di dover fare tutto a mano, beh... sono in molti a farsi indietro. Ora però ci si sta rendendo conto che la decisione non può più essere rimandata: è risaputo che i programmi VB6 non funzionano bene sotto Vista (in particolare, non funzionano molti controlli ActiveX) e soprattutto Microsoft ha dichiarato che il supporto esteso per VB6 termina a Marzo del 2008

Il problema dei convertitori automatici di codice è che quasi mai mantengono le promesse, si tratti di conversione da VB6, Java, Delphi, o qualsiasi altro linguaggio. Nel migliore dei casi, riescono a convertire circa il 95% delle istruzioni, ma con una applicazione VB6 da 100mila righe (quindi neanche estremamente complesse), il 5% di istruzioni errate significa dover mettere mano a cinque mila righe! Quel che è peggio è che quasi sempre non si tratta di piccole modifiche che prendono qualche minuto. Ad es. VB6 e VB.NET espongono due modelli completamente differenti per le istruzioni grafiche, il printing, e il drag-and-drop, quindi in realtà anche se il compilatore VB.NET segnala relativamente pochi errori, in effetti per implementare grafica, printing, e D&D in VB.NET occorrerà rifare intere porzioni di programma. Per non parlare di tutti i controlli ActiveX non supportati, il databinding, e tanto altro.

Nei test che abbiamo eseguito su centinaia di applicazioni VB6 (circa un milione di righe di codice in totale), VB Migration Partner offre la notevole media di un errore di compilazione ogni 1,100 righe di codice, ovvero una percentuale di precisione superiore al 99.9%. Scusate se è poco! Del resto, per avere una idea della precisione di questo strumento, basta un piccolo elenco delle feature di VB6 che sono pienamente supportate (eccetto dove indicato) e che sono quasi tutti fuori dalla portata di qualsiasi altro strumento sul mercato, incluso ovviamente l’Upgrade Wizard incluso in Visual Studio:

  • array con indice LBound diverso da zero
  • Gosub, On...Goto, e On ...Gosub
  • variabili auto-instancing (Dim x As New Person) che mantengono il comportamento anche in VB.NET
  • Declare con parametri As Any e indirizzi di callback (es. EnumWindows)
  • proprietà di default risolte correttamente anche in modalità late-bound
  • finalizzazione deterministica per le variabili di tipo Connection, Recordset, ecc., ad es. per ottenere che la connessione sia chiusa quando la variabile è messa a Nothing o esce dallo scope
  • supporto (limitato) per Variant e propagazione dei valori Null
  • tutti i 60+ controlli di VB6 (uniche eccezioni: OLE e Repeater)
  • control array, anche di controlli ActiveX di terze parti
  • menu popup
  • metodo Controls.Add e oggetti VBControlExtender per creare dinamicamente form di data entry
  • metodi grafici (Line, Circle, PSet, PaintPicture, ecc.) con supporto per ScaleMode custom
  • oggetto Printer e collection Printers
  • OLE drag-and-drop
  • UserControl, con supporto di feature avanzate e oggetti Ambient e Extender
  • data-binding con controlli Data ADO, RDO, ADO, con recordset ADO, con DataEnvironment
  • classi DataEnvironment non gerarchici
  • classi e user control ADO data source e simple data consumer
  • classi MultiUse, PublicNotCreatable, GlobalMultiUse
  • classi persistable, classi MTS/COM+

In realtà, è più semplice elencare cosa il nostro tool non supporta: gli UserDocument, le PropertyPage, le WebClass, gli DHTML Page designer, istruzioni non documentate (VarPtr, ObjPtr, StrPtr), il drag-and-drop “classico” (non OLE D&D), e poco altro.

VB Migration Partner è in grado di convertire un intero gruppo di progetti VB6 in una sola operazione, e riesce a generare codice di migliore qualità proprio perchè riesce a vedere “dentro” una DLL che fa parte del project group. Il tool dispone anche di un code analyzer molto sofisticato, in grado ad esempio di segnalare classi, metodi, e variabili non utilizzate: se si considera che in un progetto di grandi dimensioni che si è evoluto negli anni molto spesso il 5-10% di codice è “dead code”, si vede che questa feature da sola è in grado di far risparmiare parecchio tempo prezioso.

Uno dei segreti dell’alto tasso di conversione senza errori è il supporto per i migration pragmas. Un pragma di migrazione non è che uno speciale commento inserito nel codice VB6 che indica a VB Migration Partner come convertire certi costrutti che permettono approcci differenti. Per esempio, il seguente pragma ArrayBounds indica che l’indice di tutti gli array nel progetto corrente deve essere forzato a zero:

          '##project:ArrayBounds ForceZero

I pragma possono avere scope di progetto, di classe, di metodo o di singolo elemento; un pragma ha sempre la precedenza su tutti i pragma dello stesso tipo con scope più ampio. Per esempio, io posso fare l’override del precedente pragma dentro un metodo, e specificare un ulteriore pragma relativo ad uno specifico array:

          Sub Test()
             '## ArrayBounds Shift
             '##arr.ForceZero
             Dim names(1 To 10) As String
             Dim values(1 To 10) As Long
             Dim arr(1 To 10) As Integer
          End Sub

L’opzione Shift indica a VB Migration Partner che i valori di LBound e UBound dell’array devono essere “shiftati” verso lo zero, in modo da preservare il numero di elementi dell’array originario in VB6:

          Sub Test()
             Dim names(9) As String
             Dim values(9) As Integer
             Dim arr(0 To 10) As Short
          End Sub

VB Migration Partner supporta una cinquantina di pragma per gli scope più differenti. Ad esempio, SetName modifica il nome di un elemento, SetType il suo tipo, AutoNew implementa il comportamento auto-instancing delle variabili As New, AutoDispose fa sì che le variabili siano “disposed” correttamente prima di essere impostate a Nothing, ecc.

Uno dei difetti principali di un tool di conversione “tradizionale” è il fatto che – dopo la prima conversione – il progetto originario VB6 e il nuovo progetto VB.NET vivono due vite completamente separate. Se la migrazione dura settimane o mesi – com’è plausibile per i gestionali di grandi dimensioni – quando finalmente il programma VB.NET compila ed esegue senza errori il codice sarà già “vecchio”, ovvero non allineato alle modifiche e ai bugfix che nel frattempo sono state apportate al programma VB6.

Per evitare questo problema, VB Migration Partner offre il supporto al cosiddetto ciclo convert-test-fix. In pratica, si tratta della possibilità di migrare in modo iterativo un determinato progetto VB6 raggiungendo lo stadio “zero compilation error” e poi “zero runtime error” semplicemente arricchendo il sorgente VB6 con degli opportuni pragma, ma senza modificarlo in altro modo. Grazie al ciclo convert-test-fix è possibile affrontare migrazioni complesse – che possono durare anche settimane o mesi – mantenendo sempre sincronizzati le versioni VB6 e VB.NET della applicazione.

Io sono particolarmente contento delle feature del refactoring engine, che dopo la prima conversione “grezza” applica al codice VB.NET numerose tecniche di ottimizzazione. Ad esempio, è in grado di accorpare la dichiarazione e l’inizializzazione di variabili, di generare istruzioni Return, di sfruttare gli operatori composti di assegnazione (tipo += e *=), e di altro ancora:

Function GetValue() As Long     =>     Function GetValue() As Integer
   Dim x As Long                          Dim x As Integer = 123
   x = 123                                ...
   ...                                    If x > 0 Then
   If x > 0 Then                             Return x
      GetValue = x                        ElseIf x = 0 Then
	  Exit Function                          Return -1
   ElseIf x = 0 Then                      End If
      GetValue = -1                       ...
	  Exit Function                       x += 1
   End If                                 ...
   ...                                 End Function
   x = x + 1
End Function

Ci sono altri tipi di refactoring che si possono ottenere mediante opportuni pragma. Ad esempio, è possibile inserire la dichiarazione di una variabile dentro un ciclo For o For Each, come in questo esempio:

Dim i As Integer                =>     For i As Integer = 1 To 100
For i = 1 To 100                          ...
   ...                                 Next
Next

Ciliegina sulla torta: nonostante tutte queste feature VB Migration Partner è circa 8 volte più veloce dell’Upgrade Wizard incluso in Visual Studio. Devo dire che mi sono tolto una bella soddisfazione!

Siamo alla fine di questo lungo post, ma ne seguiranno altri. Potete scoprire tutto (ma veramente tutto!) su VB Migration Partner sul nuovo sito www.vbmigration.com, dove troverete anche molto materiale sulla migrazione tout court nonchè il mio nuovo blog in inglese, tutto dedicato alle problematiche di migrazione da VB6.

11/27/2007 1:20:44 PM (GMT Standard Time, UTC+00:00) #  | Comments [2] | 

Silverlight in primis, ma anche le anteprime di Visual Studio 2008 e Windows server 2008, sono solo alcune delle novità che promettono un inverno più caldo di questa torrida estate italiana.

Un aspetto trascurato, ma che non può lasciarci indifferenti, è che molte di queste novità hanno avuto un contributo da rappresentanti del Bel Paese. Sviluppatori, program manager, tester, evangelisti, ognuno di questi ruoli è rappresentato anche da italiani illustri come Vittorio Bertocci, Marco Ottaviani, il caro Aldo Donetti e molti altri. Vi segnalo una iniziativa da non perdere legata al nuovo tag center di Channel9: Italia9. Vittorio Bertocci, si prodigherà in una serie di interviste, rigorosamente in italiano, in giro per il campus di Redmond parlandoci direttamente nella nostra lingua. Grande !!! Altre interviste saranno disponibili nei prossimi mesi; in linea è già disponibile la prima, fatta a Marco Ottaviani, devlead per le Performance nel Connected Framework team (Windows Communication Foundation, Windows Workflow), nella quale Marco ci mostra qualche errore tipico in cui si può incorrere quando si cerca di misurare le performance di WCF e ci svela quali strumenti utilizza nel suo mestiere.

Che dire, un plauso a Vittorio e a tutti quelli che stanno sostenendo questa straordinaria inziativa oltre ad un in bocca al lupo per il prosieguo del lavoro a tutti i nostri connazionali oltreoceano dei quali essere orgogliosi.

8/1/2007 2:20:44 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Oggi gironzolavo per la rete e ho trovato un forum dove qualcuno poneva la seguente domanda: "data la stringa ABCD è possibile scrivere una regular expression che fa il match con un qualsiasi anagramma di tale stringa - ad es. DBAC o CDBA ma non ABDE oppure AABC?". Ovviamente la domanda era intrigante per uno che come me smanetta molto con le regex, e stranamente non l'avevo mai vista in giro. Qualcuno obiettava che le regex non erano abbastanza potenti, e che serviva scrivere un piccolo programmino per testare le varie combinazioni, quindi mi sono sentito in obbligo di pensarci un po' su e postare la mia soluzione.

Se state semplicemente controllando che una stringa fornita dall'utente sia una permutazione di ABCD la soluzione è in effetti molto semplice:

      ^(?=.*a)(?=.*b)(?=.*c)(?=.*d)[abcd]{4}$

dove il costrutto (?=.*a) si chiama positive lookahead ed esegue il match con l'espressione nella parentesi ma senza consumare caratteri, quindi in altre parole significa che la stringa deve contenere il carattere A da qualche parte, ovvero preceduto da zero o più caratteri. Con le successive espressioni positive lookahead si controlla che nella stringa ci siano anche i caratteri B,C e D. Quando questa fase è completata, la regex [abcd]{4} assicura che la stringa contenga solo questi quattro caratteri e non altri.

Se invece stiamo cercando tutti i possibili anagrammi di ABCD all'interno di un testo di lunghezza qualsiasi, allora la regex è solo leggermente più complicata:

      \b(?=.{0,3}a)(?=.{0,3}b)(?=.{0,3}c)(?=.{0,3}d)[abcd]{4}\b

In questo caso oltre a imporre che i caratteri A,B,C,D siano tutti presenti ci accertiamo anche che non distino più di 3 caratteri dall'inizio della parola, e usiamo i delimitatori \b per imporre che la parola stessa contenga esattamente quattro caratteri. Ovviamente dovete specificare se il case dei caratteri è significativo o meno, perchè il match avvenga indipendentemente dal fatto che le parole siano in maiuscolo o minuscolo.

 

 

 

6/6/2007 10:30:10 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Premessa. Non so se questa piccola scoperta che ho fatto di recente sia già documentata da qualche parte su Internet. Non ne ho mai sentito parlare, ma mi sembra strano che nessun’altro ci abbia pensato prima. Comunque, ci sono arrivato da solo e questo è sufficiente per scriverne sul blog.


Mi sembra inutile rimarcare il fatto che uno dei problemi che assilla chi scrive software per professione è la protezione dalle copie. In più, se programmate con .NET avete anche il problema di proteggere le applicazioni dalla decompilazione. Ovviamente i due problemi sono collegati, perchè se volete proteggere una applicazione con qualche meccanismo software (ad es. la lettura delle caratteristiche della macchina su cui il cliente installa la sua copia) occorre evitare che una semplice decompilazione + ricompilazione possa permettere ad un hacker neanche troppo esperto di bypassare la vostra protezione.

Non si tratta certo di problemi nati con .NET. Una quindicina di anni fa campavo sviluppando tool per programmatori con la mia azienda personale – la SoftWhale, dove “Whale” = “Balena” per chi non lo sapesse – e uno dei miei prodotti più gettonati era NOWAY, un programma che crittografava gli eseguibili MS-DOS proteggendoli quindi dalla decompilazione (un problema molto sentito da chi lavorava in Clipper) e permetteva di lanciarli solo sui computer per i quali la software house forniva al cliente dei codici di sblocco. NOWAY era scritto in puro Assembly 8086 e non è sopravvissuto al passaggio da MS-DOS a Windows.

In commercio esistono molti tool per .NET che servono a risolvere questi problemi, incluso obfuscator, compilatori “nativi” e chiavi hardware. Se volete proteggere la vostra IP (Intellectual Property) dovreste darci una occhiata approfondita.Per nostra fortuna, in Code Architects scriviamo soprattutto applicazioni Enterprise che non richiedono questo tipo di protezione, quindi non ho mai approfondito la questione. Immagino che questi tool facciano bene il loro lavoro, e li testerò se e quando avremo la necessità di farlo.

Se però (a) scrivete perlopiù applicazioni Windows Forms, (b) non vendete software pacchettizzato ed eseguite direttamente voi le installazioni presso il cliente, e (c) fate anche assistenza post-vendita, allora è possibile ottenere la protezione dalla decompilazione e dalle copie illegali con un meccanismo davvero molto semplice.

Cominciamo scrivendo una semplice applicazione Windows Forms. In questo esempio uso VB2005 ma ovviamente la tecnica si applica a qualsiasi linguaggio .NET.

Imports System.Reflection

Friend Module Module1

 

   <STAThread()> _

   Public Sub Main()

      ' in this demo we call the secret routine both directly and via reflection

      ' just to prove that either method works

       MySecretCode("Direct call")

       GetType(Module1).InvokeMember("MySecretCode", BindingFlags.InvokeMethod, _

          Nothing, Nothing, New Object() {"Reflection call"})

   End Sub

 

   Public Sub MySecretCode(ByVal title As String)

#If Not COPYPROTECT Then

      ' here goes all the code that you want to protect from decompilation

      MessageBox.Show("Running secret code!", title)

      ' ...

#End If

   End Sub

End Module

Poichè la variabile di compilazione COPYPROTECT non è definita, il codice nel metodo MySecretCode è incluso nell’eseguibile come se il blocco #IF non esistesse. In questo esempio proteggo un solo metodo, ma in una applicazione reale dovreste ripetere il procedimento con tutti i metodi che contengono del codice critico, magari dove implementate gli algoritmi più interessanti oppure dove controllate che l’utente abbia la licenza di eseguire il programma sul computer in questione. È importante che tutti i blocchi #IF... #ENDIF siano completamente contenuti in singoli metodi, ovvero che non capiti mai che uno di questi blocchi contenga le keyword Sub o Function (o le corrispettive End Sub o End Function). Per lo stesso motivo, potete utilizzare il meccanismo anche con le Property ma solo se utilizzate i blocchi #IF all’interno del blocco Get o Set. I blocchi #IF non devono assolutamente includere field, definizioni di eventi, o altro. 

Per completare la preparazione è opportuno definire una nuova configurazione per la soluzione. Dal menù Build selezionate il comando Configuration Manager, dalla combobox in alto a sinistra selezionate <New> e create una configurazione chiamata CopyProtected, che copia le impostazioni iniziali dalla configurazione Release.

Dopo esservi accertati che la nuova configurazione è quella attiva, create una costante condizionale di compilazione chiamata COPYPROTECT e impostata al valore True. In C# questa costante si crea dalla pagina Build delle proprietà di progetto, mentre in VB2005 dovete cliccare sul pulsante Advanced Compile Options che trovate nella pagina Compile:

Grazie a questa nuova configurazione, potete passare facilmente dalla versione normale (ossia Release) a quella copy-protected. Compilando l’applicazione in entrambe le configurazioni, avrete due eseguibili con lo stesso nome (ad es. Project1.exe) e contenenti gli stessi metadati (perchè le #IF sono sempre all’interno dei metodi e non includono campi, proprietà, o altro). L’esempio che segue presume che abbiate compilato una applicazione che si chiama Project1, che NON è firmata con uno strong name.

Preparate il setup, se necessario, utilizzando la versione Release, ma ricordatevi anche di memorizzare separatamente (ad es. su una chiavetta USB) l’eseguibile ottenuto in configurazione copy-protected e l’utility NGEN fornita con .NET Framework. Infine, recatevi presso il vostro cliente e seguite accuratamente la semplice procedura:

1) Lanciate il setup della applicazione, o più banalmente utilizzate XCOPY per copiare tutti i file sul disco rigido del cliente. In questo esempio supporremo che l’installazione sia avvenuta nella directory c:\myapp e che quindi il file eseguibile sia c:\myapp\Project1.exe.

2) Aprite una finestra di comandi, navigate nella directory che contiene l’esegubile e lanciate NGEN per creare una immagine in codice nativo della applicazione. Il comportamento di default di NGEN è OK, quindi non preoccupatevi troppo delle varie opzioni. (Lasciate aperta questa finestra, perchè vi servirà presto.)

3) Aprite una seconda finestra di comandi, navigate nella directory c:\windows\assembly\NativeImages_v2.0.50727_32\nomeassembly, dove nomeassembly è il nome dell’EXE ma senza l’estensione (Project1 in questo esempio). Noterete che in questa directory contiene una sottodirectory il cui nome è una stringa di caratteri esadecimali (nel mio esempio ho ottenuto d37032afe4f6f44588d52ca99d7bb1e5). Questa directory contiene un eseguibile dal nome nomeassembly.ni.exe (Project1.ni.exe nel nostro esempio), che contiene la versione compilata (senza IL e quindi non decompilabile) dell’eseguibile completo.

4) Dal prompt della seconda finestra dei comandi, con un comando COPY o MOVE spostate il file <nomeassembly>.ni.exe su qualche altra directory del disco rigido, ad esempio:
     MOVE d37032afe4f6f44588d52ca99d7bb1e5\Project1.ni.exe c:\
Questa operazione serve ad evitare che questo eseguibile sia cancellato dalla successiva invocazione di NGEN. Lasciate aperta anche questa seconda finestra dei comandi.

5) Sovrascrivete il file c:\myapp\Project1.exe con la versione copy-protected che avete sulla chiavetta USB.

6) Dalla prima finestra di comandi lanciate nuovamente NGEN, questa volta sulla versione copy-protected. Se questo programma venisse eseguito ora probabilmente andrebbe in errore o comunque non funzionerebbe bene, perchè alcune parti del codice non sono presenti, quindi NON lo mandate in esecuzione.

7) Dalla seconda finestra dei comandi, eseguite un comando DIR e noterete che la directory creata precedentemente (d37032afe4f6f44588d52ca99d7bb1e5, in questo esempio) è stata sostituita oppure è stata affiancata da un’altra directory dal nome random. (Nell’esempio che sto eseguendo mentre scrivo queste note è stata creata una directory di nome 9990426218ec334e9d3d62f41cb9a255.) Questa nuova directory contiene una nuova versione di Project1.ni.exe, che corrisponde alla versione in codice nativo dell'’eseguibile incompleto.

8) Sempre dalla seconda finestra dei comandi, copiate la copia precedente di Project1.ni.exe (salvata al punto #4) sulla nuova copia, ad esempio con questo comando:
     MOVE c:\project1.ni.exe 9990426218ec334e9d3d62f41cb9a255
rispondendo Yes alla domanda se intendiamo sovrascrivere il file Project1.ni.exe.

Fatto! Ora potete staccare la chiavetta USB e lasciare sul disco rigido del vostro cliente solo l'assembly "copy-protected" (in versione IL) e l'assembly completo (solo in versione compilata in modo nativo e quindi non decompilabile). Per quanto può sembrare incredibile, l'applicazione funziona correttamente e mostra le due message box :-) Ma se provate a usare ILDASM o Reflector per sbirciare dentro il metodo MySecretCode (e qualsiasi altro metodo il cui interno è racchiuso con un #IF Not COPYPROTECT) troverete che il metodo è vuoto!

Ovviamente neanche Reflector riesce a decompilare alcunchè, visto che proprio manca il codice IL. Nè aiuta usare ILDASM sull’eseguibile Project1.ni.exe, perchè l’unica cosa che si ottiene è visualizzare un manifest senza codice IL:

Come può funzionare questa piccola magia? La spiegazione è semplice: quando lanciate un qualsiasi eseguibile .NET, per prima cosa il CLR va a controllare se vi è una “native image” che corrisponde esattamente a quella applicazione, e la corrispondenza viene cercata confrontando il nome della applicazione e la sua firma (la stringona esadecimale). Se esiste una immagine nativa, quella viene lanciata al posto dell’eseguibile. La condizione però perchè tutto il codice funzioni correttamente è che i metadati coincidano anch’essi, altrimenti troppe funzioni del CLR non funzionerebbero a dovere (il garbage collector, tanto per citarne il più importante). Nel nostro caso noi abbiamo creato due eseguibili con gli stessi metadati (ecco perchè è importante che i blocchi #IF non devono eliminare alcun metodo, campo, o proprietà), e siamo riusciti a mantenere sul disco rigido la versione IL “incompleta” mentre mandiamo in esecuzione la versione nativa “completa”.

Oltre a proteggere dalla decompilazione, molto simpaticamente questo meccanismo protegge anche dalle copie illegali. Infatti, se provate a spostare tutti i file (compreso l’eseguibile Project1.ni.exe) su un’altro computer, è praticamente certo che l'applicazione NON funzionerà correttamente. Da quel che ho potuto capire, questo accade perchè il nome della directory creata da NGEN – ovvero la “firma” di un particolare eseguibile – dipende anche da qualche informazione di sistema e quindi varia da macchina a macchina.

Per quanto interessante, questa tecnica ha alcune limitazioni abbastanza serie:

A) Se si intende proteggere una DLL peer Windows Forms l'assembly non deve essere segnato con uno strong name. Il motivo è che nel manifest degli assembly che referenziano una DLL con strong-name viene memorizzato anche l'hash dell'assembly in questione, e questo è un valore che cambia quando si compila la versione copy-protected. Quindi, visto che la DLL non può avere uno strong-name, neanche gli EXE che usano la DLL possono averlo, il che rappresenta una limitazione notevole. Questo limite non esiste per le applicazioni ASP.NET.

B) Se avviene un errore in una applicazione ASP.NET protetta oppure si modifica il Web config in qualsiasi modo, l'applicazione smette di funzionare. Il problema si risolve con un IISRESET.

C) Se avviene qualche modifica sostanziale all’hardware o al software di sistema – ad es. la sostituzione della CPU, l’aggiunta di memoria, l'upgrade del sistema operativo, ecc. – il .NET Framework non può più usare l’immagine nativa e lancerà l’eseguibile normale, che ovviamente non funzionerà bene perchè alcune porzioni di codice mancano. In quel caso occorre ripetere l’installazione e tutto il procedimento descritto sopra. È importante quindi mettere in guardia il vostro cliente dai rischi che corre facendo questi upgrade durante la notte, il fine settimana, o mentre siete in vacanza alle Bahamas.

Per mitigare il problema potete almeno fare in modo che l’applicazione mostri un chiaro messaggio di errore quando l’immagine nativa non è aggiornata e il CLR torna ad usare l’eseguibile “incompleto”. Per fare questo è sufficiente avere una istruzione MessageBox.Show in un blocco #IF COPYPROTECT (senza il Not), ad es:

Friend Module Module1
    Public Sub Main()     

#If COPYPROTECT Then

        MessageBox.Show("This app requires reinstallation. Please contact tech support.")

        Exit Sub

#End If

        ' Here the real application begins

        ' ...

    End Sub

End Module

Per quanto ne so, Microsoft non ha mai documentato quali modifiche al sistema disabilitano l’immagine nativa creata con NGEN, quindi non posso essere più preciso su questo punto. Se qualcuno conosce qualche articolo che spiega questi casi, lo menzioni nei commenti.

Per il resto, ho fatto un po’ di prove, sembra che tutto quanto fili liscio e che la tecnica non abbia altre controindicazioni. Se vi imbattete in qualche problema, fatemelo sapere.

6/4/2007 9:06:09 PM (GMT Daylight Time, UTC+01:00) #  | Comments [4] | 

 

Sembra siano state scattate nella Silicon Valley e stanno diventando la barzelletta tra molti anti-Microsoft-tiani. Le potete trovare a questo indirizzo.

Ovviamente, il problema non è nella tecnologia ma in quanto bene (o male, in questo caso) la si usa. Quanto costava controllare meglio gli errori o redirezionarli su una pagina più "carina"? Sicuramente meno della brutta figura che la ditta di mobili svedese (e non Microsoft) sta facendo.

5/14/2007 5:13:40 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Ecco un post che ha poco a che fare con la tecnologia e molto a che fare con il lavoro.

Il nostro amico Eugenio Lamesa, il migliore esperto che abbiamo in Italia di Mail Marketing nonchè la mente dietro il programma/sito SalesWare (a cui sono orgoglioso di aver contribuito come consulente) ha da poco inaugurato il sito TuaPensione.it, dedicato a chi entro il 30 giugno deve decidere cosa fare del proprio TFR.

Tra le tante iniziative Eugenio ha preparato una whitepaper che spiega in modo chiaro e sintetico i dettagli della nuova leggere e come prendere la decisione più giusta e conveniente. Non solo le informazioni sono più chiare e dirette di quelle che ho potuto leggere su alcuni giornali, ma sono anche depurate dai possibili "conflitto di interessi" che abbondano anche in questo settore. Da qualche giorno su sito c'è anche un interessante calcolatore online, che permette di confrontare le varie opzioni. TuaPensione.it è assolutamente no-profit (non ci sono neanche i banner di Google!), come lo è MenoStato.it, l'altro sito a cui Eugenio lavora con passione da qualche anno.

 

 

5/9/2007 6:53:49 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Leggendo questo post sono arrivato a "Myths about the developing world", un video che merita di essere visto, per almeno tre motivi:

1) sfata alcuni luoghi comuni su quello che noi europei pensiamo del mondo in via di sviluppo
2) avanza alcune proposte su come utilizzare e linkare meglio i database contenenti dati statistici
    ma soprattutto
3) mostra come utilizzare una grafica eccezionale per supportare idee e concetti.

Dategli una occhiata, sono 20 minuti spesi bene.

4/26/2007 7:08:11 AM (GMT Daylight Time, UTC+01:00) #  | Comments [1] | 

Finalmente noi Regional Director abbiamo un magazine online tutto nostro! Abbiamo aspettato un po', ma il risultato è davvero di prim'ordine, come potete vedere voi stessi.

The Region è un magazine composto principalmente aggregando i blog dei vari RD, ma la cosa interessante è si ottiene molto più della somma di 140 feeds, perchè esistono alcuni redattori che si preoccupano di mettere online solo i post significativi, evitando doppioni, post di carattere personale oppure smaccatamente commerciale. Non solo: gli articoli sono suddivisi per categorie, quindi potete anche leggere solo quelli che riguardano le tecnologie che vi interessano maggiormente.

Ci sono anche le pagine personali, con informazioni, foto, ultimi post, ecc. Ecco ad esempio la mia home page su The Region (Francesco Balena) e quella di Giuseppe Dimauro, dove il mio socio abbraccia uno dei super-monitor 16:9 che abbelliscono l'ufficio.

4/21/2007 6:33:20 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Parte la serie dei seminari Microsoft Innovation Days, organizzati (quasi tutti) in collaborazione con Code Architects.

  • Marco Bellinaso sarà domani 19 febbrario a Torino, e parlerà di Programmare per il web con ASP.NET e le Ajax extensions.
  • Il sottoscritto sarà giovedi 22 febbrario a Padova, con una giornata dedicata alla Migrazione da Visual Basic 6 a Visual Basic.NET 2005.
  • Il 17 aprile sarà la volta di Giuseppe Dimauro, che a Roma illustrerà le potenzialità di Windows Vista for Developers.
  • La serie di seminari si conclude il 18 aprile a Roma, dove Marco Bellinaso mostrerà tecniche e trucchi dello Sviluppo con Microsoft Office System 2007.

Giovedì parlerò quindi a Padova di migrazione da VB6 a VB2005. Certo, è un argomento "vecchio" e molti lettori di questo blog probabilmente si chiederanno "come, ancora VB6?" La realtà è che sono ancora tantissime le aziende che NON sono ancora passate a .NET Framework, e spesso - ad essere onesti - per buoni motivi. La verità è che gli attuali strumenti di migrazione, ovvero l'Upgrade Wizard di Artinsoft (inserito in Visual Studio 2005) fanno un lavoro di qualità abbastanza discutibile (per usare un eufemismo), non perchè siano scritti male ma perchè la distanza tra i due linguaggi è davvero troppo ampia.

Nell'ultimo anno ho lavorato a tempo pieno sui problemi della migrazione di applicazioni VB6 e ho accumulato un bel po' di tecniche interessanti su come affrontare la migrazione, sia preparando adeguatamente il sorgente VB6 sia procedendo alla creazione di una libreria di supporto VB2005 che accorcia questa distanza tra i linguaggi. E ho anche una bella quantità di piccole e grandi "trucchi" per risolvere i tanti problemi che la migrazione crea, inclusi molti problemi che non ho mai trovato documentati in nessun libro o articolo.

2/18/2007 10:15:07 AM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Da quando ho pubblicato lo Starter Kit TheBeerHouse -- il sito web sviluppato come esempio per il mio libro "ASP.NET 2.0 Website Programming" -- su CodePlex, il sorgente ha già avuto più di 50.000 download. Niente male direi, anche considerando che se dovessi fare una stima per i download fatti direttamente dal sito di Wrox da molti lettori, e quelli fatti da .Net2TheMax prima che creassi l'account su CodePlex, i download sarebbero almeno 60-65.000.

Il sito implementa un mini CMS, una sezione di e-commerce, newsletter, sondaggi, forum gestione account, feature di personalizzazione e localizzazione, e qualcos'altro. Quindi se sviluppate in ASP.NET 2.0 e non l'avete ancora scaricato, magari lo potreste troverare interessante, anche come punto di partenza per qualche vostro sito content-based.

Questo post però non è solo a scopo pubblicitario, ma anche e soprattutto un tentativo di coinvolgere qualcuno a collaborare e far crescere questo progetto, implementando nuove funzionalità come la ricerca, un modulo per il conteggio e l'analisi di accessi e pagine viste, una vera photo gallery (ora si possono pubblicare foto, ma sotto forma di articoli all'interno del CMS), e magari qualcos'altro. Se avete qualche idea e vi piacerebbe collaborare, contattatemi!

1/7/2007 9:13:16 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Già, proprio così, qualche giorno fa nientemeno che Scott Guthrie ha pubblicato su Amazon una recensione a 5 stelle del mio libro, dicendo tra le altre cose:

"I am very impressed with this book, and have worked with a number of customers who have also found it extremely useful..."
"The result is a very readable book that provides a great deal of context about how ASP.NET 2.0 works, and how the different features integrate together..."
"I'd definitely recommend this book to anyone who understands the basics of how ASP.NET works already, and is looking for a good book to take them to the next level and really start to build applications with it."
<clicca qui per l'intera recensione>

Non posso credere di aver ricevuto l'onore di tali parole da colui che è universalmente conosciuto come il padre di ASP.NET (e non è solo questo...Scott dirige anche i team di CLR, IIS, Atlas, WinForms, Compact Framework, Commerce Server, WPF, VWD...). Sembra anche che questa sia la prima recensione che Sctott ha scritto su Amazon, il che ai miei occhi fa diventare la cosa ancora più importante :-)

Quindi wow, grazie mille Scott, parole come le tue -- e quelle dei molti altri lettori che mi hanno contattato per esprimere il loro apprezzamento -- sono veramente la più grande soddisfazione dopo aver investito così tanto tempo ed energie a scrivere il libro!

12/22/2006 3:45:38 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Fate un Web Service che riceve una stringa e restiuisce una stringa.
Fate si che l'implementazione del web service restituisca System.Environment.NewLine (la sequenza /r/n).
Fate quindi un chamante del web service e fate si che il chiamante passi anch'esso System.Environment.NewLine.
Lanciate in debug e vedete cosa succede : da entrambi i lati riceventi la sequenza /r/n viene trasformata in /n .. come mai ?

Il fatto è che c'è una specifica dell'XML che dice che una tale sequenza deve essere trasformata (normalizzata) in /n (specificatamente , la sequenza di caratteri asci 13 , 10 deve essere trasformata in 10.
Il responsabile della sparizione del /r è il lato ricevente: di default i web services usano un xmltextreader con la proprietà Normalization impostata a true (provate a serializzare e quindi deserializzare in maniera esplcita un oggetto avente una proprietà stringa impostata con un /r/n usando l'XMLSerializer a cui passate un xmltextreader con la proprietà Normalization impostato prima a true e poi false).
La sparizione della sequenza /r/n puo' essere un bel problema per molte applicazioni.

Per fortuna esiste una soluzione : Per evitare che venga persa la sequenza nella risposta del Web Service occorre intervenire sul proxy lato client andando in override del metodo GetReaderForMessage nel modo seguente:

protected override System.Xml.XmlReader GetReaderForMessage(
 System.Web.Services.Protocols.SoapClientMessage message, int bufferSize) {
 System.Xml.XmlReader l_tmp = base.GetReaderForMessage(message, bufferSize);
  ((System.Xml.XmlTextReader)l_tmp).Normalization = false;
  return l_tmp;
}

(ovviamente non fatelo sul proxy generato dinamicamente , derivate da quello)
Lato server la cosa è piu' articolata : Create una classe custom che deriva da SoapServerProtocolFactory. Tale classe deve restituire un'altra vostra classe custom che deriva da SoapServerProtocol public class MyWSFactory :

System.Web.Services.Protocols.SoapServerProtocolFactory {
 protected override System.Web.Services.Protocols.ServerProtocol CreateIfRequestCompatible(
   System.Web.HttpRequest request) {
  if (request.PathInfo.Length > 0) {
   return null;
  }
  if (request.HttpMethod != "POST") { // need reflection to create the internal UnsupportedRequestProtocol class
   Type l_t = Type.GetType
    ("System.Web.Services.Protocols.UnsupportedRequestProtocol, System.Web.Services, Version=2.0.0.0, Culture=neutral, 
    PublicKeyToken=b03f5f7f11d50a3a"
, true);
   Object l_tmp = l_t.InvokeMember(null, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic |
    BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { 0x195 });
   return (System.Web.Services.Protocols.ServerProtocol)l_tmp ;
   // return new UnsupportedRequestProtocol(0x195);
   }
   return new MyWS();
  } 
   

Ed ecco l'implementazione della SoapServerProtocol custom  in cui si puo' ottenere una referenza all'XMLTextReader utilizzato :

public class MyWS : System.Web.Services.Protocols.SoapServerProtocol {
 protected override System.Xml.XmlReader GetReaderForMessage(
  System.Web.Services.Protocols.SoapServerMessage message, int bufferSize) {
  System.Xml.XmlReader l_tmp = base.GetReaderForMessage(message, bufferSize);
  ((System.Xml.XmlTextReader)l_tmp).Normalization = false;
  return l_tmp;
 }
}

Come ultimo passo dovete aggiungere nel web .config l'entrata seguente per dire ad ASP.NET di agganciare il vostro soapServerProtocolFactory ...

<configuration>
                <system.web>
                               <webServices>
                                               <soapServerProtocolFactory type ="WSFactory.MyWSFactory, WSFactory" />

 

Poteva essere + facile .. ma per adesso le cose stanno cosi' :)


12/22/2006 1:33:32 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

In genere mi piace molto la possibilità di estendere all’infinito la potenza delle mie applicazioni semplicemente aggiungendo un reference a qualche assembly che contiene le funzioni o i controlli che mi servono. Mi piace molto meno il fatto di dover distribuire numerose DLL con i miei eseguibili. In questo articolo illustrerò una tecnica per comprimere tutte (o quasi) le DLL satelliti di una applicazione Windows Forms e “fonderle” con l’eseguibile principale.

Tutti i file che vi servono sono contenuti in questo ZIP, che contiene l’utility AsmZip.exe (che si lancia dalla riga di comando) e due file sorgenti, Unzipper.cs e Unzipper.vb. Copiate l’utility AsmZip in una directory presente nel path di sistema, per poterla richiamare facilmente.

I passi da seguire
Ecco i passi necessari per implementare questa tecnica.

1) Aggiungere il file Unzipper.vb o Unzipper.cs al progetto principale della vostra applicazione, rispettivamente se lavorate in Visual Basic o Visual C#.

2) Nella procedura Main, aggiungete una istruzione che inizializza la classe AssemblyUnzipper (contenuta nel file sorgente aggiunto al punto precedente):
       ' (Visual Basic 2005)
       CodeArchitects.AssemblyUnzipper.Initialize()
       // Visual C# 2005
       CodeArchitects.AssemblyUnzipper.Initialize();
È di fondamentale importanza che questo codice sia eseguito prima di ogni altra istruzione del programma, in particolare prima di mostrare un form che contiene al suo interno dei controlli di cui non volete distribuire le DLL. Se lavorate in VB e l’applicazione ha un form di partenza (e quindi non avete a disposizione la procedura Main), dovete inserire queste istruzioni nel costruttore statico del form di partenza:
      Shared Sub New()
        CodeArchitects.AssemblyUnzipper.Initialize()
       End Sub

3) compilate la soluzione, ovviamente in Release mode visto che utilizzerete questa tecnica poco prima di consegnare l’eseguibile al vostro cliente.

4) aprite una finestra con il prompt dei comandi nella directory di output del programma, e lanciate l’utility AsmZip in questo modo:
             AsmZip main.exe *.dll
dove main.exe è il nome dell’eseguibile principale. Il comando precedente comprime *tutte* le DLL nella directory e mette i dati compressi in coda al file main.exe. Per comprimere solo alcune DLL tra quelle presenti nella directory dovete specificarne i nomi, come in questo esempio:
             AsmZip main.exe CodeArchitects*.dll Microsoft*.dll
(Ci possono essere dei buoni motivi per non comprimere alcune DLL usate dalla applicazione, come spiego più avanti.)

5) a questo punto potete cancellare tutte le DLL che avete compresso, perchè l’applicazione – grazie alla classe AssemblyUnzipper - è in grado di trovarle da sola in coda al proprio eseguibile, di decomprimerle, e di caricarle in memoria.

I vantaggi
Prima di procedere ad una spiegazione più dettagliata di come funziona questa tecnica, proverò a riassumere i suoi vantaggi:

a) deployment semplificato: dovete distribuite un minor numero di file (spesso il solo EXE principale)
b) applicazioni più robuste: non vi è il rischio che il cliente renda il programma inutilizzabile cancellando inavvertitamente una DLL
c) minore occupazione su disco (tutte le DLL sono compresse e accodate all'EXE principale)
d) la possibilità di “nascondere” i vostri piccoli segretucci, ad esempio quali controlli di terze parti avete utilizzato nella applicazione.
e) un maggior grado di protezione: il codice nelle DLL compresse non può essere decompilato, perlomeno non senza avere estratto e decompresso i singoli eseguibili.

È evidente che gli ultimi due punti non costituiscono una vera barriera per un programmatore anche solo un po’ esperto, se è davvero intenzionato a sapere quali DLL avete usato e a decompilarle. Per ottenere queste informazioni un hacker dovrà soltanto decompilare l’EXE principale, capire come funziona la classe AssemblyUnzipper, e scrivere un programmino che ne ripete il funzionamento ma salva su disco gli assembly compressi. In definitiva, questa tecnica permette di nascondere il vostro sorgente e le vostre DLL alla prima occhiata, ma non potete certo considerarla una tecnica di protezione dal reverse engineering.

L’utility AsmZip usa la classe GZipStream per comprimere le DLL originarie, quindi il grado di compressione che si può raggiungere con questa tecnica non è paragonabile a quello ottenibile con WinZip o WinRar, ma è comunque più che sufficiente, come mostra la figura.



Come funziona
Questa tecnica si basa sull’evento AssemblyResolve dell’oggetto AppDomain, che scatenato quando il .NET Framework deve caricare un assembly richiesto dalla applicazione. Gestendo opportunamente questo evento potete mettere in pratica tante piccole “magie” che non sarebbero possibili in alcun altro modo. Ad esempio, potreste caricare gli assembly satelliti di una applicazione da uno share di rete oppure da un campo blob di un database.

La classe AssemblyUnzipper, invece, usa questo evento per cercare l’assembly richiesto da uno stream compresso che è stato accodato all’eseguibile del programma principale.
      // the handler for AssemblyResolve event
      static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs e)
      {
         // find the assembly with given name, cause error if not found
         AssemblyInfo info = null;
         if ( AsmInfos.TryGetValue(e.Name, out info) )
            return ExtractAssembly(info);
         // signal error
         Debug.WriteLine("Failed to uncompress assembly " + info.Name);
         return null;
      }
Ciascun oggetto AssemblyInfo tiene traccia di dove, nel file EXE, è memorizzato ciascuna DLL compressa. Il dictionary AsmInfos permette di trovare subito le informazioni associate ad una DLL con un certo nome; questo dictionary è creato nel metodo Initialize, alla partenza del programma, e viene poi usato ogni volta che l’applicazione richiede una DLL.

Non spiegherò nel dettaglio come funziona il codice, perchè il sorgente è commentato in modo adeguato, sia nella versione VB che C#.

I limiti
Ho provato questa tecnica con numerose applicazioni Windows Form, senza alcun problema. Il limite principale deriva dal fatto che gli assembly caricati in questo modo hanno la proprietà Location nulla, ma se non utilizzate reflection per esplorare le proprietà degli assembly non ve ne accorgerete neanche. Ad esempio, se la vostra applicazione carica gli assembly dinamicamente da una certa directory, magari per esplorarne gli attributi, è evidente che quel codice non può funzionare se le DLL sono state compresse e cancellate. In tal caso dovrete evitare di processare queste DLL con AsmZip.

La classe AssemblyUnzipper funziona solo con applicazioni Windows Forms. Da quanto ne so, è possibile utilizzare l’evento AssemblyResolve anche con applicazioni ASP.NET, ma non è possibile usare la mia classe in quel contesto. In realtà, i problemi che questa tecnica risolve non sono molto sentiti in ambito ASP.NET, quindi non credo che abbia senso preparare una versione per applicazioni di quel tipo.

L'unico altro limite è che questa tecnica funziona con le DLL, ma non con l'EXE principale. Se avete un EXE di grosse dimensioni che richiamo poche piccole DLL, il vantaggio che ne potete ricavare è molto limitato. In tal caso, potete comunque raggiungere il massimo grado di compressione "complessiva" spostando i form dall’eseguibile principale in una DLL, e comprimendo poi tale DLL con AsmZip. In teoria, l’EXE di partenza dovrebbe contenere solo lo splash screen (se ne avete uno), e poi dovrebbe caricare il form principale dalla DLL che contiene l’applicazione vera e propria. In questo modo è spesso possibile ottenere un fattore di compressione complessivo superiore al 60 per cento.

Nota: nella prima implementazione di questa tecnica ero riuscito a comprimere anche l'EXE. In tal caso accodavo poi tutti i dati compressi a un piccolo eseguibile "stub" che aveva il solo compito di fare partire l'eseguibile vero e proprio. Dopo alcune prove, però, mi sono reso conto che i problemi che si venivano a creare erano superiori ai vantaggi, per cui ho deciso di utilizzare il metodo descritto in questo articolo.

 

12/8/2006 9:01:13 PM (GMT Standard Time, UTC+00:00) #  | Comments [2] | 
Qualche tempo fa ho registrato per Microsoft Italia alcuni brevi webcast (20 minuti ciascuno) su .NET 2.0 e Visual Studio 2005. Si tratta di sessioni introduttive, ma "succose", su feature come generics, reflection, multitasking, e regular expression. C'è anche un mini-webcast su come creare applicazioni Windows Forms estendibili mediante plug ins. Finalmente tutti i webcast sono online, a questo URL: http://www.microsoft.com/italy/msdn/risorsemsdn/visualbasic/vs2005_pillole.mspx
11/22/2006 7:33:14 AM (GMT Standard Time, UTC+00:00) #  | Comments [2] | 

In questo periodo sono occupato quasi settimanalmente con lo Starting Innovation Tour, un ciclo di conferenze gratuite organizzate da Microsoft nelle principali città italiane per mostrare a sviluppatori e partner le principali novità di Vista e Office 2007. Dopo Catania, Napoli, Bologna, Bari e Roma, questa settimana tocca a Padova, poi Milano, Firenze e Torino. A mio parere la conferenza è davvero un'ottima occasione per chi non ha ancora visto con i propri occhi (o toccato con mano, con preferite) le nuove major release dei due prodotti principali di MS. Io mi occuperò delle sessioni su Office, ovvero:

  • Sviluppo con Office 2007 Client Side: come sfruttare il nuovo formato XML dei documenti per modificare o creare nuovi documenti senza avere Office installato - da applicazioni desktop o web - nonchè una panoramica sulla creazione di add-in, Action Pane e ribbon custom.
  • Windows SharePoint Services 3.0: nuove funzionalità per le liste e document library (cestino, supporto per versioning esteso, supporto RSS,...), nuovi template (blog e wiki tanto per essere moderni, ma altro ancora), utilizzo masterpage a-là ASP.NET 2.0, webpart, utilizzo workflow built-in o utilizzo del nuovo SharePoint Designer per la definizione dei propri flussi.
  • Office SharePoint Portal 2007: Excel Services (il motore di calcolo di Excel direttamente sul server + rendering via browser + esposizione del motore tramite web service!), Forms Services (praticamente le form InfoPath via browser!) e Business Data Catalog (indicizzazione e visualizzzione di record presi da un qualunque database o fonte di accesso ai dati esterna)

Insomma, mi sembra ci sia un bel po' di roba da vedere :-) Per non parlare delle bellissime e impressionanti demo grafiche fatte con WPF per Vista! Dai, ci vediamo ad una delle prossime tappe allora...

11/20/2006 2:11:10 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 
A qualcuno è sfuggito questo ? ADO.NEt VNEXT: http://msdn.microsoft.com/data/ref/adonetnext/default.aspx Forse finalmente un tool di O/R (object Relational mapping) fornito da MS.. dopo tante false partenze (ricordate Object Spaces) ? Non è ben chiaro quando sara rilasciato, direi non con .NET 3.0, per quanto ho potuto capire. Vi segnalo questi due screencast per farvi venire l'acquolina in bocca .. buona visione. http://datajunkies.net/screencasts/adonet_vnext_part1/adonet_vnext_part1.html
11/8/2006 10:05:39 PM (GMT Standard Time, UTC+00:00) #  | Comments [0] | 

Che ne dite di questo messaggio di errore in Windows 2000?

Message: Your Password Must Be at Least 18770 Characters and Cannot Repeat Any of Your Previous 30689 Passwords

Sembra una barzellatta ma non lo è. E' "solo" un bug... però vorrei vedere la faccia di un utente che si trova a leggere questo messaggio :-) Trovate tutti i dettagli qui.

10/24/2006 7:07:04 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Uno dei problemi che si devono risolvere quando si comincia a scrivere gerarchie a più livelli di form ereditate - in Windows Forms, intendo - è capire quando effettivamente il form si trova a design time, in modo ad esempio da evitare di eseguire certe azioni come il caricamento dei dati da file o database.

Basta usare la proprietà DesignMode che l'oggetto Form eredita da Component, dirà qualcuno di voi, ma evidentemente non è così semplice. Infatti, se il form MyForm deriva da FormBase, all'interno della classe FormBase quella proprietà restituisce False quando abbiamo caricato MyForm dentro il designer di Visual Studio. Allora ho tentato di determinare indirettamente se il form si trova a design time, ad esempio vendendo se la collection Application.OpenForms contiene qualche elemento, ma ho scoperto che quella collezione funziona anche a design-time. Lo stesso vale per la maggior parte degli eventi dei form, che in FormBase si scatenano sia quando l'applicazione è realmente in esecuzione sia quando MyForm è in design-mode.

Alla fine ho trovato una soluzione che sembra funzionare bene:

bool IsDesignMode = ( this.Parent != null && this.Parent.ToString().StartsWith("System.Windows.Forms.Design") );

Quando il form è ospitato in Visual Studio, esso è figlio di un altro form managed, la cui classe è appunto System.Windows.Forms.Design.DesignerFrame+OverlayControl, e il codice di cui sopra testa proprio questa condizione. La soluzione non mi piace affatto, perchè mi sembra uno sporco trucco che per giunta fallisce se sviluppate con ambienti di sviluppo differenti da Microsoft Visual Studio, però per il momento mi dovrò accontare.

Mi chiedo perchè il .NET Framework ci debba costringere a questi giri. Oppure forse esiste una soluzione più semplice e io non la conosco?

10/13/2006 6:05:53 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Una regola non scritta del blog di CA è evitare i post personali, cose tipo "mi sono rotto una gamba" o "finalmente mi è arrivato il cellulare nuovo".

Non potevo però tacere l'arrivo al mondo, sabato 7 ottobre, di Delia Dimauro, una bella bimba di quasi tre chili che darà al nostro Giuseppe la scusa per passare qualche notte in bianco, in aggiunta a quelle che già passa scrivendo codice. :-)

NOTA: Perchè questa notizia la bloggo io e non Giuseppe? Beh, lui attualmente ha altro a cui pensare. E poi, visto che non sto bloggando su un fatto mio personale, in un certo senso la regola di cui sopra è "formalmente" soddisfatta...

10/9/2006 1:44:45 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

E' risaputo che più ci si allontana da Milano e Roma, più è difficile assistere a conferenze e seminari di un ottimo livello. Se poi parliamo di città anche grandi ma periferiche come Bari, il tutto si riduce a due-tre road-show Microsoft all'anno.

Ecco perchè sono così contento di annunciare questa bella iniziativa del gruppo DotNetSide, aliasil Dot Net South Italy Developers User Group, che per il 26 Ottobre ha organizzato un bel seminario da una intera giornata, dove si parlerà di ClickOnce, Visual Studio Team System, Windows Workflow Fundation, Windows Communication Fundation, ASP.NET Provider Model. Il tutto servito da un buon numero di esperti e MVP locali e nazionali.

Ci saremo anche io e Giusepppe Dimauro, dove illustreremo un po' di tecnologia .NET "in action", in soluzioni e prodotti su cui abbiamo lavorato di recente.

L'iscrizione è completamente gratuita e potete da questa pagina.

10/9/2006 12:19:42 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Al ritorno dalle vacanze ho avuto la graditissima sorpresa di scoprire che TheBeerHouse, il sito web che ho sviluppato come esempio per il libro "ASP.NET 2.0 Website Programming / Problem - Design - Solution" è stato ritenuto da Microsoft come degno di essere pubblicato sulla loro pagina ufficiale degli Starter Kit! Il link al download del codice sorgente punta a CodePlex: ho deciso di aprire li un account in modo da poter condividere fix e modifiche non appena correggo il codice in locale e faccio il check in nel loro sistema di source control.

Un'altra notizia che dovrebbe far parecchio piacere a molti lettori è che finalmente è disponibile anche una versione in VB.NET del codice! Il porting non è ufficialmente supportato, perchè eseguito da Darren J Kindberg, un lettore pieno di buona volontà che non potrò mai ringrazia abbastanza per l'impegno e il supporto dimostrato, anche nel forum ufficiale di Wrox. Buon divertimento :-)

8/20/2006 11:14:19 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Riprendo un po’ a scrivere sul blog, approfittando di una mattina oziosa di vacanza. Lo spunto me lo offrono un paio di commenti al mio post precedente, che oramai data un mese e mezzo fa. (Forse tra qualche settimana vi svelerò a cosa ho lavorato in tutto questo tempo...)

Ogni tanto su questo blog e sui vari forum qualcuno si lamenta che i libri tecnici in italiano costano troppo, e si avanzano spesso ipotesi sulla presunta ingordigia degli editori italiani. Il ragionamento è che se un libro in originale costa, supponiamo, 50$ dovrebbe costare l’equivalente anche in italiano, ossia circa 40 euro, anzichè 50 o 60 euro come spesso accade.

Anche se suggestivo, questo ragionamento è un po’ troppo semplicistico e non tiene conto di quello che accade quando un editore italiano decide di tradurre un libro dall’inglese. Io non voglio fare il “difensore d’ufficio” di aziende come Mondadori o Apogeo, che proprio non hanno bisogno di qualcuno come me che le difenda, però credo sia giusto sgombrare il campo da tutta una serie di falsi miti, che evidentemente derivano dalla scarsa conoscenza dei meccanismi editoriali. Visto che lavore con case editrici da oltre vent’anni dovrei avere qualcosa di interessante da raccontare.

Prima di cominciare, ci tengo a precisare che questa “difesa d’ufficio” degli editori italiani per me è persino controproducente. Se devo dirla tutta, a me farebbe molto più comodo che i miei lettori comprassero direttamente i libri in inglese, per un semplice motivo: io ho un contratto con Microsoft Press e non con l’editore che traduce in italiano i miei libri (Mondadori, nella fattispecie), e per contratto il guadagno sulle copie in inglese è quasi doppio di quello sulle copie tradotte in altre lingue, italiano incluso. In altre parole: convincervi a comprare i libri in italiano va persino contro i miei interessi.


Allora, ragioniamo per assurdo e supponiamo che un editore italiano decida di tradurre un libro tecnico dall'inglese e di venderlo allo stesso prezzo dell'originale. Vediamo come se la caverebbe questo editore con evidenti tendenze suicide...

Il primo costo da sostenere sono i diritti di traduzione. Non ho modo di sapere a quanto ammontano questi diritti, ma di certo non sono irrisori. A titolo puramente di esempio, diciamo che per un libro di medio successo potrebbero essere sugli 6-8 mila euro. La cifra esatta dipende anche dal numero di copie vendute, e comunque questi dati non sono pubblici, quindi la mia è una stima ipotetica ma tutto sommato realistica.

A questo punto scatta il processo di traduzione. I costi di traduzione per un libro di 700 pagine costa non sono meno di 7-8mila euro, o anche più se si tratta di un argomento specialistico. Occorre poi correggere e impaginare le bozze, e sommando questi costi di editing ai costi di traduzione arriviamo a 10-12mila euro, solo per avere i file PDF pronti per la stampa.

I costi per la stampa sono quelli che pesano maggiormente in percentuale per le tirature piccole come quelle italiane. Quanto sono piccole le tirature in Italia? Non posso darvi i numeri esatti delle vendite dei miei libri, che non sarebbero indicativi per un discorso generale in quanto “viziati” dal fatto che io sono un autore italiano con un buon grado di visibilità in patria, però posso fornire qualche numero su cui riflettere.

Un libro di programmazione “mediamente di successo” vende nel mercato mondiale 25-30mila copie, e questo mercato comprende gli USA, il Canada, il Regno Unito e tutti gli altri paesi anglofoni, più tutti i paesi in cui comunque i libri tecnici non sono tradotti (ad esempio, i paesi scandinavi e i paesi dell’Europa dell’Est). È facile vedere che parliamo di un bacino di utenza almeno 20 volte maggiore di quello italiano. Anche abbondando, potremmo dire che la traduzione dello stesso libro “mediamente di successo” non dovrebbe vendere in Italia più di 2-3mila copie.

In realtà la situazione è persino peggiore di quanto ho descritto, poichè molti sviluppatori italiani preferiscono comunque comprare i libri in lingua originale, sia perchè non hanno errori di traduzione sia perchè sono disponibili alcuni mesi prima delle traduzioni. Una stima più attendibile è quindi non oltre le duemila copie per un libro di medio successo. Molti si stupiscono di questi volumi così piccoli, ma tenete presente che stiamo parlando di una nicchia molto specialistica: ad esempio, un libro su Windows o Office vende tipicamente molto più di tanto, anche perchè la maggior parte degli utenti finali proprio non ne vuole sapere di comprare i libri in lingua originale.

I costi di stampa per singola copia aumentano se il numero di copie stampate diminuisce, anche se non in modo proporzionale. Per una tiratura 10-15 volte inferiore i costi di stampa sono sicuramente almeno tre volte superiori. Davvero non conosco le cifre reali, ma mi stupirei se la stampa e la rilegatura di un libro di 700 pagine con una tiratura di 2mila copie e una copertina in cartoncino e a colori possa costare meno di 8-10 euro per copia. Occorre considerare, infatti, è che se anche un libro di successo venderà qualche migliaia di copie nel lungo periodo, inizialmente si stampano un numero inferiore di copie, per vedere come va in libreria e anche per ridurre i costi di magazzino (o forse credete che lo spazio in un magazzino sia gratuito?). Purtroppo, tirature inferiori significano costi per copia superiori, quindi 8-10 euro non è una stima irragionevole. Forse esagero, forse no. Se avessi tempo farei qualche indagine presso una tipografia, ma sono in vacanza e ho altro di meglio a cui pensare. E comunque non credo che qualche euro in meno cambierebbe la sostanza del ragionamento.

A questo punto il libro può essere spedito dalla tipografia al distributore, o meglio i distributori disseminati nelle varie regioni. Per fortuna questi costi di spedizione assommano a qualche frazione di euro per ciascuna copia, per cui possiamo trascurarli in questa analisi di massima.

Ovviamente, non tutti i 40 euro del prezzo di copertina vanno nelle casse dell’editore, perchè occorre considerare il guadagno delle librerie e dei distributori. Negli USA all’editore va il 52% del prezzo di copertina. In Italia non so bene, ma è ipotizzabile una percentuale simile. Allora siamo pronti a tirare le somme.

  • 2000 copie a 40 euro l’una fanno 80mila euro lordi
  • Il 52% di questa cifra lorda va all’editore, quindi 41.600 euro.
  • Togliamo 8mila euro per i diritti di traduzione, e arriviamo a 33.600 euro.
  • Sottraendo i costi di traduzione e impaginazione arriviamo a 21.600 euro.
  • Ipotizzando 8 euro per la stampa di una copia, arriviamo a 16mila euro di costi di stampa, il che porta il margine per l’editore a circa 8.000 euro.

Per guadagnare questi 8.000 euro, l’editore deve mettere in moto un meccanismo complesso che richiede la presenza di numerose figure di alto profilo professionale, tra cui, i consulenti che decidono quali libri tradurre, gli uffici legali che stilano i contratti, i manager che tengono i contatti con la casa editrice americana, ecc. Oltre agli stipendi di queste persone, occorre considerare i costi della struttura e tutto il resto. Non ho neanche una vaga idea dell’ammontare di questi costi, ma di sicuro non sono trascurabili.

Quando il libro è finalmente stampato occorre provvedere alla sua distribuzione e alla promozione. Come forse potete immaginare, la pubblicità sulle riviste non viene regalata, e anche semplicemente gestire un sito e mandare una newsletter ha dei costi. Ma il costo principale è quello del personale umano, ad esempio gli agenti di vendita. Questi signori (di solito ve ne è uno per regione) girano costantemente per tutte le librerie della penisola, proponendo le novità e raccogliendo gli ordini per i libri che devono essere ancora stampati. Se queste persone non esistessero, nelle librerie trovereste soltanto i libri di Dan Brown, Grisham, e pochi altri, perchè pochi librai capiscono la differenza tra SQL Server e Paint, tanto per fare un esempio.

Attenzione, però, perchè i costi per l’editore non sono terminati. Infatti, i libri tecnici sono soggetti a veloce obsolescenza, quindi tutte le librerie prendono i libri tecnici in conto vendita, il che significa che in qualsiasi momento possono restituire all’editore le copie invendute. Il reso può essere richiesto anche dopo uno o due anni, e questo pesa come una spada di Damocle sui conti economici. Basta un reso del 20% per azzerare o quasi il margine che abbiamo ipotizzato per il nostro editore, perchè al mancato guadagno occorre considerare i costi di spedizione, i costi del macero e dello smaltimento. (Persino buttare via i libri costa...) 

Se il 20% di reso vi sembra una stima esageratamente alta, considerate che la maggior parte delle riviste tecniche ha un reso in edicola intorno al 35-40 percento. (...e questo spiega perchè tutte le riviste inisitono così tanto per fare abbonare i loro lettori.) Del resto, basta andare in una qualsiasi libreria e contare il numero di copie impolverate di libri su Access 2000 o Visual Basic 6 per capire che si tratta di stime assolutamente ragionevoli.

Ancora: tenete presente che tutto il discorso fatto finora si riferisce a un libro “mediamente di successo”. In realtà, la stragrande maggioranza dei libri tecnici in inglese vende molto meno di quelle 30mila copie ipotizzate inizialmente, in particolare i libri su tecnologie di nicchia come SharePoint o BizTalk. Ad esempio, un libro su VB o C# “generico” vende anche il doppio di un libro su ASP.NET, che a sua volta vende due-tre volte più di un libro su Windows Forms o quattro-cinque volte più di un libro su ADO.NET.

Ultima considerazione: il nostro libro di esempio non ha un CD-ROM a corredo. Infatti, con un CD i costi salirebbero notevolmente, non solo per la creazione del CD vero e proprio ma anche per il fatto che i CD tendono a rompersi durante il trasporto o persino l’esposizione in libreria. Se un lettore trova un CD rotto o illeggibile, chiede il rimborso all’editore (e non al tipografo, al libraio o al trasportatore, che spesso sono i reali responsabili). La spedizione di un CD praticamente azzera il guadagno per quella particolare copia del libro. Per questi motivi, la presenza di un CD può fare levitare il prezzo di copertina anche di 4-5 euro, visto che occorre considerare il margine del 52% e dei libri restituiti destinati al macero.

Siamo arrivati alla fine del mio ragionamento, che è forzatamente approssimativo perchè si basa su alcuni dati non noti e di cui posso solo stimare il valore. Ma se vi armate di una calcolatrice, vedrete che anche diminuendo l’impatto di qualche voce dubbia – ad esempio i costi di traduzione e di stampa – la sostanza del ragionamento non cambia di molto.

La morale è semplice: i libri tecnici in italiano tradotti dall’inglese non potranno mai costare quanto le versioni originali. Forse nel futuro alcuni costi potranno essere eliminati o ridotti fortemente – ad esempio se si diffondesse il print on demand e si procedesse con la stampa di singole copie a costi bassissimi solo quando un acquirente prenota la sua copia via Internet – ma fino a quando le librerie rimarranno il principale mezzo di diffusione dei libri tecnici questa regola continuerà a valere. Quindi, o vi rassegnate a spedere qualche euro di più per le copie in italiano o vi decidete a imparare l’inglese, che sarebbe una decisione che avrebbe un impatto positivo per molti aspetti ben più importanti :-)

NOTA: Ho volutamente trascurato la possibilità di evitare i costi di stampa e saltare tutti i livelli della distribuzione pubblicando degli e-book scaricabili da Internet. Purtroppo, gli e-book hanno la simpatica (per il lettore) ma fastidiosa (per l’autore) caratteristica di essere facilmente duplicabili e scambiabili con gli amici e colleghi. Microsoft Press mi ha chiesto in passato di pubblicare degli e-book, ma mi sono sempre rifiutato, come si sono rifiutati praticamente tutti gli autori di una certa rilevanza. Forse le cose cambierebbero se esistesse un meccanismo anti-duplicazione sicuro e che allo stesso tempo non complichi la vita ai legittimi acquirenti, ma per il momento le cose stanno in questo modo, quindi gli e-book non sono una alternativa plausibile.

Se avete osservazioni da fare ma soprattutto se pensate che il ragionamento abbia qualche falla, fatevi sotto con i commenti.

8/19/2006 11:34:17 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Il mio libro su C# è stato pubblicato oramai da qualche mese, ma solo ora sono riuscito a mettere da parte un po' di tempo per preparare la sua home page sul nostro sito americano e uploadare due capitoli in PDF, ovvero il capitolo 5 "Arrays and Collections" e il capitolo 10 "Custom Attributes". Dalla home page potete anche scaricare il sorgente di tutti gli esempi di codice e un file doc con gli errata. Occorre registrarsi al sito, ma la registrazione è gratuita.

C# | Libri
7/1/2006 10:39:08 AM (GMT Daylight Time, UTC+01:00) #  | Comments [1] | 

Finalmente ho trovato il tempo per controllare una a una tutte le segnalazioni dei lettori su errori tipografici riscontrati nel mio ultimo libro su Visual Basic. Il risultato è un nuovo documento di errata corrige, che potete scaricare dalla home page del libro. I numeri di pagina si riferiscono alla versione inglese del libro, ma i numeri di pagina nella versione italiana sono spesso identici o comunque molto vicini.

Per la comodità di chi ha già downloadato questo documento, ho marcato con "New" tutte le aggiunte recenti. Da notare che quasi tutte le correzioni ai sorgenti non impattano sul funzionamento del codice, ad eccezione di un fix che serve a correggere un errore nella routine Evaluate del capitolo sulle regular expression.

7/1/2006 8:29:59 AM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 
6/14/2006 9:55:26 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 

Purtroppo non riesco a stare dietro ai vari commenti a questo blog, e ogni tanto me ne perdo qualcuno davvero interessante. In particolare, non avevo letto subito quello di Pasquale Esposito a un mio post di fine Aprile. In quel post parlavo di una mia DLL per VB6 e chiudevo chiedendo ai lettori VB6 quando si sarebbero decisi a passare a .NET. Pasquale ha risposto con un commento lungo e esauriente, di cui riporto qui i pezzi salienti:


Ci chiedi quand'è che ci decidiamo a migrare a VB.NET. Vedi, io sono rimasto profondamente deluso dal passaggio al byte code operato dalla Microsoft. Da circa dieci anni produco software shareware che distribuisco su Internet o nei CD allegati a riviste di informatica (ultima pubblicazione: Soluzione Bilancio 1.0 recensito a pag. 97 di PC Magazine di maggio 2006). Ho provato a tastare il terreno distribuendo qualche piccolo applicativo in .NET e mi sono subito reso conto che i tempi non sono affatto maturi per passare a questa tecnologia, almeno per chi utilizza la metodologia shareware come canale di distribuzione. Lo affermo per almeno due motivi:

1) Non è per niente facile chiedere ad un utente che non ha il Framework installato sulla propria macchina di scaricarlo da Internet solo per far funzionare la propria applicazione: per chi non ha una connessione ADSL sognificherebbe effettuare un download di qualche ora. Inoltre, anche chi ha installato il SP2 di WinXP, non è detto che abbia scelto di includere il Framework. Conclusione: molto probabilmente, l'utente in questione sceglierà di rinunciare all'applicativo in .NET e cercherà qualcos'altro di meno problematico. In poche parole, il "DLL Hell" di VB6 ha lasciato il posto al ".NET Framework Hell" di VB.NET.

2) Programmare in byte code significa produrre software estremamente vulnerabile, non solo dal punto di vista del cracking ma perfino da quello del reverse-engineering. Basta pochissima esperienza per entrare in possesso del codice sorgente altrui ed è necessario ricorrere a buoni obfuscator di terze parti per proteggere le proprie applicazioni. Questi strumenti non soltanto sono alquanto costosi ma molto spesso fanno uso di codice nativo (anziché di byte code), rendendo la piattaforma .NET non più autosufficiente. Conclusione: non ritengo che programmare in byte code sia conveniente per chi produce shareware. Al contrario, un applicativo in VB6 può essere crackato soltanto da esperti e in nessun caso è possibile ottenere i sorgenti. Il byte code, quindi, non è adatto a fini commerciali. Molti programmatori VB6 non lo hanno mai preso in considerazione, altrimenti sarebbero passati a Java già molto tempo fa.

.....

L'unico motivo che mi potrebbe far pensare alla migrazione a VB.NET è il timore che, dopo Windows Vista, Microsoft possa abbandonare strategicamente la tecnologia COM a 32 bit. Spero davvero che ciò non accada perché ciò significherebbe costringere la gente a buttare via tutto il software attualmente in commercio. Insomma, VB6 è senz'altro lo strumento che utilizzerò nei prossimi 4-5 anni dato che i runtime di base saranno ancora presenti in Windows Vista (lo stesso Microsoft Anti-Spyware è stato progettato in VB6), dopodiché potrò considerare l'ipotesi di effettuare la migrazione.

.....

La richiesta che vorrei rivolgerti è questa: VB6 non è morto. E non è neanche obsoleto. Molti programmatori si g uadagnano da vivere con VB6 e vorrebbero che tu ti occupassi ancora di questo strumento. Come hai reso il tuo MsgHookX nuovamente disponibile online, così dovresti fare con tutta la tua produzione che riguarda VB6. Anzi, dovresti creare ancora per l'ambiente VB6. Infine, se puoi, riferisci alla Microsoft che esiste ancora un esercito di programmatori VB6 che sarebbe pronto ad acquistare una nuova versione unmanaged del suo strumento di sviluppo. Dal punto di vista commerciale, sarebbe senz'altro una mossa vincente!



Io trovo che l’opinione di Pasquale sia fondata, nel senso che se io facessi il suo lavoro (vendere shareware) potrei avere dei problemi a passare armi e bagagli a .NET. Pero’ ci sono alcune considerazioni da fare.

*) Le dimensioni del runtime: La stesso problema se lo ponevano i programmatori shareware 10 anni fa, quando confrontavano il runtime di VB6 con i piccoli eseguibili scritti in C. Ma allora c'è da chiedersi: perchè gli shareware-isti che lavorano in VB hanno avuto spesso più successo di quelli che lavorano in altri linguaggi come C ? La risposta, a mio avviso, è che scrivere un programma in VB richiedeva una frazione del tempo necessario a scriverlo in C quindi a parità di impegno è possibile creare programmi più potenti e ricchi di funzioni. (solo il Delphi può competere con VB quanto a produttività ) Forse non altrettanto veloci di quelli scritti in C, ma sufficientemente veloci per la maggior parte dei compiti.

Rapportiamo questa esperienza ad oggi: i 20M circa del framework sono circa 15 volte più grandi del runtime di VB6, pero' è anche vero che ADSL è almeno 15 volte più veloce del dialup, quindi la proporzione regge. Anzi, se i vostri clienti hanno la fibra ottica, neanche se ne accorgono. Insomma, chi tanti anni fa ha fatto la scelta di passare a VB infischiandosene delle dimensioni del runtime ha avuto ragione. Secondo me, lo stesso accade a chi oggi decide di passare a VB.NET

Certo, non tutti gli utenti hanno la ADSL, ma il ragionamento che farei io se fossi un autore shareware è: quanto mi interessano davvero questi utenti? i clienti migliori per il software e per i servizi sono le aziende e il power-user, e quelli l'ADSL ce l'hanno sempre. Se qualcuno non ha i 20€ al mese per pagare la connettività, difficilmente pagherebbe per i miei programmi. Gli unici che non rientrano in questo mio ragionamento sono le aziende e gli utenti che spenderebbero volentieri questi soldi, ma purtroppo non sono serviti da ADSL. Pero' oggettivamente si tratta di casi che diventeranno sempre più rari, e penso che in 2-3 anni saranno impossibili da trovare. (A parte il fatto che uno si puo' collegare a internet anche con una scheda UMTS...)

Allora, la domanda da porsi è: se sono un autore di software (shareware o non), vale la pena davvero continuare ad usare uno strumento che era eccezionale 10 anni fa ma adesso è decisamente superato? In questi anni io credo di avere dimostrato di saper fare davvero di tutto con il "vecchio Visual Basic", eppure oggi quando devo tornare a scrivere codice con VB6 mi sento un impedito. Non si tratta solo del linguaggio, ma anche dell'IDE e degli strumenti a corredo. Dopo pochi mesi con il VB.NET ero già molto più produttivo che in VB6 (che avevo usato per 10 anni). Oggi che conosco bene il .NET Framework credo di essere, mediamente, almeno tre volte più veloce. Ovvero scrivo un programma in un terzo del tempo che ci mettevo prima. Io vendo programmi principalmente ad aziende, ma anche se facessi shareware mi porre la stessa domanda: vale la pena rinunciare a questa enorme produttività per raggiungere qualche utente in più, che probabilmente non comprerebbe comunque il mio software?

*) Anche se il .NET Framework è ben pesante, non è strettamente necessario distribuirlo tutto con le proprie applicazioni. In teoria una applicazione WinForm ha bisogno di circa un terzo dell'intero framework. Anche se non mi sono mai interessato più di tanto alla questione, ho letto che ci sono dei programmi che sono in grado di comprire un eseguibile .NET e tutte e sole le librerie che utilizza , senza cioè richiedere una installazione completa del framework. Se le dimensioni del runtime fossero davvero un problema, proverei a fare qualche ricerca più approfondita su questi prodotti.

*) come lo stesso Pasquale fa notare, lo shareware si puo' diffondere anche con mezzi che non siano Internet, ad es. sui CD allegati alle riviste. Pero' è un dato di fatto che ci sono sempre meno riviste che allegano i CD, proprio perchè la maggior parte degli utenti ha una linea veloce e preferisce scaricare dalla Rete per essere sicuri di avere la versione più recente. Ad esempio, Microsoft Press, Mondadori e altri publisher importanti non allegano più i CD ai propri libri (a meno che il contenuto non superi i 30-40M) e loro prendono queste decisioni solo dopo analisi di mercato fatte per bene.

*) se fossi un programmatore VB6, a rendere ancora più semplice e più netta la mia decisione di passare a .NET c'è la considerazione che tutto quello che scrivo oggi con VB6 dovrà forzatamente essere buttato via tra qualche anno, vuoi perchè Microsoft non supporterà più VB, vuoi perchè i programmi e i controlli ActiveX potrebbero non funzionare bene con le prossime versioni di Windows. E' giusto fare pressione su Microsoft per evitare che cio' avvenga, ma le probabilità di fare rimangiare le loro decisioni sono prossime allo zero. Quindi è un piccolo "suicidio professionale" puntare tutto su questa speranza e nel frattempo fare finta che il mondo sia quello di dieci anni fa. Se consigliassi a qualcuno di continuare a scrivere codice VB6 sarei un vero incosciente.

*) Il discorso sul byte code: E' innegabile che un programma che possa essere decompilato facilmente pone dei seri problemi. Purtroppo .NET non offre una netta risposta a questo problema, ma solo mezze soluzioni (tipo installare sul server come servizio o sito asp.net). Quando Microsoft lanciò le prime beta di .NET cinque anni fa, questo aspetto mi sembrava davvero fondamentale, in grado addirittura di rallentare la diffusione del nuovo linguaggio.

Le cose sono andate diversamente, per fortuna. C'è da tenere presente che il problema del reverse-engineering è molto sentito da tutti i programmatori, non solo quelli che vendono shareware. Se uno mette sul mercato uno shareware e qualcuno ne fa il reverse engineering, è molto facile dimostrare che il nuovo programma è un clone. Basta decompilarli entrambi e mostrare a tutto il mondo il risultato, sottolineando che il proprio programma è stato messo sul mercato prima del clone. Certo non è efficace quanto una azione legale (che pure è possibile, ma costosa) ma nel mondo di Internet è sufficiente per perdere la reputazione. Certo, uno puo' fare il reverse engineering e poi modificare il codice, ma è facile mettere delle "trappole" in giro per il sorgente, ossia delle istruzioni che non fanno nulla e che chi ha clonato il programma non saprebbe giustificare. Per togliere queste trappole uno si deve studiare meglio tutto il sorgente, ma allora il lavoro del copiatore si complica.

A parte queste considerazioni, c'è da sottolineare il fatto che questo problema è sempre esistito nel mondo dello sviluppo software. Molti linguaggi che in passato hanno avuto successo erano dei byte-code decompilabili: ad esempio dBASE, il Clipper (spacciato come compilatore), Java, e lo stesso Visual Basic fino alla versione 4.0. E anche se il linguaggio era compilato, resta il fatto che i tracciati dei record e le tabelle dei database - ovvero un aspetto fondamentale delle applicazioni gestionali - sono sempre stati visibili e interpretabili. Se vuoi capire come funziona un gestionale, la prima cosa da fare è vedere come sono strutturate le sue tabelle del DB.

Insomma, non sto dicendo che il problema non esiste: sto dicendo che è meno serio di quello che molti programmatori credono.


Termino il post rispondendo direttamente a Pasquale: hai ragione a dire che VB6 non è morto. Ci sono moltissimi programmatori che ancora lo usano e il mio libro su VB6 continua a vendere abbastanza bene, addirittura meglio di alcuni libri su VB.NET (non dei miei libri su VB.NET, per fortuna! :-) )

Pero' hai (molto) torto a dire che NON è obsoleto. Non si tratta di "rinnegare" il passato e soprattutto non dobbiamo parlare di VB come di un cagnolino a cui ci siamo affezionati, che non si vuole rottamare per ragioni sentimentali (ho letto anche interventi di questo tenore sui vari forum....). Quando è nato era eccezionale e per anni io ho campato (e bene!) con questo prodotto, ma se lo confronti con VB.NET (e con l'IDE di Visual Studio 2005) ti accorgi che il Visual Basic 6 è davvero morto e sepolto.

Ecco perchè non ho firmato alcuna petizione pro-VB6 e perchè sto facendo di tutto per convincere gli sviluppatori VB6 a passare il prima possibile a .NET: quando cominci a lavorare con la programmazione ad oggetti "vera", con gli attributi e reflection, i controlli di Windows Forms, le applicazioni ASP.NET e tutto il resto, il Visual Basic 6 ti sembra tanto ma tanto lontano.

NOTA: anche se non sviluppo più sotto VB6, forse cercando sul mio disco rigido troverei qualche cosa interessante che non ho ancora pubblicato. In tal caso sarà un piacere metterla a disposizione di tutti su questo sito. L'ho fatto per anni e posso continuare a farlo anche ora...

6/12/2006 8:07:06 PM (GMT Daylight Time, UTC+01:00) #  | Comments [0] | 
 
Feed di Blog2theMax
RSS 2.0 | Atom 0.2
Cerca nel blog
Archivio
<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910
Categorie

Powered by: newtelligence dasBlog 1.7.5016.2

 ©2004-2005 Code Architects S.r.l. - All rights reserved  Sign In