RSS

Archivos Mensuales: junio 2008

Como activar la vibración de la PDA

Solución en C# para activar el vibrador de una PDA.

Ejemplo de llamada mediante pinvoke a NLedSetDevice.

(…)

[DllImport(“coredll.dll”, SetLastError = true)]
private static extern bool NLedSetDevice(uint h, ref NLED_SETTINGS_INFO pOutput);

(…)

NLED_SETTINGS_INFO Configuracion = new NLED_SETTINGS_INFO();
Configuracion.LedNum = id;
Configuracion.OffOnBlink = 2;
NLedSetDevice(NLED_SETTINGS_INFO_ID, ref Configuracion);
(…)

El archivo adjunto inlcuye la solución desarrollada sobre VS 2005 y ha sido probada sobre una HTC P3300

Descargar solución

Anuncios
 
Deja un comentario

Publicado por en 23 junio, 2008 en Artículo, Trucos

 

Servicios en una PDA para tareas en background

Introducción

Para hacer que una aplicación se ejecute nada más resetear una PDA, basta con colocar el ejecutable de nuestra aplicación en el directorio “\Windows\StartUp”. Pero hasta que podamos disfrutar de Windows Mobile 7 (que permitirá ejecutar 32000 procesos simultaneos, ya que está basado en CE 6.0), esto es un problema, ya que los sistemas operativos basados en Windows CE 5 (Windows Mobile 5, 6 y 6.1) y anteriores, solo permiten ejecutar 32 procesos. Además a esto hay que añadir, que las PDAs suelen venir con software adicional que se ejecuta al inicio. Algunas PDAs la primera vez que las enciendes ya tienen 28 procesos en memoria.

Desde Windows Mobile 2003, Microsoft ha introducido una solución para este problema. Consiste en servicios que son DLLs corriendo como diferentes hilos en un solo proceso (services.exe).

El interface de un servicio para Pocket Pc es similar al interface de un driver. Es una DLL que exporta un conjunto de funciones, y el proceso services.exe carga esas DLLs y las inicializa llamando a una de sus funciones.

Para el ejemplo vamos a hacer un servicio en una DLL usando MFC.

Creando DLL del servicio con MFC

En Visual Studio (yo he usado 2005) creamos un proyecto de tipo DLL MFC de SmartDevice

Servicios01

Añadimos las definiciones de las funciones que exportará el servicio, con vuestro propio prefijo de tres letras (yo he puestro MRC como prefijo de mis funciones), en el archivo .cpp de nuestra solución. Este prefijo lo tendremos que poner luego en una entrada del registro. El proceso Services.exe llama a estas funciones para inicializar y comunicarse con el servicio.

extern "C" DWORD PASCAL EXPORT MRC_Close(DWORD dwData)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 0;

}

extern "C" DWORD PASCAL EXPORT MRC_Deinit(DWORD dwData)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 0;

}

extern "C" DWORD PASCAL EXPORT MRC_Init(DWORD dwData)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 1;

}

extern "C" DWORD PASCAL EXPORT MRC_IOControl(

DWORD dwData,

DWORD dwCode,

PBYTE pBufIn,

DWORD dwLenIn,

PBYTE pBufOut,

DWORD dwLenOut,

PDWORD pdwActualOut)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 1;

}

extern "C" DWORD PASCAL EXPORT MRC_Open(

DWORD dwData,

DWORD dwAccess,

DWORD dwShareMode)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 0;

}

extern "C" DWORD PASCAL EXPORT MRC_Read(

DWORD dwData,

LPVOID pBuf,

DWORD dwLen)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 0;

}

extern "C" DWORD PASCAL EXPORT MRC_Seek(

DWORD dwData,

long pos,

DWORD type)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 0;

}

extern "C" DWORD PASCAL EXPORT MRC_Write(

DWORD dwData,

LPCVOID pInBuf,

DWORD dwInLen)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return 0;

}

Ponemos los mismos nombres de funciones en el archivo .def de nuestro proyecto (el prefijo de tres letras por supuesto debe ser el mismo). El archivo .def se crea automáticamente en el asistente de creación de proyecto:

EXPORTS

; Colocar aquí las exportaciones explícitas

MRC_Close

MRC_Deinit

MRC_Init

MRC_IOControl

MRC_Open

MRC_Read

MRC_Seek

MRC_Write

Añadimos un código de inicialización específica a la función MRC_Init. Lo más normal es necesitar crear un nuevo hilo que encapsule toda la lógica del servicio. Para hacer esto definimos una función de control. Esta función contendrá la creación adicional de ventanas, timers o cualquier elemento especifico que la aplicación necesite. En nuestro ejemplo vamos a crear un timer y lanzar un mensaje en una ventana a través de un bucle.

UINT MiFuncionDeControl ( LPVOID pParam )

{

theApp.m_nTimer = SetTimer(0, 0, 10 * 1000, MiProcesoTimer);

MSG msg;

while (GetMessage(&msg, 0, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return 0;

}

extern "C" DWORD PASCAL EXPORT MRC_Init(DWORD dwData)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

theApp.m_pThread = AfxBeginThread( MiFuncionDeControl, 0);

return 1;

}

void CALLBACK MiProcesoTimer(

HWND hwnd,

UINT uMsg,

UINT idEvent,

DWORD dwTime )

{

::MessageBox(NULL, _T("Servicio :)"), _T("Información"), MB_OK);

}

Importante:  La function MRC_Initi debe devolver un valor distinto de cero, ya que el valor cero indica un fallo en la inicialización del servicio, y provoca que la DLL del servicio sea descargada inmediatamente de la memoria.

Registrar el Servicio

Para hacer que el servicio se inicie automáticamente tras un soft-reset, se debe añadir una clave de nombre único (en el ejemplo “servicio_pda”) en el registro, bajo la rama HKEY_LOCAL_MACHINE\Services y especificar los siguientes valores:

Servicios02

Prefix contendrá las 3 letras del prefijo de nuestras funciones.

Dll contendrá el nombre completo de la dll creada. No hay que especificar la ruta, ya que esta Dll siempre deberá estar en la carpeta \Windows.

Keep tiene que valer 1 para los servicios que corren en background. Si vale 0 la Dll será descargada de la memoria tras su inicialización.

Index deberá valer 0.

Description será una breve descripción del servicio.

DisplayName es el nombre de nuestro servicio.

Order indica el orden en el que Services.exe irá cargando los servicios en memoria. Cuanto menor sea antes se cargará en memoria.

Context deberá valer 0.

Conclusión

Es muy recommendable usar servicios para tareas en background, por la limitación de 32 procesos impuesta por Windows CE.

Para la implementación en una PDA, dependiendo de su configuración de seguridad, es muy importante que la DLL del servicio esté firmada digitalmente, ya que si no lo está, Services.exe la ignorará completamente.

 
Deja un comentario

Publicado por en 16 junio, 2008 en Artículo, Desarrollo

 

Como obtener el IMSI de la tarjeta SIM

Con este truquito aprenderemos a obtener el IMSI de la tarjeta SIM de un dispositivo con Windows Mobile.

El IMSI (International Mobile Subscriber Identity) es un código de identificación único para cada dispositivo de telefonía móvil, que se encuentra almacenado en la tarjeta SIM y que permite la identificación del dispositivo a través de las redes GSM y UMTS.

Está formado por 3 campos:
MCC (Mobile Country Code): código del país (3 dígitos).
MNC (Mobile Network Code): código de la red móvil (2 ó 3 dígitos, depende del país).
MSIN (Mobile Suscriber Identification Number): número que contiene la identificación de la estación móvil (10 dígitos como máximo).

El IMSI está almacenado en uno de los registros de la SIM (0x6F07). La espeficicación GSM 11.11 explica cual es la estructura de archivos de la SIM y cómo está codificado este registro:
http://www.ttfn.net/techno/smartcards/gsm11-11.pdf.

Para acceder a la información almacenada en la tarjeta SIM recurrimos a la API SimManager, que proporciona las funciones necesarias para comunicarnos con la SIM y extraer los datos que más nos interesen. Podemos encontrar más información sobre esta API en el siguiente link:
http://msdn.microsoft.com/en-us/library/aa458492.aspx

El código que nos permite obtener el IMSI es el siguiente:

//Prototipos P/Invoke

[DllImport(“cellcore.dll”)]
static extern int SimReadRecord
(
       int hSim,
       uint dwAddress,
       uint dwRecordType,
       uint dwIndex,
       byte[] lpData,
       uint dwBufferSize,
       ref int dwSize
);

[DllImport(“cellcore.dll”)]
static extern int SimInitialize
(
      uint dwFlags,
      int lpfnCallBack,
      uint dwParam,
      ref int lphSim
);

[DllImport(“cellcore.dll”)]
static extern int SimDeinitialize (int hSim);

// Función que accede a un registro de la SIM para leer el IMSI y

// lo devuelve en un string

  public string LeerIMSI()
  {
        int resul;

        const int EF_IMSI = 0x00006F07;
        const int SIM_RECORDTYPE_TRANSPARENT = 0x1;

        int hSim = 0;
        byte[] imsi = new byte[10];
        int zero = 0;

// Inicialización de la SIM
        resul = SimInitialize(0, 0, 0, ref hSim);
// Acceso al registro que contiene el IMSI –> 0x6F07
        resul = SimReadRecord (hSim, EF_IMSI,

                   SIM_RECORDTYPE_TRANSPARENT, 0,

                   imsi, (uint)imsi.Length, ref zero);
        resul = SimDeinitialize(hSim);

// Se decodifica el número IMSI y se devuelve como una

        // cadena
        StringBuilder cadena = new StringBuilder();
        cadena.Append(imsi[1] >> 4);
        for (int i = 2; i <= imsi[0]; i++)
        {
             cadena.Append(imsi[i] & 15);
             cadena.Append((imsi[i] >> 4) & 15);                
        }
        return cadena.ToString();           
  }
      

Podemos comprobar que el número IMSI leído es correcto enviando con el hyperterminal el comando:  AT + CIMI.

 
Deja un comentario

Publicado por en 2 junio, 2008 en Artículo, Desarrollo