std:cpp 2013

El proximo día 26 de noviembre en la Escuela Politécnica Superior de la Universidad Carlos III de Madrid tendrá lugar un evento sobre C++ para estudiantes y profesionales del sector.

Tendré el placer de participar en este evento dando una charla sobre programación paralela en C++ con Microsoft Parallel Pattern Library, en la que hablaré sobre la programación paralela en la versión de Task Parallel Library en C++.

Si estas interesado en asistir, la entrada es gratis, os dejo información sobre el organizador del evento.

Estimado colega,

Me gustaría presentarte la Jornada de C++: “using std::cpp 2013” organizada por el grupo de investigación ARCOS de la Universidad Carlos III de Madrid. Esta jornada nace como un foro de foro de intercambio de experiencias de uso del lenguaje C++, prestando especial atención al reciente estándar C++11 y los próximos C++14 y C++17.

¿A quien va dirigido using std::cpp 2013?

El evento va dirigido a desarrolladores profesionales que utilizan C++ como lenguaje de desarrollo de aplicaciones o software de infraestructura. También va dirigido a estudiantes de últimos años de carrera interesados en el uso de C++ como un lenguaje de programación para producir sistemas informáticos complejos con altas prestaciones.

¿Cuándo y dónde se celebra using std::cpp 2013?

Se celebrará el día 26 de noviembre de 2013 en Leganés, en la Escuela Politécnica Superior de la Universidad Carlos III de Madrid.

¿Cuál es el programa del evento?

Hemos preparado un intenso programa con presentaciones de destacados desarrolladores de empresas líderes en su sector (Indizen, TCP, BBVA, Telefónica, Digital, INDRA, Biicode, Microsoft, Programming Research Group). Puedes acceder al programa en http://www.arcos.inf.uc3m.es/~cpp-day/?page_id=13.

¿Hace falta registrarse?

La asistencia a using std::cpp es gratuita, pero necesitamos que te registres para facilitar la organización del evento. Puedes registrarte en http://www.arcos.inf.uc3m.es/~cpp-day/?page_id=2.

Caneles de información

Puedes seguirnos en

Twitter: https://twitter.com/usingstdcpp

Google Groups: //plus.google.com/u/0/communities/102726875548983454259

LinkedIn: http://www.linkedin.com/groups/Desarrolladores-C-España-2863690

Optimizaciones de JavaScript utilizadas en el proyecto de Prometheus

Introducción

Para los que no lo sepáis, próximamente se estrena la película Prometheus en Estados Unidos y desde Plain Concepts hemos desarrollado el training center del sitio web. Se puede acceder desde este enlace: http://www.projectprometheus.com/trainingcenter/. El proyecto ha estado financiado por Microsoft, más concretamente por el equipo de Internet Explorer, así que como página web que es, se ha desarrollado utilizando las últimas tecnologías web: HTML5 + CSS3.

Training center

El centro de entrenamiento es un sitio web donde los candidatos al proyecto Prometheus, de la empresa Weyland, puede probar su valía. El entrenamiento cuenta con 5 pruebas (juegos) que el recluta tiene que completar en un tiempo determinado. Una vez superadas las cinco pruebas el recluta puede formar parte de Weyland Industries. Los cinco juegos han sido desarrollados por gente de Plain Concepts:

Cada uno de los cuales ha desarrollado uno de los juegos del centro de entrenamiento. En mi caso he desarrollado el cubo de rubick en 2 y 3 dimensiones.

clip_image001

Aquí se puede ver una captura del juego en Internet Explorer llamado Prefrontal Cortex.

HTML5 / Javascript

Todos los juegos han sido desarrollados en HTML5 utilizando JavaScript para la parte de programación, en mi caso he utilizado Canvas para dibujar los cubos. Eso significa que todos los juegos funcionan perfectamente en todos los navegadores modernos, incluyendo Google Chrome, Firefox, Safari, Opera y Internet Explorer 9 y 10.

Para el desarrollo de los juegos se creo un motor en JavaScript que nos permitiera dibujar en un Canvas la geometría de los modelos de los cubos. Este motor no utiliza WebGL para renderizar los cubos, porque Internet Explorer no tiene soporte (además de en el resto es experimental), por lo que se opto por hacer un motor grafico completo desde cero. Es decir, todo el pipeline de grafica se tiene que hacer en JavaScript, esto significa, entre otras cosas, que tenemos que emular por software como funciona una tarjeta gráfica y eso normalmente es más lento que el propio hardware. Así que el desafío de implementar un pipeline gráfico por software es mayor ya que tiene que tener un rendimiento aceptable.

JavaScript 101

JavaScript es un lenguaje en el que existen varios tipos de datos básicos con los que podemos trabajar.

  • Object
  • Array
  • Number
  • String
  • Boolean

Los objetos son la forma más común a la hora de trabajar en JavaScript y se pueden utilizar de muchas maneras.

La forma más sencilla de crear un objeto es:

   1: var myObject = {}

A partir de ahí se pueden ir agregando propiedades al objeto sin ningún tipo de restricción. No son propiedades como las que se puede estar acostumbrado en C#, sino que el tipo object se comporta como una especie de diccionario de pares nombre valor.

Se pueden crear propiedades de la siguiente manera a un objeto previamente definido.

   1: myObject.name = ‘Luis’;

   2: myObject.number = 42;

Así el objeto pasará a tener dos propiedades, una llamada ‘name’ con el valor ‘Luis’ y otra llamada ‘number’ con el valor ‘42’.

También se puede acceder a esas propiedades como si de un diccionario se tratase. Las dos formas son igual de válidas y correctas.

   1: var name = myObject[‘name’];

Después de la ejecución de esta línea de código lo que se establece en la variable name es el valor de ‘Luis’, previamente establecido.

Con esta nueva forma de acceder a las propiedades no solo se pueden leer valores almacenados en un objeto sino que también se pueden guardar.

myObject[‘currentDate’] = new Date();

Vector3

Como ejemplo de objeto se va a definir Vector3; un vector de 3 dimensiones.

var vector3 = {x: 1, y: 1, z: 1}

¿Cuál es el problema con este Vector3?

El rendimiento. Como se ha dicho antes todos los objetos en JavaScript se comportan como un diccionario de pares nombre / valor, así que en cada una de las operaciones en las que se tenga que leer o escribir el valor de x, y o z, el runtime de JavaScript tiene que comprobar que la propiedad existe o no y luego leerla o almacenarla. Todo esto lleva tiempo. Es como si se programase todo el acceso a propiedades y campos en .NET utilizando únicamente la API de Reflexion (System.Reflection).

En el caso del motor de 3D en JavaScript Vector2, Vector3, Vector4, Color y Matrix son tipos que se están usando constantemente para dibujar la geometría de los cubos, así que esos tipos fueron los primero en ser optimizados.

La solución por la que se opto fue eliminar la definición de los tipos, es decir, que por ejemplo Vector2, Vector3, Vector4 y Color pasaron a ser un array de 2, 3, 4 y 4 posiciones respectivamente. Así que por convención lo que se hizo fue que la posición dentro del array representaba una coordenada de las dimensiones del vector.

  • X: array[0]
  • Y: array[1]
  • Z: array[2]
  • W: arrat[3]

En el caso de Matrix que se tenía M11, M12…M21,M22..M31..M44 pasaron a ser también las posiciones de un array.

Veamos como se ha cambiado la multiplicación de matrices, uno de los cuellos de botella, en cuanto a rendimiento se refiere.

Antes

   1: function Multiply(matrix1, matrix2) {

   2:     var matrix = new Matrix();

   3:     matrix.M11 = (((matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21)) + (matrix1.M13 * matrix2.M31)) + (matrix1.M14 * matrix2.M41);

   4:     matrix.M12 = (((matrix1.M11 * matrix2.M12) + (matrix1.M12 * matrix2.M22)) + (matrix1.M13 * matrix2.M32)) + (matrix1.M14 * matrix2.M42);

   5:     matrix.M13 = (((matrix1.M11 * matrix2.M13) + (matrix1.M12 * matrix2.M23)) + (matrix1.M13 * matrix2.M33)) + (matrix1.M14 * matrix2.M43);

   6:     matrix.M14 = (((matrix1.M11 * matrix2.M14) + (matrix1.M12 * matrix2.M24)) + (matrix1.M13 * matrix2.M34)) + (matrix1.M14 * matrix2.M44);

   7:     matrix.M21 = (((matrix1.M21 * matrix2.M11) + (matrix1.M22 * matrix2.M21)) + (matrix1.M23 * matrix2.M31)) + (matrix1.M24 * matrix2.M41);

   8:     matrix.M22 = (((matrix1.M21 * matrix2.M12) + (matrix1.M22 * matrix2.M22)) + (matrix1.M23 * matrix2.M32)) + (matrix1.M24 * matrix2.M42);

   9:     matrix.M23 = (((matrix1.M21 * matrix2.M13) + (matrix1.M22 * matrix2.M23)) + (matrix1.M23 * matrix2.M33)) + (matrix1.M24 * matrix2.M43);

  10:     matrix.M24 = (((matrix1.M21 * matrix2.M14) + (matrix1.M22 * matrix2.M24)) + (matrix1.M23 * matrix2.M34)) + (matrix1.M24 * matrix2.M44);

  11:     matrix.M31 = (((matrix1.M31 * matrix2.M11) + (matrix1.M32 * matrix2.M21)) + (matrix1.M33 * matrix2.M31)) + (matrix1.M34 * matrix2.M41);

  12:     matrix.M32 = (((matrix1.M31 * matrix2.M12) + (matrix1.M32 * matrix2.M22)) + (matrix1.M33 * matrix2.M32)) + (matrix1.M34 * matrix2.M42);

  13:     matrix.M33 = (((matrix1.M31 * matrix2.M13) + (matrix1.M32 * matrix2.M23)) + (matrix1.M33 * matrix2.M33)) + (matrix1.M34 * matrix2.M43);

  14:     matrix.M34 = (((matrix1.M31 * matrix2.M14) + (matrix1.M32 * matrix2.M24)) + (matrix1.M33 * matrix2.M34)) + (matrix1.M34 * matrix2.M44);

  15:     matrix.M41 = (((matrix1.M41 * matrix2.M11) + (matrix1.M42 * matrix2.M21)) + (matrix1.M43 * matrix2.M31)) + (matrix1.M44 * matrix2.M41);

  16:     matrix.M42 = (((matrix1.M41 * matrix2.M12) + (matrix1.M42 * matrix2.M22)) + (matrix1.M43 * matrix2.M32)) + (matrix1.M44 * matrix2.M42);

  17:     matrix.M43 = (((matrix1.M41 * matrix2.M13) + (matrix1.M42 * matrix2.M23)) + (matrix1.M43 * matrix2.M33)) + (matrix1.M44 * matrix2.M43);

  18:     matrix.M44 = (((matrix1.M41 * matrix2.M14) + (matrix1.M42 * matrix2.M24)) + (matrix1.M43 * matrix2.M34)) + (matrix1.M44 * matrix2.M44);

  19:     return matrix;

  20: }

La multiplicación simplemente accedía a cada uno de los índices de la matriz, los multiplicaba y luego los asignada de vuelta a la matriz de resultado. Como hemos dicho antes, esto implica leer una gran cantidad de propiedades durante el dibujado de un frame de la escena.

Ahora

   1: function Multiply(matrix1, matrix2) {

   2:     var matrix = new Matrix();

   3:     var position = matrix.position;

   4:     var position1 = matrix1.position;

   5:     var position2 = matrix2.position;

   6:     position[0] = (((position1[0] * position2[0]) + (position1[1] * position2[4])) + (position1[2] * position2[8])) + (position1[3] * position2[12]);

   7:     position[1] = (((position1[0] * position2[1]) + (position1[1] * position2[5])) + (position1[2] * position2[9])) + (position1[3] * position2[13]);

   8:     position[2] = (((position1[0] * position2[2]) + (position1[1] * position2[6])) + (position1[2] * position2[10])) + (position1[3] * position2[14]);

   9:     position[3] = (((position1[0] * position2[3]) + (position1[1] * position2[7])) + (position1[2] * position2[11])) + (position1[3] * position2[15]);

  10:     position[4] = (((position1[4] * position2[0]) + (position1[5] * position2[4])) + (position1[6] * position2[8])) + (position1[7] * position2[12]);

  11:     position[5] = (((position1[4] * position2[1]) + (position1[5] * position2[5])) + (position1[6] * position2[9])) + (position1[7] * position2[13]);

  12:     position[6] = (((position1[4] * position2[2]) + (position1[5] * position2[6])) + (position1[6] * position2[10])) + (position1[7] * position2[14]);

  13:     position[7] = (((position1[4] * position2[3]) + (position1[5] * position2[7])) + (position1[6] * position2[11])) + (position1[7] * position2[15]);

  14:     position[8] = (((position1[8] * position2[0]) + (position1[9] * position2[4])) + (position1[10] * position2[8])) + (position1[11] * position2[12]);

  15:     position[9] = (((position1[8] * position2[1]) + (position1[9] * position2[5])) + (position1[10] * position2[9])) + (position1[11] * position2[13]);

  16:     position[10] = (((position1[8] * position2[2]) + (position1[9] * position2[6])) + (position1[10] * position2[10])) + (position1[11] * position2[14]);

  17:     position[11] = (((position1[8] * position2[3]) + (position1[9] * position2[7])) + (position1[10] * position2[11])) + (position1[11] * position2[15]);

  18:     position[12] = (((position1[12] * position2[0]) + (position1[13] * position2[4])) + (position1[14] * position2[8])) + (position1[15] * position2[12]);

  19:     position[13] = (((position1[12] * position2[1]) + (position1[13] * position2[5])) + (position1[14] * position2[9])) + (position1[15] * position2[13]);

  20:     position[14] = (((position1[12] * position2[2]) + (position1[13] * position2[6])) + (position1[14] * position2[10])) + (position1[15] * position2[14]);

  21:     position[15] = (((position1[12] * position2[3]) + (position1[13] * position2[7])) + (position1[14] * position2[11])) + (position1[15] * position2[15]);

  22:     return matrix;

  23: }

Lo primero que se observa es que el código pasa a ser más críptico que el anterior, es decir, que ahora únicamente se tiene son los diferentes índices de position dentro de tres arrays, que representan las tres matrices con las que se esta trabajando en este momento.

Así que se ha pasado de,

matrix.M11 = (((matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21)) + 

(matrix1.M13 * matrix2.M31)) + (matrix1.M14 * matrix2.M41);

a esto:

position[0] = (((position1[0] * position2[0]) + (position1[1] * position2[4])) + 

(position1[2] * position2[8])) + (position1[3] * position2[12]);

Ya que ahora todas las posiciones de la matriz están almacenadas en un array de 16 posiciones lo que se tiene que hacer si se quiere acceder al valor M11 es acceder a la posición 0 de array, en el caso del valor M31 a la posición 8 de array y así sucesivamente.

Otras optimizaciones

Tamaño de los arrays

Si se tiene un array que tiene una propiedad length por la que se quiere iterar para realizar una acción por cada uno de los elementos del array, es recomendable no poner directamente el valor de myArray.length para comprobar si se ha llegado al final de array, sino guardar el tamaño del array en una variable y usar esta variable.

   1: var myArray = new Array();

   2:  

   3: for (var i = 0; i < myArray.length; i++) {

   4:     myArray[i] = i;

   5: }

   6:  

   7: var length = myArray.length;

   8: for (var i = 0; i < length; i++) {

   9:     myArray[i] = i;

  10: }

Cachear variables

Si durante la ejecución de un método se tiene variables que vamos a usar y estas variables son propiedades de un objeto, es mejor definirlas como variables en el ámbito del método que no referenciarlas desde el objeto original.

   1: var myObject =

   2:     {

   3:     name: 'luis',

   4:     company: {

   5:         name: 'PlainConcepts',

   6:         location: 'address'

   7:         }

   8:     };

   9:  

  10: var companyAddress = myObject.company.location;

  11: var company = myObject.company;

  12: companyAddress = company.location;

Espero que estas notas sobre optimización de JavaScript os sean útiles.

Luis Guerrero.

Dejar tus excepciones fluir

“Dejar tus excepciones fluir” es una frase que Rodrigo Corral nos repite durante el desarrollo de software constantemente, pero, ¿qué quiere decir con esta frase?

Normalmente se debería de pensar lo contrario de las excepciones, es decir, capturarlas siempre para que no se produzcan errores en el software y que todo funcione correctamente. Pero vamos a ver a través de un ejemplo, como a veces es mucho mejor dejar a las excepciones fluir por la pila y no capturarlas.

Recientemente, en un equipo de Plain Concepts que está desarrollando unas aplicaciones para Windows Phone 7, hemos tenido la necesidad de crear una clase que nos guarde en el almacenamiento aislado datos, para que después podamos leer cuando la aplicación se arranque. En principio es una clase muy sencilla, utiliza DataContractSerializer para guardar todos los datos en el isolated storage. Aquí os muestro los métodos y propiedades de esta clase.

  • Save
  • Load
  • Delete
  • Exist

La clase es genérica, eso significa que para poder usarla tienes que pasar como parámetro el tipo que quieres guardar, y todos los métodos de Save y Load devuelven T. Además tiene dos constructores públicos, uno sin parámetros en el que se utiliza el nombre del tipo como nombre del fichero para guardarlo en Isolated Storage y otro constructor con un parámetro. La lista de know types de DataContractSerializer, por si los necesita para la serialización.

En principio el funcionamiento de la clase es bastante sencillo. Especificamos el tipo y ya podemos salvar, cargar y preguntar si el fichero existe para cargar.

Si tenemos una clase como Item declarado así:

public class Item
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Podemos usar la clase SaveManager de esta manera:

SaveManager<Item> save = new SaveManager<Item>();
if (!save.Exist)
{
    save.Save(new Item()
    {
        Name = "Jonh",
        Age = 30
    });
}

Item savedItem = save.Load();

Ahora viene la parte más importante de todas, que es decidir cómo vamos a tratar las excepciones dentro de la implementación de la clase SaveManager.

Nos queda claro que la clase es un envoltorio de la clase DataContractSerializer para así hacer el guardado y la carga de clases serializadas mucho más sencilla.

public class SaveManager<T> where T : class
{
    public SaveManager() { }

    public SaveManager(List<Type> knownTypes)
    {
        serializer = new DataContractSerializer(typeof(T), knownTypes);
        saveFileName = typeof(T).Name;
    }

    public void Save(T value)
    {
        if (value == null)
        {
            throw new ArgumentNullException("value", "value can't be null");
        }

        using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (Stream saveStream = file.CreateFile(saveFileName))
            {
                serializer.WriteObject(saveStream, value);
            }
        }

    }

    public T LoadWithTryCatch()
    {
        T result = default(T);
        try
        {
            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (Stream saveStream = file.OpenFile(saveFileName, FileMode.Open, FileAccess.Read))
                {
                    result = (T)(object)serializer.ReadObject(saveStream);
                }
            }
        }
        catch { }
        return result;
    }

    public T Load()
    {
        T result = default(T);
        using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (Stream saveStream = file.OpenFile(saveFileName, FileMode.Open, FileAccess.Read))
            {
                result = (T)(object)serializer.ReadObject(saveStream);
            }
        }
        return result;
    }

    public T LoadWithAllTryCatc()
    {
        T result = default(T);
        try
        {
            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
            {
                try
                {
                    using (Stream saveStream = file.OpenFile(saveFileName, FileMode.Open, FileAccess.Read))
                    {
                        result = (T)(object)serializer.ReadObject(saveStream);
                    }
                }
                catch (IsolatedStorageException isolatedException)
                {
                    throw isolatedException;
                    // error del isolated Storage
                }
                catch (ArgumentNullException argumentNullException)
                {
                    throw argumentNullException;
                    // error en un argumento (referencia nula)
                }
                catch (ArgumentException argumentException)
                {
                    throw argumentException;
                    // error en un argumento
                }
                catch (DirectoryNotFoundException directoryNotFoudnException)
                {
                    throw directoryNotFoudnException;
                    // directorio no encontrado
                }
                catch (FileNotFoundException fileNotFoundException)
                {
                    throw fileNotFoundException;
                    // fichero no encontrado
                }
                catch (ObjectDisposedException objectDisposedException)
                {
                    throw objectDisposedException;
                    // objecto disposeado durante su utilización
                }
            }
        }
        catch (IsolatedStorageException isolatedException)
        {
            throw isolatedException;
            // se ha producido un error al acceder al isolated storage
        }
        return result;
    }

    public void Delete()
    {
        using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (file.FileExists(saveFileName))
            {
                file.DeleteFile(saveFileName);
            }
        }
    }

    public bool Exist
    {
        get
        {
            bool result = false;
            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
            {
                result = file.FileExists(saveFileName);
            }
            return result;
        }
    }

    private string saveFileName;
    private DataContractSerializer serializer;
}

¿Qué puede ir mal?

Pensando de nuevo en las excepciones, tenemos que tener claro que situaciones excepcionales se puede dar en el código y tratarlas de manera adecuada. Por situaciones excepciones nos referimos a cosas que no están planeadas en el flujo de ejecución normal de nuestra aplicación, y las excepciones son los objetos que representan los errores ocurridos durante la ejecución de la aplicación.

Veamos por ejemplo el método Load, pensemos en la lista de errores que se pueden producir:

  • Que la cuota de Isolated Storage sea 0
  • Que el Isolated Storage este deshabilitado.
  • Que la ruta del fichero esté mal formada
  • Que la ruta del fichero sea nula
  • Que el directorio del que se quiere leer no exista
  • Que no se encuentre el fichero
  • Que no se encuentre el fichero y el modo de apertura esté en Open
  • Que el contenido del fichero no sea una serialización válida del tipo que estamos intentando leer.

La lista de excepciones es bastante larga, ¿de dónde viene esta lista de excepciones? Si miramos a la implementación del método,

public T Load()
{
    T result = default(T);
    using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (Stream saveStream = file.OpenFile(saveFileName, FileMode.Open, FileAccess.Read))
        {
            result = (T)(object)serializer.ReadObject(saveStream);
        }
    }
    return result;
}

Nos damos cuenta que el método Load utiliza dos clases para hacer el trabajo, IsolatedStorageFile y DataContractSerializer. Así que viendo el uso de estas dos clases, uno ya se imagina de donde pueden venir las excepciones de la lista anterior.

Ahora bien, pongamos como ejemplo file.OpenFile(), que nos permite abrir un fichero que está en el isolated storage. Según la lista anterior tenemos 5 tipos diferentes de excepciones que se pueden producir al llamar al método OpenFile. Pero ahora bien, ¿Cuál es la mejor estrategia para tratar esas excepciones?

Deja las excepciones fluir

Hasta ahora nos hemos centrado en definir un escenario para poder discutir cual es la mejor opción para tratar esas excepciones. Según lo dicho hasta ahora, tenemos un método Load que no acepta ningún parámetro y que devuelve una instancia recién creada del tipo T leído desde el Isolaged Storage con el nombre del tipo T.

Ahora bien, tenemos dos aproximaciones a la hora de usar las excepciones, podemos por un lado, envolver el código en un gran Try/Catch cacheando una excepción de tipo System.Exception.

public T LoadWithTryCatch()
{
    T result = default(T);
    try
    {
        using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (Stream saveStream = file.OpenFile(saveFileName, FileMode.Open, FileAccess.Read))
            {
                result = (T)(object)serializer.ReadObject(saveStream);
            }
        }
    }
    catch { }
    return result;
}

El envolver el código así nos permite controlas las excepciones que se producen, pero una vez que se produce una excepción, según este código, estamos haciendo un catch sin código y como la variable result se ha inicializado a default(T) nos damos cuenta que el método Load, en caso de que se produzca una excepción, devolverá null.

¿Es esto una buena aproximación?, depende del implementador de la clase, en este caso nosotros. Tendríamos que documentar que en caso de que se produzca una excepción el método devuelve null. Desde mi punto de vista esto me parece erróneo, porque lo que estamos haciendo es ocultar el problema detrás de un catch y el llamador de la función no sabrá jamás cual es el motivo por el que se produce la excepción, podría hacer sido un feichero que no existe, que el DataContractSerializer lance una excepción porque se te ha olvidado decorar con un DataContract la clase base del tipo que estás serializando, podrían ser miles de cosas, pero nosotros decidimos devolver un null. Como ya he dicho esto reduce la visibilidad del problema, hace que la depuración de código que utiliza este componente sea mucho más complicada, ya que no devuelve ninguna información sobre el error ocurrido.

La segunda aproximación que tenemos es justamente tratar todos los tipos de excepciones, escribir código para todos los tipos y relanzar las excepciones de nuevo.

public T LoadWithAllTryCatc()
{
   T result = default(T);
   try
   {
       using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
       {
           try
           {
               using (Stream saveStream = file.OpenFile(saveFileName, FileMode.Open, FileAccess.Read))
               {
                   result = (T)(object)serializer.ReadObject(saveStream);
               }
           }
           catch (IsolatedStorageException isolatedException)
           {
               throw isolatedException;
               // error del isolated Storage
           }
           catch (ArgumentNullException argumentNullException)
           {
               throw argumentNullException;
               // error en un argumento (referencia nula)
           }
           catch (ArgumentException argumentException)
           {
               throw argumentException;
               // error en un argumento
           }
           catch (DirectoryNotFoundException directoryNotFoudnException)
           {
               throw directoryNotFoudnException;
               // directorio no encontrado
           }
           catch (FileNotFoundException fileNotFoundException)
           {
               throw fileNotFoundException;
               // fichero no encontrado
           }
           catch (ObjectDisposedException objectDisposedException)
           {
               throw objectDisposedException;
               // objecto disposeado durante su utilización
           }
       }
   }
   catch (IsolatedStorageException isolatedException)
   {
       throw isolatedException;
       // se ha producido un error al acceder al isolated storage
   }
   return result;
}

Escribiendo un manejador por cada uno de los tipos de excepciones, como se puede apreciar, disminuye la legibilidad del código y hace que la complejidad del método aumente, haciendo que mantenibilidad disminuya.

¿Esta aproximación nos aporta algo? Pensando de nuevo en nosotros mismo, los implementadores de la clase, que me aporta saber que el fichero no existe a la hora de leer el fichero para de serializar. Aunque sea capaz de interceptar una excepción de fichero no encontrado no podría hacer nada, porque la responsabilidad de mi clase se centra en leer el fichero y de serializarlo con DataContractSerializar, no es mi responsabilidad asegurarme que el fichero este ahí, sino el llamador. Imaginaros ahora que también se lanza una excepción durante la de serialización del objeto, ¿qué debería de hacer? ¿Notificar al llamador de que se ha producido la excepción?, ¿Intentar otra aproximación?, no tengo muchas opciones puesto que si el desarrollador se ha olvidado de poner el DataMember o el DataContract en alguna de las clases no voy a poner solucionar ninguno de los problemas del serializador, así que realmente, de nuevo, la responsabilidad de este error no es el implementador sino del desarrollador que la usa.

Así que de nuevo llegamos al método que teníamos implementado al principio del artículo:

public T Load()
{
    T result = default(T);
    using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (Stream saveStream = file.OpenFile(saveFileName, FileMode.Open, FileAccess.Read))
        {
            result = (T)(object)serializer.ReadObject(saveStream);
        }
    }
    return result;
}

Una implementación en la que no existe ningún bloque de Try/Catch. ¿Por qué?, porque no teniendo ningún bloque de código solucionamos los problemas de saber qué hacer con la excepción y la de notificar al usuario que algo ha ocurrido mal.

Así si durante el desarrollo de la aplicación que se esté usando esta clase, nos damos cuenta que la hacer un llamada al método Load, nos lanza una excepción directamente a nosotros, diciendo que el fichero no existe, ¿Qué representa este error?, no que tengamos un error en sí en el código del SaveManager, sino que si estamos asumiendo que debería de haber un fichero en el Isolated storage, previamente guardado con las opciones del usurario y ahora no está, que tenemos un bug en nuestro software. Es decir, que el hecho de que la excepción se lance y fluya a través de la pila hasta el gestor de preferencias de nuestra aplicación, es un síntoma de que tenemos un error en nuestro software, de que no estamos guardando correctamente las preferencias del usuario o que no estamos comprobando antes de leer el fichero que el fichero existe.

Viendo las excepciones de esta manera, uno utiliza las excepciones como mecanismo para parar la ejecución del código y notificar al usuario de que algo no está bien. Si, por ejemplo, en nuestra primera implementación hubiéramos devuelto nulo en el método Load, nunca nos habríamos dado cuenta de que no estábamos guardando los datos, o que al guardar los datos tenemos algún error.

Así que la conclusión a la que podemos llegar es que es bueno dejar que las excepciones fluyan por el código y que salvo en contadas ocasiones hagamos un catch. Esto por supuesto no es una afirmación que se pueda extender a todas las clases, porque depende de la implementación que estemos haciendo. En este caso concreto lo que estamos haciendo es utilizando bloques externos, para generar una funcionalidad concreta para nuestra aplicación, bloques, que en sí mismo manejan de manera correcta la excepciones.

La clave para decidir si tenemos que colocar un bloque de Try/Catch supongo que viene dado por la responsabilidad de las operaciones, es decir, si yo que estoy implementado la clase tengo que ser responsable de asegurarme que el fichero exista y si no existe crearlo, entonces debería de hacer las comprobaciones o poner los bloques de Try/Catch adecuados para asegurarme de que el fichero existe. Otra manera de pensar en cómo tratar las excepciones es pensar que si la ejecución del código tiene sentido cuando no existe un fichero. Es decir, ¿puedo implementar un método Load teniendo en cuenta que el fichero no existe?, en mi opinión creo que no, porque justamente ese es uno de los requisitos del método, leer el fichero y de serializarlo. Si el fichero no existe no podemos continuar.

Llevando este concepto al extremo nos encontramos con los BSOD de Windows, los pantallazos azules de Windows. ¿Por qué existen los BSOD? La primera respuesta a esta pregunta es porque existe una función para el modo kernel que se llama, KeBugCheckEx, que según la MSDN “permite apagar el sistema de manera controlada cuando el llamador descubre una inconsistencia irrecuperable que podría hacer que el sistema se corrompiese si el llamador continúa ejecutándose”. La definición lo deja bastante claro, si resulta que se ha corrompido la memoria de alguna manera y sabemos a ciencia cierta que después de esa comprobación el sistema va a dejar de funcionar correctamente, lo que tenemos que hacer es lanzar una excepción, es decir, un suicidio controlado de Windows que permita verificar el problema. Podemos ver entonces el BSOD como un síntoma de que hay un problema, no como un problema en sí. Pues esta manera que tiene Windows de avisarnos que hay un problema es el mismo ejemplo de nuestro método Load(), a diferentes niveles está claro. Pero si lo pensamos así qué sentido tiene intentar de serializar una clase de un fichero, si el fichero no existe. Pues ninguna.

Por eso no vale de nada que pongamos un catch al final del método porque es un requisito que tengamos el fichero disponible, por eso las excepciones se llaman así porque son situaciones excepcionales, que no se preveían en el flujo de ejecución. Yo como implementador del método no me espero que no exista el fichero, pero puede que el que realiza la llamada sí, así que es su responsabilidad hacer algo con esta excepción no yo.

Utilizar las excepciones como método de validación

Este es otro tópico de las excepciones que también se suele tratar de manera incorrecta, sale mucho más barato desde el punto del rendimiento comprobar que los parámetros son distintos de nulo y del tipo adecuado que envolverlo todo en un Try/Catch enorme y devolver nulo. Pero este es un tema que veremos en otro post.

El código de ejemplo aquí.

Luis Guerrero.

El recolector de basura

Todo desarrollador que haya trabajado con .NET, alguna vez ha escuchado hablar del recolector de basura. En este artículo vamos a intentar poner un poco de luz sobre ese concepto, muchas veces misterioso para los programadores.

¿Por qué existe o necesitamos un recolector de basura?

El CLR es un maquina virtual en el que se ejecutan nuestras aplicaciones y .NET es un framework. Microsoft hizo este framework para tener una capa de abastración entre el sistema operativo y las aplicaciones. Una de las cosas más problemáticas en cuanto al desarrollo de aplicaciones es la gestión de memoria. La memoria no es finita y necesita de una gestión, reservar, liberar, compactar, reorganizar. Es por esto que .NET tiene el recolector de basura, para ayudarnos a recolectar los elementos no utilizados y reorganizar la memoria.

Esta caracteristica permite que podamos usar los objetos detro de nuestros lenguajes de programación sin tener en cuenta como se reclican, nostros hacemos el new y el recolector de basura, cuando el objeto ya no sea usado, lo recolectará.

¿Como se produce esta recolección de basura? y ¿como el GC sabe que objetos no se están usando?.

Como todos sabreis todos los objetos del framework son referencias a objetos, y cuando nosotros igualamos (a excepción, claro, de que esté sobrecargado el operador ==) lo que estamos haciendo es copiar la referecia en memoria donde está el objeto, es decir copiarmos su dirección y no su contenido. Esto tambien se aplica a los ValueType (structs) solo que el framework trabaja de otra manera, pero eso está fuera de este articulo.

El hecho de que todo sean referencias, hace que cuando nostros tenemos una clase y dentro de esa clase tenemos un campo de un tipo, lo que realmente tenemos es la dirección de memoria donde vive el objeto que referenciamos, lo podemos simplificar en que tenemos un grafo, una serie de elementos representados por vertices y por las aristas que son las referencias entre objetos.

Pues bien cuando nosotros creamos un objeto y lo asignamos lo que estamos haciendo es añadiendo un vertice a nuestro grafo de referencias. Así que una vez que establecemos un objeto a null a menos que tengamos otra referencia (arista) a nuestro elemento ese objeto está completamente aislado del grafo y nadie puede acceder a el.

Pues sabiendo esto, ¿como el GC es capar de saber que un objeto ya no es necesario y puede ser recolectado?, teniendo en cuenta que nadie lo referencia. El secreto está en el heap. El heap mantiene una colección de todos los elementos que el runtime ha creado, es decir, de todos los objetos que tenemos en nuestro grafo pero en formato de lista. Pues bien lo que el GC hace es coger un objecto y saber si hay alguna referncia a ese objeto (una arista) si no es capaz de encontrar una arista hasta ese objeto es porque el objeto nadie lo está referenciado y por eso puede ser recolectado con seguridad. Así se simple.

Ahora bien, el recolector de basura no enumera todos los objetos que estan en el heap y por cada uno de ellos recorre todo el grafo para encontrar una referencia a ese objeto, en vez de eso lo que utiliza son unos objetos raiz, llamados GCRoots, por los cuales empieza el grafo de nuestra aplicación.

Todo esto que he contado como podemos tener evidencias de que es cierto, teniendo un entorno de depuración montado con simbolos y WinDBG + SOS. (Podeis encontrar información de cómo configurar el entorno de depuración en mi blog)

· !dumpheap para mostrar todos los elementos del heap

· !gcroot 0123292 para mostrar cuales son las referencias a un objeto que puede ser:

o En la pila

o En un GCHandle

o En un objeto listo para la finalización

o O en un objeto encontrado el los lugares anteriores

Además de todo eso si sois aventureros y os gusta las experiencias fuertes, podemos visualizar gráficamente el grafo de refencias con sus GCRoots a través una herramienta de profilling que tiene Microsoft, CLRProfiler disponible en www.microsoft.com/downloads

clip_image002

Aquí tenemos una captura del grafo de nuestra aplicación de ejemplo, en la que podemos ver el <root> del que os hablaba.

¿Qué son las generaciones?

Las generaciones son agrupaciones de las edades de los objetos en memoria. Cuando un objeto se crea está en la generación 0, si se produce una recoleccion de basura lo supervivientes de la generación 0, se les promueve a la generacion 1, y la generación 0 se queda libre y compactada. Se empiezan a crear objetos de nuevo, se lanza otra recoleccion de basura de 0 a 1, los objetos que sobreviven de la generacion 1 pasan a la 2 y los que sobreviven de las generacion 0 pasan a la 1, así sucesivamente.

Solamente existen 3 generaciones, la 0, 1 y 2. Normalmente donde más objeto se generan y se destruyen es en la generación 0 porque es la más usada.

Tamaños:

· Generacion 0: 256 kb (cache segundo nivel del procesador)

· Generacion 1: 2Mb

· Generacion 2: 10Mb

Además de todo eso hay una generación especial para los objetos muy grandes en el framework, LOH (Large object heap), que son los objetos con más de 64kb

¿Cuándo se lanza una recoleccion de basura?

Esta es un pregunta complicada porque no tiene una respuesta directa, hay maneras por el cual se puede generar una recolección de basura o por cuales se puede retrasar. Lo más importante a saber es que las recolecciones de basura se hacen cuando la generacion 0 del GC está llena y se va a crear un nuevo objeto, es decir la recoleccion de basura no se produce cuando se llena la memoria, sino cuando se llena la memoria y se intenta crear un objeto. Además de eso hay que tener en cuenta varios factores. El recolector de basura tiene una herustica que le permite tunearse para ofrecer el máximo rendimiento en cada tipo de aplicación, además de que tiene un historial de las acciones que realiza. Por ejemplo, una de las cosas más importantes para el GC es la cantidad de memoria que libera, no la cantidad de objetos que recolecta, si por ejemplo se lanza una recoleccion de memoria y se liberan 150000 objetos que representa 23kb, seguramente el recolector de basura hará que crezca más la memoria de la generacion 0 antes de hacer otra recoleccion, porque lo que le interesa es recolectar mucha memoria no muchas referecias. En ese sentido si el recolector de basura se encuentra con que ha recolectado 150 objetos que ha sido un total de 400kb seguramente la siguiente vez que llene la generación 0 automaticamente generará una nueva recoleccion de basura.

Porque nunca deberia de llamar a GC.Collect

Como he explicado antes el recolector de basura tiene su propia heurística que le permite tunearse de manera automática y sin intervención del programador. En las primeras versiones de .NET se podía llamar a la función GC.Collect (que teóricamente fuerza una recolección de basura), pero que en realidad lo que hacía era sugerir una recolección de basura. En ese sentido muchos desarrolladores se quejaron porque en sus aplicaciones llamaban incesantemente al recolector de basura pero no se lanzaba ninguna recolección (lo sabían porque miraban los contadores de rendimiento de .NET), así que Microsoft tuvo que dar marcha atrás e incluir una sobrecarga en la llamada de GC.Collect que aceptaba por parámetro un entero con la generación máxima en la cual se iba a producir la recolección, y los más importante de todo, un enumerado de tipo GCCollectionMode que permitia decir si la recolección era forzada u optimizada.

Como podeis imaginar el modo optimizado es el predeterminado, es decir, el modo en el que le sugieres al recolector de basura que haga su trabajo, pero el modo forzado, como su nombre indica, forzaba a una recolección de basura. Así que si llamamos a esa función así, GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced) estamos obligando a una recolección de basura completa en nuestra aplicación.

Ahora bien porque nunca se debería de llamar a esta función así, porque básicamente si nos encontramos en una situación en donde la memoria de nuestro proceso sube en todos los escenario y el recolector de basura en su modo de funcionamiento normal no es capaz de bajar la memoria de uso del proceso, nos encontramos entonces ante un escenario de pérdida de memoria (leaking) y no en una situación donde el recolector de basura no es capaz de hacer su trabajo. Porque os puedo asegurar que en el 99.99% de las veces en que he visto una aplicación que hacia llamadas al recolector de basura para intentar liberar memoria era porque el proceso en sí tenía problemas de pérdida de memoria, no porque el recolector no fuera capaz por si solo de hacer su trabajo bien.

Graficas de memoria en forma de montañas

Llegado a este punto mucha gente se imaginará que la ejecución de las aplicaciones es matemáticamente siempre la misma, es decir, que si en un punto de mi aplicación genero 40mb de memoria y después termino de usar esos objetos automáticamente tengo que ver como mi proceso baja esos 40mb de manera discreta. Pues malas noticias para todos, los sistemas de gestión de memoria sin increíblemente complejos y más para una aplicación de .NET.

Puede que el CLR haya decidido por un casual no liberar esa memoria nativa porque ya que la tiene reservada y no tiene que devolverla al S.O. y más adelante la podrá usar sin tener que volver a pedir memoria a Windows. En ese sentido el CLR también utiliza las funciones de memoria virtual de Windows (VirtualAlloc, VirtualFree, VirtualQuery, VirtualProtect) así que en ese sentido también Windows hace un uso de la memoria de manera conservativa es decir, que no por generar 40mb y ejecutar el recolector de basura automáticamente se liberan 40mb.

Así si por ejemplo el sistema necesita liberar páginas de memoria porque otro proceso, a parte del nuestro en .NET, está haciendo un uso de memoria virtual (no una gran reserva de memoria virtual, acordarse de lo que es una falta de página y como Windows gestiona la memoria de manera perezosa) en ese momento puede decidir recuperar las páginas de memoria de nuestro proceso y entonces nuestra memoria bajará.

Así que simplificar un sistema de gestión de memoria virtual de Windows más el sistema de recolección de basura del CRL de .NET de esa manera es desde mi punto de vista barbaridad. Si queremos saber cuál es el estado de nuestro proceso podemos consultar los contadores de rendimiento para .NET o podemos usar las columnas de Private Bytes, Working Set y Virtual Size en Process Explorer o el comando !address –summary en WinDBG para saber exactamente esa memoria privada en que se gasta en heap, images, ect.

¿Qué son los objetos pineados en memoria?

Cuando hablamos de recolección de basura, también hablamos de compactación de la memoria y para que esa compactación de la memoria pueda ocurrir es necesario mover los datos de direcciones de memoria. En un mundo ideal donde las aplicaciones de .NET sean completamente administradas, es decir 100% .NET sin llamadas a Windows esto debería de bastar, pero el caso es que desde .NET podemos hacer llamadas a componentes no administrados que están fuera del paraguas del recolector de basura.

Imaginaros por un momento que estáis haciendo una llamada nativa a una función hecha en C++ que os pide una dirección de memoria donde vive un objeto que él internamente va a utilizar para realizar su trabajo, resulta que como parte de su trabajo ese componente de C++ tiene un temporizador que cada 30sg comprueba una serie de parámetros en ese objeto en contra de un componente de Windows, pero resulta que entre timer y timer, se lanza una recolección de basura y justo el objeto que este componente de C++ utilizaba (porque recordad que le hemos pasado la dirección de memoria, donde vive) es movida por el recolector de basura en una recolección. Justo cuando el siguiente timer se lance y nuestro componente en C++ vaya a leer la memoria se encontrará con que su memoria ahora mismo está ocupada por otro componente.

Este es solo un ejemplo de que a veces no interesa que determinados objetos de .NET estén siempre en la misma dirección de memoria, cualquiera podría haber sugerido que hagamos una copia del objeto y se la pasemos a C++, pero recordad que siempre pasamos referencias y no copias de objetos.

Es por eso en .NET podemos pinear objetos en la memoria, que como su nombre indica, permite que podamos indicarle al recolector de basura que en ningún caso mueva de dirección de memoria este objeto.

A través de una structura llamada GCHandle podemos pinear objetos así: System.Runtime.InteropServices.GCHandle.Alloc(new object,System.Runtime.InteropServices.GCHandleType.Pinned). Esta llamada nos devuelve un objeto de tipo GCHandle en el que podemos consultar el IntPtr del objeto, si esta inicializado y podemos liberarlo.

Que son las referencias débiles y que es la resurrección de objetos zombies

Como hemos dicho anteriormente cada vez que hacemos una asignación estamos copiando la referencia en memoria de un objeto, es decir, estamos haciendo una referencia fuerte de un objeto. Si existe una referencia fuerte es porque existe una referencia débil, que siguiendo la analogía tiene que ser una referencia que el recolector de basura no tenga en cuenta para evaluar si un objeto es referenciado por otro.

Para poder usar esas referencias débiles tenemos una clase en .NET llamada WeakReference que como su nombre indica nos permite generar esas referencias débiles. Esta clase tiene varias propiedades interesantes como: IsAlive que nos permite consultar si el objeto al que apuntamos sigue vivo; Target que es una referencia (débil) del objeto y TrackRessurection que nos permite hacer tracking de la resurrección de un objeto.

Pero, ¿Qué es exactamente la resurrección de un objeto?

Cada vez que un objeto es eliminado del grafo de referencias de una aplicación, este pasa a una cola llamada la cola de finalización (comando de WinDBG !finlizaqueue) en la que se le da una última oportunidad de ejecutar el código que tenga en el destructor o en el método Dispose (si implementa IDisposable). En ese instante en el que el objeto está en la cola de finalización un objeto está fuera del grafo de objetos, pero si durante la ejecución de ese código se referencia a si mismo de otro objeto que está en el grafo de objetos de la aplicación a través de un objeto estático, diremos que el objeto ha sufrido una resurrección puesto que el recolector de basura sacará a ese objeto de la cola de finalización y el objeto será de nuevo referenciable y volverá a la vida (metafóricamente hablando).

¿Por qué el .NET tiene una finalización no determinista?

Si durante el desarrollo de nuestras clases implementamos IDisposable o creamos un destructor para nuestra clase, el CLR no nos puede asegurar que el destructor de nuestra clase será siempre ejecutado. ¿Por qué?. En ese sentido tenemos que recordar que el CLR es un runtime ejecutado dentro de un proceso y puede que el proceso o el dominio de aplicación se descargue de manera inesperada y en ese sentido el CRL no puede esperar a que todo el código que tengamos en nuestros destructores se ejecute. Por eso se dice que .NET no es determinista en cuanto a la finalización de objetos, porque solamente en el caso de que el dominio de aplicación se descargue o la aplicación se cierre el CLR no nos asegurará que nuestros destructores se ejecuten.

Pero qué pasa si necesito por contrato que el destructor de mi clase se ejecute, o dicho de otra manera, que pasa con las clases que representan recursos del sistema que tienen que ser liberados si o sí.

En este sentido podemos heredad de la clase System.Runtime.ConstrainedExecution.CriticalFinalizerObject haciendo así que el CLR nos asegure que SIEMPRE se ejecutará el destructor de la clase. Como ejemplo diremos que la clase Thread, ReaderWriterLock, SafeHandle y demás heredan de esta clase.

Conclusiones

A lo largo de este articulo hemos repasado los básicos de la gestión de memoria por parte del CLR, y hemos visto que es un sistema extraordinariamente complejo para poder simplificarlo de la manera que lo hacen algunos así que en ese sentido paciencia con la memoria y si tenéis algún problema no dudéis con contactar con el equipo DOT (Debugging and Optimization Team) de Plain Concepts que estaremos encantados de buscar problemas de memoria y rendimiento en vuestras aplicaciones.

Luis Guerrero.

Constructores y Finalizadores

Dentro de la orientación a objetos nos encontramos dos tipos de métodos especiales, el constructor y el destructor. En .NET hay dos tipos de constructores de instancia (ctor) y de tipo (cctor) también llamado constructor estático.

Constructores

Cuando un tipo es accedido por primera vez, se ejecuta el constructor estático bajo un doble lock (Como implementar un Singleton concurrente) para asegurar que solamente se llama una vez, si además se está creando una instancia del objeto se llamará al constructor del objeto para inicializarlo. También se llama a todos los constructores base hasta que se llega a System.Object.

Hay que tener en cuenta que el constructor de un objeto no es más que un método que puede aceptar parámetros y que devuelve una referencia sí mismo a this. Como programadores no tenemos que manejar esta situación puesto que el propio compilador la realiza por nosotros.

Hay un caso excepcional en el que podemos crear un objeto pero no obtener una referencia a el nunca, si en el constructor se lanza una excepción. En este caso, el objeto se inicializa, se llama a los constructores base y se aborta el método en el punto en el que se lance la excepción, pero ya se ha reservado memoria para el objeto y nunca podremos acceder a esa referencia de ese objeto a medio construir.

public class Foo
{
    public DateTime DateTime { get; set; }
    public string file { get; set; }
    public Foo()
    {
        DateTime = DateTime.Now;
        string[] v = file.Split(',');
    }
}

Aquí tenemos un ejemplo de una clase que en el constructor lanzará una excepción de tipo NullReferenceException, realmente si se ha creado el objeto solo que si hacemos algo como:

Foo f = new Foo();

Nunca llegaremos a obtener la referencia del objeto hay creado y además tenemos que tener en cuenta que se tendrá que recolectar el objeto para liberar memoria en la siguiente recolección de basura.

Es por eso que hay que tener mucho cuidado con lo que se pone en el constructor porque podemos dejar a nuestro tipo inutilizado.

La situación se agrava si se lanza una excepción en el constructor de tipo, ya que no podremos acceder a ninguna variable estática ni crear instancias de este tipo.

Destructores o finalizadores

Los destructores (o finalizadores) son los métodos que se llamarán cuando el objeto ya no es usado y se reclama la memoria usada. En este caso es el CLR el que se encarga de llamar a este finalizador pues nosotros no tenemos ningún mecanismo para liberar un objeto.

Un finalizador se define de esta manera:

~Foo()
{
   Console.WriteLine("Adiossss !!");
}

Eso significa que cuando esa instancia del tipo Foo sea recolectada se ejecutará este método. Si miramos al IL generado por este programa realmente el destructor ha pasado a llamarse Finalize() y está marcado como virtual.

Además de definir un destructor de clase hay otro mecanismo para poder definir un destructor, implementando la interfaz IDisposable. Este es el único mecanismo que tiene un programador de .NET para poder definir un destructor de objeto y además poder llamarlo el mismo de codigo de usuario.

Para evitar que este método sea llamado otra vez por el CLR existe el método GC.SupressFinalize.

¿Cómo es llamado este método?

Cada vez que se realiza una recolección de basura el GC examina todos los objetos y si encuentra alguno que es finalizable, es decir, tiene destructor o implementa IDisposable, entonces ese objeto que está listo para ser recolectado pasa a la cola de finalización.

Cuando los objetos están en esa cola de finalización un Thread de alta prioridad se encarga de sacar un elemento de la cola, ejecutar su finalizador y marcarlo como listo para ser recolectado.

Consideraciones a tener en cuenta:

· Cuando el objeto está listo para ser finalizado se lleva a la cola de ReadyToFinalize y además todos los objeto de su grafo son marcados. Eso significa que si ese grafo de objeto normalmente moriría en esta recolección, ahora es promovida a la siguiente, simplemente porque su objeto raíz es finalizable.

· Las generaciones más viejas son recolectadas menos que las generaciones más jóvenes lo que indica que nuestro grafo de objetos puede permanecer vivo durante mucho tiempo hasta que es completamente finalizado y recolectado.

· Al utiliza un único thread, aunque está en prioridad alta, puede darse el caso de que el si tenemos una maquina con 32 procesadores y solamente tenemos una finalizado objetos la cola pueda crecer mucho.

· Hay que tener mucho cuidado con lo que se ejecuta en el finalizador porque si se produce una excepción el proceso entero se descarga. No estamos hablando de que se descargue el dominio de aplicación sino todo el proceso.

Garantías de finalización:

· Durante la ejecución normal de la aplicación y durante cada recolección de basura, se llamara a los finalizadores de los objetos que haya en la cola.

· Si el proceso termina sin la coordinación del CLR, llamando a TerminateProcess o ExitProcess, el CLR tendrá su primera noticia de que el proceso se está cerrando cuando se envíe el mensaje DLL_PROCESS_DETACH en el DllMain. Así que el CLR no tiene tiempo de hacer nada y lo único que puede pasar es que termine los finalizadores críticos (CriticalFinalizerObject).

· Si el proceso es cerrado con el pleno conocimiento del CLR, llamado a Exit o Enviroment.Exit, se finalizarán los objetos que están en la cola de finalización y los finalizadores críticos. Pero los posibles objetos que salgan de una recolección de basura listos para ser finalizados se quedarán sin ser finalizados.

· Si se descarga el dominio de aplicación entonces solamente se finalizarán los objetos que estuvieran en la cola de finalización y los finalizadores criticos.

El codigo del ejemplo: http://www.luisguerrero.net/downloads/ctor.zip

Saludos. Luis.

[Curso] WPF para programadores de Windows Forms – Parte 4

Hola de nuevo a todos, continuamos con el curso de WPF para programadores de Windows Forms, en esta cuarta entrega del curso vamos a empezar a ver cosas mucho más prácticas que teóricas de WPF.

En el post anterior vimos como se creaba una ventana en WPF y como se creaba un objeto System.Windows.Application para empezar a utilizar esa ventana. En este post nos vamos a centrar en ver cuáles son las características que Application nos ofrece y Window, además de ver los dos tipos de aplicaciones que tenemos disponibles en WPF: Windows Application y Browser Application.

Lo que hemos visto hasta ahora son aplicaciones de WPF de ventana “Windows Application” y la ventana representa el contenedor de más alto nivel de la aplicación, este es el modo tradicional de desarrollo de aplicaciones. Después tenemos WPF Browser Application que como su nombre indica la aplicación de wpf es hosteada en un navegador como Internet Explorer o Firefox. Eso que significa que ya no tenemos ventana sino que tenemos un host que ya no es Windows sino una ventana de un navegador y dentro de esa ventana podemos programar.

Yo me voy a centrar en el desarrollo de aplicaciones de escritorio por ser las más comunes. Vamos a empezar a ver cuáles son los métodos, propiedades y eventos que Window define en su ámbito de clase. Nos vamos a centrar en los métodos o propiedades que no estaban disponibles en WF o que son específicos de WPF. Entre los métodos más interesantes tenemos:

  • DragMove, permite que cuando se llame a este método se pueda mover la ventana sin necesita de hacer click en la barra de título.
  • OnContentChanged, método que se llama cuando el contenido de la ventana se modifica, (recordemos que Windows hereda de ContentControl)
  • OnContentRendered, lanza el event ContentRedered.
  • OnCreateAutomationPeer, este método crea un objeto WindowAutomationPeer que se utiliza para hacer pruebas de la UI.

Ahora vamos a ver las propiedades y dependency property

  • AllowsTransparency, es una propiedad que indica si la ventana soporta transparencia.
  • OwnedWindows y Owner, son, respectivamente, la lista de ventanas de las que esta ventena es propietario y el propietario de la ventana actual, esta propiedad es usada para los diálogos modales en WPF.
  • SizeToContent, esta propiedad establece como se va a resizar la ventana en sí, basada en el contenido que muestra.

Vamos ahora a ver algunos ejemplos de ventanas creadas Visual Studio.

image

image

image

Todas estas ventanas están creadas en el mismo proyecto de Visual Studio, solamente se puede seleccionar una ventana al inicio y si queremos ir cambiando que ventana queremos iniciar tenemos que irnos al fichero App.xaml que contiene el Xaml del objeto Application de nuestro proyecto. Dentro de este fichero App.Xaml hay un atributo en el nodo Application StartupUri que es la Uri (Uniform Resource Identifier) de inicio de la aplicación, en mi caso está en VentanaNormal.xaml pero se puede cambiar por cualquier uri que contenga una ventana.

Aquí teneis el proyecto de Visual Studio con los tres ejemplos de ventanas creadas en WPF. [CursoWPF4.zip]

Espero que os guste.

[Curso] WPF para programadores de Windows Forms – Parte 1

Hola a todos, como programador de Windows Forms, a veces trabajando con WPF me encuentro con problemas que en Windows Forms solucionaba de una manera muy rápida y también me encuentro con problemas que en Windows Forms en WPF son muy sencillos de solucionar. Pues bien esta serie de post son una introducción a WPF para programadores de Windows Forms, que cosas son iguales, similitudes y en que características se diferencian.

Tenemos que decir que Windows Forms es la tecnología que viene con el .Net Framework desde la versión 1.0 y sigue actualmente. Es la tecnología de Microsoft para realizar formularios en Windows y para que lo entendamos completamente es un wrapper muy grande de la api de Win32. Aunque Windows Forms es completamente administrado hay que decir que es compatible con las tecnologías actuales, puedes interpolar con COM y hacer llamadas al sistema operativo, además de que soporta el procesado de mensajes de Windows, como cualquier aplicación Win32.

En contra tenemos WPF que, según Microsoft, es el modelo de programación de la siguiente generación de aplicaciones de escritorio, es como una versión mucho más potente de Windows Forms. Pero no se queda solo en esto las similitudes y las diferencias son a primera vista muy grandes pero en verdad son tecnologías muy similares.

Cualquier programador de C o C++ de Windows sabrá perfectamente cómo funciona el procesado de mensajes de Windows, hay un método en la API de Win32 sendmessage que nos permite enviar mensajes personalizados y que Windows lo enrute al elemento correspondiente.

LRESULT SendMessage(HWND hWnd,     UINT Msg,     WPARAM wParam,     LPARAM lParam );

Esta es la manera que tiene Windows de funcionar con la UI de cualquier programa pues se envía mensajes para cualquier evento que se produzca en el sistema, como por ejemplo:

  • El ratón a entrado en el área de una ventana
  • Se ha movido el ratón
  • Se ha hecho click

Y por ejemplo, otros mensajes no tan sutiles o perceptibles con el usuario como:

  • Se ha conectado un dispositivo USB
  • Se ha creado un proceso
  • Se ha enchufado el adaptador de corriente del equipo (portátil)

Windows Forms y WPF son dos tecnologías que siguen utilizando este procesado de mensaje y lo podemos ver presente claramente en las dos tecnologías.

En Windows Forms podemos sobrescribir una función dentro de la clase System.Windows.Forms.Control llamada WndProc(ref Message m) que justamente lo que hace es procesar esos mensajes de la interfaz de usuario, para que, por ejemplo, cuando alguien pulse en un botón, esta función capture ese mensaje y lance el evento Click. Por eso nosotros no tenemos que estar comprobando cada cierto tiempo que alguien ha pulsado el botón, es porque ese método lo hace por nosotros, los programadores de C++, seguro, conocerán muy bien esta función por ser una función en la que suele haber un switch muy grande que dependiendo del mensaje (UINT Msg) así se llamada a una función o a otra. Aquí tenemos un ejemplo de ese procesado de mensajes de Windows en C++.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {     int wmId, wmEvent;     PAINTSTRUCT ps;     HDC hdc;      switch (message)     {     case WM_COMMAND:         wmId    = LOWORD(wParam);         wmEvent = HIWORD(wParam);         switch (wmId)         {         case IDM_ABOUT:             DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);             break;         case IDM_EXIT:             DestroyWindow(hWnd);             break;         default:             return DefWindowProc(hWnd, message, wParam, lParam);         }         break;     case WM_PAINT:         hdc = BeginPaint(hWnd, &ps);         EndPaint(hWnd, &ps);         break;     case WM_DESTROY:         PostQuitMessage(0);         break;     default:         return DefWindowProc(hWnd, message, wParam, lParam);     }     return 0; }

WPF tampoco se queda lejos de este comportamiento, de hecho está también muy integrado ese concepto en WPF. Si nos fijamos en un control cualquiera de WPF, por ejemplo Label, y vemos su árbol de herencia, en concreto de la clase de la cual hereda System.Object.

System.Object   System.Windows.Threading.DispatcherObject     System.Windows.DependencyObject       System.Windows.Media.Visual         System.Windows.UIElement           System.Windows.FrameworkElement             System.Windows.Controls.Control               System.Windows.Controls.ContentControl                 System.Windows.Controls.Label

Esta clase System.Windows.Threading.DispatcherObject, es una clase especial pues es un sistema de procesado de mensajes implementados por un Dispatcher, de hecho funciona igual que el procesador de menajes de Windows Forms y que el método WndProc, y WPF utiliza los mensajes de Win32 para enviarse mensajes entre los hilos y de la UI. Si nos fijamos bien en la clase nos damos cuenta de que es una clase abstracta y por tanto no tiene implementación en todos los métodos, si buscamos con el Reflector si existe un método igual en Windows Forms (WndProc) resulta que tal método no existe. Pero se nos ha asegurado que WPF utiliza este mismo sistema que las demás aplicaciones de Windows, así que ¿dónde está ese método especial?. El hecho de que esta clase sea abstracta no es casual porque resulta que este sistema de Dispatcher puede funcionar de varias maneras, si exploramos con el reflector y vemos las clases que heredan de esta (DispatcherObject) nos encontramos en un namespace interno de Microsoft una clase llamada HwndWrapper (MS.Win32.HwndWrapper) que hereda de DispatcherObject y resulta que esta clase tiene un método WndProc. Bingo. Hemos encontrado la clase que procesa estos mensajes de Windows en las aplicaciones de WPF de la misma manera que lo hace Windows Forms y C++.

Como os he dicho Windows Forms y WPF están construidas de la misma manera en Windows, y el hecho de que las dos sean “HwndReady” permite que podamos interpolar con Windows de manera transparente y el caso es que en una aplicación de Windows Forms podemos hostear un control de WPF y al revés, en WPF podemos hostear un control de Windows Forms. Esto aunque puede parece algo obvio, es una característica que hace que estas dos tecnologías que aunque a priori sean muy diferentes resulta que son muy parecidas y pueden comunicarse de una manera muy transparente.

En el siguiente post sobre como conocer WPF para programadores de Windows Forms haremos una comparación entres los elementos básicos para la UI, las ventanas, los controles, los eventos de teclado y ratón, como se dibujan las aplicaciones y los enlaces a datos.

Espero que os guste!!.

Luis.

Experiencia de uso de .Net Framework 3.5 SP1

Como ya comente en otro post, ya ha salido el .net Framework 3.5 SP1 y el Visual Studio 2008 SP1, pues bien quiero comentar mi experiencia de uso desde que lo instale en mi portátil. Tengo que aclarar que mi portátil es tanto de uso profesional como de uso personal, así que es importante para mí que este service pack no rompa las aplicaciones que estoy desarrollando.

Voy a comentar desde varios punto de vista que características incluye este service pack que no se han comentado en los post oficiales de Microsoft y cosas que yo considero importantes.

Depuración

Dentro de la sección de depuración hay varias cosas interesantes a mencionar pero solo una negativa a comentar.

La única pega que tiene que el SP1 es que al cambiar las versiones de los ensamblados del framework la depuración con el código fuente del framework no está disponible porque no podemos bajarnos los símbolos de depuración con la información del código fuente de Microsoft. Por ejemplo, mscorlib.dll tiene de versión 2.0.50727.3031 (netfxsp.050727-3000).

Por lo demás vamos a comentar cosas positivas en el área de depuración que incluye el SP1 de Visual Studio 2008.

Dentro del menú de herramientas -> Opciones -> Depuración tenemos una opción interesante.

  • Enable .Net Framework source stepping, que nos permite depurar el código fuente del framework simplemente marcando esta opción.

image

Dentro de lo que es la depuración normal de una aplicación nos podemos encontrar con llamadas complejas que a su vez llaman a otras funciones que devuelven parámetros para pasarlo a la función principal, pues bien ahora podemos seleccionar en qué función queremos saltar, por ejemplo tenemos este trozo de código.

   1: Parse(RequestManager.Current.MakeRequestOfertas());

En el que se realizan tres llamadas a funciones, Parse, al singleton de RequestManager y al método MakeRequesOfertas, pues bien imaginemos que queremos depurar pero solo nos interesa la función MakeRequestOfertas, normalmente pulsado F11 y entrado dentro de las funciones pasaríamos por el singleton del RequestManager.Current todas las veces, lo que puede hacerse un poco molesto, pues ahora en el menú contextual podemos especificar a qué función saltar.

image

Siguiendo con esta característica del depurador de Visual Studio, una de las cosas más molestas cuando se depura código es entrar dentro de las propiedades cuando se está depurando código lo que nos hace perder un tiempo en recorrer todas las instrucciones que hay dentro de la propiedad. Dentro del framework hay un atributo que nos permite decirle al depurador que no salte dentro del código de una función para no perder tiempo en depurarla, este atributo System.Diagnostics.DebuggerStepThroughAttribute únicamente se puede usar para clases, estructuras, constructores y métodos. Pues bien ahora con el SP1 del Visual Studio 2008, en el menú contextual en modo depuración tenemos una opción que nos permite que habilitar la característica de saltar sobre las propiedades y sobrecarga de operadores (Step over properties and operators).

Otra característica más del depurador es que ahora podemos hacer depuración remota con Windows Vista, cosa que antes no era posible. Además de esto se han corregido problemas con la depuración de aplicaciones multihilo.

La depuración de consultas LINQ también se ha mejorado, permitiendo ver los resultados de una consulta LINQ directamente con el depurador, pero no se soporta la edición en tiempo de ejecución durante la depuración

image

Como últimas características, se ha mejorado la depuración de aplicaciones de WCF y de scripts, como por ejemplo JavaScript

Runtime WPF

Otra de las características que nos prometían con este service pack es un mejor rendimiento de las aplicaciones de WPF, y lo han conseguido.

Si miramos a la arquitectura actual de WPF, hay un componente no administrador dentro del arquitectura milcore.dll que es la api responsable de convertir las llamadas de dibujado de geometría de WPF a código que llama a DirectX, esto permite que nuestras aplicaciones en WPF estén aceleradas por hardware de una manera transparente, es también esta milcore.dll uno de los principales componentes que utiliza el Desktop Windows Manager para componer la interfaz de usuario de Windows Vista y permitirnos que podamos hacer las pre visualizaciones de las aplicaciones y el Switch3D.

 

image

image

Pues bien ahora en una aplicación WPF del .net Framework 3.5 SP1, milcore.dll ha desaparecido de los módulos cargados y no está en la pila de ejecución de tu proceso actual. ¿Qué es lo que hay ahora?, pues si nos fijamos en los Threads que hay en una aplicación cualquiera de WPF vemos que hay una nueva dll llamada wpfgfx.dll, que es la que se encarga de dibujar los gráficos en WPF. Esto es un cambio de concepto interesante porque ahora el modelo de dibujado de una aplicación WPF es multihilo. Esto que significa, pues que el hilo principal de tu aplicación, cuando solicite el dibujado de un control o una animación enviará un mensaje a este thread que será el responsable de dibujar esa petición, lo que hace que se mejore sensiblemente el tiempo de respuesta de una aplicación WPF y además su rendimiento.

image

Cosas a decir de wpfdfx.dll, pues es una dll no administrada, osea completamente nativa y si nos fijamos en las importaciones que hace esta dll no encontramos ninguna referencia a d3d9.dll (DirectX), pero si encontramos referencias a GDI32.dll, Knerl32.dll, Winmm.dll (Windows Multimedia) y WindowsCodects.dll (Infraestructura de codecs de imagen de WPF). Pero si somos un poco curiosos y desemsamblamos al dll con IDA vemos que no hace referencia directamente al d3d9.dll pero si lo carga con LoadLibrary y después emplaza los métodos con GetProcAddress.

 

Espero que os guste !!

Crear una evidencia de seguridad personalizada

La seguridad del framework esta basada en evidencias de seguridad que le dicen al engine de seguridad como procesar los permisos disponibles dentro del dominio del ensamblado. Hay dos niveles de evidencias de seguridad dentro del framework, a nivel del dominio de aplicación que se puede consultar con la propiedad AppDomain.Evidence que esta en System.AppDomain.CurrentDomain.Evidence y a través de cada uno de los ensamblados que hay cargardos, en System.Reflection.Assembly.Evidence.

Hay una serie de clases que vienen con el framework que se utilizan como evidencias:

  • Application directory, el directorio donde esta la aplicación
  • Hash, un hash criptografico de SH1
  • Publisher, certificado de la entidad que publica el software
  • Site, sitiode origen del sitio como http://www.luisguerrero.net/
  • Strong name, nombre seguro utilizado.
  • Url, url de origen
  • Zone, zona de origen.

Si se hiciera un símil con la vida real, la evidencia de seguridad es algo así como de donde venidos y quien somos o como vamos vestidos. Imaginemos por un momento que queremos entrar a un bar, si hiciéramos un símil con el sistema de seguridad, el bar es el recurso al cual nosotros queremos acceder, el portero es el engine de seguridad y nosotros somos el método que queremos acceder a ese recurso. El portero tiene una política de seguridad por la cual si se encuentra con una función (persona) que intenta acceder al sistema (bar) pero está vestido con zapatillas (evidencia) no le permita el acceso, lo deniegue (Deny). Las políticas de seguridad asocian evidencias de seguridad con permisos específicos, o grupos de permisos.

Imaginemos por un momento que queremos estamos definiendo el sistema de seguridad de nuestra aplicación y vamos a cargar una serie de ensamblados de terceros, que representan un sistema de plugins para nuestra aplicación. Nosotros queremos controlar el acceso a los recursos en base a unas determinadas evidencias de seguridad que los ensamblados que cargamos llevan implícitas. Las evidencias que el framework nos proporciona no son suficientes para definir esta seguridad personaliza así que necesitamos definir nuestras propias evidencias de seguridad.

Creando evidencias personalizadas

No hay ninguna clase de la que debamos derivar si queremos crear nuestra evidencia personalizada sino que hay que seguir una serie de reglas para que la evidencias sea efectiva.

  • Haz la evidencia sencilla. Las clases que representan la evidencia tiene que ser simples, tienen que representar una pieza de información, algo muy concreto. Cuanto más complejo sea la evidencia más compleja será la manera de usarla.
  • Haz la evidencia liguera. Cuando el runtime carga un ensamblado, carga también las evidencias que contiene el ensamblado y estas están disponibles durante la ejecución de la aplicación o hasta que el dominio de aplicación se descarga (es la única manera de descargar ensamblados), así que es importante hacer la evidencia simple y ligera para que no ralentice la carga del ensamblado.
  • Haz la evidencia serializable. Las evidencias se serializan en binario y se introducen como metadatos dentó del ensamblado, así que si la evidencia es sencilla no necesitarás una seralización personalizada, así que marcarlo con el atributo System.SerializableAttribute, sino habrá que hacer una serialización personalizada a través de la implementación de la interfaz System.Runtime.Serialization.ISerializable.
  • Haz la evidencia sellada (sealed). Si la evidencia no está sellada (sealed in C#), un atacante puede derivar de tu evidencia y modificarla. Todas las evidencias vistas hasta ahora del framework lo están.
  • No asumas valores predeterminados. Cuando definas la evidencia nunca la inicialices con valores predeterminados pues estarás asumiendo unos valores que después se utilizarán en el engine de seguridad del framework lo que puede conllevar un riesgo para la seguridad.
  • Haz la evidencia inmutable. Una vez que se crea la evidencia, de esto es encargado el cargador de ensablamdos, la referencia y los valores de la evidencia no pueden ser modificados porque si lo hicieran estos valores son justamente los que se utilizan para controlar la seguridad.
  • Sobrescribe ToString(). Todas las evidencias tienes que sobrescribir el método ToString para generar un representación de si misma en XML.
  • Genera permisos de identidad. Puedes implementar la interfaz System.Security.Policy.IIdentityPermissionfactory para calcular los permisos que tienes a través de la evidencia de seguridad.

Una vez sabido esto vamos a definir una evidencia personalizada llamada Autor. Esta evidencia personalizada va a contener el nombre del autor que ha generado el ensamblado para después permitir al ensamblado acceder a determinadas partes del sistema.

La definición de la clase sería así

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Security;
   6: using System.Reflection;
   7:  
   8: [assembly: AssemblyKeyFile("Key.snk")]
   9: [assembly: AssemblyVersion("1.0.0.0")]
  10:  
  11: namespace ConsoleApplication1
  12: {
  13:     
  14:  
  15:     [Serializable]
  16:     public sealed class Autor
  17:     {
  18:         private readonly string autorNombre = string.Empty;
  19:  
  20:         public string AutorNombre
  21:         {
  22:             get { return autorNombre; }
  23:         } 
  24:  
  25:         public Autor(string nombre)
  26:         {
  27:             if (string.IsNullOrEmpty(nombre))
  28:             {
  29:                 throw new ArgumentException("Nombre no peude ser nulo o vacio", new NullReferenceException());
  30:             }
  31:             else
  32:             {
  33:                 this.autorNombre = nombre;
  34:             }
  35:         }
  36:         public override string ToString()
  37:         {
  38:             SecurityElement elemt = new SecurityElement(GetType().FullName);
  39:             elemt.AddAttribute("Version", "1");
  40:             if (!string.IsNullOrEmpty(autorNombre))
  41:             {
  42:                 elemt.AddChild(new SecurityElement("Autor", autorNombre));
  43:             }
  44:             return elemt.ToString();
  45:         }
  46:     }
  47: }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Una vez que se tiene definido la evidencia esta hay que usarla dentro de los ensamblados. Para poder usar la evidencia dentro dentro de un ensamblado hay que hacer una serie de pasos previos para poder usar la evidencia.

El proceso para usar la evidencia sería el siguiente:

  • Crear una colección de evidencias.
  • Crear el objeto que se quiere añadir a la colección de evidencias.
  • Añadir las evidencias a la colección de evidencias usando el método AddAssembly
  • Serializar la colección de evidencias con un serializador binario.
  • Compilar el código como un modulo y no como un ensamblado, a través del modificador del compilador /target:module
  • Combinar los modulos y las envidencias dentro de un ensamblado a través de la herramienta al.exe Assembly Linker.

Para poder hacer eso hay que tener en cuenta que en ensamblado donde se referencia la evidencia tiene que ser de confianza total para el sistema, eso se puede conseguir añadiéndolo a la GAC, o a través de los siguiente comandos de caspol.

  • caspol –user –addfulltrust Autor.dll
  • caspol –machine –addfullturst Autor.dll
  • caspol –enterprise –addfulltrust Autor.dll

Una vez hecho esto hay que serializar una instancia de la evidencia para usarla.

   1: static void Main(string[] args)
   2: {
   3:     Evidence evidence = new Evidence();
   4:  
   5:     Autor autor = new Autor("Luis");
   6:  
   7:     evidence.AddAssembly(autor);
   8:  
   9:     string fileName = autor.AutorNombre + ".evidence";
  10:  
  11:     BinaryFormatter bf = new BinaryFormatter();
  12:     FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
  13:     bf.Serialize(fs, evidence);
  14:     fs.Dispose();
  15:  
  16: }

Conseguido esto ahora hay que combinar un modulo de un ensamblado con la evidencia previamente generada. Para ello tenemos que ejecutar esta herramienta intermedia que genere el fichero .evidence que incluiremos en el ensamblado que queremos usar.

Después con la herramienta al.exe hay que combinarlos todos.

   1: al /target:exe /out:ejemplo.exe /main:Ejemplo.Main /evidence:Luis.evidence Ejemplo.netmodule 

 

    Una vez creado esto lo que tenemos que hacer es simplemente crear una politica que utilice esta evidencia de seguridad en nuestro sistema.

     

    Saludos. Luis.

    Usar un origen http en un BitmapImage ralentiza tu aplicaci&oacute;n WPF

    Quiero contar una experiencia que me ha ocurrido recientemente. Como ya comente en otra ocasión estoy terminando el Infotouch, pues os quiero contar un problema que he tenido de rendimiento.

    Esta aplicación hace un uso intensivo de imágenes, las que la mayoría están online en un servidor y suelen cambiar cada día.

    En WPF hay un control Image (System.Windows.Controls.Image) que utiliza como origen una clase BitmapImage (System.Windows.Media.Imaging.BitmapImage), pues bien en esta última clase en el constructor puedes pasarle por parametro una Uri, con la url del recurso a establecer. Pues bien es aquí donde empieza el problema, en algunos casos suelo utilizar aproximadamente 300 instancias diferentes de BitmapImage y todas las Uri son con http, cuando ejecuto la aplicación empiezo a ver que el rendimiento baja cuando tiene que cargar todas esas imagenes, y no estoy hablando de que tarde en descargarlas ni en dibujarlas, eso lo espero, sino que trabajando en local incluro con url de localhost la aplicación tarda mucho en cargarse y se lazan pues unas 800 excepciones en mi codigo.

    Empiezo a investigar, depuro con el Visual Studio y en la ventana de salida encuentro esto:

       1: A first chance exception of type 'System.Deployment.Application.InvalidDeploymentException' occurred in System.Deployment.dll
    
       2: A first chance exception of type 'System.Deployment.Application.DeploymentException' occurred in System.Deployment.dll
    
       3: A first chance exception of type 'System.Deployment.Application.InvalidDeploymentException' occurred in System.Deployment.dll
    
       4: A first chance exception of type 'System.Deployment.Application.DeploymentException' occurred in System.Deployment.dll
    
       5: A first chance exception of type 'System.Deployment.Application.InvalidDeploymentException' occurred in System.Deployment.dll
    
       6: A first chance exception of type 'System.Deployment.Application.DeploymentException' occurred in System.Deployment.dll
    
       7: A first chance exception of type 'System.Deployment.Application.InvalidDeploymentException' occurred in System.Deployment.dll
    
       8: A first chance exception of type 'System.Deployment.Application.DeploymentException' occurred in System.Deployment.dll
    
       9: A first chance exception of type 'System.Deployment.Application.InvalidDeploymentException' occurred in System.Deployment.dll
    
      10: A first chance exception of type 'System.Deployment.Application.DeploymentException' occurred in System.Deployment.dll
    
      11: A first chance exception of type 'System.Deployment.Application.InvalidDeploymentException' occurred in System.Deployment.dll
    

    Esas excepciones que se lanzan son causadas porque se intenta acceder a la propiedad System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed o a System.Deployment.Application.ApplicationDeployment.CurrentDeployment y en la implementación de esas dos propiedades se intenta acceder a la información de despliegue con ClicOnce.

    Pues bien sabiendo esta información uno puede pensar, estas programando con clicOnce y está haciendo algo más, pues resulta que clicOnce no se utiliza en el proyecto, incluso el ensamblado System.Deployment no está referenciado con lo cual eso tiene que estar en código de Microsoft.

    Vamos a investigar.

    Para poder depurar este código lo único que sabemos es que se lanza una excepción pero no se propaga sino que está dentro de un catch, ¿Como podemos hacer que Visual Studio se pare en ese trozo de código?, la pregunta es dificil, pero se puede hacer. Se puede hacer algo parecido, ya que no disponemos del código fuente del .net framewokr (si la tenemos, pero supongamos que no) tenemos que usar el WinDbg. Lo abrimos y cargamos la dll de depuración de código administrador en Windbg (sos.dll) a través del comando .loadby sos mscorwks una vez que tenmos eso tenemos que decirle al Windbg que se pare cuando una excepción del tipo System.Deployment.Application.InvalidDeploymentException ocurra, pues bien eso se puede hacer con el comando, !soe -create System.Deployment.Application.InvalidDeploymentException 1 (soe de StopOnException), así que una vez que el Windbg se para analizamos la excepción con !analyze -v y nos encontramos con esto:

       1:  
    
       2: FAULTING_IP: 
    
       3: KERNEL32!RaiseException+58
    
       4: 766742eb c9              leave
    
       5:  
    
       6: EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
    
       7: ExceptionAddress: 766742eb (KERNEL32!RaiseException+0x00000058)
    
       8:    ExceptionCode: e0434f4d (CLR exception)
    
       9:   ExceptionFlags: 00000001
    
      10: NumberParameters: 1
    
      11:    Parameter[0]: 80131501
    
      12:  
    
      13: FAULTING_THREAD:  00000f78
    
      14:  
    
      15: DEFAULT_BUCKET_ID:  CLR_EXCEPTION
    
      16:  
    
      17: PROCESS_NAME:  infotouch2.exe
    
      18:  
    
      19: ERROR_CODE: (NTSTATUS) 0xe0434f4d - <Unable to get error code text>
    
      20:  
    
      21: NTGLOBALFLAG:  70
    
      22:  
    
      23: APPLICATION_VERIFIER_FLAGS:  0
    
      24:  
    
      25: MANAGED_STACK: 
    
      26: (TransitionMU)
    
      27: 011EDDF4 6A325FC8 System_Deployment_ni!System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment()+0xf4
    
      28: 011EDE28 6A326056 System_Deployment_ni!System.Deployment.Application.ApplicationDeployment.get_IsNetworkDeployed()+0x1a
    
      29: 011EDE54 53943BD2 PresentationCore_ni!MS.Internal.AppModel.SiteOfOriginContainer.get_SiteOfOriginForBrowserApplications()+0x62
    
      30: 011EDE60 539E289D PresentationCore_ni!MS.Internal.PresentationCore.SecurityHelper.ExtractUriForClickOnceDeployedApp()+0x15
    
      31: 011EDE64 539E28DC PresentationCore_ni!MS.Internal.PresentationCore.SecurityHelper.BlockCrossDomainForHttpsApps(System.Uri)+0x34
    
      32: 011EDE7C 53B6908A PresentationCore_ni!System.Windows.Media.Imaging.BitmapDownload.BeginDownload(System.Windows.Media.Imaging.BitmapDecoder, System.Uri, System.Net.Cache.RequestCachePolicy, System.IO.Stream)+0x502
    
      33: 011EDF20 53B77A3F PresentationCore_ni!System.Windows.Media.Imaging.LateBoundBitmapDecoder..ctor(System.Uri, System.Uri, System.IO.Stream, System.Windows.Media.Imaging.BitmapCreateOptions, System.Windows.Media.Imaging.BitmapCacheOption, System.Net.Cache.RequestCachePolicy)+0xf3
    
      34: 011EDF44 53D015FD PresentationCore_ni!System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(System.Uri, System.Uri
    
      35: EXCEPTION_OBJECT: !pe 12d8faa8
    
      36: Exception object: 12d8faa8
    
      37: Exception type: System.Deployment.Application.InvalidDeploymentException
    
      38: Message: Application identity is not set.
    
      39: InnerException: <none>
    
      40: StackTrace (generated):
    
      41: <none>
    
      42: StackTraceString: <none>
    
      43: HResult: 80131501
    
      44:  
    
      45: MANAGED_OBJECT: !dumpobj 33ec694
    
      46: Name: System.String
    
      47: MethodTable: 790fd8c4
    
      48: EEClass: 790fd824
    
      49: Size: 82(0x52) bytes
    
      50:  (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    
      51: String: Application identity is not set.
    
      52: Fields:
    
      53:       MT    Field   Offset                 Type VT     Attr    Value Name
    
      54: 79102290  4000096        4         System.Int32  1 instance       33 m_arrayLength
    
      55: 79102290  4000097        8         System.Int32  1 instance       32 m_stringLength
    
      56: 790ff328  4000098        c          System.Char  1 instance       41 m_firstChar
    
      57: 790fd8c4  4000099       10        System.String  0   shared   static Empty
    
      58:     >> Domain:Value  01300268:790d884c <<
    
      59: 7912dd40  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    
      60:     >> Domain:Value  01300268:02ca1388 <<
    
      61:  
    
      62: EXCEPTION_MESSAGE:  Application identity is not set.
    
      63:  
    
      64: LAST_CONTROL_TRANSFER:  from 79f071ac to 766742eb
    
      65:  
    
      66: PRIMARY_PROBLEM_CLASS:  CLR_EXCEPTION
    
      67:  
    
      68: BUGCHECK_STR:  APPLICATION_FAULT_CLR_EXCEPTION
    
      69:  
    
      70: STACK_TEXT:  
    
      71: 6a325fc8 System_Deployment_ni!System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment
    
      72: 6a326056 System_Deployment_ni!System.Deployment.Application.ApplicationDeployment.get_IsNetworkDeployed
    
      73: 53943bd2 PresentationCore_ni!MS.Internal.AppModel.SiteOfOriginContainer.get_SiteOfOriginForBrowserApplications
    
      74: 539e289d PresentationCore_ni!MS.Internal.PresentationCore.SecurityHelper.ExtractUriForClickOnceDeployedApp
    
      75: 539e28dc PresentationCore_ni!MS.Internal.PresentationCore.SecurityHelper.BlockCrossDomainForHttpsApps
    
      76: 53b6908a PresentationCore_ni!System.Windows.Media.Imaging.BitmapDownload.BeginDownload
    
      77: 53b77a3f PresentationCore_ni!System.Windows.Media.Imaging.LateBoundBitmapDecoder..ctor
    
      78:  
    
      79:  
    
      80: FOLLOWUP_IP: 
    
      81: System_Deployment_ni+5fc8
    
      82: 6a325fc8 8b4dd4          mov     ecx,dword ptr [ebp-2Ch]
    
      83:  
    
      84: SYMBOL_STACK_INDEX:  0
    
      85:  
    
      86: SYMBOL_NAME:  System_Deployment_ni!System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment+5fc8
    
      87:  
    
      88: FOLLOWUP_NAME:  MachineOwner
    
      89:  
    
      90: MODULE_NAME: System_Deployment_ni
    
      91:  
    
      92: IMAGE_NAME:  System.Deployment.ni.dll
    
      93:  
    
      94: DEBUG_FLR_IMAGE_TIMESTAMP:  47577e63
    
      95:  
    
      96: STACK_COMMAND:  dds 11eddf4 ; kb
    
      97:  
    
      98: FAILURE_BUCKET_ID:  CLR_EXCEPTION_e0434f4d_System.Deployment.ni.dll!System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment
    
      99:  
    
     100: BUCKET_ID:  APPLICATION_FAULT_CLR_EXCEPTION_System_Deployment_ni!System.Deployment.Application.ApplicationDeployment.get_CurrentDeployment+5fc8
    
     101:  
    
     102: Followup: MachineOwner
    

    La pila nos encontramos con MS.Internal.PresentationCore.SecurityHelper.BlockCrossDomainForHttpsApps(System.Uri) que llama a MS.Internal.PresentationCore.SecurityHelper.ExtractUriForClickOnceDeployedApp(), ahí está todo el problema, resulta que para descargarse una imagen el framewokr necesita comprobar que la aplicación esta desplegada con ClicOnce, pero ¿Porqué?. Si es una aplicación de escritorio normal porque tiene que hacer eso si simplemente lo que quiero es descargar la imagen. Si vemos la implementación de ese método nos encontramos con esto:

       1: internal static void BlockCrossDomainForHttpsApps(Uri uri)
    
       2: {
    
       3:     Uri uri2 = ExtractUriForClickOnceDeployedApp();
    
       4:     if ((uri2 != null) && (uri2.Scheme == Uri.UriSchemeHttps))
    
       5:     {
    
       6:         if (uri.IsUnc || uri.IsFile)
    
       7:         {
    
       8:             new FileIOPermission(FileIOPermissionAccess.Read, uri.LocalPath).Demand();
    
       9:         }
    
      10:         else
    
      11:         {
    
      12:             new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uri)).Demand();
    
      13:         }
    
      14:     }
    
      15: }
    
      16:  
    
      17:  
    
      18:  
    
      19:  
    
      20:  
    
      21:  
    
      22:  
    
      23: internal static Uri SiteOfOriginForBrowserApplications
    
      24: {
    
      25:     [FriendAccessAllowed]
    
      26:     get
    
      27:     {
    
      28:         Uri deploymentUri = null;
    
      29:         if (_debugSecurityZoneURL.Value != null)
    
      30:         {
    
      31:             return _debugSecurityZoneURL.Value;
    
      32:         }
    
      33:         if (_browserSource.Value != null)
    
      34:         {
    
      35:             return _browserSource.Value;
    
      36:         }
    
      37:         if (ApplicationDeployment.IsNetworkDeployed)
    
      38:         {
    
      39:             deploymentUri = GetDeploymentUri();
    
      40:         }
    
      41:         return deploymentUri;
    
      42:     }
    
      43: }
    

    En el que al final del todo se llama a ApplicationDeployment.IsNetworkDeployed causando que la aplicación lanza excepciones.

    ¿ Cómo se puede puede solucionar esto ?

    Pues de una manera muy sencilla, puesto que el problema es que el propio descargador de Bitmaps tiene que comprobar la seguridad si o sí pues descargemos nosotros esa imagen en memoria y dejemos al framework que la cargue desde un MemoryStream, así:

       1: WebClient wc = new WebClient();
    
       2: byte[] data = wc.DownloadData(new Uri("http://localhost/img.jpg"));
    
       3:  
    
       4: MemoryStream ms = new MemoryStream(data);
    
       5:  
    
       6: BitmapImage bi = new BitmapImage();
    
       7: bi.BeginInit();
    
       8:  
    
       9: bi.StreamSource = ms;
    
      10:  
    
      11: bi.EndInit();
    

    Luis.