.KEYWORD progpower
.FLYINGHEAD PROGRAMMING POWER
.TITLE Palm-sized input panel quirks and mounted database volumes
.OTHER
.SUMMARY This month, BSQUARE’s Steve Makofsky explains how to determine if the SIP (Software Input Panel) API is available on a device at runtime, how to programmatically show or hide the SIP, how to create databases on a flash card, and how to create a hidden database.
.AUTHOR Steve Makofsky
Welcome to Windows CE Magazine’s Programming Power column. Each month we address tough issues about writing software for Windows CE and provide solutions that usually include a code snippet or two.
Based on reader questions and feedback, every month we select questions that are common stumbling blocks in Windows CE development. Want to know how to work around an API that’s not available on Windows CE? Need help determining if your code will work on both a palm-sized PC and a H/PC Pro? Send us email at poweranswers@bsquare.com, including code where appropriate, and we’ll try our hardest to find a resolution for you.
Most of the topics we cover are for those of you using the Visual C++ Toolkit for Windows CE, but we’ll address occasional questions about Visual Basic for Windows CE and other development tools as well. Let’s jump into this month’s questions:
.H1 How can you determine if the SIP (Software Input Panel) API (Applications Programming Interface) is available on a device at runtime?
There are two different methods for handling application development across the various Windows CE platforms:
.H2 Method #1 — static linking using definitions
The easiest way to deal with the differences in handheld PCs (H/PCs) and palm-sized PCs (P/PCs) is to have conditional #ifdef statements in your code which will only compile specific API calls if a certain platform is defined. When compiling, the build process will ignore any code inside your #ifdef statement if the condition isn’t defined. Since you’ll be performing separate builds for each platform, you can also have multiple resource files, which make user interface development easier when dealing with the difference between palm-sized PCs and other Windows CE devices. You can use the #ifdef statement as follows:
.BEGIN_CODE
#ifdef PALMSIZEDPC
if(SipStatus() == SIP_STATUS_AVAILABLE)
fSipAvail = TRUE;
#endif
.END_CODE
You might already notice the big downside to this method — you’ll need to distribute separate executables for both the H/PC and the palm-sized PC.
.H2 Method #2 — dynamic linking
If you’d rather only distribute a single executable, you can determine what functions are available on the running platform by dynamically linking with functions. The function SipStatus() is used to determine the current availability of the SIP on Windows CE 2.1 and higher. This function (and the other SIP calls) are located in \Windows\Coredll.dll. So, in order to determine if you’re running on an H\PC Pro or a palm-sized PC, you can code this:
.BEGIN_CODE
typedef BOOL (*PCORE_SIPSTATUS)();
BOOL IsSIPAvail()
{
PCORE_SIPSTATUS lpfnSipStatus = NULL;
HINSTANCE hCoreInst = NULL;
BOOL fSIPAvail = FALSE;
\// Load up coredll.dll and hook into the SipStatus function
if((hCoreInst = LoadLibrary(TEXT(“\\windows\\coredll.dll”))) != NULL)
lpfnSipStatus = (PCORE_SIPSTATUS)GetProcAddress(hCoreInst, TEXT(“SipStatus”));
\// If SIPStatus is there, then call it to see if it’s available
if(lpfnSipStatus != NULL) {
if(lpfnSipStatus() == TRUE)
fSIPAvail = TRUE;
}
FreeLibrary(hCoreInst);
return fSIPAvail;
}
.END_CODE
.H1 Is there a way to programmatically show or hide the SIP?
Of course there is! The function SHSipInfo(), which is located in aygshell.lib, can be used to determine the current status of the SIP as well as set SIP information. So, if you wanted to hide or show the SIP, use code like this:
.BEGIN_CODE
BOOL DisplaySIP(BOOL fShow)
{
SIPINFO si;
memset(&si, 0, sizeof(si));
si.cbSize = sizeof(SIPINFO);
if(!SHSipInfo(SPI_GETSIPINFO, 0, &si, 0))
return FALSE;
if(fShow)
si.fdwFlags |= SIPF_ON;
else
si.fdwFlags &= ~SIPF_ON;
SHSipInfo(SPI_SETSIPINFO, 0, &si, 0);
return TRUE;
}
.END_CODE
.H1 Ok. I give up. How can you create databases on a flash card?
The first thing that you should be aware of is that this functionality was introduced with Windows CE 2.1, and won’t work on earlier versions of the operating system. With that said, when using any type of external device, such as a flash card, you must first mount the volume before you can work with the extended database APIs. External database volumes contain both your data and integrity logs for your various databases.
.CALLOUT This functionality was introduced with Windows CE 2.1, and won’t work on earlier versions of the operating system.
In order to mount a database volume you need to call the CeMountDBVol() function. Besides passing in a variable to receive the pointer to your CEGUID object and the null-terminated path to your external database file, you’ll also have to pass in a database creation flag. (You might notice that this flag is similar to opening a file).
The flag will be one of the following options: CREATE_NEW (create a new database), CREATE_ALWAYS (always create a new database, even if one of the same name already exists), OPEN_EXISITING (open an existing database), OPEN_ALWAYS (open the database, if doesn’t exist, create a new one), or TRUNCATE_EXISTING (open an existing database, and truncate it to 0).
Once you’ve mounted the database volume, you can use the Ex versions of the database APIs to work with your external database. The only difference between the regular and Ex functions is that these new functions take the returned CEGUID (from your call to CeMountDBVol) as an additional parameter. For example, say we wanted to enumerate the databases on a mounted volume, your code might look like this:
.BEGIN_CODE
BOOL EnumerateDatabases()
{
BOOL fEnum = TRUE;
CEGUID pCeGUID;
HANDLE hDB = NULL;
\// Mount the database volume
if(!CeMountDBVol(&pCeGUID, TEXT(“\\Storage Card\\Databases.vol”), OPEN_EXISTING))
return FALSE;
\// Enumerate through mounted databases in the volume
if(hDB = CeFindFirstDatabaseEx(&pCeGUID, 0)) {
while(fEnum) {
CEOID dbOID = CeFindNextDatabaseEx(hDB, &pCeGUID);
if(!dbOID) {
if(GetLastError() == ERROR_NO_MORE_ITEMS) {
fEnum = FALSE;
continue;
}
}
\// Get some information on the database
CEOIDINFO ceOIDInfo;
memset(&ceOIDInfo, 0, sizeof(CEOIDINFO));
if(!CeOidGetInfoEx(&pCeGUID, dbOID, &ceOIDInfo)) {
\// Some error has occurred, so, quit
fEnum = FALSE;
continue;
}
\// Do something here with the database name
MessageBox(NULL, ceOIDInfo.infDatabase.szDbaseName, TEXT(“Database Name”), MB_OK);
}
CloseHandle(hDB);
}
\// Clean things up and get outta here
CeUnmountDBVol(&pCeGUID);
return TRUE;
}
.END_CODE
Once you’re finished using the database, you must call the function CeUnmountDBVol() to free up system resources.
Note: If you need to flush all pending data to your external volume and write out your changes, you can call the CeFlushDBVol() function, passing in the CEGUID of the volume.
.H1 How do I create a hidden database?
The only difference between a hidden database and a normal one is that the filename for a hidden database begins with a backslash character.
That’s all we have room for this month. We look forward to hearing about what ails you (regarding Windows CE development, that is) at poweranswers@bsquare.com.
.BEGIN_SIDEBAR
.H1 Bulk reprints
Bulk reprints of this article (in quantities of 100 or more) are available for a fee from Reprint Services, a ZATZ business partner. Contact them at reprints@zatz.com or by calling 1-800-217-7874.
.END_SIDEBAR
.BIO Steve Makofsky is a senior software engineer at BSQUARE Corporation. Since 1995, he’s been writing software targeting Windows CE platforms, including BSQUARE’s bUSEFUL Backup, bUSEFUL FindSpace, bTASK and bMOBILE News. He recently co-authored SAMS Teach Yourself Windows CE Programming in 24 Hours. When not hiking or drinking coffee, he can be reached at poweranswers@bsquare.com.


