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.
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.
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.
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
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.
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.
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ó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.
Infotouch 2.0
Además de mi trabajo en ilita, también hago otro tipo de proyectos aparte de la empresa, uno en el que llevaba bastante tiempo pero que no hay terminado era el infotouch. Pero ahora puedo mostrar la versión 2.0 de este software para buscar ofertas de viajes para agencias del grupo Almeida.
Os dejo unas capturas de pantalla para que veáis como ha quedado el software.