Considerazioni sugli obfuscator

Questo post di Ivan Medvedev ribadisce sinteticamente quanto ho scritto nel mio libro nel capitolo dedicato al Reverse Engineering.
Un’osservazione interessante è quella relativa agli strumenti “pseudo zero impact”, anche se effettivamente non capisco quale sia il valore aggiunto del mantenere la dimensione dell’assembly originale (prima dell’obfuscation) se poi, dal punto di vista dello strong name, è comunque necessario ri-firmare l’assembly. Al limite, usare un obfuscator che riduce le dimensioni (usando dei nomi molto brevi per tipi e membri) incremente in modo molto marginale le prestazioni (e riduce i tempi di download per un assembly scaricato da Internet, fenomeno che sembra sarà sempre più spinto da Microsoft con le prossime versioni del Framework).

C# Builder di Borland

Sto approfittando di questi ultimi giorni “tranquilli” per smaltire l’elenco di “cose da fare/studiare/provare”, e questa sera è stato il turno di C# Builder. Ho scaricato la personal edition, che è gratuita per usi non commerciali.
Bene, prima di entrare nei dettagli, la cosa che colpisce è il setup. Semplice ed essenziale, è anche veloce. L’unico prerequisito (se avete Windwos 2003) è il .NET SDK 1.1, la cui installazione è molto più lenta del prodotto di Borland. C’è da dire che la versione Enterprise è molto più grande e completa, quindi le mie prove sono in qualche modo limitate all’ambiente di sviluppo “core”.
Ingenuamente e timorosamente, ho installato C# Builder su una macchina VmWare, ma credo sia poco sensato. Se si ha già Visual Studio .NET 2003, o per lo meno il .NET SDK 1.1, l’installazione di C# Builder è leggera. Sono quindi passato sulla macchina “reale”, così anche le prestazioni sono confrontabili.
Ovviamente il termine di paragone è Visual Studio: cominciamo col dire che VS.NET dà un’idea immediata di “maestosità”, non fosse altro che per i tempi di caricamento. L’ambiente è quello “solito” di Borland, veloce e intuitivo.
Pregi:

  • il pulsante per la registrazione di Macro in posizione strategica (in basso a sx dove c’è l’editor)
  • l’intuitività dell’interfaccia utente (direi migliore di VS.NET)
  • il menu per esportare un progetto in VS.NET (avete capito bene! si può anche importare, ma è l’export la cosa forte!)
  • Il Code Insight (antesignano di Intellisense, è Microsoft che l’ha copiato da Borland!) presenta tutti i prototipi di un metodo in overload, ma non mostra la documentazione di metodi e parametri (ma la lista con tutti i prototipi è molto comoda, la vorrei anche in VS.NET)
  • Highlight dei blocchi: meglio che in VS.NET
  • Editor: attivabile visualizzazione di tab e spazi (se c’è in VS.NET non l’ho mai trovata!)
  • Gestione dei layout di desktop (come per tutti gli IDE Borland) – manca veramente in VS.NET
  • Può sviluppare applicazioni ASP.NET per Cassini

Difetti:

  • Non funziona Code Insight sui delegate
  • Mancano alcune funzioni (come un object browser), ma probabilmente ci sono su versioni “commerciali” di C# Builder
  • Credo tanti altri, ma ho solo fatto un paio di programmini di prova
  • L’help esterno – ormai mi sono assuefatto al fatto di averlo interno a VS.NET!
  • L’help di Borland – non è all’altezza di quello Microsoft (vale per l’IDE, le classi .NET hanno la documentazione di Microsoft, la stessa che si trova anche su MSDN e VS.NET)

Alla fine non so bene cosa pensare. Sicuramente non regge il confronto con Visual Studio .NET (che non è solo un ambiente per C#, ricordiamolo), ma per sviluppare applicazioni Console, Windows Forms, ASP.NET e Web Service in .NET con C#… c’è il necessario, e c’è un ambiente RAD completo e (nella versione personal) gratuito.

Sicuramente vale la pena provarlo, come impegno di risorse è meno affamato di Visual Studio .NET, ma non di molto.

Interruzione di un thread con Thread.Abort

Oggi ho ultimato una delle presentazioni che farò a WPC 2003 e che tratta di programmazione multithread; questo è il motivo di alcuni dei nuovi errata corrige del libro sul CLR di questi giorni.
L’ultimo (spero) della serie riguarda l’interruzione di un thread con Thread.Abort. Ancora una volta, riporto la nota integrale perchè interessante di per sé.

Pagina 130, sezione “Manipolazione di thread”


Nel secondo paragrafo scrivo “Per arrestare definitivamente un thread si può chiamare il metodo Abort: […] questo è un modo piuttosto sicuro per forzare la fine di un thread dall’esterno […]”. Quest’affermazione è imprecisa. In effetti, anche se l’esecuzione di Abort provoca l’invio di un’eccezione “asincrona” nel thread oggetto della richiesta, tale eccezione può verificarsi in alcuni punti critici.



  • All’interno di un costruttore statico di un tipo: è il caso peggiore, perché da quel momento in avanti quel tipo, all’interno dell’Application Domain, diventa inutilizzabile; nelle versioni attuali del Framework (.NET 1.0 e 1.1) non esiste un modo per “ritentare” l’esecuzione del costruttore statico; ogni successivo accesso al tipo che ha subito questo problema genera un’eccezione TypeInitializationException. Notare che la chiamata di un costruttore statico è un evento “indipendente” dalla volontà del programmatore, perché avviene la prima volta che un tipo viene referenziato in un Application Domain, e ciò può avvenire da parte di un thread che, apparentemente, non usa tale tipo, perché lo fa in modo indiretto (attraverso la chiamata a un metodo di un tipo già usato, il cui codice però usa il tipo non inizializzato) anziché diretto.
  • All’interno di un blocco catch o di un blocco finally: la parte rimanente del blocco catch/finally non viene eseguita, e questo può portare a situazioni di inconsistenza all’interno di alcuni oggetti condivisi da altri thread nello stesso Application Domain (o da altri Application Domain se esposti con Remoting); si pensi, per esempio, all’effetto di un Abort su un thread che è appena entrato in un blocco finally e sta per chiamare il metodo per rilasciare un lock: il danno è relativo, perché all’uscita del thread il blocco è comunque rilasciato dal CLR o dal sistema operativo, ma il solo differimento di quest’operazione può essere problematico, per non parlare della possibilità che sia stata interrotta una transazione che lascia una struttura dati in uno stato inconsistente.
  • Durante l’esecuzione di codice unmanaged: se è stata fatta una chiamata attraverso COM Interop o P/Invoke, il codice nativo non reagisce allo stimolo di ThreadAbortException; tale eccezione sarà invocata non appena il thread non tornerà a del codice managed, ma fino ad allora non ci sarà risposta.

Da queste considerazioni ne deriva che l’interruzione di un thread è da considerarsi sicura solo in due casi:



  • Chiamando Thread.CurrentThread.Abort(): interrompendo il thread corrente non si corrono rischi di sorta, perché in effetti è come avere un’eccezione sincrona, che non interrompe zone di codice arbitrarie.
  • A seguito di AppDomain.Unload: se anche restano dei dati inconsistenti nell’Application Domain, questi saranno scartati perché esso è in fase di chiusura.

È possibile che future versioni del CLR possano avere delle contromisure per evitare i problemi di interruzione in un costruttore statico e in un blocco catch o finally. Al momento, però, un’interruzione di un thread diverso da quello corrente con Thread.Abort va considerata attentamente per gli effetti collaterali che può provocare. È in qualche modo controllabile, ma bisogna avere sotto controllo molti aspetti del codice eseguito nel thread che si vuole interrompere.


Per ulteriori dettagli è utile leggere questi post di Chris Brumme: http://blogs.gotdotnet.com/cbrumme/PermaLink.aspx/857c3cc9-97c1-4b49-9c46-cd99c39c2ff9 e http://blogs.gotdotnet.com/cbrumme/PermaLink.aspx/68bb6af0-d15f-44fd-b6a7-41926c415cc4.

Delegate anonimi

Don Box ha precisato in

questo post che i delegate anonimi hanno già qualche differenza rispetto a

quanto annunciato su un documento presente su GotDotNet.
In particolare, la cosa per me più interessante è che finalmente è possibile

sostituire questo codice


public MyForm() {
    listBox = new ListBox(...);
    textBox = new TextBox(...);
    button = new Button(...);
    button.Click += new EventHandler(AddClick);
}

con questo:


public MyForm() {
    listBox = new ListBox(...);
    textBox = new TextBox(...);
    button = new Button(...);
    button.Click += AddClick;
}

E’ dalla prima beta di C# che avrei voluto questa sintassi!! Ce n’è voluto!!
Chiedo scusa a Don Box per aver copiato brutalmente il suo codice (Don Box, forgive

me for the code theft!)

Perché chiamare EndInvoke

Ho appena aggiunto una precisazione negli errata corrige del mio libro sul CLR. La riporto interamente perché è di per sé una cosa interessante.
Pagina 152, nota 9 a piè di pagina su fire-and-forget
Il motivo principale per cui è necessario chiamare EndInvoke è dato dal fatto che in tale funzione potrebbe essere chiamato il metodo Dispose di alcuni oggetti; questo, di per sé, sarebbe solo un problema di prestazioni (ci penserebbe il Garbage Collector), ma la parte dolorosa è che in alcuni casi l’ordine di Dispose potrebbe essere determinante per la corretta chiusura di un’operazione. In tal caso, lasciare al Garbage Collector l’onere di chiamare i finalizzatori non garantirebbe l’ordine di esecuzione. Dunque, il fire-and-forget è da evitare, con l’unica eccezione di Control.BeginInvoke, per cui è confermato che non esistono effetti collaterali nel “dimenticare” la chiamata di EndInvoke.
Per ulteriori dettagli è utile leggere questo post di Chris Brumme.