Almacenamiento en Windows Pocket 2003

0
15447

Manejar el Object Store en Windows Pocket 2003 con eVC++:

El «Object Store» (ObS) en Windows Pocket 2003 cumple en muchos casos la
misma función que el disco duro en un equipo de escritorio.  Proporciona
almacenamiento permanente para las aplicaciones y sus datos incluso cuando la
fuente de alimentación se desconecta, siempre y cuando exista una fuente de
reserva de alimentación.  Físicamente el ObS lo componen varios chips de
almacenamiento de memoria no volátil.

Conceptualmente, el ObS consiste de tres tipos de almacenamiento persistente
– sistema de ficheros, bases de datos y registro del sistema – que comparten un
mismo heap de memoria.   

Una gran ventaja del uso del ObS para el almacenamiento de datos es que el
mecanismo para almacenar datos esta basado en transacciones (utilizando por
ejemplo algoritmos como el Commit en dos fases).  Si  se
interrumpe la alimentación mientras se están escribiendo datos al ObS, el
sistema operativo se asegura de que el almacenamiento no se corrompa y la base
de datos quede en un estado consistente, bien sea completando la operación
cuando el sistema se reinicia (asegurar el commit)  o
volviendo la base de datos a la última configuración estable conocida antes de
la interrupción (rollback).  Para el caso del sistema de ficheros y
del registro, esto puede significar la recarga de los valores iniciales de la
ROM en caso de no haber hecho un backup del sistema.

A continuación  nos centramos en el estudio de las bases de datos del
Object Store.

Base de datos del Object Store

El modelo de de base de datos que usa el ObS  es un sistema de
almacenamiento pequeño con estructura plana y optimizado para un almacenamiento
eficiente.  Esta base de datos consiste en un número de registros simples,
cada uno de los cuales contiene un número de propiedades de distinto tipo.  Un registro puede tener un número variable de propiedades, pero no puede
contener a otro registro.  Por lo tanto el Object Store tendría la
siguiente estructura:

 

Cada base de datos, cada registro de la misma y cada propiedad de cada registro tiene asociado un
identificador del tipo CEOID.  Cada registro no es más que un conjunto de
propiedades (CEPROPVAL) que se identifican unívocamente por un CEOID.

Puesto que Windows Pocket PC esta diseñado para operar en entornos
relativamente volátiles, las actualizaciones de la base de datos no solo se
realiza cuando se abre o se cierra, sino que se actualiza después de cada
escritura con la función
CeWriteRecordProps que explicaremos más adelante.

Estructura de los registros

Como hemos dicho antes, un registro no es más que un conjunto de propiedades
(CEPROPVAL):

  

 

typedef struct _CEPROPVAL { 
	CEPROPID propid;
	WORD wLenData;
	WORD wFlags;
	CEVALUNION val;
} CEPROPVAL;
typedef CEPROPVAL *PCEPROPVAL; 

 

typedef union _CEVALUNION {
           short iVal;

           USHORT uiVal;

           long lVal;

           ULONG ulVal;

           FILETIME filetime;
           LPWSTR lpwstr;

           CEBLOB blob;

           BOOL boolVal
           double dblVal
} CEVALUNION;

 

CEPROPVAL:

    Esta estructura contiene los siguientes campos:

  • CEPROPID propid : Identificador del valor de la propiedad.  La parte
    alta de la palabra consiste en  un tipo definido por la aplicación,
    definido por el programador, mientras que la parte baja de la palabra es una
    constante predefinida que indica el tipo del miembro CEVALUNION val.

CEPROPID puede tomar los siguientes valores:
(Los registros pueden tener propiedades de los siguientes tipos)

Value Description
CEVT_BLOB A BLOB structure.
CEVT_BOOL A Boolean value.
CEVT_FILETIME A FILETIME
structure.
CEVT_I2 A 16-bit signed integer.
CEVT_I4 A 32-bit signed integer.
CEVT_LPWSTR A null-terminated string.
CEVT_R8 A 64-bit signed integer.
CEVT_UI2 A 16-bit unsigned integer.
CEVT_UI4 A 32-bit unsigned integer.

A partir de estas propiedades podemos construir tipos personalizados usando
la macro PROP_TAG

 

  •  WORD wLenData: Actualmente no usado
  •  WORD wFlags:  Flags especiales de la propiedad, Puede ser uno
    de los siguientes valores:
Value Description
CEDB_PROPNOTFOUND Valor guardado por la función
CeReadRecordProps
en caso de que la propiedad no se encuentre.
CEDB_PROPDELETE Si se pasa como parámetro a la función
CeWriteRecordProps hace que dicha
popiedad sea borrada.

 

  • CEVALUNION val: En este campo es donde se almacena la información de la
    propiedad.

    Este campo consiste en una enumeración de C++, es decir un único
    valor que puede tomar distintos tipos:

    • iVal El valor es un integer.
    • uiVal El valor es un integer sin signo.
    • lVal  El valor es un long.
    • ulVal El valor es un long sin signo    

    • filetime Dato con la estructura FILETIME.
    • lpwstr Puntero a una cadena de caracteres terminada en
    • blob Estructura del tipo CEBLOB.
    • boolVal Valor booleano: un entero sin signo de 16-bits.
    • dblVal Valor double de 64-bits con signo.

    De estos posibles valores deberemos usar el adecuado según el tipo e dato
    que deseemos almacenar.

     

Diseño de nuestra base de datos

Lo complicado para utilizar el sistema de base de datos del Object Store, es
trasladar el diseño conceptual de nuestra base de datos al modelo que usa por el
Object Store.

Si por ejemplo tenemos una base de datos de Clientes donde por cada cliente
almacena los siguientes datos:

class Cliente :
public CObject{
public:
        CString dni;
        CString nombre;
        CString apellidos;
        CString mail;
        CString telefono;
Cliente();
};
 
   

 

Cada uno de los campos de información del cliente serán
almacenados como cadenas de caracteres, por lo que dentro de el CEPROPVAL, el
valor CEOID será CEVT_LPWSTR y la información se almacenará en CEPROPVAL.val.lpwstr.

Para poder distinguir entre las distintas propiedades del
registro, al ser todas del tipo
CEVT_LPWSTR, creamos tipos de dato
personalizados con la macro PROP_TAG:

#define HHPR_DNI          PROP_TAG( CEVT_LPWSTR, 0x8200 )
#define HHPR_NOMBRE       PROP_TAG( CEVT_LPWSTR, 0x8201 )
#define HHPR_APELLIDOS    PROP_TAG( CEVT_LPWSTR, 0x8202 )
#define HHPR_MAIL         PROP_TAG( CEVT_LPWSTR, 0x8203 )
#define HHPR_TELEFONO     PROP_TAG( CEVT_LPWSTR, 0x8203 )

De esta forma si tenemos un cliente para poder insertarlo en
la base de datos del ObS tendremos que copiar sus campos de la siguiente forma:

CEPROPVAL *rgProps = NULL; //registro = conjunto de propiedades

rgProps = new CEPROPVAL[ 4 ];
memset( rgProps, 0, 4 * sizeof( CEPROPVAL ) );

rgProps[0].propid = HHPR_DNI;
rgProps[0].val.lpwstr = LPWSTR(LPCWSTR (cliente->dni));

rgProps[1].propid = HHPR_NOMBRE;
rgProps[1].val.lpwstr = LPWSTR(LPCWSTR (cliente->nombre));

rgProps[2].propid = HHPR_APELLIDOS;
rgProps[2].val.lpwstr = LPWSTR(LPCWSTR (cliente->apellidos));

rgProps[3].propid = HHPR_MAIL;
rgProps[3].val.lpwstr = LPWSTR(LPCWSTR (cliente->mail));

rgProps[4].propid = HHPR_TELEFONO;
rgProps[4].val.lpwstr = LPWSTR(LPCWSTR (cliente->telefono));
 

/*Para una mayor comodidad los números anteriores pueden ser sustituidos por
#defines*/

Finalmente en la variable rgProps tenemos nuestro registro dispuesto para ser
insertado en nuestra base de datos.

Análogamente para transformar un registro leído de la base de datos en un
Cliente realizamos la operación contraria:

lpwstr = rgProps[0].val.lpwstr ;
cliente->dni = LPCWSTR(lpwstr);

lpwstr = rgProps[1].val.lpwstr ;
cliente->nombre = LPCWSTR(lpwstr);

lpwstr = rgProps[2].val.lpwstr ;
cliente->apellidos = LPCWSTR(lpwstr);

lpwstr = rgProps[3].val.lpwstr ;
cliente->mail = LPCWSTR(lpwstr);

lpwstr = rgProps[4].val.lpwstr ;
cliente->telefono = LPCWSTR(lpwstr);

(Otro método sería buscar entre cada una de las propiedades comparando rgProps[i].propid hasta encontrar la que buscamos)
 

(Nótese la gran utilidad que nos proporciona el que el campo val de CEPROPVAL sea una union, nos permite que pueda tomar valores de distinto tipo. 
En caso de tener algun valor entero como int i  = rgProps[5].val.iVal 
siendo rgProps[5].propid = CEVT_I4;)

Manejo de la base de datos

Con la base de datos debemos de ser capaz de realizar las siguientes tareas:
crear, abrir, leer, escribir, borrar y cerrar.

 

En nuestro caso para abrir nuestra base de datos debemos primero encontrar el
identificador de la base de datos en caso de no saberlo por lo que se añade la
operación «buscar» la base de datos dentro de todo el ObS.

Crear una base de datos:

Para crear una nueva base de datos usamos la función CeCreateDatabaseEx.  Para crear nuestra base de datos debemos indicarle
el nombre de la base de datos MY_DB_FILE   y el tipo DBTYPE_CLIENTES que será valores personalizados.

CEOID m_oidDatabase;
CEGUID v_guid;  // GUID para el volumen de la base de datos

 //Estos son valores personales que identifican
nuestra base de datos.

  #define MY_DB_FILE  TEXT( «\\CLIENTES_DB.DB»
)
  #define DBTYPE_CLIENTES 21238

    //Obtememos el
GUID del Object Store

    CREATE_SYSTEMGUID( &v_guid
);

 
        CEDBASEINFO info;

        memset( &info, 0, sizeof( info ) );
        info.dwFlags = CEDB_VALIDNAME | CEDB_VALIDTYPE;
        lstrcpy( info.szDbaseName, SZ_DB_FILE );
        info.dwDbaseType = DBTYPE_CLIENTES;
        m_oidDatabase = CeCreateDatabaseEx( &v_guid, &info );

    
 

Abrir base de datos:

    Para abrir una base de datos debemos saber su
identificador, o bien buscar la base de datos en todo el object store:

    Buscar identificador de una base de datos:   

    //Obtememos el
GUID del Object Store en caso de no
haberlo obtenido antes
    CREATE_SYSTEMGUID( &v_guid );

    hFind = CeFindFirstDatabaseEx( &v_guid, DBTYPE_CLIENTES );
    if ( hFind != INVALID_HANDLE_VALUE )
    {
        for ( ;; )
        {
            m_oidDatabase = CeFindNextDatabase( hFind );
            if ( !m_oidDatabase ||
                 CeOidGetInfoEx( &v_guid, m_oidDatabase, &oidInfo ) && oidInfo.wObjType == OBJTYPE_DATABASE &&
                  !wcscmp( oidInfo.infDatabase.szDbaseName, MY_DB_FILE ) )
                break;
        }
        CloseHandle( hFind );
    }
 

Una vez encontrado nuestra base de datos tenemos su identificador en m_oidDatabase y v_guid como el volumen donde se encuentra montado.

Con el identificador m_oidDatabase abrimos la base de datos con la función CeOpenDatabaseEx:

 

    m_hDatabase = CeOpenDatabaseEx( &v_guid, &m_oidDatabase, 0, 0, CEDB_AUTOINCREMENT, NULL );

    if ( !m_hDatabase )
    {
        ShowMsg( TEXT( "Error abriendo base de datos." ), GetLastError() );
        return FALSE;
    }

    return TRUE;

    

Una vez abierta la base de datos podemos operar con sus registros usando el
manejador m_hDatabase .

Escribir Registros

    Para escribir en la base de datos usaremos la función: CeWriteRecordProps.

oidWrote = CeWriteRecordProps( m_hDatabase, oid, cProps, rgProps );

Donde HANDLE m_hDatabase;  es el manejador de la base de datos una vez
abierta.

Oid es el identificador del registro que deseamos escribir.  En caso de
querer insertar un nuevo registro este valor tiene que ser 0;

cProps : Es el número de propiedades de que consta nuestro registro.

rgProps : Es el registro a insertar, con la información almacenada como se
indica mas arriba.

Esta función nos devuelve el oid con que se ha insertado el registro.

Leer registros

Antes de leer un registro de la base de datos y de la misma forma que
hiciéramos con un fichero deberemos indicarle la posición a partir de la cual
leer mediante la función CeSeekDatabase:

En caso de querer leer el primer registro de la base de datos :

 CeSeekDatabase( m_hDatabase, CEDB_SEEK_BEGINNING, 0, &dwIndex );

En caso de saber el oid de un registro en concreto y querer los datos:

 CeSeekDatabase( m_hDatabase, CEDB_SEEK_CEOID, oid, &dwIndex );

En la documentación de eVC++ se encuentran todas las posibles flags para
determinar la posición.

Una vez posicionados en la base de datos leemos el registro que nos interese
con la función: CeReadRecordProps:

oid = CeReadRecordProps( m_hDatabase, CEDB_ALLOWREALLOC, &cProps, NULL, (LPBYTE *)&rgProps, &cbProps );
 

//oid indica el identificador del registro leido. En caso de error oid será
0.
//Una vez realizada esta llamada tenemos en  rgProps un array de «cbProps»
propiedades  (PROPVALS)

/*…..  Convertimos rgProps en Cliente…….*/

LocalFree( rgProps ); // liberamos la memoria reservada por la funcion al
haber usado CEDB_ALLOWREALLOC

Cerrar base de datos:

Una vez usada la base de datos la cerramos con la función CloseHandle:

 ret = CloseHandle( m_hDatabase );
 

 

 

 

Para más información:

Ayuda del eVC++.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcemain4/html/_wcefiles_Files_Databases_and_Persistent_Storage.asp

http://msdn.microsoft.com/library/en-us/wcemain4/html/_wcefiles_Using_a_Windows_CE_Database.asp

 

 

 

 

 

 

 

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad