CEDB
Given that Windows CE evolved from Personal Organizers (CE 1), there have been native database technologies from day one to support such things as Address Books. These database engines, whilst supporting such things as queries to sort and search, were only simple technologies hosting simple data. For example, CEDB databases have no schema and so when a record is stored, the data in a field can be of any type.
Compact 7 had two lower level database engine, CEDB and EDB. CEDB is used by low level things by the kernel including object store. EDB was a requirement of any GUI based OS and was required by higher level things such as CAB installer, Media Player, SQL Compact, Pocket Outlook, MAPI and ActiveSync. Given that these are no longer the need for EDB have been obviated. Only CEDB is available in Compact 2013.
CEDB can stores databases as files. They can be in RAM, in the Object Store, the internal file system as well as external storage such a flash. In the following example a CEDB is created in Object Store.
1. A native code application is required to exercise the CEDB API. Create a C++ Console as a subproject of the OS.
2. Add the following code above WinMain( ) function in the project’s .cpp file in the order listed:
#define NUMFIELDS 2
#define NUMRECORDWRITES 4
#define RECORDTOSEEK 2
#define DATABASE_TYPE 137
#define MYDB "MyDB"
Listing 18.1 Application parameters The parameters for the application define the database name and type as well as some parameters for how the application will run (e.g. the number of records created.
void ErrorMessages (DWORD err)
{
RETAILMSG(1,( _T("Error %d "),err));
if (err = ERROR_SHARING_VIOLATION) RETAILMSG(1,( _T("ERROR_SHARING_VIOLATION: Indicates that the file is in use.")));
else if (err = ERROR_INVALID_PARAMETER) RETAILMSG(1,( _T("ERROR_INVALID_PARAMETER: Indicates that a parameter was invalid.")));
// … And more of these. See Help for error messages each of the CEDB API functions
}
Listing 18.2 Error message handler
This a shortened version of the error handler. It takes a value returned from GetLastError( ) by the calling code and generates an appropriate message. The macros and strings are in the function documentation on MSDN. With each of the API calls, if the return value is a correct value then a success message is written to the Debug window. If it is incorrect then it gets the error number and calls this function.
3. Add the following code in the WinMain( ) function in the project’s .cpp file in the order listed:
RETAILMSG(1,( _T("Starting CEDBApp ")));
//Create a CEDB in the Object Store
RETAILMSG(1,( _T("Creating DB")));
CEOID MyCEOID = CeCreateDatabase(
_T(MYDB), //DB Name
DATABASE_TYPE, //User Defined
0, //Number of s0rt orders. Use default
NULL //Array of sort orders
);
if (MyCEOID)
RETAILMSG(1,( _T("Created DB")));
RETAILMSG(1,( _T("CEOD for DB = %d "), MyCEOID ));
else
RETAILMSG(1,( _T("Creation Failed")));
ErrorMessages(GetLastError());
Listing 18.3 Create the database
The database creation requires a name, a user defined type (numeric), and default sort order specifications. No sort specifications are used in the above example. The database type can be used in searches. The creation function returns a CEOID which permanently identifies the database.
///Object Store GUID //////////////////////////////////
RETAILMSG(1,( _T("Get Object Store GUID")));
PCEGUID ObjStoreDBGUID;
try
//Get a System GUID for the Object Store
//So do it "manually". Object store is a zero GUID.
ObjStoreDBGUID = (PCEGUID ) LocalAlloc(0,sizeof(CEGUID ));
ObjStoreDBGUID->Data1=0;
ObjStoreDBGUID->Data2=0;
ObjStoreDBGUID->Data3=0;
ObjStoreDBGUID->Data4=0;
catch (char * str)
RETAILMSG(1,( _T("Exception %s "),str));
/// Got GUID /////////////////////////////////////////
RETAILMSG(1,( _T("Got Object Store GUID")));
RETAILMSG(1,( _T("Object Store GUID %d-%d-%d-%d "), ObjStoreDBGUID->Data1,ObjStoreDBGUID->Data2, ObjStoreDBGUID->Data3,ObjStoreDBGUID->Data4 ));
Listing 18.4 Get Object Store GUID
The Object Store GUID is a “zero” GUID , i.e. all tuples are zero. The CREATE_SYSTEMGUID(ObjStoreDBGUID) could be used but it was found to be a bit flakey, generating Access Violation some time. The code above is functionally the same as the macro.
RETAILMSG(1,( _T("Opening DB")));
SetLastError(0);
HANDLE hndl = CeOpenDatabaseEx2(
ObjStoreDBGUID, //Object Store GUID
&MyCEOID, //O means use name. But would need to not be just opened
_T(MYDB), //Database name. Ignored with 0 as above
NULL, //Sort order not important
CEDB_AUTOINCREMENT, //Auto incr record ptr when reading
NULL //Sort order not important
if (hndl == (HANDLE)-1 )
RETAILMSG(1,( _T("DB NOT Opened")));
RETAILMSG(1,( _T("DB Opened HANDL= %d"),hndl));
//If Open failed then don’t do write, read and close, but still delete db
Listing 18.5 Open the database
The database open requires a GUID for the volume on which it is to be created (in this case Object Store), a database CEOID, specification of sort order (none here), and some flags (auto increment the record pointer in this case). The database name could also be used to open the database, by setting the second parameter to zero. This would be used when reopening the database.
//Write Records
DWORD numFields = NUMFIELDS;
//Set up record
PCEPROPVAL rgPropVal = (PCEPROPVAL)LocalAlloc ( 0,numFields * sizeof(CEPROPVAL));
CEOID oidRecord = (CEOID) 0; ////New record
//Set up data for record, as two fields (two properties)
CEVALUNION val;
val.lpwstr = _T("Hi there!");
rgPropVal[1].val= val;
rgPropVal[1].wLenData = sizeof(DWORD); //Actually not used
rgPropVal[1].wFlags = 0; //No flags
rgPropVal[1].propid = CEVT_LPWSTR; //Val data type
CEVALUNION val2;
val2.iVal= 137;
rgPropVal[0].val= val2; //This will be index later
rgPropVal[0].wLenData = sizeof(DWORD); //Actually not used
rgPropVal[0].wFlags = 0; //No flags
rgPropVal[0].propid = CEVT_I4;
RETAILMSG(1,( _T("Writing record to DB")));
for (int i=0; i< i++)>
//Write i into record
val2.iVal= i; //Set index
rgPropVal[0].val= val2;
CEOID WriteRecordGUID = CeWriteRecordProps(
hndl,
oidRecord,
numFields,
rgPropVal
if (WriteRecordGUID)
RETAILMSG(1,(
_T("Wrote Record CEOID= %d Index= %d"), WriteRecordGUID, rgPropVal[0].val.iVal));
RETAILMSG(1,( _T("Write Failed")));
free(rgPropVal); //Freed rgPropVal
Listing 18.6 Write Records
//Seek record (0 based index)
DWORD Index=139;
DWORD dwValue= RECORDTOSEEK;
CEOID SeekCEOID = CeSeekDatabaseEx(
hndl ,
CEDB_SEEK_BEGINNING,
dwValue,
0, //Ignored for CEDB_SEEK_BEGINNING
(LPDWORD) &Index
if (SeekCEOID)
RETAILMSG(1,( _T("SEEK CEOID = %d Index found = %d Index sought = %d"),SeekCEOID, Index, dwValue));
RETAILMSG(1,( _T("Seek failed")));
Listing 18.7 Seek nth record from start
The SEEK function in this instance requires the open handle for the database a specification of where to seek from and how much to see. CEDB_SEEK_BEGINNING means seek from the beginning, dwValue number of records. There are other search option such as searching from the current record pointer and searching based upon record contents.
//Read record
DWORD dwFlags= CEDB_ALLOWREALLOC;
WORD numProps = 2;
LPWORD lpcPropID= &numProps;
CEPROPID* rgPropID=NULL;
PBYTE pBuff = 0;
LPBYTE* lplpBuffer = (LPBYTE*)&pBuff;//(LPBYTE*) LocalAlloc (0,100);;
DWORD pcBuffer = 0;
LPDWORD lpcbBuffer =(LPDWORD) &pcBuffer; ;
RETAILMSG(1,( _T("Reading Record")));
CEOID ReadRecordGUID = CeReadRecordPropsEx(
CEDB_ALLOWREALLOC,
lpcPropID,
NULL,
,
//&(LPBYTE) pBuff,
lpcbBuffer,
0 //Use LocalHeap
if (ReadRecordGUID)
_T("Read Record CEOID+ %d Num Fields = %d"), ReadRecordGUID, *lpcPropID));
if (*lpcPropID == numFields) //We only wrote two fields per record
//Lets write them
PCEPROPVAL pRecord = (PCEPROPVAL) pBuff;
RETAILMSG(1,( _T("SUCCESS: Index = %d"),(pRecord+0)->val.iVal));
RETAILMSG(1,( _T("SUCCESS: String = %s"),(pRecord+1)->val.lpwstr));
RETAILMSG(1,( _T("Error: Got %d fields, expected %d"), *lpcPropID,numFields));
free(pBuff);
//Don't forget this. It was created on heap by CeReadRecordPropsEx
RETAILMSG(1,( _T("Read failed")));
free(pBuff); //Don't forget this. It may be created on heap by CeReadRecordPropsEx, even if error
Listing 18.8 Read database record
//Close the database
RETAILMSG(1,( _T("Closing DB")));
BOOL res = CloseHandle(hndl);
if (res)
RETAILMSG(1,( _T("DB Closed")));
RETAILMSG(1,( _T("DB Close failed.")));
ErrorMessages(GetLastError()); //Need messages
Listing 18.9 Close the database
The database is closed as per closing a file. Note it would fail if the database was not open.
//Delete the database
BOOL res = CeDeleteDatabaseEx(
ObjStoreDBGUID,
MyCEOID
RETAILMSG(1,( _T("DB Deleted")));
RETAILMSG(1,( _T("DB Not Deleted")));
free(ObjStoreDBGUID);
return 0;
Listing 18.10 Delete the database
The database is specified by its volume store (object Store) GUID and its CEOID. These are sufficient information to delete the database. Note need to release memory allocated to the Object Store GUID pointer
4. Build and test run the application. A sample output is shown in Listing 18.11
1965484 PID:400002 TID:14500b6 RELFSD: Opening file cedbapp.exe from desktop
1965495 PID:1be00de TID:14500b6 Starting CEDBApp
1965495 PID:1be00de TID:14500b6 Creating DB
1965495 PID:1be00de TID:14500b6 Created DB
1965495 PID:1be00de TID:14500b6 CEOD for DB = 838860801
1965495 PID:1be00de TID:14500b6 Get Object Store GUID
1965495 PID:1be00de TID:14500b6 Got Object Store GUID
1965495 PID:1be00de TID:14500b6 Object Store GUID 0-0-0-0
1965495 PID:1be00de TID:14500b6 Opening DB
1965495 PID:1be00de TID:14500b6 DB Opened HANDL= 7682307
1965495 PID:1be00de TID:14500b6 Writing records to DB
1965495 PID:1be00de TID:14500b6 Wrote Record CEOID= 838860803 Index= 0
1965495 PID:1be00de TID:14500b6 Wrote Record CEOID= 838860804 Index= 1
1965495 PID:1be00de TID:14500b6 Wrote Record CEOID= 838860805 Index= 2
1965495 PID:1be00de TID:14500b6 Wrote Record CEOID= 838860806 Index= 3
1965495 PID:1be00de TID:14500b6 SEEK CEOID = 838860805 Index found = 2 Index sought= 2
1965495 PID:1be00de TID:14500b6 Reading Record
1965495 PID:1be00de TID:14500b6 Read Record CEOID= 838860805 Num Fields = 2
1965497 PID:1be00de TID:14500b6 SUCCESS: Index = 2
1965497 PID:1be00de TID:14500b6 SUCCESS: String = Hi there!
1965497 PID:1be00de TID:14500b6 Closing DB
1965497 PID:1be00de TID:14500b6 DB Closed
1965497 PID:1be00de TID:14500b6 DB Deleted
Listing 18.11 CEDBApp Output
Note that there is a a bit of a "fudge" in the above. If the code is run twice, then the correct index is returned once, not twice. In the following listing, the seek is set to the first record and then the seven records are read in succession. Note the slightly disjointed order of the indexes.
Run Programs s CEDBApp
434902 PID:400002 TID:1fa007a RELFSD: Opening file CEDBApp.exe from desktop
434914 PID:1be0056 TID:1fa007a Starting CEDBApp
434914 PID:1be0056 TID:1fa007a Creating DB
434914 PID:1be0056 TID:1fa007a Created DB
434914 PID:1be0056 TID:1fa007a CEOD for DB = 922746881
434914 PID:1be0056 TID:1fa007a Get Object Store GUID
434914 PID:1be0056 TID:1fa007a Got Object Store GUID
434914 PID:1be0056 TID:1fa007a Object Store GUID 0-0-0-0
434915 PID:1be0056 TID:1fa007a Openning DB
434915 PID:1be0056 TID:1fa007a DB Openned HANDL= 7675395
434915 PID:1be0056 TID:1fa007a Writing record to DB
434915 PID:1be0056 TID:1fa007a Wrote Record CEOID= 922746885 Index= 0
434915 PID:1be0056 TID:1fa007a Wrote Record CEOID= 922746884 Index= 1
434915 PID:1be0056 TID:1fa007a Wrote Record CEOID= 922746883 Index= 2
434915 PID:1be0056 TID:1fa007a Wrote Record CEOID= 922746882 Index= 3
434915 PID:1be0056 TID:1fa007a Wrote Record CEOID= 805306375 Index= 4
434915 PID:1be0056 TID:1fa007a Wrote Record CEOID= 805306376 Index= 5
434915 PID:1be0056 TID:1fa007a Wrote Record CEOID= 805306377 Index= 6
434915 PID:1be0056 TID:1fa007a SEEK CEOID = 922746882 Index found= 0 Index sought = 0
434915 PID:1be0056 TID:1fa007a Reading Record
434915 PID:1be0056 TID:1fa007a Read Record CEOID+ 922746882 Num Fields = 2
434916 PID:1be0056 TID:1fa007a SUCCESS: Index = 3
434916 PID:1be0056 TID:1fa007a SUCCESS: String = Hi there!
434916 PID:1be0056 TID:1fa007a Reading Record
434916 PID:1be0056 TID:1fa007a Read Record CEOID+ 922746883 Num Fields = 2
434916 PID:1be0056 TID:1fa007a SUCCESS: Index = 2
434924 PID:1be0056 TID:1fa007a Read Record CEOID+ 922746884 Num Fields = 2
434924 PID:1be0056 TID:1fa007a SUCCESS: Index = 1
434924 PID:1be0056 TID:1fa007a SUCCESS: String = Hi there!
434924 PID:1be0056 TID:1fa007a Reading Record
434924 PID:1be0056 TID:1fa007a Read Record CEOID+ 922746885 Num Fields = 2
434924 PID:1be0056 TID:1fa007a SUCCESS: Index = 0
434924 PID:1be0056 TID:1fa007a Read Record CEOID+ 805306375 Num Fields = 2
434925 PID:1be0056 TID:1fa007a SUCCESS: Index = 4
434925 PID:1be0056 TID:1fa007a SUCCESS: String = Hi there!
434931 PID:1be0056 TID:1fa007a Reading Record
434931 PID:1be0056 TID:1fa007a Read Record CEOID+ 805306376 Num Fields = 2
434931 PID:1be0056 TID:1fa007a SUCCESS: Index = 5
434931 PID:1be0056 TID:1fa007a SUCCESS: String = Hi there!
434931 PID:1be0056 TID:1fa007a Read Record CEOID+ 805306377 Num Fields = 2
434932 PID:1be0056 TID:1fa007a SUCCESS: Index = 6
434932 PID:1be0056 TID:1fa007a SUCCESS: String = Hi there!
434932 PID:1be0056 TID:1fa007a Closing DB
434932 PID:1be0056 TID:1fa007a DB Closed
434932 PID:1be0056 TID:1fa007a DB Deleted
The reason is because when the database was set up no sort order was defined and hence the seek and selects are jumbled. A sort order could be defined to resolve this, or all of the records could be returned as above, then sorted in software.
NEXT: SQLite
Click here to provide feedback and input