February 15, 2019
Hot Topics:

Managing Calls and Transferring Data with TAPI

  • April 29, 2005
  • By Alex Gusev
  • Send Email »
  • More Articles »

TAPI Devices Enumeration

In the previous article, "Understanding Windows CE Telephone API (TAPI): Introduction," you learned about the simplest TAPI functions. Based, on them, you are now ready to conduct an enumeration of available TAPI devices. For this purpose, you will use the lineGetDevCaps function. As you saw earlier, you have to initialize the line first. Then, after TAPI version negotiation, lineGetDevCaps can be called to obtain the required data. Below is is a tiny code snippet to illustrate the schema:

#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);
   lReturn = ::lineGetDevCaps(g_hLineApp, dwDeviceID, dwAPIVersion,
                              0, lpLineDevCaps);

All lineGetDevCaps parameters are intuitively understandable, but the last one, of the LPLINEDEVCAPS type, requires some explanation. Just to feel what is it, please refer to its definition below:

typedef struct linedevcaps_tag {
DWORD dwTotalSize;
DWORD dwNeededSize;
DWORD dwUsedSize;

DWORD dwProviderInfoSize;
DWORD dwProviderInfoOffset;

DWORD dwSwitchInfoSize;
DWORD dwSwitchInfoOffset;

DWORD dwPermanentLineID;
DWORD dwLineNameSize;
DWORD dwLineNameOffset;
DWORD dwStringFormat;
DWORD dwAddressModes;
DWORD dwNumAddresses;
DWORD dwBearerModes;
DWORD dwMaxRate;
DWORD dwMediaModes;

DWORD dwGenerateToneModes;
DWORD dwGenerateToneMaxNumFreq;
DWORD dwGenerateDigitModes;
DWORD dwMonitorToneMaxNumFreq;
DWORD dwMonitorToneMaxNumEntries;
DWORD dwMonitorDigitModes;
DWORD dwGatherDigitsMinTimeout;
DWORD dwGatherDigitsMaxTimeout;

DWORD dwMedCtlDigitMaxListSize;
DWORD dwMedCtlMediaMaxListSize;
DWORD dwMedCtlToneMaxListSize;
DWORD dwMedCtlCallStateMaxListSize;

DWORD dwDevCapFlags;
DWORD dwMaxNumActiveCalls;
DWORD dwAnswerMode;
DWORD dwRingModes;
DWORD dwLineStates;

DWORD dwUUIAcceptSize;
DWORD dwUUIAnswerSize;
DWORD dwUUIMakeCallSize;
DWORD dwUUIDropSize;
DWORD dwUUISendUserUserInfoSize;
DWORD dwUUICallInfoSize;


DWORD dwNumTerminals;
DWORD dwTerminalCapsSize;
DWORD dwTerminalCapsOffset;
DWORD dwTerminalTextEntrySize;
DWORD dwTerminalTextSize;
DWORD dwTerminalTextOffset;

DWORD dwDevSpecificSize;
DWORD dwDevSpecificOffset

DWORD dwLineFeatures;

DWORD dwSettableDevStatus;
DWORD dwDeviceClassesSize;
DWORD dwDeviceClassesOffset;

The LINEDEVCAPS struct is mostly built from Size/Offset pairs. Actually, it follows the common Windows phylosophy for returning this kind of variable-length data. The reason I note it here is pretty simple. Your application is responsible for providing TAPI with the correct memory chuck. Then, you obviously will want to use returned information. Hence, you start to dig in into the code.

The first thing to be said is that this process is cyclic. This means that you should allocate a minimal memory chunk. lineGetDevCaps will return LINEERR_STRUCTURETOOSMALL in the case when the supplied buffer is too small. In this case, lpLineDevCaps->dwNeededSize will contain the required buffer size. All you need to do is to reallocate your result buffer and call lineGetDevCaps once again. The next sample shows how all this theory can be implemented:

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

   bOK = TRUE;
   dwSize = sizeof(LINEDEVCAPS);
      lpLineDevCaps = (LPLINEDEVCAPS) new BYTE[dwSize];
      lpLineDevCaps->dwTotalSize = dwSize;
      lReturn = ::lineGetDevCaps(g_hLineApp, dwDeviceID, dwAPIVersion,
                                 0, lpLineDevCaps);
      if ( 0 != lReturn && LINEERR_STRUCTURETOOSMALL != lReturn )
         TRACE(L"Error: %X\n",lReturn);
         delete lpLineDevCaps;
         lpLineDevCaps = NULL;

         bOK = FALSE;

      if ( lpLineDevCaps->dwNeededSize <=
           lpLineDevCaps->dwTotalSize )

      TRACE(L"Reallocating to new size = %lu\n",lpLineDevCaps->

      dwSize = lpLineDevCaps->dwNeededSize;
      delete lpLineDevCaps;
      lpLineDevCaps = NULL;
   while (1);

   if ( bOK )
      lpszDevName = (LPTSTR)((LPBYTE)lpLineDevCaps + lpLineDevCaps->
      delete lpLineDevCaps;
      lpLineDevCaps = NULL;
This code runs through all available TAPI devices and tries to obtain a device name for each one. As you have seen before, most of the data is addressed by offsets in the lpLineDevCaps structure. The LINEDEVCAPS struct will guide you to which data is available. Here, I will only note a few of the struct members. DeviceClass info (pointed to by dwDeviceClassesSize/dwDeviceClassesOffset members) will be used later to make calls over TAPI devices. dwBearerModes describes the call types this device can make. dwMediaModes indicates the supported transmission modes. dwMaxRate is maximum transmission rate (in Bps). You may be required to fetch some other info.

Page 1 of 2

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date