Primer dia Tech Days 2008

banner

Hoy fué un día de los que empiezan mal… Me levanto con una contractura en el cuello, miro el reloj … «weno, voy sobrado para llegar al Hands-On lab de WPF…» (11:00), subo al coche introduzco la dirección que Google Maps me había dado para el Palacio Municipal de Congresos Campo de las Naciones … tardo 10 min en coche, genial… llego a un recinto ferial y le pregunto al de seguridad y me dice … «Eso chaval… esta en la otra punta de Madrid» … WTF!! .. prosigue «habeis venido muchos esta mañana, vienes por una conferencia de Microsoft, verdad?» … Pues si, Google Maps me ha colado un gol digno del Fail Blog :

googlemapsfail 

Muy amablemente, el guardia de seguridad me da la dirección correcta, efectivamente, el Palacio Municipal de Congresos Campo de las Naciones esta a unos 24km de donde marca Google Maps. A lo que mi GPS Navman, que me tiene contento, decide que es mejor atravesar Madrid por el centro… pero centro de verdad, he pasado al lado de la boca de metro de Gran Via xD Como consecuencia de esto, no llego al Hands-On Lab de WPF :(.

Llego por fin, al palacio en cuestión y ante la imposibilidad de encontrar sitio para aparcar… aparco en un sitio semi-recóndito pero con una bien visible señal que lo prohibía… reconozco que después de como he arrancado el día esto ha sido tentar mucho a la suerte jejeje. Un poco apurado llego al Hands-On Lab de WCF… y no hay plazas, aunque lo planifiqué en mi agenda con antelación sin problemas 🙁 Me dicen que hay que apuntarse in-situ a los Hands-On Lab, así que consigo apuntarme al de TDD 😛

Y bueno, a partir de aquí la cosa va a mejor 😀 La charla Evolución de la plataforma de Acceso a Datos ha sido muy interesante e instructiva, introduciendo como LINQ junto sus variantes, y el Entity Framework van a cambiar la forma de ver las capas de acceso a datos de nuestras aplicaciones. Cada día me gusta más LINQ.

Después me tocaba un Hands-On Lab de media hora sobre TDD… donde se ha explicado que es y hemos hecho un Debug.Assert… tal cual, y bueno, decir «hemos» es mucho decir, casi diria que solo lo he hecho yo y porque he sacado mi propio portátil (sinceramente esperando otras cosas además del Assert :D) porque los que había allí… aparte de tener el teclado en otro idioma iban leeeeeeeeentos…, el PIII de mi casa va más rápido 😛 Lo bueno es que he podido apuntarme directamente a los de mañana 😀

Kit-Kat para comer, una caja de cartón esconde un sandwich de máquina espéndedora, un bocadillo de tortilla, una botella de agua, un yogurt líquido, una manzana y un … Toblerone!! 😀

Pin pam siguiente charla, LINQ a fondo, el maestro Octavio Hernández metiendose a fondo con los principios de LINQ, ha estado bastante interesante, ha intentado no solaparse con los ejemplos que ya se había explicado en la charla de Evolución de la plataforma de Acceso a Datos y se ha metido más con los temas de interfaces IEnumerable e IQueryable en los que se basan los modelos para trabajar en local y en remoto respectivamente.

Siguiente charla, Evolución de la plataforma de servicios… a la que llegué un poco tarde … interesante también, WWF, WCF .. etc..

Y como colofón, Windows Server 2008 para desarrolladores, renombrada a Design For Operations, donde Unai Zorrilla e Ivan Gonzalez de Plain Concepts han estado bastante resueltos en una charla sobre como los desarrolladores podemos ayudar a que el trabajo de los ITPro sea más llevadero y que nuestras aplicaciones no se conviertan en cajas negras, una charla bastante entretenida y que han llevado con bastante desparpajo.

De cada una de las charlas, se podrían expandir en horas y horas, pero a mi forma de ver son para dar una visión global de las posiblidades de las nuevas tecnologías Microsoft y por lo general estoy bastante contento 😛

Unas cuantas fotos para el recuerdo.

 

Primer dia Tech Days 2008 | vtortola.NET

Advertised shortcuts, cosas que me cabrean…

En mi anterior entrada hablaba sobre como crear enlaces en SendTo y preparar nuestra aplicación para recibir los nombres de los archivos seleccionados. He de confesar, que desde que uso XP x64 y VS2008 tengo varios problemas con los instaladores MSI que genero con VS, asi que estoy esperando a que pasen los TechDays para ponerme a instalar XP normal …, el caso es que elaborando dicho artículo no pude probar correctamente la parte de instalación ya que, literalmente, aparecia un error de acceso a memoria un poco chungo cuando le daba a «Install» desde el VS, poco después me dí cuenta de que el error no aparece al ejecutar el .msi directamente y me dí cuenta que la manera de generar el enlace en la carpeta SendTo no funciona!!

Parece ser que los enlaces que generan los MSI son advertised shortcuts, un tipo especial de enlace que ejecuta Windows Installer antes de llamar al enlace, con lo que no funcionan al colocarlos en SendTo, pues no llaman directamente a la aplicación… por lo que no hay paso de parámetros. Asi que me puse manos a la obra a buscar que tenia que hacer en VS para deshabilitar esta «funcionalidad» y usar enlaces de toda la vida… y no, no hay forma. Pregunté en los foros de MSDN pero parece no haber respuesta…

Aqui llega el momento en el que uno se cabrea, porque resulta que en la creación del paquete de instalación puedes especificar que quieres enviar a la carpeta SendTo como explicaba en el artículo, carpeta que tiene un único proposito… pero… no vale para nada porque estos enlaces no funcionan alli 😛

Soluciones hay varias, pero no bonitas, una… más engorrosa pero también más compacta es editar el .msi con una herramienta llamada Orcas, la otra… ejecutar el instalador añadiendo un parámetro:

msiexec /i MiInstalador.msi DISABLEADVTSHORTCUTS=1

ó

MiInstalador.exe DISABLEADVTSHORTCUTS=1

En una página proponen alguna más. Yo he probado (ahora si :D) la segunda forma y funciona. Lo peor es que no es nuevo, pasa de siempre y todavía sigue sin haber una opcion en VS para modificar este comportamiento.

En fin, a parte del cabreo y tener que editar los posts… me flagelaré por haber publicado algo sin probar 😛

 

Advertised shortcuts, cosas que me cabrean… | vtortola.NET

Enviar a … mi aplicación

image

Si desarrollamos una aplicación que manipule archivos puede ser útil disponer en el menu contextual «Enviar a» (SendTo) un enlace que permita enviar los archivos seleccionados directamente a nuestra aplicación trabajar con ellos. Esta es la típica cosita facilona y que lleva 5 minutos pero que resulta extremandamente útil cuando lo que desarrollamos es una herramienta de uso cotidiano.

Los elementos SendTo se encuentran en la carpeta %homepath/SendTo% de cada usuario, pero explicaré como añadirlo directamente con el instalador de nuestra aplicación.

Lo primero, es preparar nuestra aplicación WinForm para recibir una lista de argumentos al igual que puede hacer una aplicación de consola. Primero indicamos que constructor de nuestro formulario recibirá dicha lista y como trabajará con ellos, en este caso simplemente los añado a un ListView:

private String[] args;
public Form1(String[] args)
{
    InitializeComponent();
    this.args = args;
}
 
private void Form1_Shown(object sender, EventArgs e)
{
    if (this.args != null)
    {
        foreach (String s in this.args)
        {
            listView1.Items.Add(
                new ListViewItem(new String[]{Path.GetFileName(s),s}));
        }
    }
}

Y luego modificamos el punto de entrada de la aplicación para que se los pase:

[STAThread]
static void Main(String[] args)
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1(args));
}

Para hacer que el instalador de nuestra aplicación añada un enlace en SendTo:

  1. Vamos al Setup Project.
  2. Vamos al File System Editor.
  3. Botón derecho sobre File System on Target Machine.
  4. Add Special Folder -> User’s Send To Menu

Una vez dentro de esta nueva carpeta, hacemos un Create New Shortcut, y seleccionamos la salida activa de nuestro proyecto, le damos un nombre y eso será lo que aparezca en nuestro «Enviar a» (ó SendTo 😛 )

image

Probando… si selecciono una ristra de iconos de mi escritorio y los mando a mi aplicación….

image

Enviar a … mi aplicación | vtortola.NET

Evento: Threading y Paradigmas de Programacion Concurrente.

Este Jueves 21-02-2008 hay una charla en las Oficinas de Microsoft Ibérica acerca de Multithreading y Paradigmas de la programación concurrente, personalmente me parece un tema muy interesante y, dicho de paso, creo que desconocido para mucha gente.

Llevo un tiempo intentando reunir fuerzas para empezar una serie de artículos sobre los modelos de memoria, sincronización, APM, multithreading en .NET y temas similares… a ver si con esta charla cojo fuerza y me pongo manos a la obra 😛

El anuncio del evento reza lo siguiente :

Threading y Paradigmas de Programación Concurrente

Los buenos años han acabado. Con la tecnología actual ya no conseguimos hacer procesadores con más megahercios y la única salida para mejorar el rendimiento es utilizar varios procesadores simultáneamente. Conseguir esto no es gratis, ahora es responsabilidad de los programadores el hacer programas para aprovechar los avances de los procesadores del presente y futuro. Se trata del gran reto de nuestra industria para los próximos años.
En esta charla exploraremos el panorama actual de la programación concurrente, desde las aproximaciones más clásicas a los nuevos paradigmas de paralelismo implícito, más exóticos, que nos ofrece Redmond , así como los fundamentos necesarios para hacer que la programación concurrente deje de ser la pesadilla que era hasta este momento. Se tratarán los siguientes temas:

  • El reto de la programación concurrente : Tipos de aplicaciones y problemas
  • Modelo clásico de Threadings (La clase Thread, locks y otras primitivas de sincronismo)
  • Modelos de memoria (Heap y Stack, variables estáticas y ThreadStatic)
  • Estructuras de datos (Estructuras de datos inmutables, atomicidad y clase Interlocker)
  • Interface de usuario (WinForms y WPF)
  • Nuevos Modelos (Breve introducción a
    • Parallel Extensions (P-Linq)
    • C-Omega o Polyphonic C#
    • Robotics CCR (Concurrency and Coordination Runtime)
  • Recomendaciones generales y conclusión

Ponente:
Olmo del Corral

Fecha y lugar:
Jueves 21 de Febrero de 2008 de 19:00h a 21:00h en las oficinas de Microsoft Ibérica

Url de registro:
http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032368815&Culture=es-ES

vtortola.NET | Evento: Threading y Paradigmas de Programación Concurrente.

Servicios Windows con dependencias

Imagina que tu servicio necesita de otro servicio para cumplir su función, supongamos que el servicio ‘a’ necesita del servicio ‘b’. Si yo reinicio ‘b’ … ‘a’ es posible que pierda la conexión … aparezcan errores … e incluso pudiese darse el caso de quedar funcionando pero inconsistente, otro problema es que puede ser que al arrancar la máquina ‘a’ arranque más rápido e intente conectar a ‘b’ de forma infructuosa … con las consecuencias que ello acarreé (que no deberían ser muchas si has pensado en «cuando las cosas no salen como deberían» cuando lo desarrollaste).

Para solucionar este problema de forma elegante están las dependencias de servicio.

Si al crear ‘a’ decimos que depende de ‘b’ logramos este comportamiento:

  • si se reinicia ‘b’ se reinicia ‘a’.
  • si se detiene ‘b’ se detiene ‘a’.
  • pero si se inicia ‘b’ … y ‘a’ esta parado NO se inicia ‘a’ (ojo con esto 😛 ).
  • cuando arranque la máquina, iniciará ‘b’ y después ‘a’.

Es una pijada bastante sencillo de hacer y es muy útil.

Voy a agregar el servicio Message Queuing (MSMQ) como dependencia del servicio de instalable de prueba que cree el otro día, imaginando que no puede trabajar sin MSMQ.

Primero necesitamos saber el Nombre del servicio del que queremos depender, que aparece en primer lugar en la ventana de propiedades:

image

Después añadimos ese nombre (ó nombres si son varios) a la propiedad ServicesDependedOn de nuestro ServiceInstaller:

image

Y listo, al instalar nuestro servicio veremos que aparece MSMQ como dependencia:

image

Vemos, que MSMQ tiene también sus propias dependencias y el comportamiento antes citado se reproducirá en cadena.

Cosa curiosa… ¿Como añadir una dependencia a un servicio que ya esta instalado? Puuuess… lamentablemente hay que añadirla directamente sobre el registro de Windows (y digo lamentablemente porque no me gusta tocar el registro… y menos para esto 😛 ).  La clave a modificar esta en:

HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServices<Service name>

image

Ale espero que sea útil 😀

Servicios Windows con dependencias | vtortola.NET

Cifrando Strings con un password

Puede ser… que el cifrado del archivo de configuración de tu aplicación no se adecue a la solución que buscas… puede ser… que necesites cifrar cadenas concretas en un archivo… en una base de datos… mandar eMails cifrados a tu novia/mujer/amante…  lo que sea…

Estas dos funciones, basadas en el ejemplo de este artículo con algunas modificaciones, cifran y descifran un String con el algoritmo simétrico Rijndael y con una palabra clave que se pasa como SecureString (¿Por qué?). De esta forma, podemos cifrar/descifrar un texto/cadena mediante ese password.

Necesitamos estos namespaces adicionales:

using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;

La función de cifrado:

public static string EncryptString(String Text, SecureString Password)
{
  String encryptedString = null;
  RijndaelManaged cipher = new RijndaelManaged();
  IntPtr bstr = IntPtr.Zero;
  try
  {
    bstr = Marshal.SecureStringToBSTR(Password);
    Byte[] passBytes = System.Text.Encoding.Unicode.GetBytes(Text);
    Byte[] saltBytes = Encoding.ASCII.GetBytes(Password.Length.ToString());
    PasswordDeriveBytes secret = new PasswordDeriveBytes(Marshal.PtrToStringUni(bstr), saltBytes);
 
    using (ICryptoTransform encryptor = cipher.CreateEncryptor(secret.GetBytes(32), secret.GetBytes(16)))
    using (MemoryStream ms = new MemoryStream())
    using (CryptoStream cryptoStream = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
    {
      cryptoStream.Write(passBytes, 0, passBytes.Length);
      cryptoStream.FlushFinalBlock();
      encryptedString = Convert.ToBase64String(ms.ToArray());
    }
  }
  finally
  {
    if (IntPtr.Zero != bstr)
      Marshal.ZeroFreeBSTR(bstr);
  }
 
  return encryptedString;
}

La función de descifrado:

public static string DecryptString(String Text, SecureString Password)
{
  String decryptedString = null;
  RijndaelManaged cipher = new RijndaelManaged();
  IntPtr bstr = IntPtr.Zero;
  try
  {
    bstr = Marshal.SecureStringToBSTR(Password);
    Byte[] encryptedBytes = Convert.FromBase64String(Text);
    Byte[] saltBytes = Encoding.ASCII.GetBytes(Password.Length.ToString());
    PasswordDeriveBytes secret = new PasswordDeriveBytes(Marshal.PtrToStringUni(bstr), saltBytes);
 
    using (ICryptoTransform decryptor = cipher.CreateDecryptor(secret.GetBytes(32), secret.GetBytes(16)))
    using (MemoryStream memoryStream = new MemoryStream(encryptedBytes))
    using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
    {
      Byte[] PlainText = new Byte[encryptedBytes.Length];
      Int32 DecryptedCount = cryptoStream.Read(PlainText, 0, PlainText.Length);
      decryptedString = Encoding.Unicode.GetString(PlainText, 0, DecryptedCount);
    }
  }
  finally
  {
    if (IntPtr.Zero != bstr)
      Marshal.ZeroFreeBSTR(bstr);
  }
 
  return decryptedString;
}

Podemos probarlo en un pequeño programa:

static void Main(string[] args)
{
  String data = "## Prueba de cifrado ##";
  
  SecureString secureS = new SecureString();
  secureS.AppendChar('m');
  secureS.AppendChar('y');
  secureS.AppendChar('p');
  secureS.AppendChar('a');
  secureS.AppendChar('s');
  secureS.AppendChar('s');
 
  Console.WriteLine("     Plano: " + data); 
 
  String encryptedData = Cipher.EncryptString(data,secureS);
  
  Console.WriteLine("   Cifrado: " + encryptedData);
  
  Console.WriteLine("Descifrado: " + Cipher.DecryptString(encryptedData,secureS)); 
 
  Console.ReadKey(true);
  secureS.Dispose();
}

Resultado:

image

Cifrando Strings con un password | vtortola.NET

MSMQ: Mensajes atascados en las colas de Outgoing

Si con MSMQ 3.0 y Windows 2003 Server SP2 se te amontonan los mensajes en las colas de Outgoing (salida)… haciendo que tarden en entregarse ó incluso que lleguen a caducar allí… antes de liarse a patadas con nada… existen diversos motivos por los que puede suceder:

  • Los más básicos, problemas de conectividad, de resolución de nombres, de permisos, de acceso…. conviene repasar este FAQ.
  • Tienes activado el modo Hardened MSMQ.
  • Las colas tienen cuotas pequeñas y los journals están llenos.
  • La red tiene una conectividad cuestionable (alta latencia, bajo ancho de banda, packetloss, …etc…) y MSMQ falla a menudo en entregar los mensajes, lo cual hace que tenga que esperar 60 segundos para el reintento. Hasta el SP1 el tiempo de espera entre reintentos era de 5 segundos (algo razonable, pero 60?!… ), para cambiar este valor hay que añadir la clave WaitTime al registro de Windows en su sección para MSMQ.
  • Tienes una cantidad ingente de colas de salida y una conectividad cuestionable:
    • Si se usa una topología en estrella ó consideras más que suficiente el ancho de banda como para comunicarte con el resto de máquinas puedes aumentar la cantidad de hilos disponibles en el thread pool de MSMQ para agilizar el envio añadiendo la clave QMThreadNo al registro de Windows en su sección para MSMQ. De esta forma, aunque las transferencias sean lentas… se podrán paralelizar más de ellas.
    • Si no, puedes recurrir a comprimir el contenido de los mensajes (no incluido en MSMQ 3.0) mediante por ejemplo los algoritmos de compresión GZip ó Deflate.
MSMQ: Mensajes atascados en las colas de Outgoing | vtortola.NET

Servicios Windows instalables

gear Aquí va un pequeño How-to rápido de como se crea e instala un servicio Windows con .NET y Visual Studio 2005 dedicado a mi compañero Juanma Otero que a cambio me explicará como funcionan los EndPoints de SQL Server 😀

Cada vez que vayamos a crear un servicio Windows, podemos seguir estos pasos para crear lo que sería el contenedor y su capacidad de instalación, para luego más tranquilamente empezar a introducir la lógica y funcionalidad del negocio que le queramos dar.

 

1. Creando.

empezando Se utiliza un proyecto Windows Service, que digamos es un contenedor en el que tenemos que implementar las acciones básicas de un servicio como son Start, Stop, Pause, Restar… etc.. En un próximo artículo me meteré un poco más a fondo en el tema de la implementación , como usar el EventLog… etc.. etc..

 

 

 

2. Haciéndolo instalable.

InstallerClass Añadimos al proyecto una Installer Class desde el menu «Add New Item», que nos provee la funcionalidad de instalación. Hay dos componentes que no están en la paleta que nos hacen falta, aunque se puede hacer «a pelo» … siempre los añado…

 

 

 

 

 

componentes Añadimos a la paleta de componentes las clases ServiceInstaller y ServiceProcessInstaller . Añadimos estos componentes al diseñador de la clase instaladora y los configuramos. No hay mucho que configurar 😛

 

 

 

ServiceInstaller En el ServiceInstaller, configuramos el nombre que mostrará, la descripción, el nombre por el que invocaremos al servicio y el tipo incio. Ponemos que el «Parent» es la clase InstallerClass.

 

 

 

 

 

ServiceProcessInstaller En el ServiceProcessInstaller configuramos la cuenta de usuario con la que se ejecutará el servicio, por lo general LocalService que es una cuenta con pocos privilegios. Ponemos que el «Parent» es la clase instaladora.

 

 

 

 

3. El instalador.

Instalador Añadimos al a solución un proyecto «Setup Project» que nos provee de la funcionalidad de un asistente instalador.

 

 

 

 

 

addprojectoutput Dentro de este proyecto, vamos al «File System Editor» y en «Application Folder» le decimos «Add Project Output», seleccionamos «Primary Output», nuestro proyecto en el combo y configuracion (Active) (para que use la que nosotros usemos … Debug ó Release).

 

 

 

 

 

actionsoutput Ahora vamos al «Custom Actions Editor» y directamente sobre el elemento «Custom Actions» añadimos la salida del proyecto que hemos puesto en «Application Folder».

 

 

 

 

4. Instalando.

Install Construimos la solución y el proyecto de instalación, este último genera un paquete .msi y un .exe asociado, con eso ya se puede instalar por ahí 😀 pero para trabajar en desarrollo nada más comodo que botón derecho e Install 😉

 

 

 

Lo instalamos y vemos que ya aparece en nuestra consola de administración de servicios. Podemos iniciarlo, pararlo, reiniciarlo… etc…

mmc

5. Haciendo que se inicie automáticamente al instalarlo.

Si, aunque se configure que debe iniciarse automáticamente… eso será en el próximo reinicio de la máquina. Para que se inicie automáticamente la primera vez … nos suscribimos al evento AfterInstall del componente ServiceInstaller que añadimos a la clase instaladora y hacemos que el servicio arranque con ServiceController:

private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
  using (ServiceController svc = new ServiceController("MiServicio"))
    svc.Start();
}

Con esto una vez el servicio este instalado automáticamente se iniciará.

Ahora podemos ponernos manos a la obra en integrar nuestra lógica dentro del servicio. Yo por comodidad… creo un proyecto «Console Application» normal para desarrollo y primeras pruebas, cuando ya esta todo listo… integro los ensamblados en el servicio, pero bueno… «cada maestrillo tiene su librillo…» 🙂

vtortola.Net | Servicios Windows Instalables

Tratando con excepciones

Buff… cuanto tiempo desde mi último post, entre estudiar y la medio-gripe que arrastro desde hace unas semanas no tengo ganas tiempo de nada 😛

Un tema que siempre me ha parecido que se deja un poco olvidado a la hora de desarrollar una aplicación es el tema de las excepciones, su flujo, su registro y como afectan al diseño y modularidad de la aplicación.

Una excepción, es un error en el flujo lógico ideal de nuestra aplicación causado por una condición inusual. Ante un error de este tipo, debemos actuar para corregirlo y/ó recuperar un estado consistente ó registrarlo en algún lugar(archivo log, EventLog, ..etc..) donde el administrador pueda ver que ha sucedido y tomar medidas. Esto es algo bien sabido por todos, pero el problema viene cuando hay que decidir que hacer con las excepciones que no podemos solucionar y como registrar de forma descriptiva el problema para poder solventarlo.

El objetivo de capturar una excepción, es enfrentarse a una situación inesperada, por lo que debemos capturar las excepciones que podamos solucionar y las que no, dejarlas subir por la pila de llamadas hasta el punto donde se registren como un error no controlado. Por este motivo, esta ampliamente desaconsejado capturar de forma generalizada:

      try
      {
        // lógica...
      }
      catch (Exception)
      {
        // Todas las excepciones son capturadas..
      }

Sin embargo, hay situaciones donde es vital capturar la excepción aún cuando no se puede hacer nada para contrarrestarla, por ejemplo cuando usamos transacciones, es vital capturarla para deshacer las operaciones realizadas. En estos casos, una vez tomadas las acciones oportunas podemos re-lanzarla de nuevo:

      MessageQueueTransaction tran = new MessageQueueTransaction();
      try
      {
        tran.Begin();
 
        // Trabajar en el contexto de la transacción
 
        tran.Commit();
      }
      catch (Exception)
      {
        // Ha habido problemas, realizamos un rollback
        tran.Abort();
 
        // Re-lanzamos la excepción para que sea tratada
        // en capas superiores.
        throw;
      }
      finally
      {
        tran.Dispose();
      }

Antaño (VB6, ASP,..etc…) era necesario capturar la excepción allí donde ocurriese para poder registrar el punto donde sucedió, pero las excepciones de .NET Framework (que derivan todas de la clase Exception) contienen la propiedad StackTrace que registra la consecución de llamadas que han sucedido en la pila hasta toparse con dicha excepción. Sabiendo esto, no necesitamos poner bloques try-catch en cada una de nuestras funciones para saber el lugar exacto donde sucedió.

Cuando se vaya a re-lanzar una excepción es importante hacerlo simplemente con «throw;» ya que preserva la información del StackTraceNO como el siguiente ejemplo, ya que se perdería el  StackTrace original:

      catch (Exception ex)
      {
        // Ha habido problemas, realizamos un rollback
        tran.Abort();
 
        // MAL: Así perdemos el StackTrace original.
        // Sería más difícil encontrar el problema.
        throw ex;
      }

Cuando se quiera usar tipos de excepciones propios ó simplemente otro para dar mensajes de error más descriptivos y/ó completos, podemos usar la propiedad InnerException para poder mantener la información original del error:

catch (ArgumentNullException ae)
{
  throw new ArgumentException("El objeto no era válido", ae);
}

De esta forma, se mantiene la información del error original.

No se debe utilizar las excepciones como elementos de nuestra lógica y debemos evadirlas en la medida de lo posible ya que el uso de excepciones implica una penalización de rendimiento. Cuando digo evadirlas, me refiero a que si esperamos un error muy concreto… mejor procurar que el error no suceda, por ejemplo:

static Int32 Divide(Int32 a, Int32 b)
{
  Int32 result = 0;
  try
  {
    result = a / b;
  }
  catch (DivideByZeroException)
  {
    result = -1;
  }
  return result;
}

En este sencillo ejemplo, contemplamos la posibilidad de que la variable ‘b’ sea 0 y una excepción de tipo DivideByZeroException ocurra. Aunque se toman medidas para devolver un posible valor inocuo, se podría evitar fácilmente:

static Int32 Divide(Int32 a, Int32 b)
{
  Int32 result = 0;
  if (b == 0) return -1;
  result = a / b;
  return result;
}

El como registrar las excepciones no controladas es algo ya más dependiente de los requerimientos del proyecto ó conveniencia técnica, se puede usar desde un simple archivo de texto, el EventLog ó por ejemplo el Logging Application Block de Enterprise Library 3.1 (del que espero escribir en breve… si no las pincho antes xD).

Como esto se hace largo ya… en un próximo post escribiré algo sobre donde y porque capturar excepciones de forma que no afecte a la modularidad de nuestra aplicación.

Links:

Tratando con excepciones| vtortola.NET

La memoria en .NET, tipos de variables

Siguiendo con el el artículo anterior de la memoria en .NET donde explicaba como esta estructurada, sigo con las variables, que en .NET principalmente son de dos tipos:

  • Tipos por valor:
Tipo (alias) Bytes Rango
Char (char) 2 Caracteres
Bolean (bool) 4 True ó False
IntPtr ? Puntero nativo
DateTime (date) 8 1/1/0001 12:00:00 AM a 12/31/9999 11:59:59 PM
SByte (sbyte) 1 -128 a 127
Byte (byte) 1 0 a 255
Int16 (short) 2 -32768 a 32767
Int32 (int) 4 -2147483648 a 2147483647
UInt32 (uint) 4 0 a 4294967295
Int64 (long) 8 -9223372036854775808 a 9223372036854775807
Single (float) 4 -3.402823E+38 a 3.402823E+38
Double (double) 8 -1.79769313486232E+308 a 1.79769313486232E+308
Decimal (decimal) 16 -79228162514264337593543950335 a 79228162514264337593543950335
    • Se conoce su tamaño antes de su inicialización, el más grande tiene 16 Bytes.
    • Heredan de System.ValueType y ninguno puede ser extendido (sealed)(ni siquiera ValueType).
    • Se almacenan en el thread stack cuando son variables locales y expresiones intermedias, y en el managed heap en el resto de situaciones (variables globales ó/y estáticas).
    • Las estructuras (struct) y enumeraciones (enum) son tipos por valor también y responden al mismo comportamiento.
    • La memoria ocupada por estos tipos se elimina en cuanto están fuera de ámbito en caso de estar en el thread stack, y por medio del GC cuando estan en el managed heap (desapareciendo junto con la clase a la que está asociada). 

 

  • Tipos por referencia (objeto):
    • No se conoce su tamaño hasta después de su inicialización.
    • Todos los demás tipos de la BCL.
    • Heredan directamente de System.Object, y pueden ser extendidos a no ser que se especifique lo contrario (sealed).
    • Se almacenan siempre en el managed heap, y son accedidos desde el thread stack mediante una referencia.
    • Cuando declaramos una variable de tipo objeto, se crea una referencia en el thread stack, y al crear la instancia en el managed heap por medio del operador new, su dirección en memoria se asigna a la susodicha variable. De esta forma se puede acceder al objeto por medio de la indirección de la referencia.
    • La memoria ocupada por estos tipos la libera el GarbageCollector de forma no determinística.

Si, no me he equivocado, los tipos por valor no se almacenan siempre en el thread stack :D, como decía solo se almacenan ahí cuando son variables locales y expresiones intermedias.

Como decía en el artículo anterior, el thread stack es un espacio de almacenamiento exclusivo de su hilo de ejecución y no puede ser compartido, sin embargo… te pones a pensar y te das cuenta de que puedes compartir variables de tipo por valor entre threads… por lo que lo primero que te viene a la mente es que este realizando boxing/unboxing para ello… pero si precisamente una de las metas del performance es evitar esas dos operaciones … no tiene sentido!

Menos mal que tiene uno el .NET Reflector siempre a mano para salir de la penumbra xD

Vamos a ver como se comporta el CLR con una variable local y una variable externa de otra clase, ambas de tipo por valor:

class Program
{
  static void Main(string[] args)
  {
    MyClass exampleClass = new MyClass();
    exampleClass.globalInt = 6;
 
    Int32 localInt = 5;
 
    Console.WriteLine(localInt);
    Console.WriteLine(exampleClass.globalInt);
  }
}
 
class MyClass
{
  public Int32 globalInt;
}

localInt es un Int32 declarado localmente y debería estar en el thread stack, y MyClass.globalInt es un Int32 declarado como miembro de un tipo por referencia, por lo que debería estar en el managed heap… vamos a verlo. El método Main genera el siguiente código CIL (obviadas las instrucciones que no interesan ahora):

   1:      .locals init (
   2:          [0] class Test.MyClass exampleClass,
   3:          [1] int32 localInt)
   4:      L_0000: newobj instance void Test.MyClass::.ctor()
   5:      L_0005: stloc.0 
   6:      L_0006: ldloc.0 
   7:      L_0007: ldc.i4.6 
   8:      L_0008: stfld int32 Test.MyClass::globalInt
   9:      L_000d: ldc.i4.5 
  10:      L_000e: stloc.1 
  11:      L_000f: ldloc.1 
  12:      L_0010: call void [mscorlib]System.Console::WriteLine(int32)
  13:      L_0015: ldloc.0 
  14:      L_0016: ldfld int32 Test.MyClass::globalInt
  15:      L_001b: call void [mscorlib]System.Console::WriteLine(int32)
  16:      L_0020: ret 

Lo primero que uno mira es si aparecen las instruciones box/unbox para realizar boxing/unboxing, pero como vemos no aparecen, por lo cual, se esta accediendo y asignando a una variable de tipo por valor en el heap sin realizar ninguna de estas dos operaciones.

Voy a explicarlo brevemente:

  • Asignación del miembro de MyClass, líneas 7 y 8: carga en valor ‘6’ en pila y lo asigna mediante la instrucción stfld, que precisamente sirve para asignar valores a una variable contenida en un objeto.
  • Asignación de la variable local localInt, líneas 9 y 10: carga el valor ‘5’ en pila y lo asigna mediante la instrución stloc a la variable local en el puesto 1 (la 0 es Test.MyClass como se puede ver en las 3 primeras líneas).

Queda claro que el tratamiento es distinto y que stfld sirve para manipular tipos por valor asociados a una instancia en el managed heap, de hecho si lo piensas bien, una vez que has localizado la instancia en el managed heap por medio de la referencia … ¿porque hacer boxing/unboxing con ella? … 😛

Al igual pasa si lo hacemos con un campo estático, hay una instrucción especial para ello:

class Program
{
  static Int32 intTest;
 
  static void Main(string[] args)
  {
    intTest = 6;
    Console.WriteLine(intTest);
  }
}

CIL:

   1:      L_0000: ldc.i4.6 
   2:      L_0001: stsfld int32 Test.Program::intTest
   3:      L_0006: ldsfld int32 Test.Program::intTest
   4:      L_000b: call void [mscorlib]System.Console::WriteLine(int32)
   5:      L_0010: ret 

Como podemos ver utiliza otra instrución distinta, stsfld, para tratar con variables estáticas.

.NET Reflector da una orientación de que significa cada instrucción.

Así que vuelvo a repetir, los tipos por valor no se almacenan siempre en el thread stack como se viene diciendo en muchos sitios, solo se almacenan ahí cuando son variables locales y expresiones intermedias. El CIL tiene sus propias instruciones para acceder a tipos por valor cuando se encuentran en el managed heap, además del boxing/unboxing para almacenarlos allí directamente.

Próximo capítulo… boxing/unboxing a fondo.

 

Crossposting from vtortola.NET