TAPI

来源:百度文库 编辑:神马文学网 时间:2024/04/28 17:28:32
二、通信过程描述  
  ----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,写操作就算完成了。