TAPI

来源:百度文库 编辑:神马文学网 时间:2024/06/13 12:29:30
二、通信过程描述
----1初始化线路(通信双方都应该初始化线路)
----通过使用lineInitialize函数初始化TAPI.DLL得到TAPI使用句柄的指针hTapi,请注意参数中回调函数的定义(所有提及函数的用法均可从BC++5.0及VisualC++5.0的帮助中获得);通过调用lineOpen函数(用到参数hTapi)获得线路句柄hLine;再利用lineGetID(用到参数hLine)获取调制解调器句柄hModem
----2配置线路(可选)
----
----调用SetCommConfig(用到hModem)改变调制解调器的设置
----3拨号(由呼叫方执行)
----使用lineMakeCall函数(用到hLine)进行拨号,完成后获得呼叫句柄hCall(呼叫方的呼叫句柄)
----4应答链接(由被呼叫方执行)
----被呼叫的一方的回调函数得到LINECALLSTATE_OFFERING消息时,调用lineAnswer函数实现自动应答(呼叫句柄hCall由回调函数的参数给出)
----5数据通信(双方)
----当回调函数收到LINECALLSTATE_CONNECTED消息后,请先清除接收缓冲区,可以使用函数为WriteFile及ReadFile函数进行数据交换,注意参数hFile为调制解调器句柄hModem
----6挂机(某一方)
----通信完毕任何一方都可以调用函数lineDrop(hCall,NULL,0)来停止呼叫,该函数还发送LINECALLSTATE_IDLE消息给回调函数
----7关闭线路(双方)
----通信双方的回调函数在收到LINECALLSTATE_IDLE消息时都应该调用函数lineDeallocateCall(hCall)释放相应呼叫占用的资源;当回调函数收到LINECALLSTATE_DISCONNECTED消息时请使用lineClose(hLine)释放由lineOpen分配的资源,调用lineShutDown(hTapi)释放为线路设备分配的资源
三、软硬件环境
----下图示意出了我们的应用程序所处的位置以及涉及到的软硬件环境:
----我们的通信应用程序通过TAPI操作Modem拨号、应答、链接、挂机控制电话呼叫,在编制DOS应用程序的时候,我们经常使用Hayes兼容的AT命令集来完成这些操作,由于各调制解调器厂家对该命令集都做了各自的扩展,因而,我们的DOS应用程序一般只能操作一小部分调制解调器,而各厂家都提供Windows驱动程序,所以,使用TAPI编制的应用程序能够操作绝大多数调制解调器;图中的通信API是应用程序发送、接收数据的编程接口。
四、程序流程结构框图
----由于Win95为多任务操作系统,我们的流程图只能代表本应用程序的执行先后关系,程序中的等待及检测实际上是等待Win95提供的消息,所以并不占用CPU时间,在下面的程序中可以看出。另外,数据交换的协议可由自己制定,也可使用已有的协议。
五、软件编制
----由于Windows编程的框架基本相同,在此我们只介绍涉及到通信的一部分源程序:
----1头文件中应该包括:
----#include
----请注意工程文件的属性应该是Windows32位应用程序
----2通信所涉及到的一些全局变量定义及类型定义:
charRecBuf[20],buf[20]//缓冲区
DWORDError;   //错误码
COMSTATStatus;   //状态码
DWORDNumLine;   //允许使用的线路设备数
LINECALLPARAMSpara;//呼叫参数
TmyDecFrame*pwin=NULL;//主窗口指针
HLINEAPPmyhTapi;//线路应用程序句柄
HLINEmyhLine;//线路句柄
HANDLEmyhModem;//调制解调器句柄
HCALLmyhCall;//呼叫句柄
typedefstructtagModemID{
HANDLEhModem;
charModemName[1];
}ModemID;
----3下面为获取调制解调器句柄的函数定义
----因为每个调制解调器的标志字符串长度不一,所以函数中用到了可变长度的字符串,处理方法是先为字符串指针分配sizeof(VARSTRING)大小的空间,再利用该空间容纳调用LineGetID时Windows返回的信息,根据返回信息判断所需空间大小重新分配空间,再次调用LineGetID就可以取得完整的标志字符串。
voidGethModem(HLINEhLine)
{   ModemIDfar*mid;
VARSTRING*str;
LONGlid;
DWORDsize;
charmark=1;
str=(VARSTRING*)malloc(sizeof(VARSTRING));
if(!str)
returnNULL;
str->dwTotalSize=sizeof(VARSTRING);
do
{   if((lineGetID(myhLine,0,NULL,LINECALLSELECT_LINE,str,
"comm/datamodem")==0)&&(str->dwTotalSizedwNeededSize))
{   dwSize=str->dwNeededSize;
free(str);
str=(VARSTRING*)malloc(dwSize);
if(!str)
{   myhModem=NULL;
mark=2;
}
str->dwTotalSize=dwSize;
}
elsemark=0;
}while(mark==1);
if(mark==0)
{   mid=(ModemIDfar*)((LPSTR)str+str->dwStringOffset);
myhModem=mid->hModem;
}
free(str);
}
----4在主窗口初始化函数中加入对线路的初始化过程:
pwin=this;//获得主窗口指针
while(lineInitialize(&myhTAPI,GetModule()->GetInstance(),
(LINECALLBACK)MakeProcInstance((FARPROC)lpfnCallback,
GetModule()->GetInstance()),"TRY",&NumLine)==LINEERR_REINIT)
{   sleep(1);//延迟   };
Error=lineOpen(hTAPI,0,&HLine,0x10004,0,0,LINECALLPRIVILEGE_MONITOR+
LINECALLPRIVILEGE_OWNER,LINEMEDIAMODE_DATAMODEM,NULL);
if(Error!=0)
{   sprintf(buf,"%lx",Error);
MessageBox(buf,0,MB_OK);   }
else
{   GethModem(myhLine);//取得myhModem的值
if(myhModem!=NULL)
{   para.dwBearerMode=LINEBEARERMODE_VOICE;
para.dwMediaMode=LINEMEDIAMODE_DATAMODEM;
para.dwTotalSize=sizeof(LINECALLPARAMS);
Error=lineMakeCall(myhLine,&myhCall,"8880751",0,?);
If(Error!=0)
{   sprintf(buf,"%lx",Error);
MessageBox(buf,0,MB_OK);   }
}
}
}
----5呼叫方回调函数的定义
voidfarpascalTMyDecFrame::lpfnCallback
(DWORDhDevice,DWORDdwMsg,
DWORDdwCallbackInstance,
DWORDdwParam1,DWORDdwParam2,
DWORDdwParam3)//
参数定义同lineCallbackFunc函数中的参数定义
{   intRec_num=0;
switch(dwParam1)
{   caseLINECALLSTATE_CONNECTED:
DWORDlen;
ClearCommError(myhModem,&Error,&Status);
Rec_num=Status.cbInQue;
ReadFile(myhModem,RecBuf,Rec_num,&len,0);
//至此已经为数据通信做好了前期准备,可设立标志
WriteFile(myhModem,"Success",7,&len,0);
ReadFile(myhModem,RecBuf,8,&len,0);
pwin->MessageBox(RecBuf,0,MB_OK);
break;
caseLINECALLSTATE_IDLE:
lineDeallocateCall(myhCall);
break;
caseLINECALLSTATE_DISCONNECTED:
lineClose(myhLine);
lineShutDown(myhTapi);
break;
}
}
----6被叫方回调函数的定义
voidfarpascalTMyDecFrame::lpfnCallback(DWORDhDevice,DWORDdwMsg,
DWORDdwCallbackInstance,DWORDdwParam1,DWORDdwParam2,
DWORDdwParam3)
{intRec_num=0;
switch(dwParam3)
{   caseLINECALLPRIVILEGE_OWNER:
myhCall=(HCALL)hDevice;
Break;
}//只有对呼叫具有私有特权的调用者才能应答呼叫,
在此获得呼叫句柄
switch(dwParam1)
{   caseLINECALLSTATE_CONNECTED:
DWORDlen;
ClearCommError(myhModem,&Error,&Status);
Rec_num=ComS.cbInQue;
ReadFile(myhModem,RecBuf,Rec_num,&len,0);//清除接收缓冲区
ReadFile(myhModem,RecBuf,7,&len,0);
WriteFile(myhModem,"Received",8,&len,0);
pwin->MessageBox(RecBuf,0,MB_OK);
break;
caseLINECALLSTE_OFFERING:
lineAnswer(myhCall,NULL,0);
break;//完成自动应答
caseLINECALLSTATE_IDLE:
lineDeallocateCall(myhCall);
break;
caseLINECALLSTATE_DISCONNECTED:
lineClose(myhLine);
lineShutDown(myhTapi);
break;
}
}
六、改进措施
----以上程序中使用的是同步读写方式,只要WriteFile或者ReadFile没有完成指定的I/O任务,它们就不会返回进程,在许多情况下,这是令人难以容忍的CPU时间浪费;改进的办法是在每次读之前采用ClearCommError函数确定系统的串行口缓冲区中到底有了多少字节的接收数据,而写方式采用异步方式,首先应该定义一个OVERLAPPED结构,从BC++5.0中获得的结构定义如下:
typedefstruct_OVERLAPPED{//o
DWORDInternal;
DWORDInternalHigh;
DWORDOffset;
DWORDOffsetHigh;
HANDLEhEvent;
}OVERLAPPED;
----我们定义OVERLAPPEDmyOVLP;
----我们只用到了其中的hEvent成员,其他成员均置0;hEvent设置为CreateEvent(NULL,TRUE,FALSE,NULL)产生的事件句柄;然后如下调用WriteFile(myhModem,"Received",8,&len,&myOVLP);
----函数将立即返回,此后,只要GetOverlappedResult函数返回TRUE,写操作就算完成了。