http://www.developer.com/

Back to article

Exploring Extended TAPI Functions


May 10, 2005

There are a lot of TAPI functions that were not touched in previous articles. I obviously don't pretend to cover them all here. Such huge task requires separate book to be described. However, I will try to overview different function groups to give you high level picture of available options.

Retrieving General Information

We will start this part of tutorial from regular things. This section describes how to obtain various general information for specified TAPI device. This function groups belong to Extended TAPI. ExTAPI, proving its name, extends wireless API with different options:

  • various device specific info
  • info about signal strength
  • info about system type
  • info about line operators
  • choosing the cellular network
  • registering lines for specific operator
  • get/set GPRS class
  • get/set equipment state
  • and more...

ExTAPI uses Telephony API (TAPI) functions and available TAPI line devices. There is only one restriction with ExTAPI. All its operations are available only if:

  • your application has successfully negotiated a device-specific extension version using lineNegotiateExtVersion
  • your application has successfully obtained a line device handle using lineOpen

What it means for you is that most of ExTAPI functions won't work if your application hasn't requested (and obtained) enough privileges (pay attention to the LINECALLPRIVILEGE_OWNER parameter of ::lineOpen in the very first sample).

Initialization

As you have seen in previous articles, first of all you have to initialize TAPI. Here, I'll put one more example of how to do it. The only difference from previous examples is that this code snipped uses the Event mechanism instead of a callback function to receive further notifications:

BOOL CExTapiDlg::OnInitDialog()
{
   CDialog::OnInitDialog();
   ...

   LINEINITIALIZEEXPARAMS LineExtParams;
   LineExtParams.dwTotalSize = sizeof(LineExtParams);
   LineExtParams.dwOptions   = LINEINITIALIZEEXOPTION_USEEVENT;

   if (::lineInitializeEx(&m_hLineApp, 0, 0, _T("Developer.com Test"),
                          &m_dwNumDevs, &m_dwAPIVersion,
                          &LineExtParams))
   {
      EndDialog(IDOK);
      return FALSE;
   }

   m_dwTSPILineDeviceID = GetTSPLineDeviceID(CELLTSP_LINENAME_STRING);
   if ( m_dwTSPILineDeviceID == (DWORD)-1 )
   {
      ::lineShutdown(m_hLineApp);
      EndDialog(IDOK);
      return FALSE;
   }

   // open the line
   if( ::lineOpen(m_hLineApp, m_dwTSPILineDeviceID,
                  &m_hLine, m_dwAPIVersion, 0, 0,
                  LINECALLPRIVILEGE_OWNER,
                  LINEMEDIAMODE_DATAMODEM, 0) )
   {
      ::lineShutdown(m_hLineApp);
      EndDialog(IDOK);
      return FALSE;
   }

   // set up ExTAPI
   if ( ::lineNegotiateExtVersion(m_hLineApp, m_dwTSPILineDeviceID,
                                  m_dwAPIVersion, EXT_API_LOW_VERSION,
                                  EXT_API_HIGH_VERSION,
                                  &m_dwExtVersion) )
   {
      ::lineClose(m_hLine);
      ::lineShutdown(m_hLineApp);
      EndDialog(IDOK);
      return FALSE;
   }
}

DWORD CExTapiDlg::GetTSPLineDeviceID(const TCHAR*
                                     const psTSPLineName)
{
   DWORD dwReturn = -1;
   for(DWORD dwCurrentDevID = 0 ; dwCurrentDevID < m_dwNumDevs ;
       dwCurrentDevID++)
   {
      LINEEXTENSIONID LineExtensionID;
      if( ::lineNegotiateAPIVersion(m_hLineApp, dwCurrentDevID,
                                    TAPI_API_LOW_VERSION,
                                    TAPI_CURRENT_VERSION,
                                    &m_dwAPIVersion,
                                    &LineExtensionID) == 0 )
      {
         LINEDEVCAPS LineDevCaps;
         LineDevCaps.dwTotalSize = sizeof(LineDevCaps);
         if( ::lineGetDevCaps(m_hLineApp, dwCurrentDevID,
                              m_dwAPIVersion, 0, &LineDevCaps) == 0 )
         {
            BYTE* pLineDevCapsBytes = new BYTE[LineDevCaps.dwNeededSize];
            if(0 != pLineDevCapsBytes)
            {
               LINEDEVCAPS* pLineDevCaps =
                  (LINEDEVCAPS*)pLineDevCapsBytes;
               pLineDevCaps->dwTotalSize = LineDevCaps.dwNeededSize;
               if( ::lineGetDevCaps(m_hLineApp, dwCurrentDevID,
                                    m_dwAPIVersion, 0,
                                    pLineDevCaps) == 0 )
               {
                  if( _tcscmp((TCHAR*)
                     ((BYTE*)pLineDevCaps+pLineDevCaps->
                       dwLineNameOffset), psTSPLineName) == 0 )
                  {
                     dwReturn = dwCurrentDevID;
                  }
               }
               delete[]  pLineDevCapsBytes;
            }
         }
      }
   }
   return dwReturn;
}

'Get/Set' functions

There are a couple dozen get/set-functions that give you a lot of useful information and allow managing line devices. It doesn't make sense to describe them all here; I'll only put a brief 'get' calls reference. You will find detailed explanations in SDK help:

  • lineGetCallBarringCaps
  • lineGetCallBarringState
  • lineGetCallWaitingCaps
  • lineGetCallWaitingState
  • lineGetCurrentAddressID
  • lineGetCurrentHSCSDStatus
  • lineGetCurrentOperator
  • lineGetCurrentSystemType
  • lineGetEquipmentState
  • lineGetGeneralInfo
  • lineGetGPRSClass
  • lineGetHSCSDCaps
  • lineGetHSCSDState
  • lineGetMuteState
  • lineGetNumberCalls
  • lineGetOperatorStatus
  • lineGetRadioPresence
  • lineGetRegisterStatus
  • lineGetSendCallerIDState
  • lineGetUSSD

Your application can use some of the above functions to verify radio presence, barring caps and states, system type, and so forth. The following sample code illustrates how to retrieve information about a given line device:

void CExTapiDlg::OnGetGeneralInfo()
{
   LPBYTE pLineGeneralInfoBytes = NULL;
   const DWORD dwMediaMode = LINEMEDIAMODE_DATAMODEM;
   LINEGENERALINFO lviGeneralInfo;
   LPLINEGENERALINFO plviGeneralInfo;
   LPTSTR tsManufacturer, tsModel, tsRevision, tsSerialNumber,
          tsSubscriberNumber;
   CString sInfo;

   lviGeneralInfo.dwTotalSize = sizeof(lviGeneralInfo);

   LONG lRes = ::lineGetGeneralInfo(m_hLine, &lviGeneralInfo);
   if (lRes != 0 && lRes != LINEERR_STRUCTURETOOSMALL)
   {
      TCHAR szMsg[255];
      GetTAPIErrorMsg(szMsg,sizeof(szMsg), lRes);
      AfxMessageBox(szMsg);
      return;
   }

   pLineGeneralInfoBytes = new BYTE[lviGeneralInfo.dwNeededSize];
   plviGeneralInfo = (LPLINEGENERALINFO)pLineGeneralInfoBytes;

   if(pLineGeneralInfoBytes != NULL)
   {
      plviGeneralInfo->dwTotalSize = lviGeneralInfo.dwNeededSize;
      if ( (lRes = ::lineGetGeneralInfo(m_hLine, plviGeneralInfo)) != 0 )
      {
         TCHAR szMsg[255];
         GetTAPIErrorMsg(szMsg,sizeof(szMsg), lRes);
         AfxMessageBox(szMsg);
      }
   else
   {
      TCHAR szUnavailable[] = L"Unavailable";
      if(plviGeneralInfo->dwManufacturerSize)
      {
         tsManufacturer = (WCHAR*)(((BYTE*)plviGeneralInfo)
                          +plviGeneralInfo->dwManufacturerOffset);
      }
      else
      {
         tsManufacturer = szUnavailable;
      }

      if(plviGeneralInfo->dwModelSize)
      {
         tsModel = (WCHAR*)(((BYTE*)plviGeneralInfo)
                   +plviGeneralInfo->dwModelOffset);
      }
      else
      {
         tsModel = szUnavailable;
      }

      if(plviGeneralInfo->dwRevisionSize)
      {
         tsRevision = (WCHAR*)(((BYTE*)plviGeneralInfo)
                      +plviGeneralInfo->dwRevisionOffset);
      }
      else
      {
         tsRevision = szUnavailable;
      }

      if(plviGeneralInfo->dwSerialNumberSize)
      {
         tsSerialNumber = (WCHAR*)(((BYTE*)plviGeneralInfo)
                           +plviGeneralInfo->dwSerialNumberOffset);
      }
      else
      {
         tsSerialNumber = szUnavailable;
      }

      if(plviGeneralInfo->dwSubscriberNumberSize)
      {
         tsSubscriberNumber = (WCHAR*)(((BYTE*)plviGeneralInfo)
            +plviGeneralInfo->dwSubscriberNumberOffset);
      }
      else
      {
         tsSubscriberNumber = szUnavailable;
      }

      sInfo.Format(L"Manufacturer: %s\nModel: %s\nRevision:
                   %s\nSerial No: %s\nSubscriber No: %s\n",
                   tsManufacturer,
                   tsModel,
                   tsRevision,
                   tsSerialNumber,
                   tsSubscriberNumber);
      AfxMessageBox(sInfo);
      }
   }

   delete [] pLineGeneralInfoBytes;
}

The companion project contains more examples of this group of functions; here, I'll drop the simplest one:

void CExTapiDlg::OnGetEquipmentState()
{
   TCHAR *szEqStates[] =
   {
      L"LINEEQUIPSTATE_MINIMUM",
      L"LINEEQUIPSTATE_RXONLY",
      L"LINEEQUIPSTATE_TXONLY",
      L"LINEEQUIPSTATE_NOTXRX",
      L"LINEEQUIPSTATE_FULL"
   };
   TCHAR *szRadioSupport[] =
   {
      L"LINERADIOSUPPORT_OFF",
      L"LINERADIOSUPPORT_ON",
      L"LINERADIOSUPPORT_UNKNOWN"
   };

   DWORD dwState = 0;
   DWORD dwRadioSupport = 0;
   LONG lRet = lineGetEquipmentState(m_hLine,&dwState,&dwRadioSupport);
   if ( lRet == 0 )
   {
      CString sInfo;
      sInfo.Format(L"State = %s, Radio = %s",
         szEqStates[dwState - 1],
         szRadioSupport[dwRadioSupport-1]);
      AfxMessageBox(sInfo);
   }
   else
   {
      TCHAR szMsg[255];
      GetTAPIErrorMsg(szMsg,sizeof(szMsg), lRet);
      AfxMessageBox(szMsg);
   }
}

Network operator related functions

ExTAPI exposes several functions that you can use to manage network operator-related stuff:

  • lineRegister
  • lineUnregister
  • lineGetRegisterStatus
  • lineGetCurrentOperator
  • lineGetOperatorStatus
  • lineSetPreferredOperator

If your application requires you to deal with these kinds of operations, these functions will help you to handle it. The tiny sample below queries all available network operators:

void CExTapiDlg::OnGetAvailOps()
{
   LINEOPERATORSTATUS lops;

   lops.dwTotalSize = sizeof(LINEOPERATORSTATUS);

   BeginWaitCursor();
   LONG lRes = ::lineGetOperatorStatus(m_hLine, &lops);
   if (lRes != 0 && lRes != LINEERR_STRUCTURETOOSMALL)
   {
      EndWaitCursor();

      TCHAR szMsg[255];
      GetTAPIErrorMsg(szMsg,sizeof(szMsg), lRes);
      AfxMessageBox(szMsg);
      return;
   }


   CString sInfo(L"Available Operators:\r\n");
   LPBYTE pLineOperatorStatusBytes = new BYTE[lops.dwNeededSize];
   LPLINEOPERATORSTATUS pStatus =
      (LPLINEOPERATORSTATUS)pLineOperatorStatusBytes;

   if(pLineOperatorStatusBytes != NULL)
   {
      pStatus->dwTotalSize = lops.dwNeededSize;
      if ( (lRes = ::lineGetOperatorStatus(m_hLine, pStatus)) != 0 )
      {
         TCHAR szMsg[255];
         GetTAPIErrorMsg(szMsg,sizeof(szMsg), lRes);
         AfxMessageBox(szMsg);
      }
      else
      {
         LPLINEOPERATOR pOperator = (LPLINEOPERATOR)((LPBYTE)pStatus+
                                    (pStatus->dwAvailableOffset));
         for(DWORD dwOperatorNumber = 0;dwOperatorNumber < pStatus->
                                      dwAvailableCount;
                                      dwOperatorNumber++)
         {
            sInfo += pOperator->lpszLongName;
            sInfo += L"\r\n";
            pOperator++;
         }
      }
   }

   delete[] pLineOperatorStatusBytes;
   EndWaitCursor();

   AfxMessageBox(sInfo);
}

If you run this sample, you will see that it takes some time to obtain the required information. Except for this, the code is quite common for TAPI:

  1. get required data buffer size
  2. allocate enough memory
  3. call the same function again
  4. iterate through returned data
  5. cleanup all allocated memory

Usually, you will store the original network operator, set up your own, and finally restore the previous settings. ExTAPI serves you just fine here.

Conclusion

In this acticle, you have observed Extended TAPI operations. They can or cannot be required in your applications, but anyway it's better to know your enemy before it beats you. You will continue investigating TAPI in next articles, because it is utterly not ended yet. Possible areas of intrest are managing calls, setting up conferences, and so forth. If all goes well, you will learn about these topics in the coming articles. Enjoy!

Download

Download the accompanying code's zip file here.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.

Sitemap | Contact Us

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