子佩电话录音盒连接示例
官方网站为本节内容提供了示例,下载地址:
http://www.foxtable.com/samples/caller.rar
本节的任务是设计一个窗口,以软件方式实现电话终端的管理。本例实现来电弹屏、通话录音存储、通话详单记录、自动答录、快速拨号等多项功能
一. 录音盒
本节的例子以南京子佩软件科技有限公司的子佩录音盒为例,该公司的网址:
需要说明的是,该公司和我们没有任何关系,类似的电话录音盒有很多,大家可以根据需要选择。
二. 录音盒连接
根据说明书,连接好电话录音盒设备,并安装好设备驱动程序,正常情况下,在Windows的设备管理中,会出现下面一项:
这就是录音盒设备。
三. Foxtable示例
3.1 使用SDK
3.1.1根据该设备的开发文档,将其SDK文件复制到Foxtable的安装目录下,该设备的SDK文件包括qnviccub.dll和bridge.dll两个必须的DLL文件。
在编写本节内容时,其SDK的最新版本是6.6。
提示:由于他们的开发包是传统的非托管DLL,所以只需复制到Foxtable安装目录即可使用,千万不要再去引用这些DLL文件。
3.2.2. 参考开发文档和例子,在全局代码中定义好API函数和必须的全局变量,由于内容较多,具体请看示例。
3.2 设计数据表
示例设计了三个表进行电话录音管理示例,具体应用需要根据实际情况自行设计表格进行管理:
表"电话记录"主要记录一次呼叫从摘机到挂机的通讯状态;表"用户管理"用来管理联系人和电话号码;表"设置"用来记录示例的一些配置。
3.3 设计设备管理窗口
示例设计了一个窗口(电话录音基本功能演示)来进行设备的控制操作。
窗口分为左右2个部分,左边是设备控制,右边是设备的通讯状态。通讯状态在窗口关闭后会追加到日志文件“通讯状态.txt”中,日志文件默认保存在项目所在目录中。
3.3.1 打开设备
要使用设备必须先调用SDK打开设备,根据开发文档,打开设备的调用为:
Dim lret As Int32 = BriSDKLib.QNV_OpenDevice(BriSDKLib.ODT_LBRIDGE, 0, "")
如果返回值大于0,则设备打开成功,设备LED灯亮。
然后就可以获取设备可用的通道数,一个设备至少有一个通道,获取通道的函数调用为:
BriSDKLib.QNV_DevInfo(0, BriSDKLib.QNV_DEVINFO_GETCHANNELS)
全部代码参考示例打开设备按钮的Click事件代码。如果想一打开窗口就自动打开设备,可以把打开设备的调用放到窗口的AfterLoad事件中。甚至,可以把打开设备的调用放到项目的AfterOpenProject事件中,那么一打开项目就可以打开设备进行监控,不用再手动操作。
3.3.2 关闭设备
设备不用的时候,关闭设备,释放资源,关闭设备的函数调用为:
BriSDKLib.QNV_CloseDevice(BriSDKLib.ODT_ALL, 0)
示例同样在关闭窗口的事件BeforeClose中调用执行了关闭操作。如果打开设备是在项目的AfterOpenProject事件中,那么建议在项目的BeforeCloseProject中关闭设备,避免占用设备的通道,而导致无法再次被使用
3.4 设备消息处理
该设备主要使用了消息发布的方式来触发相应的事件,所以我们在全局代码定义一个自定义类进行通讯事件处理,类名称FormEventReport,类定义为:
Public Class FormEventReport
Inherits System.Windows.Forms.Form
......
End Class
FormEventReport类使用之前必须先打开设备。
FormEventReport类继承自System.Windows.Forms.Form,所以实际上就是一个自定义的窗体,这样我们就可以重载基类中的DefWndProc过程来接收设备的事件消息。
首先在FormEventReport的Load过程中给设备的所有通道注册事件信息:
Private Sub FormCall_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
For i As Int16 = 0 To BriSDKLib.QNV_DevInfo(0, BriSDKLib.QNV_DEVINFO_GETCHANNELS) - 1
'在windowproc处理所有通道接收到的消息
BriSDKLib.QNV_Event(i, BriSDKLib.QNV_EVENT_REGWND, CType(Me.Handle, Int32), "", New StringBuilder(0), 0)
Next
vars("IsEventFormOpened") = True
End Sub
然后重载DefWndProc过程,判断接收到的消息参数,进行相应的操作:
Protected Overrides Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
Select Case m.Msg
Case BriSDKLib.BRI_EVENT_MESSAGE
If True Then
Dim EventData As BriSDKLib.TBriEvent_Data = DirectCast(Marshal.PtrToStructure(m.LParam, Gettype(BriSDKLib.TBriEvent_Data)), BriSDKLib.TBriEvent_Data)
Select Case EventData.lEventType
Case BriSDKLib.BriEvent_PhoneHook '电话机摘机
AppendStatus("电话机摘机",EventData.uChannelID)
drCurrentRow = DataTables("电话记录").AddNew()
Tables("电话记录").Position = Tables("电话记录").Rows.Count-1
AppendCallRecord("电话机摘机",EventData.uChannelID)
Exit Select
Case BriSDKLib.BriEvent_PhoneHang '电话机挂机
......
......
End Sub
这里只是部分代码,完整代码请查看示例。
做法无非就是根据系统的消息,判断是否是本录音设备的消息,如果是,则取得消息中返回的设备信息,判断是设备的那个消息类型,来进行对应的操作。比如判断到是设备的摘机事件(BriEvent_PhoneHook),就增加一条电话记录。
示例定义了一个全局的FormEventReport类型变量EventForm,在打开设备后创建EventForm的实例,打开窗体后隐藏起来即可。因为这个窗体设计只是用来接收系统消息的,所以不需要显示。
最后在关闭设备的时候记得注销设备的消息处理,如本示例写在关闭设备按钮中:
'删除事件接收
For i As Integer = 0 To cboCurChn.Items.Count - 1
BriSDKLib.QNV_Event(i,BriSDKLib.QNV_EVENT_UNREGWND,CType(basemainform.Handle, Int32),Nothing,Nothing,0)
Next
3.5 用户管理
在“电话记录”单元格中右键点击,出现有“添加联系人”菜单,点击后会转到“用户管理”表,如果已经有同一个电话号码的记录,则定位到相应的记录;如果没有,则会增加一条记录,默认添加上电话号码。
3.6 其它
3.6.1 关于设备返回值
SDK某些函数的调用,返回字符串的,实际是返回了字符串的内存起始地址,即Intptr类型。所以示例使用Marshal.PtrToStructure将数据从非托管内存块封送到新分配的指定类型的托管对象中。示例定义了TBriEvent_Data结构(位于SDK库类BriSDKLib中)和CallLog_Data结构(全局代码)来取得非非托管内存块数据。例如CallLog_Data用法如下:
1. 定义结构:
'呼叫信息结构,用于号码,录音文件路径等
<StructLayout(LayoutKind.Sequential)> _
Public Structure CallLog_Data
<MarshalAs(UnmanagedType.ByValArray, SizeConst: = BriSDKLib.MAX_BRIEVENT_DATA)> _
Public szData As [Byte]()
End Structure
由于未知返回值的长度是多少,所以示例统一使用SDK定义的MAX_BRIEVENT_DATA值,即600字节
2. 获取返回的地址
如获取呼叫号码:
Dim ipt As IntPtr = BriSDKLib.QNV_CallLog(EventData.uChannelID,BriSDKLib.QNV_CALLLOG_CALLID,Nothing,BriSDKLib.MAX_BRIEVENT_DATA)
3. 将数据从非托管内存块封送到新分配的CallLog_Data结构中
Dim calllogData As CallLog_Data = DirectCast(Marshal.PtrToStructure(ipt , Gettype(CallLog_Data)), CallLog_Data)
4. 使用Encoding.Default.GetString转换结构中的字节数组,目前封装在示例的函数ByteArrayToString中,如:
ByteArrayToString(calllogData.szData)
需要注意的是,转换后的字节数组长度是600字节,内容是大于实际返回值的,其中多余的字节中的数据必须处理掉,不然就会返回一些乱码,还好正常返回值后是以Null字符(chr(0))结束的,所以ByteArrayToString函数中判断取得第一个Null字符,截断后面的内容。