C#开发纽曼USB来电小秘书客户端总结
在使用C#开发完CRM的来电弹屏之后,有些客户有了新的要求,他们希望不但能够实现来电弹屏,更希望能够将呼入呼出的电话录音并上传到CRM服务器上,方便日后跟踪记录。于是便有了来电小秘书客户端的开发。
本文所述的来电小秘书客户端的开发是基于纽曼USB来电通客户端的基础上进行开发的,由于纽曼USB来电通的硬件没有录音功能,于是硬件上使用了纽曼的另一个硬件产品来电小秘书,虽然是同一个厂家的产品,可是它们的API却是完全不兼容,更烦的是,来电小秘书API没有来电的回调接口,无法通过回调触发程序,也没有C#的Demo,很多功能只能通过一个不是那么详细的文档和一个Delphi的Demo摸索着做了,经历了一些挫折和困惑,终于完成了这个客户端程序。
首先,开发要做的就是与硬件的API进行沟通,依然通过C#的P/Invoke来完成,以下是来电小秘书的P/Invoke代码。
usingSystem; usingSystem.Collections.Generic; usingSystem.Text; usingSystem.Runtime.InteropServices; namespaceWindowsApplication1 { classLDT1 { [DllImport("usbms.dll",EntryPoint="LoadDRV")] publicstaticexternintLoadDRV(); [DllImport("usbms.dll",EntryPoint="EnableCard")] publicstaticexternintEnableCard(); [DllImport("usbms.dll",EntryPoint="StopSigCheck")] publicstaticexternintStopSigCheck(intHandle); [DllImport("usbms.dll",EntryPoint="ReSetUsb")] publicstaticexternintReSetUsb(intHandle); [DllImport("usbms.dll",EntryPoint="HangUp")] publicstaticexternintHangUp(intHandle); [DllImport("usbms.dll",EntryPoint="InitDtmfBuf")] publicstaticexternintInitDtmfBuf(intHandle); [DllImport("usbms.dll",EntryPoint="SetDialPara")] publicstaticexternintSetDialPara(UInt16RingBack1,UInt16RingBack0,UInt16BusyLen,UInt16RingTimes,UInt16SendNoSignalLen); [DllImport("usbms.dll",EntryPoint="DisableCard")] publicstaticexternintDisableCard(); [DllImport("usbms.dll",EntryPoint="FreeDRV")] publicstaticexternintFreeDRV(); [DllImport("usbms.dll",EntryPoint="GetDtmfCode")] publicstaticexternintGetDtmfCode(UInt16Line); [DllImport("usbms.dll",EntryPoint="IsRing")] publicstaticexternboolIsRing(UInt16Line); [DllImport("usbms.dll",EntryPoint="GetCallerIDStr")] publicstaticexternUInt16GetCallerIDStr(UInt16Line,StringBuilderIDStr); [DllImport("usbms.dll",EntryPoint="IsOffHook")] publicstaticexternboolIsOffHook(UInt16Line); [DllImport("usbms.dll",EntryPoint="StartRecordFile")] publicstaticexternboolStartRecordFile(UInt16Line,stringFileName,UInt32dwRecordLen); [DllImport("usbms.dll",EntryPoint="CheckRecordEnd")] publicstaticexternboolCheckRecordEnd(UInt16Line); [DllImport("usbms.dll",EntryPoint="StopRecordFile")] publicstaticexternboolStopRecordFile(UInt16Line); [DllImport("usbms.dll",EntryPoint="PCMtoWave")] publicstaticexternintPCMtoWave(stringSourceFileName,stringTargetFileName); [DllImport("usbms.dll",EntryPoint="ReadCheckResult")] publicstaticexternintReadCheckResult(intline,intmode); [DllImport("usbms.dll",EntryPoint="StartSigCheck")] publicstaticexternvoidStartSigCheck(intline); [DllImport("usbms.dll",EntryPoint="ReadUsbState")] publicstaticexternboolReadUsbState(intline); [DllImport("usbms.dll",EntryPoint="GetRingNum")] publicstaticexternintGetRingNum(intline); [DllImport("usbms.dll",EntryPoint="InitRingNum")] publicstaticexternvoidInitRingNum(intline); [DllImport("usbms.dll",EntryPoint="ReadSerialNo")] publicstaticexternintReadSerialNo(intline,StringBuilderserialNo); } }
然后就是关于设备状态检测了,由于没有API直接支持来电回调,所以只能自己手动的检测设备状态来判断,要实现这一部分一般有两种方式,使用Timer或者使用Thread,Delphi的Demo中使用了Timer,可是Timer实现的弊端需要使用异步的思考方式,不符合我们的思维模式,灵活度也不够,而且C#创建线程太方便了,而线程是通过同步方式思考的,所以使用了Thread模式。
然后在特定的时刻,记录电话号码、弹屏(如果是来电)、电话结束后录音和上传文件和信息到CRM服务器,其中来电号码可以很容易的获取,可是播出的号码获取就比较的麻烦了,C#中可以使用如下代码:
while(LDT1.IsOffHook((ushort)this.line)) { inttemp=LDT1.GetDtmfCode((ushort)this.line); if(temp>0) { phonenum=phonenum+this.convertInt(temp); } Thread.Sleep(300); } privatestringconvertInt(intcode) { stringret=""; switch(code) { case10: ret="0"; break; case11: ret="*"; break; case12: ret="#"; break; case13: ret="A"; break; case14: ret="B"; break; case15: ret="C"; break; case16: ret="D"; break; default: ret=code.ToString(); break; } returnret; }
下面说一下C#中的大文件上传吧,网上有很多例子了,可以参考如下文章中的代码进行开发https://www.nhooo.com/article/53377.htm,可是无法上传成功,于是解读了一下代码,发现他将信息中的\r\n用空字符代替了,导致服务器无法识别,于是在更改了他的代码之后,问题解决了,代码如下:
publicstaticstringUploadFileEx(stringuploadfile,stringurl, stringfileFormName,stringcontenttype,NameValueCollectionquerystring, CookieContainercookies) { if((fileFormName==null)|| (fileFormName.Length==0)) { fileFormName="file"; } if((contenttype==null)|| (contenttype.Length==0)) { contenttype="application/octet-stream"; } stringpostdata; postdata="?"; if(querystring!=null) { foreach(stringkeyinquerystring.Keys) { postdata+=key+"="+querystring.Get(key)+"&"; } } Uriuri=newUri(url+postdata); stringboundary="----------"+DateTime.Now.Ticks.ToString("x"); HttpWebRequestwebrequest=(HttpWebRequest)WebRequest.Create(uri); //webrequest.CookieContainer=cookies; webrequest.ContentType="multipart/form-data;boundary="+boundary; webrequest.Method="POST"; stringhuanhang="\r\n"; byte[]huanhangbyte=Encoding.UTF8.GetBytes(huanhang); //Buildupthepostmessageheader StringBuildersb=newStringBuilder(); 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"); stringpostHeader=sb.ToString(); byte[]postHeaderBytes=Encoding.UTF8.GetBytes(postHeader); //Buildthetrailingboundarystringasabytearray //ensuringtheboundaryappearsonalinebyitself byte[]boundaryBytes= Encoding.ASCII.GetBytes("--"+boundary+""); FileStreamfileStream=newFileStream(uploadfile, FileMode.Open,FileAccess.Read); longlength=postHeaderBytes.Length+fileStream.Length+ boundaryBytes.Length+huanhangbyte.Length; webrequest.ContentLength=length; StreamrequestStream=webrequest.GetRequestStream(); //Writeoutourpostheader requestStream.Write(postHeaderBytes,0,postHeaderBytes.Length); //Writeoutthefilecontents byte[]buffer=newByte[checked((uint)Math.Min(4096, (int)fileStream.Length))]; intbytesRead=0; while((bytesRead=fileStream.Read(buffer,0,buffer.Length))!=0) requestStream.Write(buffer,0,bytesRead); requestStream.Write(huanhangbyte,0,huanhangbyte.Length); //Writeoutthetrailingboundary requestStream.Write(boundaryBytes,0,boundaryBytes.Length); fileStream.Dispose(); requestStream.Dispose(); WebResponseresponce=webrequest.GetResponse(); Streams=responce.GetResponseStream(); StreamReadersr=newStreamReader(s); stringretval=sr.ReadToEnd(); sr.Dispose(); if(File.Exists(uploadfile)) { try { File.Delete(uploadfile); }catch(Exceptione) { } } returnretval; }
CRM来电小秘书客户端完成了,当然要配合这个功能,服务器端CRM系统也要做一些修改,不过不是这篇文章的主要内容,关于服务器端的修改的小节,后续会有相关的报导。