RSS

Servicios en una PDA para tareas en background

16 Jun

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.

Anuncios
 
Deja un comentario

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

 

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: