首页 | 社区 | 博客 | 招聘 | 文章 | 新闻 | 下载 | 读书 | 代码
亲,您未登录哦! 登录 | 注册

用TAPI为掌上电脑开发通讯应用程序

打印文章

分享到:
中国地质大学(北京)计算机系 田东风

掌上电脑(即PPC,Palmsize PC)以Windows CE为操作系统 。Windows CE是一种模块化的、开放的、实时的、具有强大通信能力的、抢先式、多任务的嵌入式操作系统。它具备高性能、高效率的OS特性,包括按需换页、共享存储、交叉处理同步、支持大容量堆(heap)等。它支持文件系统、注册表、以及对象存储(ObjectStore)技术。通过UNICODE完全支持国际字符集。它具有灵活的电源管理,包括睡眠/唤醒模式。它有支持特定目标应用的丰富服务,例如手写体、流式Video等等。
由于掌上电脑有Windows图形用户界面,因此操作简单方便。更由于掌上电脑只有手掌大小, 因此特别适用于移动计算和移动通讯领域。Windows CE提供了丰富的通信支持:拨号连接、LAN,提供与PC、内联网、以及Internet的连接。提供基本的通信基础结构,包括:套接字(socket)、Internet的TCP/IP、PPP、IrDA、远程访问、TAPI(Telephoney API)以及Unimodem、WinInet、Win32串行、SLIP驱动程序,以及PC连接性的支持。并支持广泛的通信硬件。
下面以两个掌上电脑之间通过MODEM进行远程通讯为例,说明掌上电脑上的通讯程序设计的主要过程。两个掌上电脑之间通过MODEM进行远程通讯程序设计时主要有两种方法。一种方法是直接利用串行通讯函数而不用TAPI,另一种方法是利用TAPI及串行通讯函数。由于利用TAPI可以简化使用MODEM的过程,并且TAPI已经成为事实上的工业标准,因此,本文介绍采用TAPI的方法。
TAPI函数以lineXXXXX...的形式命名,其主要函数的具体功能介绍如下。
1. LONG lineGetID(HLINE hLine, DWORD dwAddressID, HCALL hCall, DWORD dwSelect, LPVARSTRING lpDeviceID, LPCTSTR lpszDeviceClass):获取或指定开放线路,地址或呼叫相关联的设备标识符。它返回一个MODEM句柄。。在进行呼叫之前,程序常需要MODEM句柄来检索MODEM的配置和性能,程序可以通过调用GetCommProperties()以读取COMMPROP和COMMCONFIG结构来了解MODEM的信息,而GetCommProperties()就需要lineGetID()返回的MODEM句柄。
2. LONG lineInitialize(LPHLINEAPP lphLineApp, HINSTANCE hInstance, LINECALLBACK lpfnCallback, LPCTSTR lpszAppName, LPDWORD lpdwNumDevs):初始化TAPI线路。在用TAPI通过MODEM进行远程通讯程序设计时,必不可少的是调用lineInitialize()函数以初始化TAPI。lineInitialize()分配为支持逻辑线路设备的使用而必须的内部资源。它还登记了一个回调函数,使操作系统通过该函数将有关线路状态的消息返回给程序。
3.lineShutDown(hTAPI):关闭TAPI。它是与lineInitialize()相对应的函数,当程序使用完线路设备后,应调用lineShutDown(hTAPI)以释放为线路设备分配的资源。
4.lineNegotiateAPIVersion(hAPI,dwDeviceID,dwLowVersion,dwHighVersion,&dwVersionToUse,&extensions):协商TAPI版本号。TAPI是需要版本协商的一种Win32 API。版本协商确保当安装了新版本的TAPI驱动程序时,程序仍能安全正常运行。
5.lineGetDevCaps(hAPI,dwLine,dwVersionToUse,dwExtVersion,&linedevcaps):返回指定逻辑线路设备的能力。
6.LONG lineGetDevConfig (DWORD dwDeviceID, LPVARSTRING lpDeviceConfig, LPCTSTR lpszDeviceClass):返回指定逻辑线路设备的缺省配置。
7.LONG lineSetDevConfig (DWORD dwDeviceID, LPVOID const lpDeviceConfig, DWORD dwSize, LPCTSTR lpszDeviceClass):设置指定媒体流设备。
8.LONG lineConfigDialogEdit (DWORD dwDeviceID, HWND hwndOwner, LPCTSTR lpszDeviceClass, LPVOID const lpDeviceConfigIn, DWORD dwSize, LPVARSTRING lpDeviceConfigOut):显示一个允许用户改变逻辑线路设备的配置数据的对话框。
9.LONG lineOpen(HLINEAPP hLineApp, DWORD dwDeviceID, LPHLINE lphLine, DWORD dwAPIVersion, DWORD dwExtVersion, DWORD dwCallbackInstance, DWORD dwPrivileges, DWORD dwMediaModes, LPLINECALLPARAMS const lpCallParams):打开指定逻辑线路设备,并提供后续监视和控制线路的功能。调用lineInitialize()函数初始化TAPI后,就可以调用lineOpen()函数以打开线路。
10.LONG lineClose(HLINE hLine):关闭指定逻辑线路设备。
11.LONG lineMakeCall(HLINE hLine, LPHCALL lphCall, LPCTSTR lpszDestAddress, DWORD dwCountryCode, LPLINECALLPARAMS const lpCallParams): 初始化输出拨号,进行呼叫,并返回线路句柄以进行数据传输。
12.LONG lineDrop(HCALL hCall, LPCTSTR lpsUserUserInfo, DWORD dwSize):收线或断开呼叫。当程序结束呼叫时,需要调用lineDrop()函数以断开或中止呼叫。
13.LONG lineDeallocateCall(HCALL hCall):释放系统为呼叫分配的内存。当程序结束呼叫时,除了需要调用lineDrop()函数以断开或中止呼叫外,还调用lineDeallocateCall()函数以释放系统为呼叫分配的内存,调用lineClose()函数以关闭线路,调用lineShutdown()函数以将程序与TAPI断开。
总之,在通过MODEM进行远程通讯程序设计时,必不可少的是要调用lineInitialize()函数以初始化TAPI,以及调用lineOpen()函数以打开线路,调用lineMakeCall()函数以进行拨号。当程序拨号成功后,就可以使用由lineMakeCall()函数返回的线路句柄进行数据传输了!除此之外,为了编写可靠的远程通讯程序,还应调用有关检测线路设备能力的函数,检测TAPI版本兼容性的函数,检测线路当前使用状态的函数等以适应各种情况。当程序结束呼叫时,需要调用lineDrop()函数以断开和中止呼叫,调用lineDeallocateCall()函数以释放系统为呼叫分配的内存,调用lineClose()函数以关闭线路,调用lineShutdown()函数以将程序与TAPI断开。

下面结合主要代码说明以上描述的过程。
#include <tapi.h> //TAPI.DLL 头文件
#define TAPI_VERSION_2_0 0x00020000
#define TAPI_CURRENT_VERSION TAPI_VERSION_2_0
typedef struct tagLINEINFO
{
HLINE hLine;// 由lineOpen()返回的线路句柄
BOOL bVoiceLine;
DWORD dwAPIVersion,dwNumOfAddress,dwPermanentLineID;
TCHAR szLineName[256];
} LINEINFO, *LPLINEINFO;
......
BOOL CALLBACK MainWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
...
// 初始化TAPI
if (InitializeTAPI ())
{
if (g_hLineApp)
lineShutdown (g_hLineApp);//关闭TAPI!
DestroyWindow (hwnd);
}
return TRUE;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_DIAL:
{
......
// 拨号!
MakePhoneCall (g_szCurrentNum); return TRUE;
}
}
break;
}
default:
return DefWindowProc (hwnd, uMsg, wParam, lParam);
}
return FALSE;
}
//初始化TAPI函数:
DWORD InitializeTAPI ()
{
DWORD dwLineID, dwReturn, dwTimeCount = GetTickCount ();
//初始化TAPI
while ( (dwReturn = lineInitialize (&g_hLineApp, g_hInst, (LINECALLBACK) lineCallbackFunc, g_szAppName,
&g_dwNumDevs)) == LINEERR_REINIT)
{
if (GetTickCount () > 5000 + dwTimeCount)
{
if (MessageBox (g_hwndMain, TEXT("不能初始化..."),
EXT("警告"), MB_OKCANCEL) == IDOK)
break;
dwTimeCount = GetTickCount ();
}
}
if (dwReturn)
return dwReturn;
if (g_dwNumDevs == 0)
{
return LINEERR_NODEVICE;
}
if (! (g_lpLineInfo = (LPLINEINFO) LocalAlloc (
LPTR,
sizeof (LINEINFO) * g_dwNumDevs)))
{
return LINEERR_NOMEM;
}
for (dwLineID = 0; dwLineID < g_dwNumDevs; ++dwLineID)
{//获取线路信息
GetLineInfo (dwLineID, &g_lpLineInfo [dwLineID]);
}
return ERR_NONE;
}

//拨号函数!
VOID MakePhoneCall (LPCTSTR lpszPhoneNum)
{
DWORD dwReturn,
dwSizeOfTransOut = sizeof (LINETRANSLATEOUTPUT),
dwSizeOfCallParams = sizeof (LINECALLPARAMS);
LPLINECALLPARAMS lpCallParams = NULL;
LPLINETRANSLATEOUTPUT lpTransOutput = NULL;
TCHAR szDialablePhoneNum[TAPIMAXDESTADDRESSSIZE + 1] = {'\0'};
g_MakeCallRequestID = 0;
//打开线路
if (dwReturn = lineOpen (g_hLineApp,
g_dwCurrentLineID,
&g_CurrentLineInfo.hLine, g_CurrentLineInfo.dwAPIVersion, 0, 0, LINECALLPRIVILEGE_NONE,
0, NULL))
{
goto exit;
}
......
//拨号:
g_MakeCallRequestID = lineMakeCall (g_CurrentLineInfo.hLine,
&g_hCall,
szDialablePhoneNum,
0,
lpCallParams);
if (g_MakeCallRequestID > 0)
{
g_bCurrentLineAvail = FALSE;
DialogBoxParam (g_hInst,
MAKEINTRESOURCE(IDD_DIALING),
g_hwndMain,
(DLGPROC) DialingProc, 0);
}
else
{//关闭线路
CurrentLineClose ();
}

......
return;
}

//为确定线路的状态,发生的事件而必须提供的回调函数,
VOID CALLBACK lineCallbackFunc (DWORD hDevice,
DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
LPTSTR lpszStatus;
switch (dwMsg)
{
case LINE_CALLSTATE:
if (g_hCall != (HCALL) hDevice) return;
switch (dwParam1)
{
case LINECALLSTATE_DIALING:
lpszStatus = TEXT("拨号");
break;
case LINECALLSTATE_PROCEEDING:
lpszStatus = TEXT("拨号完毕,正在呼叫")
break;
case LINECALLSTATE_CONNECTED:
lpszStatus = TEXT("线路接上");
break;
case LINECALLSTATE_BUSY:
ErrorBox (TEXT("线路忙, 关闭"));
CurrentLineClose ();
if (g_hwndDial)
SendMessage (g_hwndDial, WM_COMMAND, MAKEWPARAM(IDOK,0), 0);
break;
......
}
break;
default:
break;
}
}

//关闭当前线路函数:
VOID CurrentLineClose ()
{
if (g_CurrentLineInfo.hLine)
lineClose (g_CurrentLineInfo.hLine);
g_CurrentLineInfo.hLine = NULL;
g_bCurrentLineAvail = TRUE;
g_hCall = NULL;
}
//获取线路信息
DWORD GetLineInfo (DWORD dwLineID, LPLINEINFO LineInfo)
{
DWORD dwSize, dwReturn;
LPTSTR lpszLineName = NULL;
LPLINEDEVCAPS lpLineDevCaps = NULL;
//协商TAPI版本号
if (dwReturn = lineNegotiateAPIVersion (
g_hLineApp, dwLineID, TAPI_VERSION_1_0, TAPI_CURRENT_VERSION, &(lpLineInfo->dwAPIVersion), NULL))
{
goto exit;
}
dwSize = sizeof (LINEDEVCAPS);
do
{
if (!(lpLineDevCaps = (LPLINEDEVCAPS) LocalAlloc (LPTR, dwSize)))
{
dwReturn = LINEERR_NOMEM;
goto exit;
}
lpLineDevCaps->dwTotalSize = dwSize;
//获取指定逻辑线路设备的能力。
if (dwReturn = lineGetDevCaps (g_hLineApp,
dwLineID, lpLineInfo->dwAPIVersion, 0, lpLineDevCaps))
{
goto exit;
}
if (lpLineDevCaps->dwNeededSize <= lpLineDevCaps->dwTotalSize)
break;
dwSize = lpLineDevCaps->dwNeededSize;
LocalFree (lpLineDevCaps);
lpLineDevCaps = NULL;
} while (TRUE);
lpLineInfo->dwPermanentLineID = lpLineDevCaps->dwPermanentLineID;
......
dwReturn = ERR_NONE;
exit:
if (lpLineDevCaps)
LocalFree (lpLineDevCaps);
if (lpszLineName)
LocalFree (lpszLineName);
return dwReturn;
}

以上代码在Visual C++ 5.0及WCE Toolkit for Visual C++ 5.0下调试通过。

本栏文章均来自于互联网,版权归原作者和各发布网站所有,本站收集这些文章仅供学习参考之用。任何人都不能将这些文章用于商业或者其他目的。( Pfan.cn )

编程爱好者论坛

本栏最新文章