Luis Guerrero Hablando de .NET, Silverlight, WPF, Windows Phone 7 y depuración

17Jul/100

Silverlight 4 Metro Training (7/7) Dashboard de administración de eventos

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Dashboard de administración de eventos

Install Microsoft Silverlight

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

17Jul/100

Silverlight 4 Metro Training (5/7) Programador de tareas

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Programador de tareas

Install Microsoft Silverlight

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

17Jul/100

Silverlight 4 Metro Training (6/7) Impresión

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Impresión

Install Microsoft Silverlight

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

17Jul/100

Silverlight 4 Metro Training (4/7) Perfil de usuario

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Perfil de usuario

Install Microsoft Silverlight

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

17Jul/100

Silverlight 4 Metro Training (3/7) Registro de usuario

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Registro de usuario

Install Microsoft Silverlight

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

17Jul/100

Silverlight 4 Metro Training (2/7) Gestor de eventos

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Gestor de eventos

Install Microsoft Silverlight

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

17Jul/100

Silverlight 4 Metro Training (1/7) Que hay de nuevo en Silverlight 4

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

What’s New in Silverlight 4

Install Microsoft Silverlight

El resto del material:

Saludos.

Luis Guerrero.

30Apr/100

Detectando problemas de memoria con Silverlight

Hola a todos!

Hoy vamos a hablar sobre un memory leak encontrado en Silverlight 4 y reportado en el foro de silverilght.net por el usuario tsheflin. Como sabrás Silverlight está basado en tecnología .net lo que significa que puedes escribir tus aplicaciones en C# o en Visual Basic y tienes todas las cosas buenas de .net como gestión automática de memoria, recolección de elementos no utilizados y seguridad de tipos

Pero algunas veces algo sale más y aun teniendo la gestión automática de memoria de .net nos podemos encontrar con problemas, es por eso que hoy vamos a ver cómo podemos identificar estos problemas con WinDBG y sos para Silverlight.

Voy a usar como ejemplo el codigo subido por el usuario del foro de Silverlight.net. Puedes descargarlo desde aquí.

Lo primero de todo necesitamos ejecutar nuestra aplicación en modo release. Esto es importante porque hay pequeñas diferencias entre el modo debug y el release. Una vez que tenemos la aplicación ejecutándose es tiempo de WinDGB.

Si tu navegador es Windows Internet Explorer 8 y estás ejecutándolo sobre Windows 7 o Vista, sabrás que IE ejecuta cada tab en un proceso separado y por eso primero tienes que identificar cual es el proceso de IE que está ejecutando el código, en mi caso es el proceso 7958.

Con WinDBG abierto (puedes descargarlo de aquí) tienes que ir a File -> Attach to process (F6), y selecciona la id de tu proceso (en mi maquina es 7958), inmediatamente aparecerá en la ventana de salida toda la información básica de depuración. Teniendo en cuenta que nuestra aplicación es una aplicación administrada y WinDBG está diseñado para depurar aplicaciones nativas necesitamos una extensión del depurador para trabajar con aplicaciones administradas. Lo que necesitamos es SOS (Son of Strike) que está localizado en C:\Program Files (x86)\Microsoft Silverlight\4.0.50401.0\sos.dll. Para cada version de silverligh hay una versión de SOS, así que depende de la versión de Silveright que estés depurando tendrás que usar una u otra.

Para cargar SOS escribimos

.load C:\Program Files (x86)\Microsoft Silverlight\4.0.50401.0\sos.dll

Lo siguiente que necesitamos encontrar es una direccion valida del tipo que estamos buscando SilverlightVisualTreeRemovalFail.SilverlightControl1 y para eso tenemos que usar el comando !dumpheap para volcar todos los tipos filtrados por este tipo.

!dumpheap -type SilverlightVisualTreeRemovalFail.SilverlightControl1

0:005> !dumpheap -type SilverlightVisualTreeRemovalFail.SilverlightControl1

Address MT Size

087aad6c 04a45230 92

087ab460 04a45230 92

087ab6f0 04a45230 92

087ac01c 04a45230 92

087ac710 04a45230 92

087ace04 04a45230 92

087ad4f8 04a45230 92

087adbec 04a45230 92

087ae2e0 04a45230 92

087ae9d4 04a45230 92

087af0c8 04a45230 92

087af7bc 04a45230 92

087afeb0 04a45230 92

087b05a4 04a45230 92

087b0c98 04a45230 92

087b138c 04a45230 92

087b1a80 04a45230 92

087b2174 04a45230 92

087b2868 04a45230 92

087b2f5c 04a45230 92

087b3650 04a45230 92

087b3d44 04a45230 92

087b4438 04a45230 92

087b4b2c 04a45230 92

087b5220 04a45230 92

087b5914 04a45230 92

087b6008 04a45230 92

087b66fc 04a45230 92

087b6df0 04a45230 92

087b74e4 04a45230 92

087b7bd8 04a45230 92

087b82cc 04a45230 92

087b89c0 04a45230 92

087b90b4 04a45230 92

087b97a8 04a45230 92

087b9e9c 04a45230 92

087ba590 04a45230 92

087bac84 04a45230 92

087bb378 04a45230 92

087bba6c 04a45230 92

087bc160 04a45230 92

087bc854 04a45230 92

087bcf48 04a45230 92

087bd63c 04a45230 92

087bdd30 04a45230 92

087be424 04a45230 92

087beb18 04a45230 92

087bf20c 04a45230 92

087bf900 04a45230 92

087bfff4 04a45230 92

En la ventana de salida encontramos direcciones del tipo que hemos solicitado, seleccionamos una 087b1a80 y buscamos que objetos están referenciando este objeto (esto es lo que está causando que el objeto no sea recolectado) escribiendo esto:

!gcroot 087b1a80

0:005> !gcroot 087b1a80

Note: Roots found on stacks may be false positives. Run "!help gcroot" for

more info.

Scan Thread 5 OSTHread 10e8

Scan Thread 22 OSTHread 1f8

Scan Thread 23 OSTHread 1588

DOMAIN(07134BE0):HANDLE(Pinned):4a912f8:Root: 09784260(System.Object[])->

08796c78(System.Collections.Generic.Dictionary`2[[System.IntPtr, mscorlib],[System.Object, mscorlib]])->

09788260(System.Collections.Generic.Dictionary`2+Entry[[System.IntPtr, mscorlib],[System.Object, mscorlib]][])->

087b1e90(System.Windows.Controls.ControlTemplate)->

087b1a80(SilverlightVisualTreeRemovalFail.SilverlightControl1)

Encontramos que el objeto (087b1a80) está referenciado por otro objeto y en lo alto de la lista podemos encontrar cual es el objeto que lo está referenciando.

Ahora que hemos identificado que el objeto está referenciado por un GCHandle pineado el recolector siempre encontrará un camino hasta este objeto haciendo que nunca sea recolectado. Como el código que controla esta funcionalidad es de Microsoft no podemos hacer nada al respecto, pero en caso de que sea nuestro lo que podemos hacer es simplemente eliminar las referencias para que el objeto sea recolectado.

Además podemos volcar todos los tipos que hay en el heap y abrirlo con el CLRProfiler usando !traverseheap.

Saludos.

Luis Guerrero.

29Apr/100

El player de Tele5 es ahora un Microsoft Case Study

Hola a todos!

Trabajar como desarrollador no es fácil, los cliente están siempre nerviosos y quieren tener su software a tiempo siempre. Así que cuando descubres que el software que tú has hecho se ha convertido en un caso de éxito hay que contárselo a todo el mundo!

Unos meses atrás empezamos un reproductor de video en Silverlight para Tele5. Querían más flexibilidad para publicar su contenido, así que empezamos este reproductor de video. Empezamos antes de que MEF existiera, así que tuve que implementar mi propia arquitectura de plug-ins basada en conceptos similares a los que tiene MEF.

Así que aquí os dejo toda la información del caso de estudio y por supuesto un enlace al player para que lo podáis disfrutar.

http://www.microsoft.com/casestudies/Case_Study_Detail.aspx?casestudyid=4000007022 (EN)

http://www.microsoft.com/casestudies/Case_Study_Detail.aspx?casestudyid=4000007045 (ES)

http://www.informativos.telecinco.es/

Saludos a todos!

Luis Guerrero.

Filed under: Silverlight No Comments
13Nov/090

Silverlight, un mejor manejo de los servicios

Como todos es sabido Silverlight trae soporte para invocar servicios web, tanto servicios web tradicionales de .net como servicios de WCF. En esto últimos solo con soporte para httpBasicBinding.

Cada vez que generamos un proxy en Visual Studio para un proyecto de Silverlight, el proxy generado solo soporta invocaciones asíncronas. No podemos de ninguna manera hacer una invocación síncrona a un servicio web y esperar a la respuesta. Puede haber muchas maneras por las cuales Microsoft implemento este comportamiento predeterminado, pero creo que el más importante de todos es hacer que la UI de las aplicaciones de Silverlight *nunca* dejen de responder.

Así que puestos en antecedentes, podemos ahora estudiar nuestro problema e intentar mitigarlo. Cuando generamos un proxy en Visual Studio, lo que obtenemos es una función con el mismo nombre que la operación del servicio + “Async” y un evento que es el nombre de la operación + “Completed” en la que en los argumentos de respuesta tenemos el resultado de la invocación del servicio.

Algo como esto:

public partial class TraditionalServices : UserControl
{
    private ServiceClient service;
    public TraditionalServices()
    {
        InitializeComponent();

        service = new ServiceClient();
        service.GetDateCompleted += new EventHandler<GetDateCompletedEventArgs>(OnGetDateCompleted);
        service.GetEmployeeCompleted += new EventHandler<GetEmployeeCompletedEventArgs>(OnGetEmployeeCompleted);

        Loaded += new RoutedEventHandler(OnLoaded);
    }

    void OnLoaded(object sender, RoutedEventArgs e)
    {
        service.GetDateAsync();
    }

    void OnGetEmployeeCompleted(object sender, GetEmployeeCompletedEventArgs e)
    {

    }

    void OnGetDateCompleted(object sender, GetDateCompletedEventArgs e)
    {

    }
}

Conforme uno empieza a trabajar con los servicios de Silverlight encuentra que el código se puede volver un poco complicado puesto que no podemos invocar al servicio y después trabajar directamente con el resultado sino que lo tenemos que hacer en el método que se llamará cuando se termine la invocación del servicio.

Lo que os propongo en este artículo es que construyáis una clase Helper que os ayude a lidiar con estos problemas y hacer así el código un poco más claro.

Del código anterior para realizar la invocación a un servicio pasamos a esta:

service.GetDateTime(value =>
{
    string currentDateTime = value.ToString();
});

Como se puede apreciar el resultado es mucho mejor que la manera tradicional de trabajar con los proxies además de que el código queda mucho más claro.

¿Cómo está este helper implementado?

La idea es usar expresiones Lambda para hacer el código más legible, lo único que necesitamos es un delegado de tipo Action<T> en el que T será del mismo tipo del resultado de nuestra invocación al servicio web.

public void GetDateTime(Action<DateTime> value)
{
    service.GetDateAsync(value);
}

Con este simple patrón podemos simplificar las llamadas al servicio web y esconder su complejidad.

public partial class MainPage : UserControl
{
Helpers.ServiceHelper service;
public MainPage()
{
   InitializeComponent();

   service = Helpers.ServiceHelper.Instance;

   service.GetDateTime(value =>
   {
       string currentDateTime = value.ToString();
   });

   service.GetEmployee(value =>
   {
       ListBox listbox = new ListBox();
       listbox.ItemsSource = value;
       LayoutRoot.Children.Add(listbox);
   });

   service.DoStuff(() =>
   {
       // do stuff done
   });
}
}

Lo que hace la clase Helper es, cada vez que se realiza una invocación del servicio web se pasa por parámetro una referencia del delegado de tipo Action<T> que contiene la función que se llamará cuando termine la invocación. Una vez que termina la invocación del servicio lo que hacemos es obtener ese valor de vuelta y invocar al delegado con el resultado que es del mismo tipo que la signatura del delegado Action<T>.

public class ServiceHelper
{
private static ServiceHelper instance = new ServiceHelper();
public static ServiceHelper Instance { get { return instance; } }

private ServiceClient service;

private ServiceHelper()
{
   service = new ServiceClient();
   service.GetDateCompleted += new EventHandler<GetDateCompletedEventArgs>(OnGetDateCompleted);
   service.GetEmployeeCompleted += new EventHandler<GetEmployeeCompletedEventArgs>(OnGetEmployeeCompleted);
   service.AddCompleted += new EventHandler<AddCompletedEventArgs>(OnAddCompleted);
   service.DoStuffCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(OnDoStuffCompleted);
}

void OnDoStuffCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action action = (Action)e.UserState;
       action();
   }
}

void OnAddCompleted(object sender, AddCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action<int> action = (Action<int>)e.UserState;
       action(e.Result);
   }
}

void OnGetEmployeeCompleted(object sender, GetEmployeeCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action<ObservableCollection<Employee>> action = (Action<ObservableCollection<Employee>>)e.UserState;
       action(e.Result);
   }
}

void OnGetDateCompleted(object sender, GetDateCompletedEventArgs e)
{
   if (e.Error == null)
   {
       Action<DateTime> action = (Action<DateTime>)e.UserState;
       action(e.Result);
   }
}

public void GetDateTime(Action<DateTime> value)
{
   service.GetDateAsync(value);
}

public void GetEmployee(Action<ObservableCollection<Employee>> value)
{
   service.GetEmployeeAsync(value);
}

public void Add(AddArguments args, Action<int> value)
{
   service.AddAsync(args.Operand1, args.Operand2, value);
}

public void DoStuff(Action value)
{
   service.DoStuffAsync(value);
}

public class AddArguments
{
   public int Operand1 { get; set; }
   public int Operand2 { get; set; }
}
}

Os podeis descargar de aquí el ejemplo completo. http://www.luisguerrero.net/downloads/servicehelper.zip

Saludos. Luis.

Filed under: .Net, Silverlight No Comments