http://www.developer.com/db/article.php/3449281/The-Anatomy-of-a-CE-Database-Record.htm
In this lesson, you'll finish examining the RemoteDBScan example program, devising a strategy by which to interpret and display the retrieved records. The first step is to get a handle on exactly what a CE database record is and how it is constructed. A CE database record is a compact and self-documenting block of variable size data, comprised of an array of CEPROPVAL structures. Here is the typedef for CEPROPVAL: There are a couple of important things to note about the way CE treats its database records. First, a CE database record may be "sparse." Put another way, a record may or may not contain a value for each property. Also, a given CE database may contain only one record type. There is no explicit support for hierarchically structured data. Having said this, it is important to note that the member of the CEPROPVAL that stores record data is a union, one of whose members is of type BLOB. This means that should you need to create hierarchical data relationships, you can implement your own mechanisms for doing so, inside the record's data. Here are the steps required to interpret an array of CEPROPVAL structures to create and display a relational style "record" from the property value array: In CRecordListDialog::PublishRecord(), you discriminate the property ID and data type of each property. For the sake of brevity, you insert them serially in a list box rather than laying them out as rows in a separate list control. At the top the list of each record's set of properties, you insert the record OID, the number of properties returned for this record, and the total size of the record in bytes. These data were passed to PublishRecord() as parameters. They were reported by CeReadRecordProps() in the caller. Here, you set up an iteration of the array of CEPROPVAL structures. You use the HIWORD macro to recover the property ID from the propid member. The property ID is then formatted and inserted in the list box. Now, you have to retrieve and format the record's value data. The way in which you do this depends on the data's type. Here are the type constants for the data in a CE database record, along with the name of the structure member in the CEVALUNION in which that type is stored. Table 1: CE Database Types and Corresponding CEVALUNION Members You declare a variable to handle translation of FILETIME data outside the switch, and then use the CE value type as a switch test. Notice that with the integer and file time types, all of the record's data is stored within the CEPROPVAL structure. To format the FILETIME for insertion in the list box, you wrap the raw data in the tFileTime object, and use the CTime Format() member to create a printable string. The CEVT_LPWSTR member represents a slightly different case than the previous types. A pointer to the Unicode string is stored in pRecord[i].val.lpwstr, but the actual string data is stored elsewhere. This isn't of that much importance if you are reading records. However, if you are writing records, remember that when you allocate memory to hold record data, you must allocate a block of memory large enough for the array of CEPROPVAL structures and for the string data to which pRecord[i].val.lpwstr points. (The actual strings are stored in the memory beyond what is occupied by the CEPROPVAL array.) The CEVT_BLOB data type is useful for typical types of binary data, but also as a way to implement application-defined data types and one-to-many record relationships. The CEVALUNION member blob is actually yet another structure, CEBLOB. Here's the typedef for CEBLOB: You insert the the data type, the size in bytes of the binary data, and the first byte of the datum in the list box. Whew! That's remote database access. Once you are connected to the remote database via RAPI, a cut and paste of the sample code shown in the earlier article on CE database management will take you most anywhere you want to go. The function names and parameter lists are identical for the RAPI and CE side versions of the following functions: A final reminder: Any string parameters passed into or returned from these functions are in Unicode. The functions will fail if you forget to translate between character formats. In the next example, you explore the most powerful RAPI capability of all: the ability to remotely invoke a function on the CE device. You'll pair this sophisticated capability with the Windows CE HTML viewer, to create a flexible and dynamic means of communicating with the user. Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, and entry time data validation. In addition to writing for Developer.com, she has written several books, including Making Win 32 Applications Mobile.
The Anatomy of a CE Database Record
December 17, 2004
The CE Database Record
typedef struct _CEPROPVAL {
CEPROPID propid; //LOWORD is CE value type
//HIWORD is property ID
WORD wLenData; //unused
WORD wFlags; //flags: CEDB_PROPNOTFOUND returned
//if prop isn't found
//passing in CEDB_PROPDELETE causes
//property to be deleted from record
CEVALUNION val; //Union includes members for all CE
//database types
} CEPROPVAL;
Interpreting CEPROPVAL Structures
void CRecordListDialog::PublishRecord( CEOID oid, WORD wProps,
PCEPROPVAL pRecord,
DWORD dwRecSize)
{
char szInsertString[124];
sprintf(szInsertString, "%s: 0x%x", "Record CEOID", oid );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%s: %i", "Properties Returned", wProps );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%s: %i", "Record Size", dwRecSize );
m_RecordListCtrl.InsertString( -1, szInsertString );
m_RecordListCtrl.InsertString( -1, "" );
WORD iPropId;
int iPropDataType;
for( int i = 0; i < wProps; i++ )
{
iPropId = HIWORD(pRecord[i].propid );
sprintf(szInsertString, "%s: %i", "Property Id", iPropId );
m_RecordListCtrl.InsertString( -1, szInsertString );
iPropDataType = LOWORD(pRecord[i].propid );
CE Database Value Type Constant
_CEVALUNION Member Type And Name CEVT_I2
short iVal; CEVT_UI2
USHORT uiVal; CEVT_I4
long lVal; CEVT_UI4
ULONG ulVal; CEVT_FILETIME
FILETIME filetime; CEVT_LPWSTR
LPWSTR lpwstr; CEVT_BLOB
CEBLOB blob;
CTime tFileTime;
CString csFileTime;
switch( iPropDataType )
{
//ce type
case CEVT_I2:
sprintf(szInsertString, "%s: %s", "Data Type", "CEVT_I2" );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%i", pRecord[i].val.iVal );
m_RecordListCtrl.InsertString( -1, szInsertString );
break;
case CEVT_UI2:
sprintf(szInsertString, "%s: %s", "Data Type", "CEVT_UI2" );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%ui", pRecord[i].val.uiVal );
m_RecordListCtrl.InsertString( -1, szInsertString );
break;
case CEVT_I4:
sprintf(szInsertString, "%s: %s", "Data Type", "CEVT_I4" );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%li", pRecord[i].val.lVal );
m_RecordListCtrl.InsertString( -1, szInsertString );
break;
case CEVT_UI4:
sprintf(szInsertString, "%s: %s", "Data Type", "CEVT_UI4" );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%uli", pRecord[i].val.ulVal );
m_RecordListCtrl.InsertString( -1, szInsertString );
break;
case CEVT_FILETIME:
sprintf(szInsertString, "%s: %s", "Data Type", "CEVT_FILETIME" );
m_RecordListCtrl.InsertString( -1, szInsertString );
tFileTime = pRecord[i].val.filetime;
csFileTime = tFileTime.Format("%A, %B %d, %Y" );
m_RecordListCtrl.InsertString( -1,
csFileTime.GetBuffer(csFileTime.GetLength()) );
break;
case CEVT_LPWSTR:
sprintf(szInsertString, "%s: %s", "Data Type", "CEVT_LPWSTR" );
m_RecordListCtrl.InsertString( -1, szInsertString );
wcstombs( szInsertString, pRecord[i].val.lpwstr,
sizeof(szInsertString));
m_RecordListCtrl.InsertString( -1, szInsertString );
break;
typedef struct _CEBLOB {
DWORD dwCount; //size of the BLOB data in bytes
LPBYTE lpb; //address of byte stream
} CEBLOB;
case CEVT_BLOB:
sprintf(szInsertString, "%s: %s", "Data Type",
"CEVT_BLOB" );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%s: %li", "Size in bytes",
pRecord[i].val.blob.dwCount );
m_RecordListCtrl.InsertString( -1, szInsertString );
sprintf(szInsertString, "%s: 0x%x", "Buffer Address",
pRecord[i].val.blob.lpb );
break;
default:
break;
}
//blank line between properties for readability
m_RecordListCtrl.InsertString( -1, "" );
}
}
CeOpenDatabase
CeReadRecordProps
CeWriteRecordProps
CeDeleteRecord
CeDeleteDatabase
CeSeekDatabase
Looking Ahead
About the Author