首页 > 编程 > C# > 正文

C#开发纽曼USB来电小秘书客户端总结

2020-01-24 02:34:05
字体:
来源:转载
供稿:网友

在使用C#开发完CRM的来电弹屏之后,有些客户有了新的要求,他们希望不但能够实现来电弹屏,更希望能够将呼入呼出的电话录音并上传到CRM服务器上,方便日后跟踪记录。于是便有了来电小秘书客户端的开发。

本文所述的来电小秘书客户端的开发是基于纽曼USB来电通客户端的基础上进行开发的,由于纽曼USB来电通的硬件没有录音功能,于是硬件上使用了纽曼的另一个硬件产品来电小秘书,虽然是同一个厂家的产品,可是它们的API却是完全不兼容,更烦的是,来电小秘书API没有来电的回调接口,无法通过回调触发程序,也没有C#的Demo,很多功能只能通过一个不是那么详细的文档和一个Delphi的Demo摸索着做了,经历了一些挫折和困惑,终于完成了这个客户端程序。

首先,开发要做的就是与硬件的API进行沟通,依然通过C#的P/Invoke来完成,以下是来电小秘书的P/Invoke代码。

using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;  namespace WindowsApplication1 {   class LDT1   {     [DllImport("usbms.dll", EntryPoint = "LoadDRV")]     public static extern int LoadDRV();      [DllImport("usbms.dll", EntryPoint = "EnableCard")]     public static extern int EnableCard();      [DllImport("usbms.dll", EntryPoint = "StopSigCheck")]     public static extern int StopSigCheck(int Handle);      [DllImport("usbms.dll", EntryPoint = "ReSetUsb")]     public static extern int ReSetUsb(int Handle);      [DllImport("usbms.dll", EntryPoint = "HangUp")]     public static extern int HangUp(int Handle);      [DllImport("usbms.dll", EntryPoint = "InitDtmfBuf")]     public static extern int InitDtmfBuf(int Handle);      [DllImport("usbms.dll", EntryPoint = "SetDialPara")]     public static extern int SetDialPara(UInt16 RingBack1, UInt16 RingBack0, UInt16 BusyLen, UInt16 RingTimes, UInt16 SendNoSignalLen);       [DllImport("usbms.dll", EntryPoint = "DisableCard")]     public static extern int DisableCard();      [DllImport("usbms.dll", EntryPoint = "FreeDRV")]     public static extern int FreeDRV();      [DllImport("usbms.dll", EntryPoint = "GetDtmfCode")]     public static extern int GetDtmfCode(UInt16 Line);      [DllImport("usbms.dll", EntryPoint = "IsRing")]     public static extern bool IsRing(UInt16 Line);      [DllImport("usbms.dll", EntryPoint = "GetCallerIDStr")]     public static extern UInt16 GetCallerIDStr(UInt16 Line, StringBuilder IDStr);       [DllImport("usbms.dll", EntryPoint = "IsOffHook")]     public static extern bool IsOffHook(UInt16 Line);       [DllImport("usbms.dll", EntryPoint = "StartRecordFile")]     public static extern bool StartRecordFile(UInt16 Line, string FileName, UInt32 dwRecordLen);      [DllImport("usbms.dll", EntryPoint = "CheckRecordEnd")]     public static extern bool CheckRecordEnd(UInt16 Line);       [DllImport("usbms.dll", EntryPoint = "StopRecordFile")]     public static extern bool StopRecordFile(UInt16 Line);      [DllImport("usbms.dll", EntryPoint = "PCMtoWave")]     public static extern int PCMtoWave(string SourceFileName, string TargetFileName);      [DllImport("usbms.dll", EntryPoint = "ReadCheckResult")]     public static extern int ReadCheckResult(int line, int mode);      [DllImport("usbms.dll", EntryPoint = "StartSigCheck")]     public static extern void StartSigCheck(int line);      [DllImport("usbms.dll", EntryPoint = "ReadUsbState")]     public static extern bool ReadUsbState(int line);      [DllImport("usbms.dll", EntryPoint = "GetRingNum")]     public static extern int GetRingNum(int line);      [DllImport("usbms.dll", EntryPoint = "InitRingNum")]     public static extern void InitRingNum(int line);      [DllImport("usbms.dll", EntryPoint = "ReadSerialNo")]     public static extern int ReadSerialNo(int line,StringBuilder serialNo);    } } 

然后就是关于设备状态检测了,由于没有API直接支持来电回调,所以只能自己手动的检测设备状态来判断,要实现这一部分一般有两种方式,使用Timer或者使用Thread,Delphi的Demo中使用了Timer,可是Timer实现的弊端需要使用异步的思考方式,不符合我们的思维模式,灵活度也不够,而且C#创建线程太方便了,而线程是通过同步方式思考的,所以使用了Thread模式。

然后在特定的时刻,记录电话号码、弹屏(如果是来电)、电话结束后录音和上传文件和信息到CRM服务器,其中来电号码可以很容易的获取,可是播出的号码获取就比较的麻烦了,C#中可以使用如下代码:

while (LDT1.IsOffHook((ushort)this.line)) {    int temp = LDT1.GetDtmfCode((ushort)this.line);     if (temp > 0)     {        phonenum = phonenum + this.convertInt(temp);     }     Thread.Sleep(300);         } private string convertInt(int code)   {     string ret="";     switch (code)     {       case 10:         ret = "0";         break;       case 11:         ret = "*";         break;       case 12:         ret = "#";         break;       case 13:         ret = "A";         break;       case 14:         ret = "B";         break;       case 15:         ret = "C";         break;       case 16:         ret = "D";         break;       default:         ret = code.ToString();         break;     }     return ret;   } 

下面说一下C#中的大文件上传吧,网上有很多例子了,可以参考如下文章中的代码进行开发//www.VeVB.COm/article/53377.htm,可是无法上传成功,于是解读了一下代码,发现他将信息中的/r/n用空字符代替了,导致服务器无法识别,于是在更改了他的代码之后,问题解决了,代码如下:

public static string UploadFileEx(string uploadfile, string url,    string fileFormName, string contenttype, NameValueCollection querystring,    CookieContainer cookies)  {    if ((fileFormName == null) ||      (fileFormName.Length == 0))    {      fileFormName = "file";    }     if ((contenttype == null) ||      (contenttype.Length == 0))    {      contenttype = "application/octet-stream";    }      string postdata;    postdata = "?";    if (querystring != null)    {      foreach (string key in querystring.Keys)      {        postdata += key + "=" + querystring.Get(key) + "&";      }    }    Uri uri = new Uri(url + postdata);     string boundary = "----------" + DateTime.Now.Ticks.ToString("x");    HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(uri);    //webrequest.CookieContainer = cookies;    webrequest.ContentType = "multipart/form-data; boundary=" + boundary;    webrequest.Method = "POST";    string huanhang = "/r/n";    byte[] huanhangbyte = Encoding.UTF8.GetBytes(huanhang);     // Build up the post message header      StringBuilder sb = new StringBuilder();    sb.Append("--");    sb.Append(boundary);    sb.Append("/r/n");    sb.Append("Content-Disposition: form-data; name=/"");    sb.Append(fileFormName);    sb.Append("/"; filename=/"");    sb.Append(Path.GetFileName(uploadfile));    sb.Append("/"");    sb.Append("/r/n");    sb.Append("Content-Type: ");    sb.Append(contenttype);    sb.Append("/r/n");    sb.Append("/r/n");     string postHeader = sb.ToString();    byte[] postHeaderBytes = Encoding.UTF8.GetBytes(postHeader);     // Build the trailing boundary string as a byte array      // ensuring the boundary appears on a line by itself      byte[] boundaryBytes =        Encoding.ASCII.GetBytes("--" + boundary + "");     FileStream fileStream = new FileStream(uploadfile,                  FileMode.Open, FileAccess.Read);    long length = postHeaderBytes.Length + fileStream.Length +                        boundaryBytes.Length + huanhangbyte.Length;    webrequest.ContentLength = length;     Stream requestStream = webrequest.GetRequestStream();     // Write out our post header      requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);     // Write out the file contents      byte[] buffer = new Byte[checked((uint)Math.Min(4096,                 (int)fileStream.Length))];    int bytesRead = 0;    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)      requestStream.Write(buffer, 0, bytesRead);    requestStream.Write(huanhangbyte, 0, huanhangbyte.Length);    // Write out the trailing boundary      requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);    fileStream.Dispose();    requestStream.Dispose();    WebResponse responce = webrequest.GetResponse();    Stream s = responce.GetResponseStream();    StreamReader sr = new StreamReader(s);    string retval=sr.ReadToEnd();    sr.Dispose();     if (File.Exists(uploadfile))    {      try      {        File.Delete(uploadfile);      }catch(Exception e)      {      }    }    return retval;  } 

CRM来电小秘书客户端完成了,当然要配合这个功能,服务器端CRM系统也要做一些修改,不过不是这篇文章的主要内容,关于服务器端的修改的小节,后续会有相关的报导。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表