在前面用C#开发完CRM的来电弹屏之后,有些客户有了新的要求,他们希望不但能够实现来电弹屏,更希望能够将呼入呼出的电话录音并上传到CRM服务器上,方便日后跟踪记录。于是便有了来电小秘书客户端的开发。
来电小秘书客户端的开发是基于纽曼USB来电通客户端(详情参考:
http://renzhen.iteye.com/blog/580746)的基础上进行开发的,由于纽曼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#中的大文件上传吧,网上有很多例子了,我参考了如下blog的代码进行开发
http://www.cnblogs.com/bccu/archive/2009/01/05/1363771.html,可是无法上传成功,于是我读了一下代码,发现他将信息中的\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系统也要做一些修改,不过不是这篇文章的主要内容,关于服务器端的修改的小节,就等下次再说吧。
分享到:
相关推荐
主要介绍了C#开发纽曼USB来电小秘书客户端总结,对于C#项目开发来说有一定的参考借鉴价值,需要的朋友可以参考下
C# 开发 websocket 服务端和客户端 demo 使用,测试通过,服务端最小化到系统托盘,使用websocket-sharp
C#开发webservice接口,对客户端post服务的Json数据进行接收反馈 接收到的数据流转换成string类型,有其他需求对json解析,自己写个解析去查询下. 然后反馈json发送给请求端。
最近做项目用到了SVN 废了好大劲才搞到手 分享一下
完全的通用USB开发库,不需要要任何的驱动(Windows自带),本人开发USB上位机软件包时,发现都是非托管的C++调用库,C#调用库非常少。所以收集了这个,供用.NET方式开发USB的朋友使用。(也包含VC++调用案例)
C#_使用C#开发的Unity3D客户端
C#高并发SOCKET服务器和客户端完整工程实例源码,工程现场测试使用的源码,适合新手及有一定经验开发人员
利用c#开发的餐厅点菜系统客户端,初学者所写,很好学,可直接编译运行,个人空间有管理端下载,仅供下载参考。
c# SocketAsyncEventArgs高性能socket例子包含服务端和客户端
基于C#的在线聊天程序,c#网络stock编程,实现多人在线聊天,上线提醒,下线通知。分为客户端和服务端
C#聊天软件开发包括客户端和服务器端C#聊天软件开发包括客户端和服务器端C#聊天软件开发包括客户端和服务器端
C# SuperSocket框架 ---客户端源程序,源程序包里有一个TCP测试工具,可以方便你进行测试,同时还附有测试成功的截图。
使用C#语言编写的Socket通信客户端和服务端应用程序,实现客户端和服务端的网络通信。
c#实现局域网服务端向客户端内多文件的传输c#实现局域网服务端向客户端内多文件的传输c#实现局域网服务端向客户端内多文件的传输c#实现局域网服务端向客户端内多文件的传输c#实现局域网服务端向客户端内多文件的传输...
C# Socket服务端向指定的客户端发送消息(包含服务器)
本实例包含: 1. C# SOCKET同步通讯实例的服务端和客户端端程序 2. C# SOCKET异步通讯实例的服务端和客户端端程序 需要用到胡知识点有:多线程 和 委托
C# 基于MQTTNet的服务端与客户端通信案例
利用C#编写MQTT客户端上位机,简单实用,自带MQTT使用库
实现Java服务端和C#客户端联通 Java使用Netty 开发环境为IDEA C#使用DotNetty 开发环境为VS2017 运行时先开启Java服务端 再开启客户端