Error: No se puede mostrar un mensaje de error porque no se pudo encontrar un ensamblado de recursos opcional que lo contiene.

Aka: Could not find resource assembly

Esta mensaje de error aparece cuando se produce una excepción y no se encuentra el archivo de recursos de descripciones de errores para .NET Compact Framework.

Todas las cadenas de error asociadas a .NET Compact Framework 2.0 se almacenan en un archivo de recursos externo, llamado System.SR.dll. Este ensamblado no se distribuye para liberar memoria RAM del dispositvo.

La solución pasa por instalar el CAB System.SR.[Lang].cab dónde Lang denota el idioma en el que queremos mostrar el mensaje de la excepción. Dicho CAB se encuentra en:

%PROGRAM_FILES%Microsoft.NETSDKCompactFrameworkv2.0WindowsCEDiagnostics

NOTA: De forma análoga sucede lo mismo para mensajes de error relacionados con .NET Compact Framework 3.5. Alejandro Mezcúa publicó hace poco un post sobre ello.

Bluetooth en .NET [Compact] Framework

Hará aproximadamente tres años empecé a desarrollar una  libreria en .NET para la gestión de Bluetooth en Windows Mobile a raíz de un artículo que se publicó en dotNetMania num 35 (Marzo 2007).

Durante el tiempo que estuve desarrollando dicha libreria, pude apreciar realmente la dificultad que entraña un desarrollo sobre dicha tecnologia. De hecho, la libreria para Windows Mobile no pude finalizarla –se quedó con algunos Bugs- y la de Windows XP/Vista (unicamente 32 bits) logré que permitiera gestionar la radio Bluetooth, buscar dispositivos cercanos, emparejarse y comunicarse via COM mediante el servicio RFCOMM; quise expandir la libreria mediante el uso de la capa SDP (Service Discovery Protocol) de búsqueda y uso de servicios específicos a través del API de Bthioctl.dll de Windows Vista, pero ahí se quedó por falta de tiempo.

Desde que el material está expuesto en desarrolloMobile.NET, recibo muy a menudo preguntas o curiosidades en mi buzón de correo personal de las que, pese a que algunas desconozco, la mayoria hacen referencia a cómo empezar y de ahí el motivo de este post, para todos aquellos que quieran saber por dónde pueden empezar a implementar soluciones basadas en Bluetooth en sus proyectos.

Aspectos básicos

Incialmente, tanto Windows Mobile como Windows XP/Vista nos ofrece librerias nativas para poder inicializar y obtener la información de la radio bluetooth local y algunas de esas APIs las podemos ver en el siguiente código y cuyas referencias las encontraréis aqui.

   1: public enum BthEstadosRadio : int
   2: { Desactivado, Activado, Detectable };
   3:  
   4: public class BTHRadio : IDisposable
   5: {
   6:     [DllImport("bthutil.dll", SetLastError = true)]
   7:     private static extern int BthGetMode(out BthEstadosRadio mode);
   8:     [DllImport("bthutil.dll", SetLastError = true)]
   9:     private static extern int BthSetMode(BthEstadosRadio mode);
  10:     [DllImport("Btdrt.dll", SetLastError = true)]
  11:     private static extern int BthReadLocalVersion(
  12:     ref byte phci_version, ref ushort phci_revision,
  13:     ref byte plmp_version, ref ushort plmp_subversion,
  14:     ref ushort pmanufacturer, ref byte plmp_features);
  15:     [DllImport("Btdrt.dll", SetLastError = true)]
  16:     private static extern int BthReadCOD(ref CategoriaDispositivo pcod);
  17:     [DllImport("Ws2.dll", SetLastError = true)]
  18:     public static extern int WSACleanup();
  19:     [DllImport("Ws2.dll", SetLastError = true)]
  20:     public static extern int WSAStartup(ushort version, byte[] wsaData);
  21:     //los enumeradores Fabricantes y CategoriaDispositivo son
  22:     //identificadores únicos (http://www.bluetooth.org)
  23:     private Fabricantes _fabricante;
  24:     private short _versionRadio;
  25:     private CategoriaDispositivo _claseDisp;
  26:     //constructor
  27:     public BTHRadio()
  28:     { // inicializamos sockets
  29:         ushort wsv =
  30:         ((ushort)(((byte)(2)) | ((ushort)((byte)(2))) << 8));
  31:         byte[] _data = new byte[512];
  32:         WSAStartup(wsv, _data);
  33:         //obt. versión y fabricante
  34:         byte version = 0, lversion = 0, caract = 0;
  35:         ushort revision = 0, lsubrevision = 0, fab = 0;
  36:         BthReadLocalVersion(ref version, ref revision,
  37:         ref lversion, ref lsubrevision, ref fab, ref caract);
  38:         this._fabricante = fab;
  39:         this._versionRadio = (short)lsubrevision;
  40:         //obtenemos categoría disp.
  41:         BthReadCOD(ref this._claseDisp);
  42:     }
  43:     //propiedad lectura/escritura del estado de radio
  44:     public BthEstadosRadio EstadoRadio
  45:     {
  46:         get
  47:         {
  48:             BthEstadosRadio currentMode;
  49:             BthGetMode(out currentMode);
  50:             return currentMode;
  51:         }
  52:         set
  53:         { BthSetMode(currentMode); }
  54:     }
  55:     //liberamos WS2.dll
  56:     public void Dispose()
  57:     { WSACleanup(); }
  58: }

Con esta clase podemos empezar a gestionar nuestro Bluetooth (siempre y cuando se MS Bluetooth Stack) pero en realidad nos ofrecerá muy poca funcionalidad al no ser que la finalidad de la aplicación sea mostrado el estado e información de la radio. La información, en definitiva, que muestra este código es la que podemos ver en la siguiente captura de pantalla:

Inforadio

Aspectos avanzados

Sin embargo, el uso de esta tecnología en nuestros desarrollos requiere unos requisitos previos. En primer lugar es altamente recomendable que se entienda el funcionamiento de Bluetooth. A grandes rasgos, remarcaría dos conceptos muy distintos.

 PRIMERO: Debemos conocer el perfil sobre el queremos trabajar. Bluetooth es un protocolo y com tal gestiona el intercambio de mensajes en base a un estándard desarrollado con el fin de comunicar dos dispositivos. No haremos “nada” sólo con Bluetooth, en todo caso sobre Bluetooth y lo haremos a través de los perfiles los cuales definen el comportamiento de los dispositivos y la comunicación. No es lo mismo que una aplicación haga uso de Bluetooth para la gestión de un manos libres que para el intercambio de archivos entre un dispositivo Windows Mobile y Windows Vista, por ejemplo. Los perfiles definen el cómo se lleva a cabo la comunicación. La comunicación con un manos libres utiliza un perfil (HFP), y el intercambio de objectos (archivos, contactos y demás) utiliza otro (OBEX).especificacion

SEGUNDO: En cuanto a la conexión entre dispositivos recordad que el proceso que normalmente realizamos para el emparejamiento manual se debe hacer programaticamente, con todo lo que ello conlleva. Para conectarnos a un dispositivos necesitamos saber la dirección del dispositivo y el Guid del servicio al que queremos conectarnos. El proceso de emparejamiento podría ser algo así:

   1: public class BTHDispositivo
   2: {
   3:     [DllImport("Btdrt.dll", SetLastError = true)]
   4:     private static extern int BthCreateACLConnection(byte[]
   5:     remoteBthAdd,
   6:     ref ushort phandle);
   7:  
   8:     [DllImport("Btdrt.dll", SetLastError = true)]
   9:     private static extern int BthCloseConnection(ushort phandle);
  10:  
  11:     [DllImport("Btdrt.dll", SetLastError = true)]
  12:     private static extern int BthAuthenticate(byte[] remoteBthAdd);
  13:  
  14:     [DllImport("btdrt.dll", SetLastError = true)]
  15:     private static extern int BthSetPIN(byte[] localBthAdd,
  16:     int pinLength, byte[] ppin);
  17:  
  18:     public string _nombre;
  19:     public byte[] _direccion;
  20:  
  21:     //pin.Length => 0 y pin.Length <= 16
  22:     public void EstablecerPIN(string pin)
  23:     {
  24:         byte[] pinbytes = System.Text.Encoding.ASCII.GetBytes(pin);
  25:         int len = pin.Length;
  26:         BthSetPIN(_direccion, len, pinbytes);
  27:     }
  28:     public bool QuitarPin()
  29:     {
  30:         BthRevokePIN(_direccion);
  31:     }
  32:  
  33:     public void Emparejar(string pin)
  34:     {
  35:         ushort handle = 0;
  36:         //conectamos
  37:         BthCreateACLConnection(_direccion, ref handle);
  38:         //autenticamos
  39:         BthAuthenticate(_direccion);
  40:         //desconectamos
  41:         BthCloseConnection(handle);
  42:     }
  43: }

Una vez emparejado toca conectar mediante un servicio común entre ellos cuya conexión se puede realizar mediante Sockets a través de un EndPoint que exponga el servicio o perfil a utilizar (SOCKADDR_BTH) tal y como muestra el siguiente enlace. En modo de ejemplo a continuación se muestra el método de conexión de la libreria de desarrolloMobile.NET para WinXP.

   1: public void Conectar(Servicios.BthServicio servicio)
   2: {
   3:     clienteSck  = new BthSocket();
   4:     
   5:     BthEndPoint endPoint = new BthEndPoint(this, servicio);
   6:  
   7:     try
   8:     {
   9:         clienteSck.Connect(endPoint);
  10:     }
  11:     catch (SocketException se)
  12:     {
  13:         throw;
  14:     }
  15:     catch (Exception ex)
  16:     {
  17:         throw;
  18:     }
  19: }

¿Ahora bien, cómo averiguamos la dirección y el Guid del Servicio?

En cuanto a los servicios o perfiles, todos y cada uno tienen un Guid asociado que es universal (estandarizado). En sitio oficial de Bluetooth podeis encontrar una lista completa (NOTA: Debéis registraros).

En cuanto a la dirección del dispositivo a conectar hay que saber lo siguiente. Hablando desde el punto de vista programático las únicas formas de conocer la dirección destino es a través del descubrimiento de dispositivos cercanos o consultando los dispositivos ya emparejados (previo descubrimiento). Es aqui, en este punto, dónde la mayoria de desarrollos quedan atascados, puesto que el código de desubrimiento es complejo y se lleva a cabo a través de las clases WSALookUpServiceBegin, WSALookUpServiceNext y WSALookUpServiceEnd y cuya forma de proceder la podéis encontrar aquí. Siempre podemos conocer la dirección del dispositivo emparejandolo manualmente y por norma general, MS Bluetooth Stack almacena la dirección de todos los dispositivos previamente emparejados en el registro.

Microsoft tiene un ejemplo de uso para Bluetooth que podéis encontrar aquí. En este ejemplo, además, podéis ver el método de descubrimiento de servicios dado un dispositivo. Además muestra en la clave dónde se almacenan los dispositivos emparejados o de confianza.

Debug

Otros de los quebraderos de cabeza fue el Debug del proyecto. Si éste se realiza sobre una aplicación Windows Desktop no hay nada que decir pero si se realiza sobre Windows Mobile, entonces el emulador de poco me servía. Debía emparejar un dispositivo físico, realizar el despliegue sobre él a través de ActiveSync y depurar la aplicación sobre la misma.

Sin embargo, hace poco me topé con un artículo de codeproject que permitia emular la radio Bluetooth sobre un emulador Windows Mobile. El autor es

Dmitry Klionsky, y el enlace lo podéis encontrar aquí

Recomendaciones

  • Utiliza, en la medida de lo posible, componentes de gestión de pilas Bluetooth de terceros. No voy a decir ningún proveedor puesto que no he utilizado ninguno en especial. Pero el desarrollo de una libreria o componente propio debería ser la última alternativa.
  • Conoce la pila de tu dispositivo Bluetooth(http://en.wikipedia.org/wiki/Bluetooth_stack). Las mas comunes son Microsoft Bluetooth Stack y Widcomm y la mayoria de componentes de terceros ofrecen servicios para como mínimo ambas pilas. Recuerda que una libreria para Bluetooth desarrollada para Microsoft Bluetooth stack NO es compatible con Widcomm y viceversa. De hecho las librerias que encontrarás en desarrolloMobile.NET no son válidas para dispositivos Widcomm y demás. Encontraras un enlace con el tipo de pilas utilizas por dispositivos (modelo/marca) aqui.
  • Si te apatece profundizar sobre el tema, en desarrolloMobile.NET (sección Bluetooth) encontrarás más recursos. Otros de los sitios que no puedes dejar de visitar es el proyecto 32feet.net.

DotNetNuke con Vicenç Masanas

IMAGE_024Esta misma tarde hemos tenido la reunión mensual con el grupo de usuarios de la Cataluña Central (CatDotNet) y hemos tenido el placer de contar con Vicenç Masanas (Disgrafic), MVP en ASP.NET y miembro del Core Team de DotNetNuke, quién nos ha estado hablando tanto de las novedades y roadmap de DotNetNuke como de la forma de desarrolloar módulos y de la propia plaforma de desarrollo.

Esta comunidad OpenSource sobre ASP.NET, está dando pasos cualitativos tras cada versión, la última versión, la 5, que se prevee aparezca en el Q2 de este mismo año. Como novedades, aqui podéis encontrar una lista. Nos ha estado explicando, además, las diferencias IMAGE_023y enfoques entre la edición Professional y la Community, y el papel de la recién creada DotNetNuke Corporation.

Me ha encantado la riqueza del marco de desarrollo de DotNetNuke, -cuyo código puede ser descargado por todo el mundo- y lo que aún no acabo de ver es que pese a que este dentro de un entorno OpenSource siga siendo fiel únicamente a SQL Server –y no es que tenga nada en contra de SQL Server, al contrario- y no haya posibilidad de alternativas, aunque, esto podría cambiar en medio plazo.

En fín, de las dos horas inciales se han convertido en casi 3 horas de cháchara y esperamos volver a contar de nuevo con él. Ahora toca esperar a la aparición de la versión 5 (+/- junio) y actualizar desarrolloMobile.NET!!!