用TAPI实现MODEM通信编程

转自:http://bbs.tech.ccidnet.com/read.php?tid=192210

TAPI意即Telephony API,是一组Windows操作系统提供的一组针对电话MODEM进行通信编程的API函数。下面就几年前笔者在工程应用中的实际代码对使用TAPI实现电话MODEM通信编程的通信过程和相关API函数作详细的介绍。
1、初始化线路
通过使用lineInitializeEx函数初始化TAPI.DLL得到TAPI使用句柄的指针个g_hTapi,通过调用lineOpen函数(用到参数g_hTapi)获得线路句柄m_hLine;再利用lineGetID(用到参数m_hLine)获取调制解调器句柄m_hModem。
(1)首先应使用lineInitializeEx初始化TAPI.DLL得到使用TAPI的句柄,该函数是使用TAPI的任何其它函数之前必须调用的函数,它除返回使用句柄g_hTapi外,还返回系统中的可用线路数g_dwLineTotalNum,LineCallBackProc为设置的回调函数,后面会讲到。如果成功返回值为0,否则为错误信息。在下面封装的函数TelephonyInitialize中还通过版本协商确定了线路号是否可用,这些可用的线路号在打开线路时会用到。同时用到几个全局变量。
BOOL g_bTapiInitialized = 0;//确保lineInitializeEx在程序中只被调用一次
HLINEAPP g_hTapi = 0;//使用TAPI的访问句柄。
DWORD g_dwLineTotalNum = 0;//系统中安装的线路条数。
DWORD g_dwUsedTapiVersion[100];//保存协商后使用的TAPI版本,后面的函数将会用到。
DWORD g_dwlineUsable[100];//系统中可用的线路号
long TelephonyInitialize(int nRetryNum)
{
if(g_bTapiInitialized) return 0;
LONG lrc = -1l;
DWORD i;
LINEEXTENSIONID extensions;
DWORD dwVersion;
LINEINITIALIZEEXPARAMS lineParam;
lineParam.dwOptions = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;
lineParam.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS);
for(i = 0; i < USABLELINE_MAX; i++)
g_dwlineUsable = 0xffffffff;
int nRetryCount = 0;
while (nRetryCount <= nRetryNum)
{
lrc = ::lineInitializeEx(&g_hTapi, ::AfxGetApp()->m_hInstance,
  (LINECALLBACK)LineCallBackProc, ::AfxGetAppName(),
  &g_dwLineTotalNum, &dwVersion, &lineParam);
if(lrc == LINEERR_REINIT)
{
  Sleep (5);   // sleep for five seconds
  TRACE("电话系统初始化....../n");
  nRetryCount++;
  continue;
}
else
  break;
} // end while (TAPI reinitializing)
if(lrc) return lrc;
g_bTapiInitialized = TRUE;
DWORD dwUsedVersion = 0;
for (i = 0; (unsigned)i < g_dwLineTotalNum; i++)
{
// negotiate version of TAPI to use
lrc = ::lineNegotiateAPIVersion(g_hTapi, i,
  WIN95TAPIVERSION, WIN95TAPIVERSION,
  &dwUsedVersion, &extensions);
if (lrc) continue;
g_dwUsedTapiVersion = dwUsedVersion;
g_dwlineUsable = i;
}
return 0l;
}
(2)使用lineOpen打开指定的线路,使用到上一个函数返回的g_hTapi和一个要打开的线路号(m_dwActulLineNo),返回已打开的线路句柄m_hLine保存在笔者封装的一个类CPhoneComm的成员变量中。须注意的是此处lineOpen的参数dwCallbackInstance带入了this指针,供回调函数时使用。
long CPhoneComm::OpenLine()
{
long lrc;
if(!g_bTapiInitialized)
{
lrc = TelephonyInitialize();
if(lrc) return lrc;
}
if(g_dwlineUsable[m_dwActulLineNo] == m_dwActulLineNo)
{
lrc = ::lineOpen(g_hTapi, m_dwActulLineNo, &m_hLine,
    g_dwUsedTapiVersion[m_dwActulLineNo], 0, (DWORD)this,
    LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM, NULL);
if (lrc) return lrc;
}
m_bLineUsable = TRUE;
return lrc;
}
return LINEERR_NODEVICE;
}
(3)使用lineGetID取得设备的通信句柄m_hModem,保存它后面将会用到,同时可保存该线路的名称备用。
long CPhoneComm::LineGetID()
{
  CommID FAR *cid;
  VARSTRING *vs;
  LONG lrc;
  DWORD dwSize;
  vs = (VARSTRING *) calloc (1, sizeof(VARSTRING));
  vs->dwTotalSize = sizeof(VARSTRING);
  do {
lrc = ::lineGetID(m_hLine, 0, NULL, LINECALLSELECT_LINE, vs, "comm/datamodem");
if (lrc) {free (vs);return lrc;}
if (vs->dwTotalSize < vs->dwNeededSize)
{
        dwSize = vs->dwNeededSize;
        free (vs);
        vs = (VARSTRING *) calloc(1, dwSize);
          vs->dwTotalSize = dwSize;
  continue;
    } /* end if (need more space) */
    break; /* success */
  } while (TRUE);
cid = (CommID FAR *) ((LPSTR)vs + vs->dwStringOffset);
  m_szModemName = &cid->szDeviceName[0];
// save modem handle
m_hModem = cid->hComm;
free (vs);
return 0l;
}
2、配置线路(可选):
调用SetCommConfig(用到hModem)改变调制解调器的设置。
long CPhoneComm::SetModemConfig()
{
DWORD dwsize = sizeof(COMMCONFIG)+sizeof(MODEMSETTINGS);
LONG lrc;
do
{
if (m_pCommconfig != NULL) {
  free (m_pCommconfig);
  m_pCommconfig = NULL;
}
m_pCommconfig = (COMMCONFIG *) calloc (1, dwsize);
  m_pCommconfig->dwSize = dwsize;
lrc = ::GetCommConfig(m_hModem, m_pCommconfig, &dwsize);
m_pModemsettings = (MODEMSETTINGS *) m_pCommconfig->wcProviderData;
// try again
if (m_pCommconfig->dwSize < dwsize) continue;
break;
} while (TRUE);
m_pCommconfig->dcb.fTXContinueOnXoff = 1;
dwsize = m_pCommconfig->dwSize + m_pModemsettings->dwActualSize;
lrc = ::SetCommConfig(m_hModem, m_pCommconfig, dwsize);
return 0l;
}
在应答方,就可以等待呼入了。

3、拨号与应答
(1)呼叫方:使用lineMakeCall函数(用到m_hLine)进行拨号,完成后获得呼叫句柄m_hCall(呼叫方的呼叫句柄),这样在呼叫方就等待应答方的连接了。
long CPhoneComm::DialCall(LPCTSTR lpszDialNum, LPCTSTR szCountryCode)
{
char szTemp[100], szCode[20];
memset(szTemp, 0, 60);
memset(szCode, 0, 20);
LPLINETRANSLATEOUTPUT lto;
LINECALLPARAMS *pCallParams;
long lResult;
if(lpszDialNum == NULL)//默认拨打组态号码
lpszDialNum = m_szDialNo;
if(szCountryCode == NULL)
::tapiGetLocationInfo (szCode, szTemp);
else
memcpy(szCode, szCountryCode, strlen(szCountryCode));
memset(szTemp, 0, 100);
if(szCode[0] != '+')
szTemp[0] = '+';
strcat(szTemp, szCode);
strcat(szTemp, lpszDialNum);
lto = (LINETRANSLATEOUTPUT *)
  calloc(sizeof(LINETRANSLATEOUTPUT)+5000,1);
lto->dwTotalSize = sizeof(LINETRANSLATEOUTPUT)+5000;
lResult = ::lineTranslateAddress(g_hTapi,
  m_dwActulLineNo, g_dwUsedTapiVersion[m_dwActulLineNo],
  szTemp, 0,
  0,lto);
if (lResult) {free(lto);return lResult; }
memcpy (szTemp, (LPSTR)((DWORD)lto+lto->dwDialableStringOffset), lto->dwDialableStringSize);
szTemp[lto->dwDialableStringSize] = 0;
pCallParams = (LINECALLPARAMS *)
    calloc(sizeof(LINECALLPARAMS),1);
memset(pCallParams, 0, sizeof(LINECALLPARAMS));
pCallParams->dwTotalSize = sizeof(LINECALLPARAMS);
pCallParams->dwBearerMode = LINEBEARERMODE_VOICE;
pCallParams->dwMediaMode = LINEMEDIAMODE_DATAMODEM;
lResult = ::lineMakeCall(m_hLine, &m_hCall,
  szTemp, 0, pCallParams);
free (pCallParams);
free(lto);
if (lResult < 0) return lResult;
return 0l;
}
(2)应答方:应答方的回调函数得到LINECALLSTATE_OFFERING消息时,调用lineAnswer函数实现自动应答(呼叫句柄m_hCall由回调函数的参数给出)。代码参见回调函数。
4、回调函数与数据通信
回调函数是在调用lineInitializeEx设置给TAPI.DLL的,当有消息时DLL通过该回调函数叫应用程序处理。回调函数处理了已建立连接和对方挂线消息。
void FAR PASCAL LineCallBackProc(DWORD dwDevice, DWORD dwMessage,DWORD dwInstance,DWORD dwParam1, DWORD dwParam2,DWORD dwParam3)
{
CPhoneComm * pPhone = (CPhoneComm *)dwInstance;//该参数在lineOpen时带入。
If(pPhone == NULL) return;
switch (dwMessage)
{
case LINE_CALLSTATE:
  pPhone->DoLineCallState(dwDevice, dwParam1, dwParam2, dwParam3);
  break;
default:
  break;
} //end switch
return;
} /* LineCallBackProc */

void CPhoneComm::DoLineCallState(DWORD dwDevice, DWORD dwParam1, DWORD dwParam2,DWORD dwParam3)
{ //LINE_CALLSTATE
long lrc;
switch (dwParam1)
{
case LINECALLSTATE_IDLE:
  StopReadWrite();
  ::lineDeallocateCall (m_hCall);
  m_bCallValid = FALSE;
  break;
case LINECALLSTATE_OFFERING:
  if(m_bAllowAnswer)
  {
  if(!m_bCallValid)
  {
  m_hCall = (HCALL)dwDevice;
  m_bCallValid = TRUE;
  }
  lrc = ::lineAnswer(m_hCall,NULL,0);
  }
  break;
case LINECALLSTATE_CONNECTED:
  StartReadWrite();
  break;
case LINECALLSTATE_DISCONNECTED:
lineClose(m_hLine);
default:
  break;
} //end switch
}
当回调函数收到LINECALLSTATE_CONNECTED消息后,就可以使用函数为WriteFile及ReadFile函数进行数据交换了,调类成员函数StartReadWrite(),此处不在详述只用。
5、某一方挂机、关闭线路
通信完毕任何一方都可以调用函数lineDrop来停止呼叫,使用到呼叫句柄m_hCall,该函数还发送LINECALLSTATE_IDLE消息给回调函数同时对方在挂断之后也会收到该消息,这时应首先停止数据通信如StartReadWrite(),可对呼叫调用lineDeallocateCal释放占用的资源,参见回调函数。使用lineClose释放由lineOpen分配的资源。
long CPhoneComm::LineDrop()
{
long lResult = ::lineDrop(m_hCall,NULL,0);
if (lResult < 0)
return lResult;
return 0l;
}
6、释放TAPI.DLL
最后当不在使用TAPI时使用lineShutdown释放TAPI为你分配的资源。
void telephonyShutdown()
{
if(g_bTapiInitialized)
::lineShutdown(g_hTapi);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值