StackTrace, Assembly e .NET
Oggi io e Cristian abbiamo
perso quasi un'ora a rincorrere un bug che si presentava in un'applicazione
che, compilata in debug funzionava benissimo, mentre in release si schiantava
all'avvio. Bello eh?! :-)
Alla fine, dopo aver tribulato per parecchio tempo, abbiamo scoperto una cosa a
dir poco singolare. Non voglio parlare di bug, perchè non ne sono ancora
sicuro, però di effetto collaterale :-) di qualcosa di strano sì ...
Guardate questo codice (scaricabile per intero
da qui):
using System;
using System.Reflection;
using System.Diagnostics;
namespace ClassLibraryTest
{
public class TestClass
{
public String ExecutingAssemblyName(Boolean showProblem)
{
if (showProblem)
return(GetExecutingAssembly(2).FullName);
else
return(GetExecutingAssemblyOk(2).FullName);
}
private System.Reflection.Assembly GetExecutingAssembly(Int32 stackWalkLevel)
{
// Ottengo lo stack
StackTrace stack = new StackTrace(stackWalkLevel, false);
// Ottengo il frame
StackFrame frame = stack.GetFrame(0);
// Ottengo il nome del metodo
MethodBase method = frame.GetMethod();
// Leggo e restituisco l'assembly che contiene il chiamante
return(method.DeclaringType.Assembly);
}
private System.Reflection.Assembly GetExecutingAssemblyOk(Int32 stackWalkLevel)
{
// Ottengo lo stack
StackTrace stack = new StackTrace(stackWalkLevel, false);
// Ottengo il frame
StackFrame frame = stack.GetFrame(0);
// Ottengo il nome del metodo
MethodBase method = frame.GetMethod();
// Leggo e restituisco l'assembly che contiene il chiamante
return(Assembly.Load(method.DeclaringType.Assembly.FullName));
}
}
}
Provate a chiamare il metodo ExecutingAssemblyName da una classe di un progetto
ASP.NET, perchè negli altri casi non succede.
Ora se siete in debug il
metodo GetExecutingAssembly funziona correttamente. Se passate in Release,
smette di funzionare, in quanto la lettura della proprietà FullName restituisce
il fullname di System.Web, anzichè quello corretto.
Lo so sembra assurdo ...
per questo allego il progetto. Così potete provare anche voi e dirmi se: 1)
succede anche a voi, io ho provato su 2 macchine diverse e succede 2) se vi
viene in mente il perchè (Marco?! Hai delle idee in merito?)
La cosa è molto curiosa. Purtroppo FullName ha una implementazione riservata:
[MethodImpl(MethodImplOptions.InternalCall)]
private extern string GetFullName();
come si vede con Reflector. Chissà che succede lì dentro :-) ...
Cambiando l'istruzione:
return(method.DeclaringType.Assembly);
con:
return(Assembly.Load(method.DeclaringType.Assembly.FullName));
che semplicemente ricarica l'assembly, che poi tanto è
già in memoria nell'AppDomain corrente, tutto funziona regolarmente. Sono
abbastanza propenso a pensare che il problema si in FullName perchè se lo chiamo
prima di restituire l'assembly, funziona, viceversa se lo chiamo fuori dal mio
metodo GetExecutingAssembly smette di funzionare.
Bello vero?! Un'ora di vita persa per questa ragione :-) !!!