Understanding Windows CE Telephone API (TAPI): Introduction

TAPI Overview

Telephone API gives to programmer a control over manipulating calls over various telephonic hardware. It can be an intertnal modem, GSM, and so forth. TAPI provides a powerful mechanism for operating on those devices and hides all low-level details from the application developer. You no longer need to work with AT commands. TAPI will translate all required items calling to the telephone service and provides through a Telephone Service Provider Interface (TSPI). Hence, if your target device was equipped with some telephone hardware, it definitely has all necessary drivers installed. Thus, you are perfectly set up to implement your specific task using TAPI.

Dealing with TAPI, you will face two major types of devices:

  • line devices
  • phone devices

A notion of a Line device is used to represent a device-independent abstraction of phone line. It can contain one or more communication channels. In turn, Phone device is a representation of a phone handset; in other words, microphone, volume controls, and so forth. Under Windows CE, in most of the cases, Line and Phone devices are associated one-to-one. Nevertheless, it is possible that one Line device supports several Phone devices; for example, Integrated Services Digital Network (IDSN).

Having said these general words, I’ll move forward and start exploring TAPI capabilities. Actually, you can use TAPI not only for making calls but also for retrieveing various types of information about TAPI devices. This data may be useful in other areas, such as RAS or Connection Manager API calls and so forth.

Line Initialization and TAPI Version Negotiating

I will start your tour of TAPI with very basical stuff. No matter what you will do afterwards, the first thing you must carry out with TAPI is line initialization:

LONG WINAPI lineInitialize(
     LPHLINEAPP          lphLineApp,
     HINSTANCE           hInstance,
     LINECALLBACK        lpfnCallback,
     LPCWSTR             lpszAppName,
     LPDWORD             lpdwNumDevs

This function takes three incoming parameters: application or DLL instance handle, callback function to return asynchronous notifications, and application name to indicate who is calling TAPI. The outgoing lphLineApp handle will be used throughout the rest of the TAPI functions calls. Please note that, with all its simplicity, lineInitialize gives you important information on exit: how many TAPI devices are available for application. All this is illustarted below:

VOID PASCAL LineCallbackFunc(
  DWORD hDevice,
  DWORD dwMsg,
  DWORD dwCallbackInstance,
  DWORD dwParam1,
  DWORD dwParam2,
  DWORD dwParam3)

HLINEAPP g_hLineApp;
DWORD dwNumDevices = 0;
DWORD dwRet = ::lineInitialize(&g_hLineApp,g_hInst,
                               _T("Developer.com Test"),

if ( dwRet != 0 )
// Report error

For now, leave LineCallbackFunc untouched; you’ll see it a bit later in this tutorial.

The second TAPI call you will usually execute is lineNegitiateAPIVersion:

LONG lineNegotiateAPIVersion(
     HLINEAPP hLineApp,
     DWORD dwDeviceID,
     DWORD dwAPILowVersion,
     DWORD dwAPIHighVersion,
     LPDWORD lpdwAPIVersion,

Version negotiation is used to reach some kind of agreement between all communication parties: TAPI, the service provider, and your application. You can call it for any device in range from 0 to dwNumDevices – 1. This function takes the low and high boundaries of the TAPI version required by the application and at exit returns its negotiated value.

The last parameter is a pointer to a structure of type LINEEXTENSIONID. For Windows CE 3.0, you have had to set this parameter to NULL. Latest OS versions allow a service provider supplying provider-specific extensions for the specified dwDeviceID parameter. In such a case, upon a successful negotiation, lpExtensionID will be filled with the extension identifier of these extensions. Otherwise, you will get zeros here. Your application may freely ignore this structure if it does not need any extensions. Hence, the typical code you will write for TAPI version negotiation may look like the following:

#define EARLY_TAPI_VERSION 0x00010003    // Early TAPI version

DWORD dwNumDevs    = 0;
LONG lReturn       = 0;
DWORD dwAPIVersion = 0;

// Get number of available TAPI devices
lReturn = ::lineInitialize(&g_hLineApp,
                           _T("Developer.com Test"),

for (DWORD dwDeviceID = 0; dwDeviceID < dwNumDevs; dwDeviceID ++)
   lReturn = ::lineNegotiateAPIVersion(g_hLineApp, dwDeviceID,
            &dwAPIVersion, &lineExtID);

You eventually can carry out above two tasks in one shot, calling

LONG WINAPI lineInitializeEx(
     LPHLINEAPP lphLineApp,
     HINSTANCE hInstance,
     LINECALLBACK lpfnCallback,
     LPCWSTR lpszFriendlyAppName,
     LPDWORD lpdwNumDevs,
     LPDWORD lpdwAPIVersion,

This function has minor differences in paramaters from the pair lineInialize/lineNegitiateAPIVersion, but finally results in the same outcome. What is important to notice regarding lineInitializeEx is that you can select one of two mechanisms by which TAPI will notify the application of telephony events: Hidden Window or Event Handle.

More by Author

Must Read