Acerca de...
El equipo
Encuestas
Política de privacidad
WinTablets

Encuesta

¿Cual crees que triunfará?

Ver Resultados

Cargando ... Cargando ...

últimas entradas importantes

Categorías

Archivos

14
Sep 2014
DesarrolloInteroprfogdev

Mezclar código nativo y manejado en aplicaciones de la Tienda de Windows y Windows Phone

¿Cómo puedo añadir una DLL nativa a mi código en Windows Phone o Windows 8? ¿Tengo que hacer algo especial para que funcione? ¿Me va a ayudar C++/CX? ¿Qué pasa si no funciona, incluso con aquellas instaladas desde NuGet?

A todas esas preguntas, y a alguna más, os voy a responder en esta entrada. Vamos allá.

De todos modos, si has venido aquí para responder a la duda de la excepción de tipo System.TypeLoadException o System.TypeInitializationException durante la ejecución de un componente instalado a través de NuGet, ve al final de la entrada.

Ya lo he comentado en otros sitios, en .NET todas las clases son objetos COM. COM es la evolución de una tecnología que nació en las primeras versiones de Windows para el intercambio de datos.

Primero se llamó DDE, luego se transformó en OLE, siguió subiendo de versión a OLE2, OCX, COM, COM+, DCOM…

En su momento la idea era competir con una cosa llamada CORBA que fue un intento de estandarizar el modelo de componentes. La idea de CORBA (y de todas sus variantes, incluidas las de Microsoft) es tener un conjunto de componentes autónomos que sean capaces de ofrecer de forma estándar sus servicios y que sea el propio componente el que ofrezca la forma sobre cómo acceder a ellos.

Básicamente, por lo menos en la implementación de Microsoft, lo que ocurren es que cuando quieres usar un componente instalado en el sistema, te vas al catálogo de componentes, eliges el que quieras, y comienzas una batería de preguntas, de forma que, al final, obtienes su interfaz pública con todos sus métodos y cómo se llaman.

A partir de ahí ya puedes usarlo en tu código. No voy a entrar en detallas sobre cómo se hace eso. Es complicado, abstruso, retorcido y necesita de ciertas herramientas especiales que se deben ejecutar en tu programa para que todo eso sea funcional. Además, a fecha de hoy está obsoleto.

(En el ínterin, y a título personal, diré que todo eso es un galimatías bastante infumable cuyo único propósito era el de posibilitar la comunicación remota entre diferentes arquitecturas de procesadores y de sistemas operativos. Creo que nadie llegó a usarlo nunca del todo. En su momento, la parte más útil de todo esto era embeber Word o Excel dentro de tu aplicación.)

Todo aquello ha ido evolucionando, y lo que en principio necesitaba a un programador especialista en todo aquello, a fecha de hoy es enormemente sencillo, sobre todo en el mundo .NET y en las aplicaciones de la tienda de Windows y Windows Phone.

De hecho, como ya he comentado, cualquier clase de .NET es un objeto COM listo para ser utilizado de cualquier modo. Si quieres usarlo desde otra clase .NET, simplemente lo llamas, lo instancias o haces lo que quieras con él.

Por ejemplo, la forma que tiene Visual Studio de mostrarnos el IntelliSense es utilizar la potencia COM de cada clase .NET para desplegar todos sus métodos, objetos y demás mediante reflexión, que es la técnica actual para obtener de forma dinámica qué métodos y qué clases hay dentro de un ensamblado .NET (ya sea una DLL o un EXE). Y no es complicado de hacer. Aquí tenéis la referencia sobre el tema.

Por lo tanto, cuando uno añade una DLL .NET a un proyecto existente, tan sólo hay que añadir la referencia de la misma al proyecto de Visual Studio en el que se quiera usar. La magia del IDE y del compilador hacen el resto, que es registrar el fichero durante la carga de la aplicación para que nosotros sólo tengamos que usarla.

¿Pero qué pasa con una DLL nativa? En principio no puedes usarla de forma nativa. Es decir, no puedes dejarla caer en la sección de referencias de tu proyecto, armar un envoltorio mediante Interop a través de una clase .NET y usarla. No en las aplicaciones de la tienda de Windows y de Windows Phone.

[Nota: al menos yo creo que no, pero no estoy totalmente seguro de ello ya que no he probado a hacerlo.]

Bien, ¿entonces cómo?

A través de C++/CX. ¿Qué es C++/CX? Nada más y nada menos que el C++ clásico (y no tan clásico ya que Microsoft está soportando C++11 casi en su totalidad) pero con un sugar syntax para la parte COM que debe ser compatible con .NET.

Es algo similar al C++/CLI con la diferencia de que el primero genera código nativo y el segundo código MSIL que luego debe ser compilado por la máquina virtual .NET en tiempo de ejecución.

Si sabes C++, la parte CX la aprendes en una tarde, menos si ya sabías C++/CLI. Básicamente todas las clases CX compatibles con .NET han de ser sealed y del tipo ref class. No hay punteros sino “tejaícos” (el circunflejo), que devuelve una referencia en lugar de un puntero, la parte pública de la misma debe usar y devolver tipos válidos de la tienda de Windows y poco más. De hecho en la parte privada de la clase puedes usar cualquier cosa nativa siempre que esté dentro de las API Win32 autorizadas, que son casi todas las importantes.

Tenemos, pues, que envolver nuestra DLL en otra escrita en C++/CX y llamar a los métodos de esta última, que a su vez usarán los de la nativa.

O si estamos construyendo un componente para la tienda que necesite de código nativo, simplemente declaramos una interfaz pública compatible y usamos el API de Win32 sin más dentro de los métodos, teniendo cuidado de convertir los tipos entre los nativos y los de la tienda cuando haya que devolverlos.

Básicamente es lo que hace el propio interfaz .NET de las aplicaciones de la tienda de Windows cuando se programa en C# ya que las tripas están escritas en C++/CX.

Un ejemplo práctico. Vamos a obtener el nombre que le hemos dado a nuestro Windows Phone cuando lo enchufamos al ordenador, servicio que no está disponible en las API Modern UI de Windows Phone. Para ello vamos a seguir la vía didáctica y no la que deberíamos seguir si estuviéramos haciendo esto, que es primero investigar dónde está el método nativo que obtiene dicho valor y si esa llamada al API de Win32 es válida o no para una aplicación de la tienda.

Por lo tanto el primer paso es añadir un proyecto del tipo “Windows Runtime Component” desde la rama de C++ de nuestro gestor de proyectos, que nos va a crear un esqueleto de proyecto en C++/CX con dos ficheros, pch.cpp y pch.h. Hay más ficheros, pero estos son los únicos que nos interesan.

Imagen del portapapeles 6

Esos dos ficheros dan soporte a lo que se llama cabeceras precompiladas, que es una técnica o patrón o como queráis llamarlo que acelera de forma muy singular el proceso de compilación del código escrito en C++, pero no vamos a entrar en más detalles sobre ello que tener que añadir los siguientes ficheros a pch.h, dejando el correspondiente cpp sin tocar:

Imagen del portapapeles 5

Una vez hecho esto, renombramos la pareja de ficheros Class1.cpp y Class1.h por NetHelper.cpp y NetHelper.h, aunque podríamos dejar sin tocar los nombres. No obstante, es una buena práctica poner los nombres correctos.

Podríamos compilar ahora el proyecto y obtendríamos un componente nativo que se puede usar en Windows Phone, con una clase pública llamada Class1 dentro del espacio de nombres NetHelper, pero vamos a añadir ya nuestro código.

Sustituimos el contenido de NetHelper.h por el siguiente:

Imagen del portapapeles 4

SI os fijáis, tenemos aquí todos los elementos de un componente C++/CX. Clase sellada de tipo  ref, que tiene una propiedad estática que devuelve una referencia a una cadena de Platform, que es el espacio de nombres base de las aplicaciones de la tienda escritas en C++/CX. El código interior a la propiedad es la forma que tenemos en el lenguaje de definir una propiedad.

Y finalmente, la implementación, que va en NetHelper.cpp:

Imagen del portapapeles 3

Aquí podemos observar a C++/CX en todo su esplendor. El método PhoneName::get() que implementa el getter de la propiedad es un envoltorio que recibe parámetros de la tienda de Windows (en este caso ninguno) y que devuelve un parámetro del mismo tipo. El cuerpo del método, salvo la última línea, que convierte una cadena de C en un string adecuado, es código no C++, sino C, que es la forma de hablar con Win32.

(Nota: el código del ejemplo está sacado de aquí. Lo que yo he hecho ha sido explicar con más detalle el citado artículo de CodeProject).

Si construimos el proyecto, y compila, hemos obtenido una DLL que puede ser usada en un proyecto de Windows Phone como si estuviera escrito en C#.

No hay nada en contra de repetir lo mismo para la tienda de Windows 8, incluso bajo una aplicación universal. Tan solo hay que elegir el tipo de proyecto adecuado, en este caso bajo la rama de Windows Store dentro de C++.

El último paso para utilizar NetHelper en otra aplicación, es añadir la referencia al mismo en dicha aplicación, lo que se hace con el botón derecho sobre la carpeta “Referencias” en el Explorador de Soluciones, añadiendo la DLL a mano o a través de las disponibles en la ventana del “Manejador de Referencias”. Quizás haya que recompilar la solución para que nuestro proyecto esté disponible.

Imagen del portapapeles 2

Finalmente, el uso de todo esto es idéntico al de cualquier otra clase en C#. La única diferencia es interna, o más bien que el código que se va a ejecutar ha sido creado por nosotros en lugar de por Microsoft, porque es así como funciona por dentro la plataforma de las aplicaciones para las tiendas y cómo se llama a código nativo desde los envoltorios escritos en C# que nos ofrecen los SDK de Windows Phone y de Windows.

Imagen del portapapeles

Fijaos que hasta ahora no hemos hablado ni de COM, ni de objetos activables, ni nada que se le parezca. Dad gracias de que sea así, porque la otra opción… Digamos que si esta os parece complicada, no os digo la de usar el compilador de MIDL, crear el fichero de exportación WINMD, realizar el registro de los objetos declarados de forma manual y toda la parafernalia del COM.

Ahora es cuando viene el tío de la rebaja. Si seguís todos los pasos descritos aquí, lo más seguro es que no podáis usar el código de la última captura. Si el IDE y el compilador os encuentra la referencia (que podría no hacerlo incluso después de reconstruir la solución completa), es muy posible que a la hora de ejecutar ese código obtengáis una retahíla de excepciones encadenadas, empezando por una de tipo

 

A first chance exception of type ‘System.TypeLoadException’ occurred in NetHelper.dll

Additional information: Requested Windows Runtime type ‘ClrCompression.ZLibInterop’ is not registered.

 

Seguida por otra de

 

A first chance exception of type ‘System.TypeInitializationException’ occurred in NetHelper.dll

Additional information: The type initializer for ‘System.IO.Compression.Interop’ threw an exception.

 

Y alguna que otra más. Estamos ante un bug del compilador de C# que vamos a explicar cómo sortear en una segunda entrada.

Por RFOG | 12 Comentarios | Enlaza esta entrada

12 Comentarios


Fatal error: Uncaught Error: Call to undefined function ereg() in /homepages/4/d443751538/htdocs/wintablet.info/wp-content/themes/wintabletinfo/functions.php:92 Stack trace: #0 /homepages/4/d443751538/htdocs/wintablet.info/wp-content/themes/wintabletinfo/functions.php(27): commenter_avatar() #1 /homepages/4/d443751538/htdocs/wintablet.info/wp-includes/class-walker-comment.php(179): custom_comments(Object(WP_Comment), Array, 1) #2 /homepages/4/d443751538/htdocs/wintablet.info/wp-includes/class-wp-walker.php(145): Walker_Comment->start_el('', Object(WP_Comment), 1, Array) #3 /homepages/4/d443751538/htdocs/wintablet.info/wp-includes/class-walker-comment.php(139): Walker->display_element(Object(WP_Comment), Array, '5', 0, Array, '') #4 /homepages/4/d443751538/htdocs/wintablet.info/wp-includes/class-wp-walker.php(387): Walker_Comment->display_element(Object(WP_Comment), Array, '5', 0, Array, '') #5 /homepages/4/d443751538/htdocs/wintablet.info/wp-includes/comment-template.php(2174): Walker->paged_walk(Array, '5', 0, 0, Array) in /homepages/4/d443751538/htdocs/wintablet.info/wp-content/themes/wintabletinfo/functions.php on line 92