Que hacer con un proyector y un grupo de amigos

Pues eso, nos han regalado un proyecto en mi casa, y con la terraza que hay en mi casa que íbamos ha hacer?, pues jugar el RockBand y al Guitar Hero, aquí os dejo algunas fotos del evento, :p

DSC06666 DSC06671 DSC06655 DSC06665

Para los que no lo sepáis, vivo en Madrid y ahora con el frio que hace ha sido curioso jugar así. Espero encontrar algo parecido en Londres!!

Saludos. Luis.

¿De donde sale este DataRow?

Hace poco gracias al equipo DOT de PlainConcepts, tuve oportunidad de participara en un caso muy interesante, el software de la empresa en cuestión tenía un problema de fuga de memoria porque las referencias de los objetos no eran recolectadas por el GC, así que WinDGB en mano me puse a averiguar porque era.

Una de las primeras cosas que se hace cuando te encuentras un caso como este es intentar que la aplicación que vas a depurar se encuentre en ese caso, ósea que este fugando memoria, en mi caso lo era así que el segundo paso es hacer un dump completo de la aplicación. Esto lo podemos hacer con una herramienta que viene en la carpeta de instalación del “Debugging tools for Windows” que se llama ADPlus.vbs, la sintaxis sería la siguiente:

adplus -hang -pn miprograma.exe

Una vez hecho eso tenemos un dump completo de la aplicación en el estado de fuga de memoria que os comentaba antes, ahora tenemos que ponernos a investigar. Una de las cosas que suelo hacer siempre que me encuentro con un caso de fuga de memoria es hacer un !dumpheap para ver que nos encontramos en el heap (monton) de .NET, que es básicamente donde están todos los objetos que se instancian en el ciclo de vida de una aplicación .NET.

Aquí podemos ver el resultado en WinDBG:

0:003> !dumpheap -stat
total 5003168 objects
Statistics:
      MT    Count    TotalSize Class Name
6b441898        1           12 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
6b440b48        1           12 System.Security.Permissions.ReflectionPermission
6b43f850        1           12 System.Resources.FastResourceComparer
6b43ccec        1           12 System.__Filters
6b43cc9c        1           12 System.Reflection.Missing
6b43cba4        1           12 System.RuntimeType+TypeCacheQueue
6b43bcb8        1           12 System.Runtime.InteropServices.GCHandle
6b439cf0        1           12 System.RuntimeTypeHandle
6b4382c8        1           12 System.Text.DecoderExceptionFallback
6b438284        1           12 System.Text.EncoderExceptionFallback
6b41c7a8        1           12 System.Security.Permissions.FileDialogPermission
6b41c76c        1           12 System.Security.PolicyManager
6b41abec        1           12 System.Reflection.__Filters
6b41a004        1           12 System.DBNull
61e8508c        1           12 System.Data.IndexField[]
61e7f984        1           12 Bid+BindingCookie
00584a78        1           12 DataRowMemoryLeak.Foo
6ba6bc08        1           16 System.IO.TextReader+SyncTextReader
6b440ca0        1           16 System.Enum+HashEntry
6b43fad4        1           16 System.Resources.ResourceReader+TypeLimitingDeserializationBinder
6b43a0d8        1           16 System.Int64
6b439ef0        1           16 System.Globalization.GlobalizationAssembly
6b417760        1           16 System.Security.Permissions.UIPermission
61e85f8c        1           16 System.Data.DataRowBuilder
6b445bd4        1           20 System.Environment+ResourceHelper
6b441bac        1           20 System.Security.Permissions.EnvironmentPermission
6b438988        1           20 System.Text.StringBuilder
6b437a6c        1           20 Microsoft.Win32.SafeHandles.SafeFileMappingHandle
6b437a14        1           20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle
6b437988        1           20 System.Text.InternalEncoderBestFitFallback
61e85754        1           20 System.Data.DataRowCollection
61e7f9c0        1           20 Bid+AutoInit
6b4447e8        1           24 System.Collections.BitArray
6b441914        1           24 System.String[][]
6b440e8c        2           24 System.OrdinalComparer
6b43a470        1           24 System.OperatingSystem
6b437d2c        1           24 System.IO.TextWriter+SyncTextWriter
6b4379d4        1           24 System.Text.InternalDecoderBestFitFallback
6b421078        1           24 System.Collections.Generic.List`1[[System.Int32, mscorlib]]
6b4192a0        1           24 System.Collections.Generic.List`1[[System.WeakReference, mscorlib]]
6b417bb0        1           24 System.Collections.Stack
61e85e44        1           24 System.Collections.Generic.List`1[[System.Data.Index, System.Data]]
61e854f8        1           24 System.Collections.Generic.List`1[[System.Data.DataViewListener, System.Data]]
61e8520c        1           24 System.Collections.Generic.List`1[[System.Data.DataView, System.Data]]
61e7da60        1           24 <CrtImplementationDetails>.ModuleUninitializer
6b43ecb4        1           28 System.Text.DecoderNLS
6b438f18        1           28 System.SharedStatics
6b4383a8        1           28 System.IO.Stream+NullStream
6b438188        1           28 System.Text.EncoderNLS
6b437bfc        1           28 Microsoft.Win32.Win32Native+InputRecord
6b441c5c        1           32 System.Text.UnicodeEncoding+Decoder
6b43ec2c        1           32 System.Text.UTF8Encoding+UTF8Decoder
6b43ae5c        1           32 System.Security.PermissionTokenFactory
6b43820c        1           32 System.Threading.IOCompletionCallback
6b438108        1           32 System.Text.UTF8Encoding+UTF8Encoder
6b41cbd8        1           32 System.Security.Util.Tokenizer+StringMaker
6b415650        1           32 System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode
6b4155c4        1           32 System.Runtime.CompilerServices.RuntimeHelpers+TryCode
6b415394        1           32 System.Text.UnicodeEncoding
61e856e4        1           32 System.Data.ConstraintCollection
61e85630        1           32 System.Data.RecordManager
61e7f90c        1           32 Bid+CtrlCB
6b43ac78        1           36 System.Security.Permissions.FileIOPermission
6b43a028        1           36 System.Int64[]
6b41d0e0        3           36 System.Security.Policy.AllMembershipCondition
6b418058        1           36 System.Resources.RuntimeResourceSet
6b43fa50        1           40 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
6b43f694        1           40 System.IO.BinaryReader
6b43d778        2           40 Microsoft.Win32.SafeHandles.SafeFileHandle
6b43adb0        2           40 System.Security.PermissionToken
6b41edfc        1           40 System.Int32[][]
6b439338        1           44 System.AppDomainSetup
6b415be0        1           44 System.Threading.ReaderWriterLock
61e87720        1           44 System.Data.Common.StringStorage
61e875fc        1           44 System.Data.Common.Int32Storage
61e857c4        1           44 System.Data.DataRowCollection+DataRowTree
6b440e18        3           48 System.CultureAwareComparer
6b43d9e4        4           48 System.UInt16
6b43ba44        3           48 System.Text.DecoderReplacementFallback
6b43b9f4        3           48 System.Text.EncoderReplacementFallback
6b43a3f4        2           48 System.Version
6b41d2a0        4           48 System.Security.Permissions.StrongNamePublicKeyBlob
6b43f74c        1           52 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Resources.ResourceLocator, mscorlib]]
6b415314        1           52 System.Resources.ResourceManager
6b43b8f0        2           56 System.Text.UTF8Encoding
6b438e20        1           56 System.Threading.Thread
6b4164b4        1           56 System.LogLevel[]
61e85674        1           56 System.Data.DataColumnCollection
6b440be4        1           60 System.UInt64[]
6b4242f8        1           60 System.IO.StreamReader+NullStreamReader
6b42420c        1           60 System.IO.StreamReader
6b41ac44        2           64 System.Reflection.TypeFilter
6b4190d8        2           64 System.EventHandler
6b43b674        1           68 System.Globalization.CultureTable
6b439d98        3           72 System.Reflection.Assembly
6b438b90        1           72 System.ExecutionEngineException
6b438b00        1           72 System.StackOverflowException
6b438a70        1           72 System.OutOfMemoryException
6b437c50        2           72 System.IO.__ConsoleStream
6b43786c        1           76 System.Text.SBCSCodePageEncoding
6b41d320        5           80 System.Security.Policy.ZoneMembershipCondition
6b41d230        4           80 System.Security.Policy.PolicyStatement
6b417fdc        1           80 System.Resources.ResourceReader
6b43cd48        3           96 System.Reflection.MemberFilter
6b43c23c        2           96 System.Reflection.Module
6b43a72c        8           96 System.Security.Permissions.SecurityPermission
6b4156d4        3           96 System.Globalization.CompareInfo
6b43cc64        1          100 System.Reflection.MetadataArgs+SkipAddresses
6b43b708        5          100 System.Globalization.CultureTableItem
6b43902c        1          100 System.AppDomain
6b4163c0        1          108 System.SwitchStructure[]
6b437e88        2          112 System.IO.StreamWriter
6b416db0        2          120 System.Collections.Hashtable+SyncHashtable
6b439f44        2          128 System.IO.UnmanagedMemoryStream
6b422314        4          128 System.Collections.ArrayList+SyncArrayList
6b417b4c        7          140 Microsoft.Win32.SafeHandles.SafeRegistryHandle
6b43a510        4          144 System.Security.PermissionSet
6b438c20        2          144 System.Threading.ThreadAbortException
6b41f04c        3          156 System.Security.Policy.PolicyLevel
6b43ae10        5          180 System.Security.Util.TokenBasedSet
6b43aa98       17          204 System.Int32
6b41794c        7          224 Microsoft.Win32.RegistryKey
6b43b7b0        2          256 System.Globalization.NumberFormatInfo
6b438468       22          264 System.Object
61e84fb4        2          296 System.Data.DataColumn
61e82cc8        1          296 System.Data.DataTable
6b43b624        7          336 System.Globalization.CultureTableRecord
6b43fc20        1          352 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Resources.ResourceLocator, mscorlib]][]
6b41ce18       10          360 System.Security.Policy.UnionCodeGroup
6b43b448        6          408 System.Globalization.CultureInfo
6b41f0a4        9          432 System.Security.NamedPermissionSet
6b43aea0       18         1008 System.Collections.Hashtable
6b41cd3c       65         1560 System.Security.Policy.StrongNameMembershipCondition
6b43b2bc       12         1676 System.Byte[]
6b4399cc      110         2200 System.RuntimeType
6b43af9c       18         2664 System.Collections.Hashtable+bucket[]
6b43947c       24         3444 System.Char[]
6b41cae8      216         6048 System.Security.SecurityElement
6b43a87c      266         6384 System.Collections.ArrayList
61e85bd0      347         9716 System.Data.RBTree`1+TreePage[[System.Data.DataRow, System.Data]]
00466bf0      235       395780      Free
6b43a9e8      368      4455624 System.Int32[]
6b43884c  1000553     31651760 System.String
61e85d88      347     32089008 System.Data.RBTree`1+Node[[System.Data.DataRow, System.Data]][]
6b414e78      320     37795900 System.Object[]
6b415b88  2999997     47999952 System.WeakReference
61e85048   999999     63999936 System.Data.DataRow
Total 5003168 objects

El caso es que tenía aproximadamente un millón de instancias de la clase System.Data.DataRow, que como bien sabéis representa una fila de un DataTable de ADO.NET, pues resulta que la aplicación por algún motivo seguía teniendo las referencias a esas DataRow.

Ya sabemos cuál es el problema, ahora tenemos que saber desde donde se referencia a esas DataRow y encontrar el trozo de código que lo hace, esto normalmente es lo más complicado porque tiene que conocer el software que estás depurando y en mi caso son clientes externos y no conozco el caso, así que lo que normalmente hago son dos cosas:

· Con el comando !gcroot le paso una dirección de memoria de uno de esos DataRow para ver desde que Thread y que pila se ha referenciado el objeto. Con el identificador del Thread cambio el WinDGB del Thread actual al nuevo thread con el comando “~23s” siendo 23 el identificador del Thread y ahora ejecuto un !clrstack con el que puedo ver la pila administrada del Thread actual. Ahora simplemente tengo que buscar en esas clases y en esos métodos referencias a los DataRows para encontrar la fuente de las referencias. Además puedo utilizar el comando !dso que me permite ver los parámetros de las funciones y las variables locales de todos lo métodos de la pila actual.

0:000> !dso
OS Thread Id: 0x7a0 (0)
ESP/REG  Object   Name
0030ef94 13cff17c Microsoft.Win32.SafeHandles.SafeFileHandle
0030efa4 13cff17c Microsoft.Win32.SafeHandles.SafeFileHandle
0030efd8 13cff3e0 System.Byte[]
0030efdc 13cff190 System.IO.__ConsoleStream
0030effc 13cff1c0 System.IO.StreamReader
0030f000 13cff1c0 System.IO.StreamReader
0030f018 13cff1c0 System.IO.StreamReader
0030f01c 13cff6f8 System.IO.TextReader+SyncTextReader
0030f03c 13cff6f8 System.IO.TextReader+SyncTextReader
0030f060 13cfe1b8 System.Int64
0030f064 1017bd90 System.String    total memory - 
0030f068 13cfe1a8 System.WeakReference
0030f06c 0b597af8 System.Data.DataRow
0030f070 0180a954 System.Data.DataRowCollection
0030f074 01866d48 System.Collections.Generic.List`1[[System.WeakReference, mscorlib]]
0030f078 0180a954 System.Data.DataRowCollection
0030f07c 13cfe1ec System.String    total memory - 218038312
0030f080 13cfe1b8 System.Int64
0030f084 1017bd90 System.String    total memory - 
0030f088 01866d70 System.Data.DataTable
0030f0a4 01866d3c DataRowMemoryLeak.Foo
0030f0b4 0b597af8 System.Data.DataRow
0030f0b8 0180a954 System.Data.DataRowCollection
0030f0bc 01866d48 System.Collections.Generic.List`1[[System.WeakReference, mscorlib]]
0030f0c0 0180a954 System.Data.DataRowCollection
0030f0c4 01866d70 System.Data.DataTable
0030f0c8 01866d3c DataRowMemoryLeak.Foo
0030f0e8 0b597af8 System.Data.DataRow
0030f0ec 0180a954 System.Data.DataRowCollection
0030f0f0 01866d48 System.Collections.Generic.List`1[[System.WeakReference, mscorlib]]
0030f0f4 0180a954 System.Data.DataRowCollection
0030f0f8 01866d70 System.Data.DataTable
0030f0fc 01866d3c DataRowMemoryLeak.Foo
0030f118 01866d24 System.String    ID
0030f120 0180a954 System.Data.DataRowCollection
0030f12c 01866d08 System.String    Name
0030f13c 01866d24 System.String    ID
0030f148 01866d08 System.String    Name
0030f150 01866d70 System.Data.DataTable
0030f154 01866d3c DataRowMemoryLeak.Foo
0030f158 0b597af8 System.Data.DataRow
0030f15c 01866d70 System.Data.DataTable
0030f160 01866d3c DataRowMemoryLeak.Foo
0030f16c 0180a1dc System.Object[]    (System.String[])
0030f400 0180a1dc System.Object[]    (System.String[])
0:000> !gcroot 088a1630 
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
eax: 0030eec4 (invalid object)
Scan Thread 0 OSTHread 7a0
ESP:30eeb4: 0030eec4 (invalid object)
ESP:30f06c:Root:0b597af8(System.Data.DataRow)->
01866d70(System.Data.DataTable)->
0180a954(System.Data.DataRowCollection)->
0180a968(System.Data.DataRowCollection+DataRowTree)->
07f166bc(System.Object[])->
0b033360(System.Data.RBTree`1+TreePage[[System.Data.DataRow, System.Data]])->
088a1630(System.Data.RBTree`1+Node[[System.Data.DataRow, System.Data]][])

· Si todo esto no funciona también puedo hacer otro tipo de análisis de la aplicación. Con el Reflecto, programa que por cierto todo programador de .net debería de tener, podemos agregar la referencia del ensamblado el cual queremos analizar y el ensamblado de la clase la cual queremos saber si nuestro ensamblado referencia. En este caso miEnsamblado.dll y System.Data.dll que contiene System.Data.DataRow y pulsando el botón contrario encima de la clase tengo la opción de Analyze que me permite ver varias opciones.

  1. Depends On
  2. Used By
  3. Exposed By
  4. Instantiated By

image

 

· Como podréis imaginar las dos que me interesan son Used By y Instantiated By, en este caso Instantiated By no tanto porque no soy el responsable directo de crear las instancias de los DataRow, pero Used By me va a permitir ver de los ensamblados que tengo cargados cuales referencia a esta clase.

image

Así que como podéis ver la vida de un depurador no es fácil pero hay una serie de trucos que os pueden ayudar a solucionar la vida.

Espero que os haya gustado y hasta otra.

Saludos. Luis.

Actualización: (16 enero 2008)

Aquí esta la URL del ejemplo usado con WinDGB, http://www.luisguerrero.net/downloads/DataRowMemoryLeak.zip

Para sacar una dirección de memoria de un objeto y después usarlo para por ejemplo !do (DumpObject) o !gcroot tenemos que usar el comando !dumpheap que nos muestra toda la lista de objetos de hay en el heap (montón).

Si hacemos un comando como este: !dumpheap –type System.Data.DataRow tenemos lo siguiente:

Address      MD
0b6d6598 77a95048       64     
0b6d65f8 77a95048       64     
0b6d6658 77a95048       64     
0b6d66b8 77a95048       64     
0b6d6718 77a95048       64     
0b6d6778 77a95048       64     
0b6d67d8 77a95048       64     
0b6d6838 77a95048       64     
0b6d6898 77a95048       64     
0b6d68f8 77a95048       64     
0b6d6958 77a95048       64     
0b6d69b8 77a95048       64     
0b6d6a18 77a95048       64     
0b6d6a78 77a95048       64   
total 1000696 objects
Statistics:
      MT    Count    TotalSize Class Name
77a95f8c        1           16 System.Data.DataRowBuilder
77a95754        1           20 System.Data.DataRowCollection
77a957c4        1           44 System.Data.DataRowCollection+DataRowTree
77a95bd0      347         9716 System.Data.RBTree`1+TreePage[[System.Data.DataRow, System.Data]]
77a95d88      347     32089008 System.Data.RBTree`1+Node[[System.Data.DataRow, System.Data]][]
77a95048   999999     63999936 System.Data.DataRow
Total 1000696 objects


0:004> !do 0b6d6a78 
Name: System.Data.DataRow
MethodTable: 77a95048
EEClass: 779adf30
Size: 64(0x40) bytes
 (C:WindowsassemblyGAC_32System.Data2.0.0.0__b77a5c561934e089System.Data.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
77a92cc8  4000720        4 ...em.Data.DataTable  0 instance 01956d70 _table
77a95674  4000721        8 ...aColumnCollection  0 instance 018fa804 _columns
71ebaa98  4000722       18         System.Int32  1 instance       -1 oldRecord
71ebaa98  4000723       1c         System.Int32  1 instance   999964 newRecord
71ebaa98  4000724       20         System.Int32  1 instance       -1 tempRecord
71ebaa98  4000725       24         System.Int32  1 instance   999965 _rowID
77ea7fc8  4000726       28         System.Int32  1 instance        0 _action
71ebea68  4000727       38       System.Boolean  1 instance        0 inChangingEvent
71ebea68  4000728       39       System.Boolean  1 instance        0 inDeletingEvent
71ebea68  4000729       3a       System.Boolean  1 instance        0 inCascade
77a94fb4  400072a        c ...m.Data.DataColumn  0 instance 00000000 _lastChangedColumn
71ebaa98  400072b       2c         System.Int32  1 instance        0 _countColumnChange
77ec3bf4  400072c       10 ...em.Data.DataError  0 instance 00000000 error
71eb8468  400072d       14        System.Object  0 instance 00000000 _element
71ebaa98  400072e       30         System.Int32  1 instance 22676893 _rbTreeNodeId
71ebaa98  4000730       34         System.Int32  1 instance   999965 ObjectID
71ebaa98  400072f      498         System.Int32  1   static   999999 _objectTypeCount


0:004> !dumpmt 77a95048
EEClass: 779adf30
Module: 779a1000
Name: System.Data.DataRow
mdToken: 02000081  (C:WindowsassemblyGAC_32System.Data2.0.0.0__b77a5c561934e089System.Data.dll)
BaseSize: 0x40
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 99

De la primera columna podemos sacar la dirección del objeto que queremos investigar.

Forzar un BSOD en Windows

Hola a todos de nuevo !!

Cualquiera me puede decir que el título del post es un poco raro, y lo es !! :), resulta que quiero forzar un pantallazo azul, pero, ¿porqué?.

Como bien sabéis Windows es perfecto (cási perfecto) y solamente se puede desencadenar un BSOD cuando se produce una operación no valida dentro de la memoria del Kernel. Esto no ocurre todas las veces que uno quisiera.

Y claro imaginad por un momento que queréis hacer un dump completo porque tenéis un memory leak en una aplicación nativa que usa memoria virtual, y queréis explorar el árbol de VAD (Virtual Address Descriptors), que son los identificadores del Kernel de Windows para ver en que se está gastando la memoria, pero claro a esto solo tenemos acceso desde una depuración en modo kernel, !vad solo funciona si WinDBG está en modo kernel.

Pues nos os preocupéis que el driver de vuestro teclado os salvará la vida, tenéis que ir a esta entrada de la base de conocimiento de Microsoft y os explican como activarlo.

http://support.microsoft.com/kb/244139

Y funciona!!

Saludos. Luis.

Primera aplicación de Microsoft Surface

Este fin de semana, y gracias a un proyecto que tenemos ente unos amigos, hemos hecho la primera aplicación de Microsoft Surface de PlainConcepts.com

 

Es un videojuego, porque claro no vamos a hacer una aplicación comercial, (de esas hacemos muchas en el día a día), y el videojuego es para jugar al hockey sobre hielo, os dejo unas capturas de pantalla y un vídeo en mp4.

El videojuego está hecho en WPF (Windows Presentation Foundation) + API de Surface  + Pixel Shaders, y está hecho a 1080P con una resolución de 1920×1200

Flowkey Flowkey2

Flowkey3 Flowkey4

http://www.luisguerrero.net/downloads/flowkey.zip [Video MP4, 13MB]

Personas que han participado en este 12Meses12Proyectos, una oportunidad de hacerse rico, TM:

  • Luis Guerrero
  • Ricardo Acosta
  • Anton Molleda
  • Pedro Laguna
  • Olmo del corral
  • Vicente Cartas

Espero que os guste. Saludos. Luis.

[Evento] Windows Presentation Foundation y Silverlight para programadores (Recursos)

Del 1 al 5 de Diciembre en Vic (Barcelona), se celebró un curso sobre Windows Presentation Foundation y Silverlight, en este post pondré los ejemplos de codigo que se generarón en el curso, además enlaces de intenrés y libros recomendados.

image

En el Zip que os poideis descargar desde aquí, encontrareis ejemplos sobre:

  • UnhandledException, evento que se lanza en el dominio de aplicación cuando una excepción no es filtrada y se propaga hacia abajo en la pila de llamadas.
  • Un ejemplo de EntityFramework
  • El ejemplo de la pelota moviéndose (sin colisiones), hecha con ContenContent
  • Dos aplicaciones Silverlight
  • Un ejemplo de concatenación de cadenas con += y con StringBuilder (nunca usar +=).
  • El código de la aplicación de WFP con los ejemplos de DependencyProperty y demás cosas de WPF.

 

Saludos. Luis.

[Curso] WPF para programadores de Windows Forms 7

Hola a todos de nuevo, volvemos a la carga con otro artículo sobre WFP para programadores de Windows Forms. En este articulo voy a hablar sobre los diferentes tipos de controles que hay en WPF y como encontramos similitud con los controles de WF.

En artículos anteriores estuvimos hablando sobre el árbol de herencia de WPF y sobre las diferentes clases de las que heredaba un control normal de WPF, pusimos como ejemplo Label, al ser un control que vamos a usar mucho en nuestras aplicaciones.

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

Ahora en este curso nos vamos a posicionar en System.Windows.UIElement y vamos a ver las diferentes ramas de las cuales salen los demás controles de WPF y ver que funcionalidad nos proporciona.

UIElement es la primera clase de WPF que tiene soporte para dibujado (heredado de Visual), soporte para entrada (Input, heredado de DispatcherObject) y soporte para RouteEvents. Así que técnicamente esta es la clase que tendríamos que usar si queremos empezar a dibujar algo en la pantalla, es la clase que da un soporte más primitivo para el dibujado. En la clase UIElement tenemos un método virtual OnRender que podemos sobreescribir en nuestras clase para poder implementar la funcionalidad personalizada de dibujado de esa clase, en la que se nos pasa un DrawingContext con el que dibujamos la UI del control. DrawingContextes muy parecido a la clase Graphics de GDI y de hecho tiene métodos muy parecidos.

Aquí podemos ver un ejemplo de un control que se dibuja a si mismo con este método.

public class WigoVisual : UIElement
    {
        private Pen pincel;
        public WigoVisual()
        {
            pincel = new Pen(new SolidColorBrush(Colors.Red), 20);
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            drawingContext.DrawLine(pincel, new Point(0, 0), new Point(100, 100));
            drawingContext.DrawRectangle(new SolidColorBrush(Colors.Green), new Pen(new SolidColorBrush(Colors.Blue), 5),
                new Rect(0, 0, 20, 20));
        }
    }

El control dibuja una línea desde (0,0) hasta (100,100) con el bolígrafo especificado, evidentemente esta clase no tiene soporte para Layout de ninguna manera porque nosotros al dibujar el control no tenemos en cuenta propiedades con Width o ActualWidth para dibujar la línea.

Como podréis comprender esta clase es muy básica en cuanto al soporte de Layout pero muy ponente para un dibujado más personalizado, si queremos ganar en funcionalidad existente tenemos que bajar en el árbol de herencia. La única clase pública del framework que hereda de UIElement es FrameworkElement, y es en esta clase donde se define mucha de la funcionalidad común de todos los controles del Framework, (es por eso que creo que se llama FrameworkElement).

FrameworkElement añade una serie de funcionalidad para que el soporte de Layout de la clase sea mucho más rico que con UIElement. Veámosla.

· Definición del sistema de Layout: define una serie de métodos que eran virtuales en UIElement para implementar el sistema de Layout de WPF.

· Define el árbol lógico de una aplicación WPF y deja la responsabilidad de definir cuál es a las clases herederas.

· Define los eventos del ciclo de vida de un control, eventos como Initialized, Loaded, Unloaded.

· Soporte para enlace a datos dinámico y búsqueda dinámica de recursos estáticos dentro del control.

· Define los estilos (Style) para los controles.

· Define el soporte para las animaciones de los controles.

FrameworkElement define una serie de propiedades básicas con las que podremos empezar a definir el Layout y los demás propiedades de la UI, a partir de esta clase podemos empezar a realizar Layout más complicados sin tener que explícitamente programarlos.

WPF permite que los programadores puedan sacar todo el partido del Layout a partir de una serie de clases que añaden funcionalidad especifica del nivel en el que se encuentran. Si volvemos atrás en el tiempo y recordamos como en Visual Basic 6 se hacían las cosas, los programadores más avanzados se hacían los cálculos ellos mismos para hacer que el control se moviese o fluyera a través del formulario, pero todos los cálculos tenían que ser hechos a mano, después con Windows Forms Microsoft nos dio dos propiedades muy interesantes de la clase Control Anchor y Dock, que permiten que un control se puedan quedar pillado a alguna de sus caras, o lo que es lo mismo que mantenga siempre la misma distancia. Todo este trabajo que antes se hacía a mano en Visual Basic ahora lo tenemos de “serie” dentro de Windows Forms, pues si seguimos avanzando en WPF ese cálculo de distancias se ha llevado al extremo, eso no significa que no se calcule internamente sino que no somos nosotros los responsables de hacer esos cálculos sino que WPF los hace por sí mismo.

Saludos. Luis.

[Evento] Taller de Nuevos interfaces para aplicaciones ( WPF/SILVERLIGHT)

El lunes de la semana que viene en Pamplona, se va a celebrar un evento para programadores sobre WPF / Silverlight.

Introducción al WPF / Silverlight

La mayoría de desarrollos actuales son como los de hace 15 o 20 años: La información se presenta de la misma manera, no se tiene en cuenta para nada la usabilidad de la UI y la experiencia de usuario (UX). Con Windows Presentation Foundation se pueden crear aplicaciones para Windows y la Web de una manera mucho más flexible, los controles son mucho más potentes que los Common Controls de Win32. Ahora podemos integrar multimedia, animaciones, 3D, databinding avanzado, efectos (PixelShaders), etc. Todos los controles de WPF están acelerados por el hardware de tarjeta gráfica gracias a Microsoft DirectX, así que ahora vamos a sacar potencia de la tarjeta gráfica para la composición de la UI de nuestras aplicaciones. WPF supone una revolución en la manera de hacer aplicaciones, además con XAML podemos integrar dentro del flujo de creación a los diseñadores. Ahora tanto programadores como diseñadores comparten XAML a través de Microsoft Blend y Visual Studio 2008.

Con este curso, se pretende exponer de manera general todas las características que hace de WPF una de las tecnologías más rompedoras de Microsoft para UX.

El Centro de Excelencia Microsoft .NET (CES) quiere poner en marcha una  intensa jornada sobre WPF / Silverlight en la que contaremos con uno de los mayores expertos  en la materia: Luis Guerrero.

  • Audiencia: Desarrolladores, Jefe de Proyecto,
  • Requisitos previos: Conocimientos del entorno Visual Studio 2005 / Visual Studio 2008, UI

Temario:

· Introducción a WPF ¿Por qué otra herramienta para componer aplicaciones? Historia

· XAML, herramientas para WPF / Silverlight

· Layout

· Controles

· Dependency properties

· Routed Input Events

· Animaciones

· Databinding

· Estilos (Style)

 

 

http://www.cesnavarra.net/Lists/Eventos%20CES/DispForm1.aspx?List=05834153%2Deec1%2D4089%2Da40e%2D5355dbd305f4&ID=90&Source=http%3A%2F%2Fwww%2Ecesnavarra%2Enet%2Fdefault%2Easpx

WPF Advanced – ISealable

Recientemente en un cliente me ha ocurrido un caso muy extraño con las Dependecy Property de WPF y quiero escribir sobre el tema. Es un poco enrevesado pero creo que puede ser útil. Veamos.

WPF tiene un sistema de Binding muy potente que hace que se puedan hacer enlaces a datos a entidades de negocio. Pues bien el caso es que para que una clase pueda tener Dependency Property (DP a partir de ahora), es necesario que heredemos como mínimo de la clase DependencyObject que permite que una clase almacene DP, pues bien el caso es que mis entidades de negocio heredan de esta clase para que las propiedades que lo define sean DP y poder utilizar toda la potencia de WPF, hasta aquí todo bien. Para lo que no estéis tan al tanto de DP es un sistema que permite tener propiedades con metadatos y un sistema centralizado de almacenamiento de valores. Para definir un DP es así.

public static readonly DependencyProperty CurrentBusinessEntityProperty =
    DependencyProperty.Register("CurrentBusinessEntity", typeof(BusinesEntity), typeof(BusinessManager), new UIPropertyMetadata(new BusinesEntity()));

public BusinesEntity CurrentBusinessEntity
{
    get { return (BusinesEntity)GetValue(CurrentBusinessEntityProperty); }
    set { SetValue(CurrentBusinessEntityProperty, value); }
}

Si os fijáis en el primer parámetro del UIPropertyMetadata es el valor predeterminado del DP, hay que tener en cuenta que esto se inicializa en el constructor de tipo (static en C#) y que por tanto es seguro frente a subprocesos. Pues bien si el valor predeterminado que nosotros usamos es un objeto que hereda de DependencyObject WPF lanza una excepción. La excepción es:

clip_image002

Default value for the ‘CurrentBusinessEntity’ property cannot be bound to a specific thread.

Es algo bastante raro, porque si pongo null funciona si hago otro tipo de DP funciona correctamente y solamente si utilizo como valor predeterminado una entidad de negocio mía (que hereda de DependencyObject) la no cosa funciona. Pero la excepción que se nos lanza no nos da mucha información. Utilizando el código fuente del framework y depurando todo el proceso de inicialización del DP, llego hasta este bonito trozo del código.

if (checkThreadAffinity) 
{
// If the default value is a DispatcherObject with thread affinity 
// we cannot accept it as a default value. If it implements ISealable
// we attempt to seal it; if not we throw  an exception. Types not
// deriving from DispatcherObject are allowed - it is up to the user to
// make any custom types free-threaded. 

DispatcherObject dispatcherObject = defaultValue as DispatcherObject; 

if (dispatcherObject != null && dispatcherObject.Dispatcher != null)
{ 
    // Try to make the DispatcherObject free-threaded if it's an
    // ISealable.

    ISealable valueAsISealable = dispatcherObject as ISealable; 

    if (valueAsISealable != null && valueAsISealable.CanSeal) 
    { 
        Invariant.Assert (!valueAsISealable.IsSealed,
               "A Sealed ISealable must not have dispatcher affinity"); 

        valueAsISealable.Seal();

        Invariant.Assert(dispatcherObject.Dispatcher == null, 
            "ISealable.Seal() failed after ISealable.CanSeal returned true");
    } 
    else 
    {
        throw new ArgumentException(SR.Get(SRID.DefaultValueMustBeFreeThreaded, propertyName)); 
    }
}
}

En el que básicamente los señores que prograron WPF vienen a decir algo así como que el valor predeterminado es un DispathcerObject (en mi caso lo es) no pueden acceptar solo si implementa ISealable entonces lo sellan y si no pueden lanzan una excepción.

Una bonita manera de encontrar el error, continuando la investiagación, me dispongo pues a que mio clase implemente esta interfaz ISealable, pero cual es mi sorpresa que la interfaz no existe. La busco en el MSDN nada, abro el reflecto y para mi sorpresa me encuentro con esto

[FriendAccessAllowed]

internal interface ISealable

{

// Methods

void Seal();

// Properties

bool CanSeal { get; }

bool IsSealed { get; }

}

Que la internfaz es internal y encima se permite el acceso de los ensamblado amigos, pues con el mismo reflecto abierto me dispongo pues a ver que clases dentro del framework (por curiosidad mas que nada) implementan esta fatídica interfaz, y me encuentro con esto:

clip_image004

Que las tres únicas clases que implementan esa funcionalidad son FrameworkTemplate, Freezable y Style.

Los que halláis leído otras entradas de WPF sabréis que Freezable está presente en el árbol de algunos tipos de controles de WPF permitiendo que el contenido del control se pueda de alguna manera congelar para aumentar el rendimiento de la aplicación. Pues bien parece que para que pueda poner como valor predeterminado una entidad mia de negocio tengo que heredar no de DependencyObject sino de Freezable.

Resumen:

Esta una bonita historia sobre como WPF es a veces un sistema muy complejo que creo que ni la gente de Microsoft a veces entiende, no porque esté mal diseñada, sino por lo compleja que es. Así que me pareció un exceso tener que heredar de Freezable únicamente por poder poner una entidad de negocio mía como valor predeterminado de un DP, así que lo deje como estaba antes.

Ahora bien porque WPF hace esto?, al ser la DP usadas por una clase que hereda de DependencyObject esta clase tiene una propiedad muy importante el Dispatcher que es el gestor de threads dentro de WPF así que supongo que la comprobación vendrá por si el objeto se está iniciando en un thread que no contiene un Dispatcher o se tendrá que hacer invocación. Y por *intentar* hacer la vida más sencilla a los programadores Microsoft ofrece la clase Freezable, pero por lo menos lanza una excepción explicando el tema o algo porque sino la gente se vuelve loca.

 

Aquí teneis el codigo fuente de ejemplo [SealedExample.rar]

Saludos. Luis.

CCR and DSS Toolkit 2008

Microsoft ha anunciado a través del blog de Robotics Studio la disponibilidad de CCR and DSS Toolkit 2008.

¿Qué es CCR and DSS Toolkit 2008?, como he hablado anteriormente Robotics Studio tiene dos componentes principales CCR (Concurrency and coordinator runtime) y DSS (Descentralizated Software Services), que hacen posible que todo el procesamiento de los mensajes que los Robots y el software procesan sean concurrentes, seguros frente a subprocesos y además los servicios que se diseñen sean bajamente acoplados y descentralizados de serie.

También explique en mi blog que estos dos componentes, que están aislados dentro de Robotics Studio, se podían usar para el desarrollo normal de aplicaciones distribuidas y concurrentes, tanto aplicaciones de escritorio / servidor como también en Compact Framework, así que aquí tenéis la prueba de cómo Microsoft se ha dado cuenta de lo mismo y ahora lo vente en un cómodo paquete para empresas J, en la web http://www.microsoft.com/ccrdss/ podéis encontrar información sobre licencias, precios y demás.

Descentralized Software Services (DSS)

En esta tercera entrega sobre Robotics Studio (MSRS) vamos a hablar sobre DSS, o los servicios descentralizados de software. En los dos artículos previos sobre CCR estuvimos hablando sobre la base sobre la que se construirían los servicios de Robotics a través de esas colas asíncronas y concurrentes representadas por la clase Port<T>. Ahora lo que queremos construir son servicios autónomos que representan una funcionalidad concreta dentro de Robotics Studio.

Los servicios son los bloques básico con los que se construyen las aplicaciones de Robotics, y estas pueden representar acceso al hardware y software, como lectura de sensores (bumper, laser, batery, ect) en lo que a hardware se refiere y por la parte de software, podemos utilizar los servicios para UI (User Interface), Storage, Directory Services, ect.

DSS está basado en un arquitectura REST de servicios web que permite que los dispositivos se puedan comunicarse en un entorno distribuido desde el principio. Además esto permite que aplicaciones de terceros puedan interpolar con Robotics studio, terceros como aplicaciones java, servicios web en asp.net ect. DSSP (Descentralized Software Services Protocol) es el protocolo de comunicación a través de REST y está basado en SOAP.

Además DSS es el runtime que se encarga de cargar todos los servicios, crearlos y comunicarlos entre ellos. La sintaxis de algunas partes nos recuerda mucho a WCF, pero hay que decir que este producto está creado desde cero y no ha tenido influencia de ningún producto de Microsoft. La parte del runtime además permite que se puedan cargar servicios en demanda como ensamblados de .net y que podamos hostear en nuestras aplicaciones en runtime de DSS.

Vamos a ver entonces, la anatomía de un servicio de Robotics Studio.

clip_image002

En este gráfico podemos ver un resumen de cómo está definido un servicio de MSRS. A continuación vamos a definir estos conceptos.

  • Service Identifier: Es la url que representa el servicio. Por ejemplo http://schemas.tempuri.org/2007/08/servicetutorial8.html
  • Contract Identifier: Es el identificador del contrato de operaciones que este servicio acepta. Como hemos dicho antes MSRS utiliza REST para comunicarse de una manera descentralizada así que tenemos que de alguna manera definir cuáles son las operaciones o puerto que este servicio define. Además de esto hay que definir cuáles son los tipos de datos de las operaciones, ya sean simples o compuestos.
  • State: Representa el estado del servicio y es la información que se va a obtener a través de la operación GET. Estados como, por ejemplo, el valor de carga de la batería, el último fotograma de la web cam o cualquier otro valor que indique el estado del servicio.
  • Main Port: es el puerto principal de comunicaciones del servicio. Como dijimos antes DSS está basado en CCR, y la clase PortSet que vimos con anterioridad es el lugar en el que la vamos a empezar a usar. Main Port es un PortSet con las operaciones o mejor dicho las DssOperations<TBody,TResponse> que define el servicio. Y ¿qué definimos con esa DssOperation?, pues básicamente el tipo de dato de la petición y de la respuesta a la operación del servicio. DSSP está basado en REST y esta es un tecnología que normalmente usa HTTP como protocolo de transporte, HTTP está basado en peticiones y respuestas (Request, Response), pues básicamente lo que estamos haciendo aquí es decirle en la operación del protocolo DSSP cuál es el tipo de la petición, ósea que valores necesito para mi petición, y cuál es el tipo de respuesta, que son los valores de respuesta de la operación. Cuando me refiero a operación es algo parecido a los verbos de HTTP (GET,POST,UPDATE,DELETE,DEBUG,ect), pero verbos de DSSP.
  • Partners: son la lista de servicios externos que este servicio necesita para funcionar. Si es que tiene algún partner.

Como se traduce toda esta información a clases de .net? Pues veamos.

El identificador del contrato se define así:

/// <summary>
/// Bank Contract class
/// </summary>
public sealed class Contract
{

   /// <summary>
   /// The Dss Service contract
   /// </summary>
   public const String Identifier = "http://schemas.tempuri.org/2008/10/bank.html";
}

Como una constante dentro de una clase Contract, esto se hace así siempre por convención.

/// <summary>
/// The Bank State
/// </summary>
[DataContract()]
public class BankState
{
    private Dictionary<string, Item> dic = new Dictionary<string, Item>();

    [DataMember]
    public Dictionary<string, Item> Clientes
    {
        get { return dic; }
        set { dic = value; }
    }
}
[DataContract]
public class Item
{
    private string cliente;
    private int dinero;

    [DataMember]
    public string Cliente
    {
        get { return cliente; }
        set { cliente = value; }
    }

    [DataMember]
    public int Dinero
    {
        get { return dinero; }
        set { dinero = value; }
    }
    public Item(string cliente, int dinero)
    {
        this.cliente = cliente;
        this.dinero = dinero;
    }
    public Item() { }
}

El estado del servicio es una clase decorada con los atributos DataContract y DataMember, como en WCF.

/// <summary>
/// Bank Main Operations Port
/// </summary>
[ServicePort()]
public class BankOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Transferir, Conectar>
{
}

Así es como se define el puerto principal. Un PortSet con las operaciones del servicio, que en este ejemplo el servicio acepta 4.

  • DsspDefaultLookup, es una manera de anunciar su defunción de servicio, es lo que usa el runtime de DSS para saber las operaciones del servicio.
  • DsspDefaultDrop es la operación de eliminar servicio.
  • Get, esta operación se puede usar para muchas cosas pero en principio es para obtener el estado actual del servicio, ósea obtener el estado.
  • Transferir y Conectar son operaciones personalizadas.

Si nos fijamos en el código anterior, se han definido dos servicios más además de los que MSRS define por nosotros, ahora bien, ¿Cómo se definen operaciones?

Como se ha señalado anteriormente estamos es un entorno web en que usamos REST + SOAP para comunicarnos. A todo esto lo llamamos DSSP. Como bien sabréis la web se basa en peticiones y respuesta, pues nosotros vamos a definir una operación de la misma manera, una petición y una respuesta. Siguiendo con el concepto de puertos introducido en los dos artículos anteriores, esas peticiones y esas respuestas van a ser dos puertos de CCR. Ahora bien, Port<T> es la clase usada para definir el puerto y tenemos que suministrarle un tipo con el que crear el puerto. Ese es el motivo de porque nosotros tenemos que definir dos tipos de datos personalizados que van a ser los parámetros de la petición.

En vez de hacer un método que acepte una serie de parámetros, que sería lo normal, vamos a encapsular todos esos parámetros en una clase nueva, y por supuesto vamos a decorar esa clase con el atributo Contract. Así que básicamente una operación es una petición de un tipo de dato personalizado por nosotros en la definición del servicio y una respuesta que también es un tipo de dato personalizado con los valores de respuesta de la operación.

public class Transferir : Get<TranferirRequestType, PortSet<TransferirResponseType, Fault>>{ }

Esta es la definición de la operación Transferir, en la que especificamos que el tipo de petición es TransferirRequestType y el de respuesta es TransferirResponseType. La clase Get<TRequest,TResponse> hereda de la clase DsspOperation<TBody, TResponse> que es la clase base de todas las operaciones del protocolo DSSP. TResponse en este caso está definido a su vez como un PortSet<TransferirResponseType,Fault>, un grupo de puertos de dos tipos. Fault es una clase definida en el namespace W3C.Soap y define un tipo de dato para las excepciones que se produzcan cuando se realicen las peticiones al servicio. Así que la cosa se queda así, para la respuesta tenemos un PortSet para que cuando consumamos la operación de este servicio, si algo ocurre más seamos notificados a través del puerto de tipo Fault y si todo ha ido correcto seremos notificados al puerto TransferirResponseType.

Aquí las definiciones de esos tipos.

[DataContract]
public class TranferirRequestType
{
    private int dinero;
    private string clienteDestino;
    private string clienteOrigen;

    [DataMember]
    public int Dinero
    {
        get { return dinero; }
        set { dinero = value; }
    }

    [DataMember]
    public string ClienteOrigen
    {
        get { return clienteOrigen; }
        set { clienteOrigen = value; }
    }

    [DataMember]
    public string ClienteDestino
    {
        get { return clienteDestino; }
        set { clienteDestino = value; }
    }
}
[DataContract]
public class TransferirResponseType
{
    private string resultado;

    [DataMember]
    public string Resultado
    {
        get { return resultado; }
        set { resultado = value; }
    }
}

Por ahora solo hemos definido el cuerpo del servicio, en el siguiente articulo terminaré de especificar el servicio y sube el código fuente del ejemplo completo.

Saludos. Luis.