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

 更新时间:2020年6月25日 11:35  点击:1949

在使用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#中的大文件上传吧,网上有很多例子了,可以参考如下文章中的代码进行开发https://www.jb51.net/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系统也要做一些修改,不过不是这篇文章的主要内容,关于服务器端的修改的小节,后续会有相关的报导。

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25