欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-02-17 09:21:34
在使用c#开发完crm的来电弹屏之后,有些客户有了新的要求,他们希望不但能够实现来电弹屏,更希望能够将呼入呼出的电话录音并上传到crm服务器上,方便日后跟踪记录。于是便有了...

在使用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#中的大文件上传吧,网上有很多例子了,可以参考如下文章中的代码进行开发,可是无法上传成功,于是解读了一下代码,发现他将信息中的\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系统也要做一些修改,不过不是这篇文章的主要内容,关于服务器端的修改的小节,后续会有相关的报导。