Win98下TAPI的调制解调器编程

调制解调器编程 专栏收录该内容
1 篇文章 0 订阅
一、简介:


 随着Win98操作系统的普及,计算机之间的通信已经成为大多数应用程序开发的要求,这其中的主要原因是Win98不仅仅能够支持大多数硬件,而且为硬件的操作提供了方便的编程接口(API),Win98的应用程序接口(API)提供的通信手段大致分为以下几类:1基于TCP/IP协议的WinsockAPI,可实现局域网上或互联网上的微机通信;2基于进程之间的通信技术:动态数据交换(DDE);3基于直接电缆连接的通信技术,可直接操作串行口、并行口以及远红外线接口;4基于电话线路的通信应用程序接口(TAPI/TelephonyAPI),可方便地控制调制解调器;从目前的发展状况看来,调制解调器已经成为远距离通信的一种重要工具,为此Microsoft及Intel公司联合开发了TAPI这样一个编程接口,而且,使用API函数编制的程序段既适用于BorlandC++编译器,同时也能插入VisualC++程序中编译运行,作为Win98的应用程序编制人员,学会使用TAPI编程操作调制解调器通过电话线路进行通信这一技术是很有必要的。下面就TAPI编程进行讨论:


 二、通信过程描述


 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是应用程序发送、接收数据的编程接口。


 四、程序流程结构框图


 由于Win98为多任务操作系统,我们的流程图只能代表本应用程序的执行先后关系,程序中的等待及检测实际上是等待Win98提供的消息,所以并不占用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,写操作就算完成了。

 
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值