TAPI(Telphony Application Programming Interface)可以称作电话编程接口,它是微软提供的计算机和电话网相联系的编程接口,使程序员可以利用这个接口通过电话线使用多种计算机复杂的通讯工作。TAPI能提供的功能主要有:自动拨号;以文件、传真、电子邮件的方式传送文件;访问Internet或其他形式信息服务、组织会议呼叫、使用主叫识别处理入呼叫、计算机间通过电话线的互相协作。这里提到的有的功能很早就已经广泛应用了,还有一些由于我国交换设备不提供此类功能很少问津。这里笔者不想对TAPI能实现什么功能多加讨论,只就编程谈一点体会。
促使笔者利用TAPI编写程序主要是由于工作中经常需要发传真,而每个传真都需要确认,如果电话是功能最少的那种,每次都要花大量的时间在上面。如果确认时再没人应答,之后再来一次,效率就更低了。因此,笔者想到利用TAPI编写一个程序能用MODEM自动拨号,通话结束后记录一下内容,省了每次辛苦地看着号码拨号。下面将程序与读者共享。
笔者利用delphi作为开发工具,原因主要是delphi开发程序方便快捷,同时数据库的使用比较简单。使用过Visual C++的朋友都知道它的帮助是十分详细的,笔者开发的许多程序都参考了其中的WIN32 SDK帮助,尤其是在编写TAPI程序时,仔细地研究了其中的TAPI部分。使用VC编写TAPI程序可以利用现成的“tapi.h”头文件,但使用delphi就需要将C++版本的头文件翻译成Object Pascal的格式,再将“tapi32.dll”中的函数引用到头文件中来。幸好这项工作不用我们去做,从Inprise公司的站点可以获得delphi 关于TAPI编程的头文件,省了不少事,笔者的头文件tapi.pas是Davide Moretti编写的,如果需要的朋友可以通过报社和笔者联系。
首先介绍程序中用到的几个函数:lineInitialize、lineNegotiateAPIVersion、lineOpen、lineMakeCall、lineHandOff、lineShutDown、lineConfigDialog、lineCallBackProc、lineGetIcon。函数的使用过程依次是:lineInitialize初始化线路、lineNegotiateAPIVersion确定TAPI版本号、lineOpen 打开线路、lineConfigDialog显示线路设备属性、lineMakeCall自动拨出电话号码、
lineGetIcon获得线路设备图标的句柄、lineHandOff将MODEM控制线路权转到用户、lineShutDown关闭线路。这里所用的函数名和tapi32.dll中输出的函数名相同。
由于delphi和VC中类型定义的区别,笔者有必要举例说明函数定义在VC和delphi中的区别。以lineInitialize为例,在VC中定义为
LONG lineInitialize(LPHLINEAPP lphLineApp,HINSTANCE hInstance,LINECALLBACK lpfnCallback,LPCSTR lpszAppName,LPDWORD lpdwNumDevs)。lphLineApp是线路TAPI应用的句柄指针,hInstance是实例指针,lpfnCallback指向呼叫返回处理函数,lpszAppName指向用户提供的应用程序名字符串,lpdwNumDevs指向可供使用的线路设备个数。在delphi文件tapi.pas中对此函数的定义如下:
function lineInitialize(var lphLineApp: THLineApp;hInstance: HInst;
lpfnCallback: TLineCallback;
lpszAppName: PChar;var lpdwNumDevs: Longint): Longint;
其中我们定义THLineApp、TLineCallback为指向Longint的指针(&LongInt)。其他函数的定义类推,分别用delphi中的相应类型代替,如果是长指针如句柄指针可以用“&LongInt”来定义。
下面介绍程序的代码部分,在此笔者假定读者具备delphi数据库和一般编程基础。首先用Database Desktop建立两个Paradox7表,分别为“workphone.db”和“friendphone.db”。在主form上放置TTable、TDataSource、TDBGrid、TPopupMenu控件。程序定义sAppDir:string保存应用程序所在目录,设置sAppDir:=GetCurrentDir。在弹出式菜单中定义两个过程分别在TDBGrid中载入两个数据库表。
procedure TMainForm.friendphoneClick(Sender: TObject);
begin
MainForm.Table1.Active:=False;
MainForm.Table1.TableName:=sAppDir+‘friendphone.db’;
MainForm.Caption:=‘自动拨号——朋友电话’;
MainForm.Table1.Active:=True;
end;
procedure TMainForm.selectclassmateClick(Sender: TObject);
begin
MainForm.Table1.Active:=False;
MainForm.Table1.TableName:=sAppDir+‘workphone.db’;
MainForm.Caption:=‘自动拨号——工作电话’;
MainForm.Table1.Active:=True;
end;
将TDBGrid的Options属性中的dgEditing设为True,允许用户直接修改表中的内容。在弹出菜单中定义DialPhone和DeleteRecord分别用于拨打电话号码和删除当前记录。下面主要介绍拨打电话的TAPI应用部分。电话号码是通过 MainForm.Table1.Fields.Fields[0].AsString作为字符串来传递的。
实现的代码部分如下,其中为了文章不会太长笔者省去了不必要的错误提示和部分代码。
变量定义
lineApp:THLineApp;//TAPI应用句柄
line:THLine;//线路句柄
call:THCall;//呼叫句柄
CallParams:TLineCallParams;//呼叫参数
nDevs,tapiVersion,errorcode:Longint;//线路设备数、TAPI版本号、错误代码
extid:TLineExtensionID;//TAPI扩展版本号
lineIcon:HICON;//线路设备图标
过程定义
procedure lineCallback(hDevice, dwMsg, dwCallbackInstance,
dwParam1, dwParam2, dwParam3: LongInt);//异步呼叫返回处理函数
{$IFDEF WIN32}
stdcall;
{$ELSE}
export;
{$ENDIF}
var
hCall: THCall;
begin
if dwMsg = LINE_REPLY then { LineMakeCall的响应结果}
if dwParam2 < 0 then {呼叫响应错误处理};
else if dwMsg = LINE_CALLSTATE then { 呼叫状态信息 }
begin
hCall := THCall(hDevice); //类型转换 THCall为LongInt类型
case dwParam1 of
LINECALLSTATE_IDLE: {呼叫无效处理}
if hcall <> 0 then
begin
lineDeallocateCall(hCall);
end;
LINECALLSTATE_PROCEEDING:{呼叫正常处理}
LINECALLSTATE_DIALTONE:{检测到拨号音}
LINECALLSTATE_DIALING: {正在拨号}
LINECALLSTATE_DISCONNECTED:{连接已断开}
begin{断开原因}
if dwParam2=LINEDISCONNECTMODE_NORMAL then{正常断开}
else if dwParam2=LINEDISCONNECTMODE_BUSY then {线路忙}
LINECALLSTATE_BUSY:{线路忙处理}
end;
end;
procedure TDialForm.(dialnumber:string);//dialnumber为电话号码字符串
begin
with CallParams do {CallParams的类型参考VC中的定义,将DWORD改为LongInt}
begin
dwTotalSize := sizeof(CallParams);
dwBearerMode := LINEBEARERMODE_VOICE;//承载模式为语音
dwMediaMode:=LINEMEDIAMODE_INTERACTIV
EVOICE;//媒体模式为交互式语音
end;
if lineInitialize(lineApp,HInstance,lineCallBack,nil,nDevs)<0
then //线路不能初始化处理
else
if nDevs=0 then //无TAPI线路设备
begin
lineShutDown(lineApp);
lineApp:=0;
end
else
if lineNegotiateAPIVersion(lineApp,0,$00010004,$00020000,
tapiVersion,extid)<0 //协商TAPI版本号 TAPI1.4~TAPI2.0
then begin {TAPI版本不兼容}
lineShutDown(lineApp);
lineApp:=0;
end
else
errorcode:=lineOpen(lineApp,LINEMAPPER,line,tapiVersion,0,0,
LINECALLPRIVILEGE_NONE,LINEMEDIAMODE_
INTERACTIVEVOICE,@CallParams);//打开线路
if errorcode<0 then {线路不能打开}
else
begin{线路打开成功}
lineConfigDialog(0,Self.Handle,nil); //显示线路设备属性
lineGetIcon(0,`tapi/line`,lineIcon);
//lineIcon为线路设备图标句柄,可以赋值给TICON.Handle
if lineMakeCall(line,call,PChar(dialnumber),0,@CallParams)<0 then{呼叫失败处理}
else
lineHandOff(call,`电话拨号程序`,LINEMEDIAMODE_INTERACTIVEVOICE);
//在程序中应当以按钮来触发lineHandOff,从TAPI控制转为人工控制电话
end;
end;
以上代码由于脱离了实际的程序显得有些凌乱,相信对delphi编程稍有了解的朋友很容易看懂。本程序运行后的效果是,首先出现一个数据库表格,用右键打开一个通讯录库文件,右键选择拨号弹出对话框显示线路处理的状态,在拨完号之后提示用户是否拿起听筒进行对话。整个过程类似系统自带的电话拨号程序,不过笔者介绍的TAPI方法可以让我们编写更多的功能和更友好的界面。今后笔者将针对TAPI编程进行深入的介绍,希望能对不太了解TAPI的朋友在开发程序时起到帮助。