严重征求用 modem 和声卡打电话的代码 (100分)

  • 主题发起人 xiao.lit
  • 开始时间
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
用 AT 指令实现使用 modem 和声卡打电话的代码。
如果有能用的控件的代码也可以,主要是想研究一下 AT 指令集
高分感谢
(收到后另开贴子给分)
 
J

jsxjd

Unregistered / Unconfirmed
GUEST, unregistred user!
下面应该有所拨号的代码,不知道能不能满足要求?

在Delphi中程序管理拨号网络
用MODEM拨号上网,仍是大多数个人网民选择上网的方式.如果能在我们的应用程序中管理拨号网络(如Foxmail、Sygate中的拨号功能),无疑将会方便我们的软件用户(不用再切换应用程序,运行拨号网络),提高我们的软件的友好性从而提高软件的竞争力.
在WIN9X下,如果安装了拨号网络,则在WINDOWS系统的系统目录System下将有两个拨号网络管理程序库RasApi32.DLL和RasApi16.DLL,我们可利用其中的函数来获取和设置拨号连接网络的信息。在Delphi帮助文件中,有相关函数的说明。
  在此,我们要讨论的管理项目有:
    1、获取当前系统中可用的拨号连接名称
    2、新建拨号连接、修改拨号连接的属性
    3、获取和设置拨号连接的拨号参数
    4、用指定的拨号连接拨号、挂断指定的拨号连接
    5、获取当前活动的连接及其连接状态
一、获取当前系统中可用的拨号连接名称
即获取系统中已建立的拨号连接的名称,可用来让用户选择使用哪个拨号连接进行拨号.
可以用两种方法来实现.一种是利用RasAPI函数;另一种是不用RasAPI函数,直接在注册表中查询.
1、不用RasAPI函数,直接在注册表中查询
在注册表的HKEY_USERS/.Default/RemoteAccess/Addresses下,列出了已经在拨号网络中建立了的拨号连接的名称及其属性设置,其中各项目的名称即为可用的拨号连接的名称;各项目的值即为各拨号连接的属性设置,不过是二进制串,笔者还看不懂.由此可见,我们只要读出各项目的名称即可获取当前系统中可用的拨号连接名称.
var
registryTemp : TRegistry;
stringsTemp : TStringlist;
begin
registryTemp := TRegistry.Create;
stringsTemp := TStringlist.Create;
with registryTempdo
begin
RootKey := HKEY_USERS;//根键设置为HKEY_USERS
//如果存在子键.Default/RemoteAccess/Addresses
if OpenKey(
'.Default/RemoteAccess/Addresses',false)
then
GetValueNames( stringsTemp );
//读出各项目的名称,即拨号连接名称
end;
combobox1.Items.assign( stringsTemp );
//显示,供选择
end;

2、用RasAPI函数
RasAPI函数RasEnumEntries可获取当前系统中可用的拨号连接名称.其函数原型为
function RasEnumEntries(
reserved : PChar;//保留字段,必须为NIL
lpszPhonebook : PChar;
//电话本名称,在Win9X下无作用,可为空字符串
lprasentryname: LPRASENTRYNAME;
//接收拨号连接名称的缓冲区,
//是一个RASENTRYNAME类型数组的指针
var lpcb : DWORD;
//接收拨号连接名称的缓冲区的大小(Bytes)
var lpcEntries: DWORD
//实际获得的拨号连接名称的数目
):DWORD;
stdcall;
function RasEnumEntries;external
RasApiDll name 'RasEnumEntriesA';
参数lprasentryname提供了一个RASENTRYNAME类型数组的指针,指向一个接收拨号连接名称的缓冲区,其中RASENTRYNAME及LPRASENTRYNAME的类型说明如下:
LPRASENTRYNAME = ^RASENTRYNAME;
RASENTRYNAME = record
dwSize : DWORD;
//该结构所占内存的大小(Bytes),
//一般设置为SizeOf(RASENTRYNAME)
szEntryName:array[0..RAS_MaxEntryName] of char;
//拨号连接名称
end;

lpcb为缓冲区的大小,一般设置为dwSize的倍数,倍数为可能有的连接的个数.
lpcEntries实际的连接的个数.
下面是一个应用例子,列出了当前系统中可用的拨号连接名称.注意,应在RASENTRYNAME缓冲区的第一个RASENTRYNAME结构中设置dwSize.
const
MaxPhoneEntries = 10;//最多的拨号连接数目
var
intIndex : integer;
PhoneEntries : array[ 0..MaxPhoneEntries - 1 ]
of RASEntryName;
dwSize,dwEntries,dwResult : DWORD;
begin
//在RASENTRYNAME缓冲区的
//第一个RASENTRYNAME结构中设置dwSize
PhoneEntries[ 0 ].dwSize := sizeof( RASEntryName );
dwSize:=MaxPhoneEntries*sizeof(RASEntryName);
//为缓冲区的大小
//调用RasAPI函数,获取当前系统中可用的拨号连接名称
dwResult := RasEnumEntries ( NIL,'',
@PhoneEntries[ 0 ],dwSize, dwEntries );
if dwResult <> 0 then
begin
//RasAPI函数,执行错误
memo1.lines.add('RasEnumEntries错误:' +
GetRasError( dwResult ));
exit;
end;

//显示当前系统中可用的拨号连接名称
memo1.lines.add('共有' +
inttostr( dwEntries ) + '个RAS连接,如下所示');
for intIndex := 0 to dwEntries -1do
memo1.lines.add(strpas(
PhoneEntries[ intIndex ].szEntryName ) );
end;

3、获取默认的拨号连接的名称
默认的拨号连接,即用户在浏览器中设置的拨号连接,该连接可以认为是用户最常用的拨号连接.
在注册表的HKEY_USERS/.Default/RemoteAccess位置,有一个字符串名Profile,它对应字符值即为HKEY_USERS/.Default/RemoteAccess/Addresses.
二、新建拨号连接、修改拨号连接的属性
RasAPI函数RasCreatePhonebookEntry、RasEditPhonebookEntry通过调用Win9X的新建拨号连接、修改拨号连接的设置界面,允许用户新建拨号连接、修改拨号连接,具体的设置操作还要由系统来完成.
1、新建拨号连接
新建拨号连接的RasAPI函数为RasCreatePhonebookEntry,其函数原型为:
function RasCreatePhonebookEntry(
hwnd : THandle;
//新建拨号连接窗口的父窗口的句柄,
//为NIL表示桌面(DeskTop)
lpszPhonebook: pchar;
//电话本名称,在Win9X下无作用,可为空字符串
):DWORD;stdcall;
function RasCreatePhonebookEntry;
external RasApiDll name 'RasCreatePhonebookEntryA';
函数返回值为0表示执行成功;否则为错误代码.
下面是一个应用例子,允许用户新建一个拨号连接.
var
dwResult : DWORD;
begin
//在当前窗口中新建拨号连接
dwResult := RasCreatePhonebookEntry( handle, '' );
if dwResult = 0 then
memo1.lines.add('新建拨号连接成功!')
else
memo1.lines.add('新建拨号连接失败:!' +
GetRasError( dwResult ))
end;

2、修改拨号连接的属性
修改拨号连接的属性的RasAPI函数为RasEditPhonebookEntry,其函数原型为:
function RasEditPhonebookEntry(
hwnd : THandle;
//新建拨号连接窗口的父窗口的句柄,
//为NIL表示桌面(DeskTop)
lpszPhonebook: pchar;
//电话本名称,在Win9X下无作用,可为空字符串
lpszEntryName: pchar;
//拨号连接的名称,如'163'、'169'等
):DWORD;
stdcall;
function RasEditPhonebookEntry;
external RasApiDll name 'RasEditPhonebookEntryA';
函数返回值为0表示执行成功;否则为错误代码.
下面是一个应用例子,允许用户修改指定拨号连接的属性.
var
dwResult : DWORD;
begin
//在当前窗口中修改拨号连接的属性
dwResult:=RasEditPhonebookEntry(handle,'','163');
if dwResult = 0 then
memo1.lines.add('修改拨号连接成功!')
else
memo1.lines.add('修改拨号连接失败:!' +
GetRasError( dwResult ))
end;

三、获取和设置拨号连接的拨号参数
用RasAPI函数RasGetEntryDialParams、RasSetEntryDialParams可以直接获取和设置指定拨号连接的拨号参数,其中包括用户名称和用户密码!
1、获取拨号连接的拨号参数
获取拨号连接的拨号参数RasAPI函数为RasGetEntryDialParams,其函数原型为:
function RasGetEntryDialParams(
lpszPhonebook: pchar;
//电话本名称,在Win9X下无作用,可为空字符串
pRASDIALPARAMS:LPRASDIALPARAMS;
//拨号参数,是一个RASDIALPARAMS类型的指针
var lpfPassword : WordBool
//是否需要用户密码
):DWORD;stdcall;
function RasGetEntryDialParams;
external RasApiDll name 'RasGetEntryDialParamsA';
参数pRASDIALPARAMS是一个RASDIALPARAMS类型的指针,指向一个拨号连接的拨号参数数据的缓冲区,其中RASDIALPARAMS及LPRASDIALPARAMS的类型说明如下:
LPRASDIALPARAMS = ^RASDIALPARAMS;
RASDIALPARAMS = record
dwSize : DWORD;
//该结构所占内存的大小(Bytes),
//一般设置为SizeOf(RASDIALPARAMS)
szEntryName:array[0..RAS_MaxEntryName] of char;
//拨号连接名称
szPhoneNumber:array[0..RAS_MaxPhoneNumber] of char;
//拨号号码
szCallbackNumber:array[0..RAS_MaxCallbackNumber]
of char;
//回叫号码
szUserName : array[0..UNLEN] of char;
//用户名称
szPassword : array[0..PWLEN] of char;
//用户密码
szDomain : array[0..DNLEN] of char;
//域名
end;

函数返回值为0表示执行成功;否则为错误代码.
下面是一个应用例子,获取指定拨号连接的拨号参数.
var
dwResult : DWORD;
RASDIALPARAMSData : RASDIALPARAMS;
NeedPWD : WordBool;
begin
//指定拨号连接的名称
with RASDIALPARAMSDatado
begin
dwSize := sizeof( RASDIALPARAMS );//结构大小
szEntryName := '163';//指定拨号连接的名称
szUserName := '';//其它五个参数初始化
szPassword := '';
szDomain := '';
szCallbackNumber := '';
szPhoneNumber := '';
end;
NeedPWD := true;//需要用户密码
//获取指定拨号连接的拨号参数
dwResult := RasGetEntryDialParams( '',
@RASDIALPARAMSData,NeedPWD );
if dwResult <> 0 then

//获取指定拨号连接的拨号参数失败
memo1.lines.add(
'获取'+
StrPAS(RASDIALPARAMSData.szEntryName )+
'拨号参数失败:'+
GetRasError( dwResult ))
else
begin
//显示指定拨号连接的拨号参数
memo1.lines.add(
StrPAS(RASDIALPARAMSData.szEntryName )+
'拨号参数如下');
memo1.lines.add( '用户名称:' + StrPAS(RASDIALPARAMSData.szUserName ));
memo1.lines.add( '用户密码:' +
StrPAS(RASDIALPARAMSData.szPassword ));
memo1.lines.add( '域 名:' +
StrPAS(RASDIALPARAMSData.szDomain ));
memo1.lines.add( '回叫号码:' +
StrPAS(RASDIALPARAMSData.szCallbackNumber ));
memo1.lines.add( '拨号号码:' +
StrPAS(RASDIALPARAMSData.szPhoneNumber ));
end;
end;

2、设置拨号连接的拨号参数
设置拨号连接的拨号参数RasAPI函数为RasSetEntryDialParams,其函数原型为:
function RasSetEntryDialParams(
lpszPhonebook: pchar;
//电话本名称,在Win9X下无作用,可为空字符串
pRASDIALPARAMS:LPRASDIALPARAMS;
//拨号参数,是一个RASDIALPARAMS类型的指针
var lpfPassword : WordBool//是否删除用户密码
) : DWORD;
stdcall;
function RasSetEntryDialParams;
external RasApiDll name 'RasSetEntryDialParamsA';
参数pRASDIALPARAMS的说明同RasGetEntryDialParams.
参数lpfPassword的含义与RasGetEntryDialParams不同,在此表示是否删除用户密码,为TRUE时将原来的用户密码删除.函数返回值为0表示执行成功;否则为错误代码.
下面是一个应用例子,设置指定拨号连接的拨号参数.
var
dwResult : DWORD;
RASDIALPARAMSData : RASDIALPARAMS;
RemovePWD : WordBool;
begin
//指定拨号连接的拨号参数
with RASDIALPARAMSDatado
begin
dwSize := sizeof( RASDIALPARAMS );//结构大小
szEntryName := '163';//指定拨号连接的名称
szUserName := 'MyName';//指定用户名称
szPassword := 'MyPassword';//指定用户密码
szDomain := 'MyDomain';//指定域名
szCallbackNumber := '';//指定回叫号码
szPhoneNumber := '';//指定拨号号码
end;
RemovePWD := false;//不需要删除用户密码
//设置指定拨号连接的拨号参数
dwResult := RasSetEntryDialParams( '',
@RASDIALPARAMSData,RemovePWD );
if dwResult <> 0 then

//设置指定拨号连接的拨号参数失败
memo1.lines.add( '设置'+
StrPAS(RASDIALPARAMSData.szEntryName )+
'拨号参数失败:'+ GetRasError( dwResult ))
else
begin
//显示指定拨号连接的拨号参数
memo1.lines.add(
StrPAS(RASDIALPARAMSData.szEntryName )+
'拨号参数设置成功');
memo1.lines.add( '用户名称:' +
StrPAS(RASDIALPARAMSData.szUserName ));
memo1.lines.add( '用户密码:' +
StrPAS(RASDIALPARAMSData.szPassword ));
memo1.lines.add( '域 名:' +
StrPAS(RASDIALPARAMSData.szDomain ));
memo1.lines.add( '回叫号码:' +
StrPAS(RASDIALPARAMSData.szCallbackNumber ));
memo1.lines.add( '拨号号码:' +
StrPAS(RASDIALPARAMSData.szPhoneNumber ));
end;
end;

四、用指定的拨号连接拨号、挂断指定的拨号连接
1、用指定的拨号连接拨号
拨号有两种方法,一种是调用拨号网络中的拨号程序,就象用户自己用鼠标双击拨号连接名称一样,用户可以修改拨号号码、用户名称和用户密码,最后由拨号网络来完成拨号过程;另一个方法则是调用RasAPI函数.
(1)用拨号网络中的拨号程序,在程序中可以用如下代码:
winexec('rundll32.exe rnaui.dll,RnaDial 169',
SW_SHOWNORMAL);
其中字符串中的最后一个参数“169”为拨号连接的名称。
(2)用RasAPI函数拨号
用拨号连接拨号的RasAPI函数为RasDial,其函数原型为:
function RasDial(
pRasDialExtensions : LPRASDIALEXTENSIONS;
//在WIN9X下无用,可设置为NIL
lpszPhonebook : PChar;
//电话本名称,在Win9X下无作用,可为空字符串
lpRasDialParams : LPRASDIALPARAMS;//拨号参数
dwNotifierType : DWORD;//消息通知方式
handle : TRasDialFunc;//消息处理事件
var lphRasConn : DWORD//返回的成功连接的连接句柄
):DWORD;
stdcall;
function RasDial;external RasApiDll name 'RasDialA';
参数pRASDIALPARAMS的说明同RasGetEntryDialParams.
参数dwNotifierType表示消息通知方式.在拨号过程中,系统发出拨号事件消息可以通知拨号进度,因而需要提供接受消息的方式和处理消息的函数.当其值为$FFFFFFFF时,则参数handle被视为一个窗口的句柄,事件消息被发生该窗口处理;当其值为0时,handle被视为TRasDialFunc类型的函数的指针;当其值为1时,handle被视为TRasDialFunc1类型的函数的指针.
参数handle表示消息处理函数指针,其类型可以THandle、TRasDialFunc、TRasDialFunc1.当该参数不为NULL或NIL时,其代表的消息处理函数指针将接受拨号进度通知消息;为NIL时,拨号过程由异步方式变为同步方式,直到拨号过程成功或失败后RasDial函数才返回.详细可参见Delphi帮助文件中关于RasDial函数的帮助。
当拨号连接成功时,lphRasConn将表示其连接句柄.函数返回值为0表示执行成功;否则为错误代码.下面是一个应用例子,按指定的拨号参数拨号.
var
RASDIALPARAMSData : RASDIALPARAMS;
dwResult : DWord;
RasCon : DWord;
begin
//指定拨号连接的拨号参数
with RASDIALPARAMSDatado
begin
dwSize := sizeof( RASDIALPARAMS );//结构大小
szEntryName := '163';//指定拨号连接的名称
szUserName := 'MyName';//指定用户名称
szPassword := 'MyPassword';//指定用户密码
szDomain := 'MyDomain';//指定域名
szCallbackNumber := '';//指定回叫号码
szPhoneNumber := '';//指定拨号号码
end;

//用指定的拨号参数拨号,采用同步拨号方式
dwResult := RasDial( NIL,'',
@RASDIALPARAMSData,0,NIL,RasCon );
if dwResult <> 0 then

//设置指定拨号连接的拨号参数失败
memo1.lines.add(
'用'+StrPAS(RASDIALPARAMSData.szEntryName )+
'拨号失败:'+ GetRasError( dwResult ))
else
memo1.lines.add(
'用'+StrPAS(RASDIALPARAMSData.szEntryName )+
'拨号成功!' );
end;

2、挂断指定的拨号连接
挂断拨号连接的RasAPI函数为RasHangUp,其函数原型为:
function RasHangUp(
hRasConn : DWORD
//要挂断的拨号连接的句柄
):DWORD;stdcall;
function RasHangUp;
external RasApiDll name 'RasHangUpA';
函数返回值为0表示执行成功;否则为错误代码.下面是一个应用例子,挂断由RasDial建立的拨号连接.
//挂断由RasDial建立的拨号连接
dwResult := RasHangUp( RasCon );
if dwResult <> 0 then
//挂断失败
memo1.lines.add( '挂断失败:' +
GetRasError( dwResult ))
else
memo1.lines.add( '挂断成功!');

五、获取当前活动的连接及其连接状态
1、获取当前活动的连接
获取当前活动的连接的RasAPI函数为RasEnumConnections,其函数原型为:
function RasEnumConnections(
var lprasconn : RASCONN ;
//接收活动连接的缓冲区的指针
var lpcb: DWORD;//缓冲区大小
var lpcConnections : DWORD//实际的活动连接数
):DWORD;
stdcall;
function RasEnumConnections;
external RasApiDll name 'RasEnumConnectionsA';
参数lprasconn提供了一个RASCONN类型数组的指针,指向一个接收活动连接的缓冲区,其中RASCONN的类型说明如下:
RASCONN = record
dwSize : DWORD;
//该结构所占内存的大小(Bytes),
//一般设置为SizeOf(RASCONN)
hrasconn : HRASCONN;//活动连接的句柄
szEntryName:array[0..RAS_MaxEntryName] of char;
//活动连接的名称
szDeviceType:array[0..RAS_MaxDeviceType] of char;
//活动连接的所用的设备类型
szDeviceName:array[0..RAS_MaxDeviceName] of char;
//活动连接的所用的设备名称
end;

参数lpcb为缓冲区大小(Bytes).
参数lpcConnections将返回实际的连接数目.
函数返回值为0表示执行成功;否则为错误代码.
2、获取指定连接的连接状态
获取指定连接的连接状态的RasAPI函数为RasGetConnectStatus,其函数原型为:
function RasGetConnectStatus(
hrasconn : HRASCONN;
//指定活动连接的句柄
lprasconnstatus : LPRASCONNSTATUS//连接状态参数
):DWORD;
stdcall;
function RasGetConnectStatus;
external RasApiDll name 'RasGetConnectStatusA';
连接状态参数lprasconnstatus是一个RASCONNSTATUS类型的指针,将返回连接状态参数.RASCONNSTATUS和LPRASCONNSTATUS的类型说明如下:
LPRASCONNSTATUS = ^RASCONNSTATUS;
RASCONNSTATUS = record
dwSize : DWORD;
//该结构所占内存的大小(Bytes),
//一般设置为SizeOf(RASCONNSTATUS)
rasconnstate : RASCONNSTATE;
//连接状态标识,一组DWORD类型数值的集合。
dwError : DWORD;//错误类型标识符
szDeviceType:array[0..RAS_MaxDeviceType] of char;
//活动连接的所用的设备类型
szDeviceName : array[0..RAS_MaxDeviceName] of char;
//活动连接的所用的设备名称
end;
函数返回值为0表示执行成功;否则为错误代码.
下面是一个应用例子,列出了当前系统中活动的连接的名称及其连接状态.注意,应在RASCONN缓冲区的第一个RASCONN结构中设置dwSize.
const
MaxConnections = 10;//最多的拨号连接数目
var
connections : array[0..MaxConnections-1] of RASCONN;
longSize : dword;
intAvailabelConnections : dword;
intIndex : integer;
dwResult : DWORD;
strTemp : string;
RASCONNSTATUSData : RASCONNSTATUS;
begin
connections[ 0 ].dwSize := sizeof(RASCONN);
//结构的大小
longSize := MaxConnections *
connections[ 0 ].dwSize;//缓冲区大小
intAvailabelConnections := 0;
//实际的活动连接的数目
//获取当前系统中活动的连接
dwResult := RasEnumConnections(
connections[ 0 ], longSize,
intAvailabelConnections );
if dwResult <> 0 then

//获取当前系统中活动的连接
memo1.lines.add(
'获取当前系统中活动的连接:' +
GetRasError( dwResult ))
else
begin
memo1.lines.add(
'当前系统中活动的连接' +
inttostr( intAvailabelConnections )+
'个,列举如下' );
for intIndex := 0 to
intAvailabelConnections - 1do
begin
strTemp := '连接名称:' +
StrPAS(connections[intIndex].szEntryName)+
'设备类型:' + StrPAS(
connections[ intIndex ].szDeviceType )+
' 设备名称:' + StrPAS(
connections[ intIndex ].szDeviceName );
//获取连接状态
dwResult := RasGetConnectStatus(
connections[ intIndex ].hRasConn,
@RASCONNSTATUSData );
if 0 <> dwResult then
strTemp := strTemp + ' 连接状态未知:'+
GetRasError( dwResult )
else
if RASCONNSTATUSData.rasconnstate =
RASCS_Connected
then
strTemp := strTemp + ' 连接状态:已连接'
else
strTemp := strTemp + ' 连接状态:('+
inttostr(
RASCONNSTATUSData.rasconnstate)+')';
memo1.lines.add( strTemp );
end;
end;
end;

 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
谢谢jsxjd兄提供的答案,虽然不符合我的要求,但是还是给jsxjd记上 100 分,
虽然我用不上,但是也许会有人需要。
我另开贴子给你分数。
我想说一下,我想要的是使用 AT 指令控制 modem 的,因为想研究一下 AT 指令
的工作过程,不想用 API 来操作 modem,虽然这比较简单,但是不利于研究modem
是如何工作的。
大家踊跃发言,有价值的都有分 [8D]
 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
请 jsxjd 到:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1513009
拿分
 
J

jsxjd

Unregistered / Unconfirmed
GUEST, unregistred user!
AT指令我以前在do
s 下做过的,
直接将 modem 端口用打开文件的方式打开,
然后用写文件的方式发送指令。
 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
我也知道可以用文件方式打开。
但是我不知道具体的 AT 指令是如何对 Modem 进行控制的,而且能找到的一些
关于 at 指令打电话的代码大多是
at dial xxxxxx
之类的,根本就没办法通过声卡把电话的声音放出来。如果老兄有相关的代码
或者中文的文档,请给我发一点过来,我再给分 [:D]
 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
Upping .....
 
D

down186

Unregistered / Unconfirmed
GUEST, unregistred user!
数据通讯的重要性是不言而喻的,特别是在写程序的过程中掌握数据通讯技术,了解各种通讯的方法及其优缺点是很重要的。调制解调器是目前进行远程通讯的一种重要工具,基于调制解调器的应用越来越多,TAPI就是Microsoft及Intel公司联合开发的,基于MODEM,电话线进行数据传输的一种应用程序接口,TAPI既适用于BC++也适用于VC++。
近一段时间我一直使用TAPI实现两台机器间用MODEM,电话线之间进行数据传输,现总结如下:
Windows95/98基于API的通信手段大致分为以下几种:
1. 基于TCP/IP协议的WinSock API,可实现局域网上或互联网上的微机通信;
2. 基于进程之间的通信技术:动态数据交换(DDE);
3. 基于直接电缆连接的通信技术,可直接操作串行口、交行口以及远红外线接口;
4. 基于电话线路的通信应用程序接口(TAPI),可方便地控制调制解调器。
二.通讯过程
TAPI的基本通讯过程包括如下几步:
步骤 说明
初始化TAPI环境 使用lineInitialize函数初始化TAPI32.DLL,获得TAPI句柄。此时必须指定一个回调函数,由此回调函数处理消息。
应答方初始化 1. 通过lineNegotiateAPIVersion确定使用TAPI的版本;
2. 用lineOpen打开线路,得到HLINE句柄,dwPrivileges函数必须使用LINECALLPRIVILEGE_MONITOR+LINECALLPRIVILEGE_OWNER,与呼叫方不同;
呼叫方进行呼叫 1. 通过lineNegotiateAPIVersion确定使用TAPI的版本;
2. 用lineOpen打开线路,得到HLINE句柄,dwPrivileges函数必须使用LINECALLPRIVILEGE_NONE;
3. 创建拨号参数,类型为:LPLINECALLPARAMS;
4. 用lineMakeCall或lineDial进行拨号,两者的区别是,使用前者是还没有HCALL句柄的情况下,使用此拨号可得到HCALL句柄,而lineDial是在HCALL句柄已得到的情况下使用。
回调函数的处理 回调函数对消息进行处理,最主要的消息包括:
1. LINECALLSTATE_OFFERING:此函数由应答方捕获,得到此消息表明已接收到拨号方的呼叫,应进行回应,回应的格式为:lineAnswer( (HCALL)dwDevice,NULL,0 );
2. LINECALLSTATE_BUSY, LINECALLSTATE_IDLE, LINECALLSTATE_SPECIALINFO:在拨号或应答过程中出现错误,应断接;
3. LINECALLSTATE_DISCONNECTED:某一方断接,此时可以判断到底是哪一种原因造成断接,同时也应调用断接函数;
4. LINECALLSTATE_CONNECTED:已连接上并建立线路,此时应调用lineGetID函数得到MODEM的句柄,必要时应清空MODEM缓冲区,以便开始进行数据的传输。
断接 1. 任何一方可以调用linDrop(HCALL)来停止呼叫,该函数将会发送LINECALLSTATE_IDLE消息给回调函数
2. 当任何一方收到LINECALLSTATE_IDLE消息时应调用lineDeallocateCall(hCall)来释放掉占用的呼叫资源;
3. 当收到LINECALLSTATE_DISCONNECTED 消息时应使用lineClose(HLINE)释放由lineOpen 分配的资源,调用lineShutDown(HLINEAPP)释放为线路设备分配的资源
数据的发送与接收 使用ReadFile与WriteFile函数发送与接收数据,共中的文件句柄好是MODEM句柄。

三.数据发送与传输
ReadFile与WriteFile并非TAPI的函数集成员函数,TAPI并不提供数据传输的函数。
(一)函数调用格式:
ReadFile的调用格式为:
ReadFile(文件句柄,发送的数据的缓冲区地址,欲发送多少字节数据,实际发送了多少字节数据,一个指向OVERLAPPED结构的指针);
1. 文件句柄:此处即为MODEM句柄;
2. 发送数据的缓冲区地址:可为char *或LPDWORD格式,传送的数据为二进制格式;
3. 欲发送多少字节数据:用户指定,但也可以使用ClearCommError函数来测到当前MODEM缓冲区有多少字节数据,然后有多少读多少。
4. 实际发送了多少字节数据:返回值;
5. OVERLAPPED结构的指针:
(二)传输模式
在95/98下:ReadFile与WriteFile有两种模式,一种是等待模式,一种是非等待模式。
等待模式的格式为:
DWORD len;
ReadFile(g_hCommFile,lpBuffer,256,&amp;len,NULL);
此时必须完成了读写操作函数才会返回,实际读写的字节数在len中,最后一个参数必须为NULL。
非等待模式的格式为:
OVERLAPPED myOVLP = {0, 0, 0, 0, NULL};
myOVLP.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ReadFile(g_hCommFile,lpBuffer,256,NULL,&amp;myOVLP);
GetOverlappedResult(g_hCommFile,&amp;myOVLP,&amp;len,TRUE)
CloseHandle(myOVLP.hEvent);
此时ReadFile函数会马上返回,第四个参数会被忽略,实际读写的字节数由GetOverlappedResult函数返回,如果读写成功,此函数为返回非0值,最后一个函数为TRUE,表示等待方式,为FALSE表示非等待。
在NT下必须使用OVERLAPPED参数,调用格式与95/98下的非等待方式类似。
(三)如何保证数据传输的正确性
在等待模式下,如果读写操作不完成,函数就会一直处于挂起状态;
在非等待模式下,函数调用后会马上返回,此时应调用GetOverlappedResult函数来判断,如果此函数返回FALSE,就应进一步判断,例如:
WriteFile(g_hCommFile,lpBuffer,256,NULL,&amp;myOVLP);
while(!GetOverlappedResult(g_hCommFile,&amp;myOVLP,&amp;len,TRUE))
{
dwError=GetLastError();
if(dwError == ERROR_IO_INCOMPLETE)
continue;
else
出错;
}

(四)如何提高传输性能
因为MODEM的缓冲区毕竟非常有限,如果某一方出现阻塞,另一方就不得不陷入等待之中,而且双方之间的协调会使速率非常之低,此时可以采用缓冲区的办法,发送与接收双方都开辟一个缓冲区(应是循环的),数据直接写入缓冲区中,然后用线程的方法,发送方不断的判断缓冲区中是否有数据,如果有发送方就发送到MODEM中,接收方则不断判断MODEM中是否有数据,如果有就读入缓冲区中。
就MODEM传输速度,我使用的环境是内线,没有通过交换机,满载的情况下每秒在6K字节/秒左右。
四.消息的处理
TAPI由回调函数进行消息处理,回调函数在初始化TAPI时创建,消息的处理在TAPI的使用过程中是至关重要的。以下是一些主要的消息:
1. LINECALLSTATE_IDLE
没有呼叫,为空,此时应断接,释放掉占用的资源;
2. LINECALLSTATE_BUSY
线路忙或设备忙,此时应断接,释放掉占用的资源;
3. LINECALLSTATE_SPECIALINFO
特别的消息,此时应断接,释放掉占用的资源;
4. LINECALLSTATE_OFFERING
应答方已收到呼叫方信号,此时应进行应答,调用lineAnswer函数;
5. LINECALLSTATE_CONNECTED
已连接成功,此时可进行数据的传输,但必须先得到MODEM的句柄;
6. LINECALLSTATE_DISCONNECTED
已断接,此时应释放掉占用的资源。
 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
todo
wn186,
老兄离题了 :) 不过还是给你分
 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
另外, 还想问个问题: 同一个 PC 上, 最多可以接几个 Modem 阿?
我上次给一个 modem 厂家发 email, 客服的说可以接两个。
 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
down168 请到
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1585339
取分
我有一本 TAPI 的书,覆盖了 3.0 之前的版本。上面有详细的说明。
但是我并不打算用它。因为我想实现跨平台应用。不管怎样,提供
有用消息的都有分。
大家请继续。
 
X

xiao.lit

Unregistered / Unconfirmed
GUEST, unregistred user!
多人接受答案了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
2K
DelphiTeacher的专栏
D
顶部