Phone API

Differences between the standard Pocket PC device and the Phone Edition include not only the latter's integrated data capabilities, but its functionality as a cell phone. As the name suggests, the Phone API is used to programmatically access the call information logs, call history, and core phone capabilities on a Pocket PC Phone Edition device. These functions enable you to query the statistics associated with both incoming and outgoing calls; make new phone calls; and get statistical information about a particular call, such as whether the call was roaming, the time connected, and caller ID information (see Figure 8.2).

Figure 8.2. Pocket PC Phone Edition call log

graphics/08fig02.gif

Creating applications that access the Phone API should include the phone.h header file, and link with the phone.lib library.

Call History

Before you can make any queries regarding the device's phone history, you must first get the handle to the call log by using the PhoneOpenCallLog() function. This will also initialize an internal seek pointer for the log to the first entry. The function is defined as follows:

HRESULT PhoneOpenCallLog(HANDLE *ph);

The function takes a single parameter?a pointer that contains the handle to the call log if it is opened and initialized successfully:

// Open Phone log
HANDLE hPhoneLog = NULL;
HRESULT hr = S_OK;

hr = PhoneOpenCallLog(&hPhoneLog);
if(FAILED(hr))
   return FALSE;

// Log opened, continue with application

Once the call log has successfully been opened, you can either start reading individual call log entries or search for a particular record. In addition, every time you read a log entry, the internal seek pointer is automatically advanced to the next one until it reaches the end of the log. This can be extremely useful when you just need to enumerate all of the calls in the call history, which are organized according to the time the call was started, with the most recent being returned first.

To read the current log entry, the Phone API provides the function PhoneGetCallLogEntry():

HRESULT PhoneGetCallLogEntry(HANDLE h, PCALLLOGENTRY pentry);

The first parameter is the handle to the call log and is followed by a pointer to a CALLLOGENTRY structure that contains the actual call information. Remember that before you can call PhoneGetCallLogEntry(), you should set the cbSize member to the size of the CALLLOGENTRY structure.

The structure is defined as follows:

typedef struct{
   DWORD cbSize;
   FILETIME ftStartTime;
   FILETIME ftEndTime;
   IOM iom;
   BOOL fOutgoing;
   BOOL fConnected;
   BOOL fEnded;
   BOOL fRoam;
   CALLERIDTYPE cidt;
   PTSTR pszNumber;
   PTSTR pszName;
   PTSTR pszNameType;
   PTSTR pszNote;
} CALLLOGENTRY, *PCALLLOGENTRY;

The structure's first field, cbSize, must be set to the size of CALLLOGENTRY before calling the function. The next two fields, ftStartTime and ftEndTime, provide the start and end time details for the call. The next field, iom, specifies the type of call, which can be one of the following:

  • IOM_MISSED, if the incoming call was not answered

  • IOM_INCOMING, if the incoming call was answered

  • IOM_OUTGOING, if the call is outgoing

The next four fields of CALLLOGENTRY provide basic call information: fOutgoing will be TRUE if the call is being made instead of received; fConnected will be TRUE if the call went through, or FALSE if there was a busy signal or no answer; fEnded will indicate if the call has ended; and fRoam will be FALSE if the call was made from the home calling area, as opposed to roaming coverage.

The cidt field contains details about caller ID information for the call, and can be one of the following:

  • CALLERIDTYPE_UNAVAILABLE, if the caller ID information is not available

  • CALLERIDTYPE_BLOCKED, if the caller ID type information was blocked

  • CALLERIDTYPE_AVAILABLE, if the caller ID information is available

The last four fields contain contact information related to the call. The pszNumber field points to the phone number of the call, pszName points to the name associated with the call, pszNameType points to a single letter that represents the call type (such as "w" for work or "h" for home), and pszNote points to a filename of a note file (if one exists) associated with the call.

One word of caution: Once you have finished using the information that you received from a call to PhoneGetCallLogEntry(), you must call the LocalFree() function on any strings in the CALLLOGENTRY structure that are valid in order to properly release memory.

The following code shows the proper way to enumerate through all of the current phone log entries:

// Walk through all Phone log entries
CALLLOGENTRY phoneEntry;
TCHAR tchPhoneEntry[1024] = TEXT("\0");
TCHAR tchIOM[64] = TEXT("\0");
TCHAR tchFrom[128] = TEXT("\0");

do {
   // Clean up the structure
   memset(&phoneEntry, 0, sizeof(CALLLOGENTRY));
   memset(&tchPhoneEntry, 0, 1024);
   memset(&tchIOM, 0, 64);
   phoneEntry.cbSize = sizeof(CALLLOGENTRY);

   // Get the entry
   hr = PhoneGetCallLogEntry(hPhoneLog, &phoneEntry);
   if(FAILED(hr))
      break;

   // Have we reached the end of the log?
   if(hr == S_FALSE)
      break;

// Look at the phonebook data
switch(phoneEntry.iom) {
   case IOM_MISSED:
      wsprintf(tchIOM, TEXT("Missed Call"));
   break;
   case IOM_INCOMING:
      wsprintf(tchIOM, TEXT("Incoming Call"));
   break;
   case IOM_OUTGOING:
     wsprintf(tchIOM, TEXT("Missed Call"));
   break;
}

if(phoneEntry.pszName != NULL)
   _tcsncpy(tchFrom, phoneEntry.pszName, 128);
else if(phoneEntry.pszNumber != NULL)
   _tcsncpy(tchFrom, phoneEntry.pszNumber, 128);
else
   wsprintf(tchFrom, TEXT("Unknown"));

// Display a MessageBox to the user
SYSTEMTIME sysTime;

FileTimeToSystemTime(&phoneEntry.ftStartTime, &sysTime);
wsprintf(tchPhoneEntry, TEXT("%s from %s on %d-%d-%d"),
   tchIOM, tchFrom, sysTime.wDay, sysTime.wMonth,
   sysTime.wYear);
MessageBox(NULL, tchPhoneEntry, TEXT("Phone Log"), MB_OK |
   MB_ICONINFORMATION);

// Clean up any allocated strings
if(phoneEntry.pszNumber)
   LocalFree(phoneEntry.pszNumber);
if(phoneEntry.pszName)
   LocalFree(phoneEntry.pszName);
if(phoneEntry.pszNameType)
   LocalFree(phoneEntry.pszNameType);
if(phoneEntry.pszNote)
   LocalFree(phoneEntry.pszNote);
} while(1);

If you need to search the call log for a particular entry, you can move the current seek pointer to the beginning, end, or a particular log record by using the following API:

HRESULT PhoneSeekCallLog(HANDLE hdb, CALLLOGSEEK seek, DWORD
   iRecord, LPDWORD piRecord);

The first parameter is the handle to the call log returned from the original call to PhoneOpenCallLog(). The next parameter, seek, determines where the search should start, and should be set to either CALLLOGSEEK_BEGINNING to start from the beginning of the log, or CALLLOGSEEK_END to start from the end. The iRecord parameter is the zero-based index of the entry to search for, and will begin the search based on the value of the seek parameter. The last parameter, piRecord, is a pointer that will return the zero-based index value after the search is completed.

The following example moves the seek pointer to the beginning of the log:

DWORD dwRecord = 0;
HRESULT hr = PhoneSeekCallLog(hPhoneLog,
  CALLLOGSEEK_BEGINNING, 0, &dwRecord);

The next example reads the third entry from the beginning of the log:

DWORD dwRecord = 0;
HRESULT hr = PhoneSeekCallLog(hPhoneLog,
  CALLLOGSEEK_BEGINNING, 3, &dwRecord);

You can also use the PhoneSeekCallLog() function to determine how many entries are in the log by using the following code:

DWORD dwCallCount = 0;
HRESULT hr = PhoneSeekCallLog(hPhoneLog, CALLLOGSEEK_END, 0,
   &dwCallCount);

When you are finished querying data in the call history, you should release the handle by calling the following function:

HRESULT PhoneCloseCallLog(HANDLE h);

The only parameter that PhoneCloseCallLog() needs is the handle to the call log that was returned from the previous call to PhoneOpenCallLog():

if(hPhoneLog)
   PhoneCloseCallLog(hPhoneLog);

Making a Phone Call

To make a phone call using the Phone APIs, you can simply use the PhoneMakeCall() function, which is defined as follows:

LONG PhoneMakeCall(PHONEMAKECALLINFO *ppmci);

The only parameter that the function takes is a pointer to a PHONEMAKECALLINFO structure. This structure contains details about the call you are going to make:

typedef struct tagPHONEMAKECALLINFO{
   DWORD cbSize;
   DWORD dwFlags;
   PCWSTR pszDestAddress;
   PCWSTR pszAppName;
   PCWSTR pszCalledParty;
   PCWSTR pszComment;
} PHONEMAKECALLINFO, *PPHONEMAKECALLINFO;

The first member, cbSize, should be set to the size of the PHONEMAKECALLINFO structure. The next member is dwFlags, which should be set to PMCF_DEFAULT or PMCF_PROMPTBEFORECALLING, if you want to prompt the user to confirm that a phone call should be made.

You should place the null-terminated phone number that you want to call into the pszDestAddress member. Note that the validity of the phone number is not checked, so be especially careful before using the PhoneMakeCall() function to ensure that the number is correct. The pszCalledParty member can optionally be set to a null-terminated string that points to the name of the person you are calling. The remaining members, pszAppName and pszComment, are not used and should be set to NULL.

The following code shows how to use the PhoneMakeCall() function to place a phone call:

// Make a phone call
PHONEMAKECALLINFO phoneCall;
TCHAR tchPhoneNumber[64] = TEXT("\0");

memset(&phoneCall, 0, sizeof(PHONEMAKECALLINFO));
wsprintf(tchPhoneNumber, TEXT("5555551212"));

phoneCall.cbSize = sizeof(PHONEMAKECALLINFO);
phoneCall.dwFlags = PMCF_PROMPTBEFORECALLING; // Prompt
phoneCall.pszDestAddress = tchPhoneNumber;

if((PhoneMakeCall(&phoneCall) == 0))
   MessageBox(NULL, TEXT("Success!"), TEXT("Phone call
      attempt"), MB_OK|MB_ICONINFORMATION);
else
   MessageBox(NULL, TEXT("Failure!"), TEXT("Phone call
      attempt"), MB_OK|MB_ICONERROR);