Download the source code

NOTE: To work with RIL and to compile this sample you have to install the Platform Builder for Windows CE.

Radio Interface Layer (RIL) is an interface between the radio hardware and the CellCore system. This is an abstraction layer which enables to create single driver modal for different radio hardware. This is a part the Platform Builder of Windows CE and is not present in the standard SDKs. So in order to work with the RIL layer, you should install the Platform Builder for Windows CE. The APIs are defined in ril.h and the library file is ril.lib.

RIL consists of two modules: RIL proxy layer and RIL driver layer. The RIL proxy layer is created by Microsoft and RIL driver layer is created by hardware vendors.

To use work with RIL, first we have to initialize the RIL system. The RIL_Initialize API is used to initialize the RIL for the client. The syntax is:

HRESULT RIL_Initialize(DWORD dwIndex, RILRESULTCALLBACK pfnResult, RILNOTIFYCALLBACK pfnNotify, DWORD dwNotificationClasses, DWORD dwParam, HRIL* lphRil);

The first parameter dwIndex is the index of the RIL port to use, this is normally 1.
The second and third parameters are callback function which will be called for different notifications. The fourth parameter represents the classes of notification. This represents different notifications we have to register for. There are different classed for the notification. They are defined in the header file ril.h as:

#define RIL_NCLASS_FUNCRESULT (0x00000000)
#define RIL_NCLASS_CALLCTRL (0x00010000)
#define RIL_NCLASS_MESSAGE (0x00020000)
#define RIL_NCLASS_NETWORK (0x00040000
#define RIL_NCLASS_SUPSERVICE (0x00080000)
#define RIL_NCLASS_PHONEBOOK (0x00100000
#define RIL_NCLASS_SIMTOOLKIT (0x00200000)
#define RIL_NCLASS_MISC (0x00400000)
#define RIL_NCLASS_RADIOSTATE (0x00800000)
#define RIL_NCLASS_POLLING (0x01000000)
#define RIL_NCLASS_NDIS (0x40000000)
#define RIL_NCLASS_DEVSPECIFIC (0x80000000)
#define RIL_NCLASS_ALL (0x01ff0000)

The fifth parameter is an application defined value that will be passed to the callback functions. Sixth parameter is an out parameter which is the returned handle to the RIL instance.

There are two types of callback functions. On is unsolicited callback and other is solicited callback. The parameter pfnResult represents the unsolicited callback function and pfnNotify represents the solicited callback function. The unsolicited callback function (pfnResult ) will be events occur, for example for an incoming call or for incoming SMS messages. The other callback will be called for the responses to the RIL function calls. For example the API RIL_GetCellTowerInfo which is used to retrieve the Cell Tower Information. We use this API in our sample application to retrieve the cell information. The syntax of the callback functions are:

void CALLBACK ResultCallback(DWORD dwCode,HRESULT hrCmdID,const void *lpData,DWORD cbData,DWORD dwParam);
void CALLBACK NotifyCallback(DWORD dwCode,const void *lpData,DWORD cbData, DWORD dwParam);

For ResultCallback function, the dwCode parameter will be the notifiacation code which depends on the notifications. The hrCmdID parameter will be the HRESULT value of the resulting function call. This parameter is used to ensure that the response callbacks are correctly mached with the corresponding function calls. The function call will return and HRESULT and the same HRESULT value will be passed to the ResultCallback function for the response of the particular function. The lpData parameter depents on the notification and wil be different for each notification. The cbData is the size in bytes of the memory that the lpData points to. The dwParam will be the application supplied value supplied in RIL_Initialize call.

For the NotifyCallback function the parameters are same but the HRESULT will not be present.

In our sample application we use the RIL_GetCellTowerInfo API to retrieve the Cell Tower Information. The Cell Tower Information consists of Mobile Country Code (MCC), Mobile Network Code (MNC), Location Area Code (LAC), Cell ID, etc… The MCC is a part of IMSI Number which is uniquely identifies the subscriber and is stored in SIM card. The MCC combined with MNC (MCC/MNC) to identify the networks. For a list of MCCs refer to this link and for MNCs refer to this.

The RIL_GetCellTowerInfo will reurn the RILCELLTOWERINFO in the lpData parameter of the ResultCallback function. This callback will be called for different API calls and it is very important to check the HRESULT value that is returned by the API and the hrCmdID paramter of the ResultCallback function. The RILCELLTOWERINFO structure is defined as:

typedef struct rilcelltowerinfo_tag {
DWORD cbSize;
DWORD dwParams;
DWORD dwMobileCountryCode;
DWORD dwMobileNetworkCode;
DWORD dwLocationAreaCode;
DWORD dwCellID;
DWORD dwBaseStationID;
DWORD dwBroadcastControlChannel;
DWORD dwRxLevel;
DWORD dwRxLevelFull;
DWORD dwRxLevelSub;
DWORD dwRxQuality;
DWORD dwRxQualityFull;
DWORD dwRxQualitySub;
DWORD dwIdleTimeSlot;
DWORD dwTimingAdvance;
DWORD dwGPRSCellID;
DWORD dwGPRSBaseStationID;
DWORD dwNumBCCH;
BYTE rgbBCCH[MAXLENGTH_BCCH];
BYTE rgbNMR[MAXLENGTH_NMR];
} RILCELLTOWERINFO, *LPRILCELLTOWERINFO;

In this sample we are interested in dwMobileCountryCode, dwMobileNetworkCode, dwLocationAreaCode and dwCellID members only. The sample displays these member in a EDIT control.

I have included the binary of the sample which is working well on my HTC TyTn II. It is not tested on other platforms.

{ 4 comments }

Displaying Notification Bubble in Windows Mobile

by Krishnaraj Varma on September 28, 2008

Download the source code

The API SHNotificationAdd is used to display Notification Bubbles in Windows Mobile. The API has only one parameter, a pointer to the SHNOTIFICATIONDATA structure and is defined as:

typedef struct _SHNOTIFICATIONDATA
{
DWORD cbStruct;
DWORD dwID;
SHNP npPriority;
DWORD csDuration;
HICON hicon;
DWORD grfFlags;
CLSID clsid;
HWND hwndSink;
LPCTSTR pszHTML;
LPCTSTR pszTitle;
LPARAM lParam;
union
{
SOFTKEYMENU skm;
SOFTKEYNOTIFY rgskn[NOTIF_NUM_SOFTKEYS];
};
LPCTSTR pszTodaySK;
LPCTSTR pszTodayExec;
}SHNOTIFICATIONDATA;

The SHNotificationAdd will be asynchronous. The system will notify the hwndSink window when the user is notified and some action is taken. The member pszHTML can an HTML string. You can include any valid HTML tags. The given HMTL text will be rendered when the notification is displayed. You can take input from the user using standatd HTML input tags, for example:

<input type=button name=’cmd:1000′ value=’Click’>

The system will send WM_COMMAND with wParam set to 1000 to the hwndSink window. You can display a link like this:

<a href=’cmd:1000′>Click Me</a>

It will send the WM_COMMAND with wParam 1000.

You can also have a form in HTML text and when it is submitted it will send the notification back to the window. The system will send you a WM_NOTIFY message with NMSHN structure. The NMSHN structure is defined as:

typedef struct _NMSHN
{
NMHDR hdr;
LPARAM lParam;
DWORD dwReturn;
union
{
LPCTSTR pszLink;
BOOL fTimeout;
POINT pt;
};
} NMSHN;

typedef struct tagNMHDR
{
HWND hwndFrom;
UINT idFrom;
UINT code; // NM_ code
} NMHDR;
typedef NMHDR FAR * LPNMHDR;

the code member of the NMHDR will be SHNN_LINKSEL and the pszLink member will contain the URL encoded from.

The following code snippet displays the notification:

SHNOTIFICATIONDATA sIconData = {0};
SHNP shnp = SHNP_INFORM;
DWORD dwDuration = SHN_DURATION;
DWORD dwFlags = SHNF_CRITICAL | SHNF_ALERTONUPDATE;

ZeroMemory(&sIconData,sizeof(SHNOTIFICATIONDATA));

sIconData.cbStruct = sizeof(SHNOTIFICATIONDATA);
sIconData.dwID = SHN_ID_NOTIFICATION;
sIconData.npPriority = shnp;
sIconData.csDuration = dwDuration;
sIconData.hicon = m_hIcon;
sIconData.grfFlags = dwFlags;
sIconData.clsid = CLSID_SampleNotification;
sIconData.hwndSink = m_hWnd;
sIconData.pszHTML = m_strHTMLData;
sIconData.pszTitle = m_strTitle;
sIconData.lParam = 0;
sIconData.rgskn[0].pszTitle = TEXT("Dismiss");
sIconData.rgskn[0].skc.grfFlags = NOTIF_SOFTKEY_FLAGS_DISMISS;

LRESULT lResult = SHNotificationAdd(&sIconData);

You can use SHNotificationRemove to remove the notification.

{ 2 comments }

Enumerating storage cards in Windows Mobile

by Krishnaraj Varma on September 27, 2008

To enumerate the storage cards use FindFirstFlashCard and FindNextFlashCard APIs. These APIs fill the WIN32_FIND_DATA structure that is passed in. The member dwOID of the WIN32_FIND_DATA structure will contain the Object Identifier of the Flash Card. This OID can be used later in APIs like EnumProjects or EnumProjectsEx (more on this later). The following code enumerate the storage cards:

WIN32_FIND_DATA wfd = {0};
HANDLE hFind = FindFirstFlashCard(&wfd);
BOOL bContinue = (INVALID_HANDLE_VALUE != hFind);

while(bContinue)
{
// Use the WIN32_FIND_DATA members
//.
//.

// Find the next card
bContinue = FindNextFlashCard(hFind,&wfd);
}

if(INVALID_HANDLE_VALUE != hFind)
{
FindClose(hFind);
}

{ 0 comments }

Turning Bluetooth on/off in Windows Mobile

by Krishnaraj Varma on September 24, 2008

Two APIs BthGetMode and BthSetMode can be used to get or set Bluetooth mode in Windows Mobile. The enumeration BTH_RADIO_MODE contains the Bluetooth modes. These functions are defined in bthutil.h header file and the library file is bthutil.lib. The following code snippet toggles the bluetooth on/off:

DWORD dwMode;

BthGetMode(&dwMode);

if(BTH_DISCOVERABLE == dwMode || BTH_CONNECTABLE == dwMode) dwMode = BTH_POWER_OFF;
else dwMode = BTH_DISCOVERABLE;

BthSetMode(dwMode);

{ 2 comments }

Enumerating Stores in Windows CE

by Krishnaraj Varma on September 23, 2008

The APIs FindFirstStore, FindNextStore can be used to enumerate stores in Windows CE. These APIs return store information in STOREINFO structure. These APIs are defined in storemgr.h header files and library file is storeapi.lib library. This library is a part of the Platform Builder, so you should have the Platform Builder for the Windows CE. The following code snippet enumerates the stores and inserts the device name and store name to a list control.

void EnumerateStorage(CListCtrl* pList)
{
STOREINFO sInfo = {sizeof(STOREINFO)};
HANDLE hFind = FindFirstStore(&sInfo);
BOOL bContinue = (INVALID_HANDLE_VALUE != hFind);
int nItem = 0;
int nIndex = 0;
CString strClass;

while(bContinue)
{
nItem = pList->InsertItem(nIndex,sInfo.szDeviceName);

pList->SetItemText(nItem,1,sInfo.szStoreName);

bContinue = FindNextStore(hFind,&sInfo);
}

if(INVALID_HANDLE_VALUE != hFind)
{
FindCloseStore(hFind);
}
}

{ 0 comments }

Using SIM Manager APIs for Windows Mobile

by Krishnaraj Varma on September 22, 2008

Download the source code

SIM Manager APIs can be used to get information stored on the SIM card, such as General Phone Number list, fixed dialing list, Capacity of the SIM card, etc… Here is an application that makes use of these APIs. The application displays the list of General Numbers stored on the SIM card and information about the SIM card such as Maximum address book entries; number of address book entries used, SMS message capacity, etc…

{ 0 comments }

Retrieving emergency call list on Windows Mobile

by Krishnaraj Varma on September 21, 2008

The API SHGetEmergencyCallList retrieves the emergency call list from Windows Mobile based devices. Emergency call can be made at any time even the device is locked on no SIM card is present. The following code retrieves the emergency call list:


TCHAR szEmergencyCallList[MAX_CALLLIST];

SHGetEmergencyCallList(szEmergencyCallList,MAX_CALLLIST);

This API is not implemented for Windows CE based device, it will just return E_NOTIMPL.

{ 0 comments }

Windows CE Database Programming using EDB

by Krishnaraj Varma on September 21, 2008

Download the source code

Windows CE supports CEDB and EDB databases. CEDB (Property Database) is lightweight database API for Windows CE. CEDB provides storage, access and sorting records. The new EDB replaces CEDB database. The CEDB is considered as obsolete for Windows Mobile 6 onwards. EDB volumes can contain multiple databases.

An EDB database contains a number of records. Records are a collection of properties. A property in Windows CE Database can have be one of the Windows CE supported data type.

To open or create a database, we should mount the volume. A volume is a file which is specially and can contain CE database. Before using the database we have to mount the volume and after the use we should unmount the volume. After mounting it will be assigned a unique GUID and is represented as CEGUID structure. It should be noted that mounting is reference counted, each time the volume is mounted reference count is incremented and when unmounted the reference is decremented and when the count is zero it will be closed. A volume can contain multiple databases. Once a volume is mounted, we can open any database that is the volume.

To mount a volume we can use the API CeMountDBVolEx API. Syntax is defined as:

BOOL CeMountDBVolEx(PCEGUID pGuid,LPWSTR lpwszDBVol, CEVOLUMEOPTIONS* pOptions,DWORD dwFlags);

The parameter pGuid will receive the CEGUID of the mounted volume. The third parameter is a pointer to CEVOLUMEOPTIONS structure which is used to configure the database volume options such as buffer pool size, flush intervals, password, etc… After mounting the volume we create database session using the API CeCreateSession. After creating a session we can open an existing database using the API CeOpenDatabaseInSession or create a new database using the API CeCreateDatabaseWithProps. If we create the database we should still open the database in order to work.

After opening we can peform read or write operations. To read a record we can use the API CeReadRecordPropsEx. The syntax is defined as

CEOID CeReadRecordPropsEx(HANDLE hDbase,DWORD dwFlags,LPWORD, CEPROPID* prgPropID,LPBYTE* lplpBuffer,LPDWORD lpcbBuffer,HANDLE hHeap);

The dwFlags can be either 0 or CEDB_ALLOWREALLOC. The CEDB_ALLOWREALLOC flag tells the system to use the buffer specified by the lplpBuffer parameter is allocated using LocalAlloc and can reallocate if necessory. The record retrieved will be copied to the lplpBuffer. This will be an array of CEPROPVAL structure. This structure contains the data type and the data itself. The last parameter can be NULL or a handle of an application created heap. If this parameter is not NULL, the system will use this heap handle to reallocate the lplpBuffer. The CeReadRecordPropsEx returns a CEOID which represents the current row. This value is an identifier that represents the row fetched. We can use this value to seek to a particular row.

To write to the database we use the API CeWriteRecordProps and the syntax is defined as:

CEOID CeWriteRecordProps(HANDLE hDatabase, CEOID oidRecord,WORD PropID,CEPROPVAL* prgPropVal);

The past parameter prgPropVal is an array of CEPROPVAL structure which contains the actual data.

The CEPROPVAL structure is defined as:

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

The member propid is the identifier or data type of the property value. The higher order word can be an application defined value and low order word is one of the supported data type. The data type can be one of the folowing constants:

CEVT_BLOB
CEVT_BOOL
CEVT_FILETIME
CEVT_I2
CEVT_I4
CEVT_LPWSTR
CEVT_R8
CEVT_UI2
CEVT_UI4

These values are self explanatory. We can use a #define for a particular field such as

#define FIELD_TITLE MAKELONG(CEVT_LPWSTR,1)

The member val is a union of type CEVALUNION which defined as:

typedef union _CEVALUNION {
short iVal;
USHORT uiVal;
long lVal;
ULONG ulVal;
FILETIME filetime;
LPWSTR lpwstr;
CEBLOB blob;
BOOL boolVal;
double dblVal;
} CEVALUNION;

We have to set the values based on the data type of the corresponding property such as lpwstr for type, blob for CEVT_BLOB, etc…

The sample application presented here is a simple application to manage book details such as title, ISBN, authors, description etc… You can add, delete and modify the book details. The application is compiled using Visual Studio 2008 using Windows Mobile SDK 6.

{ 2 comments }

Download the source code

Here is an application that will monitor SMS message and if the message is coming from predefined numbers then reply that SMS with some predefined text.

There are two components in this sample. MailRuleClient and DatabaseManager. The Mail Rule Client is a COM component. The Database Manager manages the database. The database stores the numbers we have to monitor, a flag which represents the actions (delete, reply message) we have to take and the Message to reply with. For the database part EDB is used instead of CEDB. For the Mail Rule Client component I took most of the code from the MailRule sample of the Windows Mobile 6 SDK. You can find sample code in the folder drive:Program FilesWindows Mobile 6 SDKSamplesCommonCPPWin32MapiRule

To monitor SMS messages there are two methods. First method is to use one of the State and Notification Broker functions like RegistryNotifyApp, RegistryNotifyWindow, etc… Upon receiving SMS message, the system will execute the application specified or send a message to specified window. If you are using RegistryNotifyApp API, our application will start with command line parameter /notify with the value name you specified in the third parameter of the API. We parse the command line and if /notify is found we read the new SMS message and reply to that.

A better approach is to use IMailRuleClient interface. This interface allows you to implement a mail rule that can process the incoming messages. There are two methods we have to implement. Initialize and ProcessMessage. A rule client is a regular COM component which is registered in the system. In addition to the registration we have to create an additional registry entry under

[HKEY_LOCAL_MACHINESoftwareMicrosoftInboxSvcRules] = dword: 1 There is only one < TransportName> currently supported, “SMS”.

If we are our component is the first registered mail rule client, then default messaging application (tmail.exe) will launch our DLL and call the Initialize method of the IMailRuleClient. Whenever and SMS comes in, the ProcessMessage method will be called with appropriate parameters. The return value of this method determines whether system process the SMS normally.

The syntax of these methods is:

HRESULT Initialize(IMsgStore* pMsgStore, MRCACCESS* pmaDesired);
HRESULT ProcessMessage(IMsgStore* pMsgStore,ULONG cbMsg,LPENTRYID lpMsg, ULONG cbDestFolder, LPENTRYID lpDestFolder, ULONG* pulEventType, MRCHANDLED* pHandled);

The first parameter of the Initialize method is a pointer to the IMsgStore. This represents the message store of the incoming messages. The second parameter is pointer to the MRCACCESS enum which we have to fill with our desired access. Possible values can be

MRC_ACCESS_NONE
MRC_ACCESS_READ_ONLY
MRC_ACCESS_WRITE

The first parameter of the ProcessMessage method is a pointer to IMsgStore representing the message store. Second parameter is the length of the third parameter lpMsg which pointer to the ENTRYID structure which represents the message. The ENTRYID contains an identifier of an MAPI object. This is a unique identifier used by the message store and address book providers. The fourth parameter is the length of the fifth parameter which is again a pointer to the ENTRYID structure which represents the folder that contains the message. We have to fill the sixth parameter which is a ULONG pointer with appropriate bit flags. Possible flags are:

fnevObjectCreated
fnevObjectDeleted
fnevObjectModified
fnevObjectMoved
fnevObjectCopied

These flags are self explanatory and do not need any explanation. The seventh parameter is a pointer to the MRCHANDLED which we have to fill with appropriate values. Possible values are:

MRC_NOT_HANDLED
MRC_HANDLED_CONTINUE
MRC_HANDLED_DONTCONTINUE

Again these are self explanatory and explanation is not needed.

About the implementation part, when our ProcessMessage method is called first we take the IMessage pointer using the OpenEntry method of the IMsgStore that is passed in.

hr = pMsgStore->OpenEntry(cbMsg,lpMsg,NULL,0,NULL,(LPUNKNOWN *)&pMsg);

Then we take the subject and sender address of the message using the code:


SizedSPropTagArray(1, sptaSubject) = {1,PR_SUBJECT};
SizedSPropTagArray(1, sptaEmail) = {1,PR_SENDER_EMAIL_ADDRESS};

hr = pMsg->GetProps((SPropTagArray*)&sptaSubject,MAPI_UNICODE,&cValues,&pspvSubject);

if (FAILED(hr))
{
return hr;
}

hr = pMsg->GetProps((SPropTagArray *)&sptaEmail,MAPI_UNICODE,&cValues,&pspvEmail);

if (FAILED(hr))
{
return hr;
}

Then we open an EDB database and check whether the address is there in the database and if there we get the message to reply with. The database contains 3 fields

Number to monitor
Bit flags representing what action to be taken and
The message body used to reply.

The database part is handled by two classes: CSMSRespondDB and CSMSRespondRecord.

Both sample workspaces are compiled using Visual Studio 2008 using Windows Mobile 6 SDK.

The handling of EDB database deserves another article and I am working on that. I will update this blog when it is finished.

Hope this gives you a simple introduction to the Message Rule clients.

{ 8 comments }

Retrieving Call History on Windows Mobile

by Krishnaraj Varma on September 18, 2008

Download the source code

To retrieve call history use the following API functions:

PhoneOpenCallLog
PhoneSeekCallLog
PhoneGetCallLogEntry
PhoneCloseCallLog

The following code fragment shows how to retrieve call history and add to a ListCtrl:

HANDLE hCallLog = NULL;
HRESULT hResult = PhoneOpenCallLog(&hCallLog);

if(E_FAIL == hResult)
{
return;
}

DWORD dwCount = 0;

hResult = PhoneSeekCallLog(hCallLog,CALLLOGSEEK_END,0,&dwCount);

PCALLLOGENTRY pLog = NULL;
SYSTEMTIME sTime = {0};
int nItem = 0;
int nImage = 0;
CString strLog;
CString strTime;

for(DWORD dwLog=0; dwLog<=dwCount; ++dwLog)
{
pLog = new CALLLOGENTRY;
pLog->cbSize = sizeof(CALLLOGENTRY);

PhoneGetCallLogEntry(hCallLog,pLog);
FileTimeToSystemTime(&pLog->ftStartTime,&sTime);

strLog.Format(TEXT("%s"),pLog->pszName ? pLog->pszName : pLog->pszNumber);
strTime.Format(TEXT("%d-%d-%d %d:%02d"),sTime.wDay,sTime.wMonth,sTime.wYear,sTime.wHour,sTime.wMinute);

if(pLog->iom == IOM_MISSED)
nImage = 2;
else if(pLog->iom == IOM_INCOMING)
nImage = 1;
else
nImage = 0;

nItem = pList->InsertItem(dwLog,strLog,nImage);
pList->SetItemText(nItem,1,strTime);

if(pLog->pszName)
LocalFree(pLog->pszName);
if(pLog->pszNumber)
LocalFree(pLog->pszNumber);
if(pLog->pszNote)
LocalFree(pLog->pszNote);

delete pLog;
}

PhoneCloseCallLog(hCallLog);

{ 0 comments }