语音机的基本就是像声讯台那样的能够自动接电话放录音,并且接受用户手机输入的数字然后做相应的处理。最近要做一个用语音机接受用户输入的产品防伪码,查询产品的真假。语音机长的差不多就是这个样子.
语音机一头接在电话线上,一头通过USB接在电脑上,然后它会告诉程序电话线上的各种状态,程序做相应的处理。这个小项目的业务逻辑并不复杂,关键是要调用这个语音机提供的api,它没有提供.net的api,只有一个windows的dll。我以前没有做过类似的项目,就在这记录下,顺便介绍下.net中调用非托管代码的方法。.
一般来说,这种设备都会提供一个驱动盘和开发用的dll和一个文档说明。首先要安装好驱动,连接好设备,将dll复制到项目的bin文件夹中。这些dll一般提供的都是C的接口,要在.net中使用非托管的代码,要是用.net interop service.关于 interop service的详细内容打算后文介绍。这里先简单介绍下用法。Consuming Unmanaged DLL Functions 这里有更为详细的介绍。关键的一步是声明dll中的函数和变量,相当于将C的头文件翻译成C#的。C#使用static extern关键字来表示这个方法是外部的dll,同时还是要DllImport属性来标识dll的信息。
例如,在C的头文件中有如下函数声明:
#ifndef WIN32 #define TWCALLFUNC #else #define TWCALLFUNC WINAPI #endif void TWCALLFUNC TV_SetChannelType (int ch,int type);
这个TWCALLFUNC有什么用?我C语言也不精通,也不太清楚,估计起类似于命名空间的作用吧,不管它。那么在C#中的某个类中,可以如下声明:
[DllImport("tw16vid.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] public static extern void TV_SetChannelType(int ch, int type);
对于C中的常量,struct则基本上只要对应的写成C#的语法形式就可以。写好以后就可以在程序中正常的调用这些方法。其实简单用一下还是挺简单的。下面再简单说下这个语音机的使用。语音机的原理就是当外线有电话打进来的时候,它的一个函数可以返回是否响铃,然后程序中可以调用一个方法接电话,紧接着可以放录音,然后可以接受用户输入的数字。接受数字是靠一个叫DTMF码的方式送过来的,这是电话上常用的,我也不怎么懂,不过API也提供了一个方法接受DTMF码。接受到用户的输入后,你的程序就可以做相应的处理了。这些就是我这个项目用到的,当然语音机硬件还有其他很多功能,好像还支持多方通话什么的,比较复杂,我也没有仔细研究。
要判断是否有电话打进来,只有采用轮询的方式——古老而实用。
private void listenChannel() { for (int i = 0; i < iChannels; i++) { CS[i] = TW8Vid.WS_HANGUP; } while (flag) { Application.DoEvents(); for (int i = 0; i < iChannels; i++) { switch (TW8Vid.TV_ChannelType(i)) { case TW8Vid.CT_EXTERNAL: externalChannel(i); break; default: break; } } System.Threading.Thread.Sleep(300); } } private void externalChannel(int ch) { int sig, sigCount = 0, sigLen = 0; StringBuilder callingNumber=new StringBuilder(); switch (CS[ch]) { case TW8Vid.WS_HANGUP: if (TW8Vid.TV_RingDetect(ch) > 1) { textBoxInfo.Text += "振铃\r\n"; char[] phoneBuffer = new char[64]; int length = TW8Vid.TV_ReceiveCallingID(ch, phoneBuffer, 64); for (int i = 0; i < length; i++) callingNumber.Append(phoneBuffer[i]); TW8Vid.TV_OffHookCtrl(ch); TW8Vid.TV_FlushDTMF(ch); if (TW8Vid.TV_StartPlayCh(ch, "请输入条码".ToCharArray()) >= 0) { CS[ch] = TW8Vid.WS_PLAYING; textBoxInfo.Text += "播放\r\n"; } else { TW8Vid.TV_HangUpCtrl(ch); System.Threading.Thread.Sleep(200); CS[ch] = TW8Vid.WS_HANGUP; textBoxInfo.Text += "播放失败\r\n"; } }; break; case TW8Vid.WS_PLAYING: sig = TW8Vid.TV_CheckSignal(ch, ref sigCount, ref sigLen); if ((sig == TW8Vid.SIG_BUSY1 || sig == TW8Vid.SIG_BUSY2) && sigCount > 3) { textBoxInfo.Text += "外线挂机"; TW8Vid.TV_HangUpCtrl(ch); CS[ch] = TW8Vid.WS_HANGUP; } else { if (TW8Vid.TV_PlayChRest(ch) <= 0) { TW8Vid.TV_StopPlayCh(ch); } int d = TW8Vid.TV_GetDTMFChar(ch); if (d != -1) { if ((char)d == '#') { textBoxInfo.Text += "用户#确认"; textBoxInfo.Text += msgBuffer[ch].ToString() + "\r\n"; string res = CheckCode(callingNumber.ToString(),msgBuffer[ch].ToString(),DateTime.Now); if (TW8Vid.TV_StartPlayCh(ch, "你成功输入条码".ToCharArray()) >= 0) { CS[ch] = TW8Vid.WS_PLAYING; textBoxInfo.Text += "播放2\r\n"; } else { TW8Vid.TV_HangUpCtrl(ch); System.Threading.Thread.Sleep(200); CS[ch] = TW8Vid.WS_HANGUP; textBoxInfo.Text += "播放失败\r\n"; } } else { msgBuffer[ch].Append((char)d); } } }; break; case TW8Vid.WS_TIMER: break; default: break; } }