-
C#教程之C#开发纽曼USB来电小秘书客户端总结
在使用C#开发完CRM的来电弹屏之后,有些客户有了新的要求,他们希望不但能够实现来电弹屏,更希望能够将呼入呼出的电话录音并上传到CRM服务器上,方便日后跟踪记录。于是便有了来电小秘书客户端的开发。
本文所述的来电小秘书客户端的开发是基于纽曼USB来电通客户端的基础上进行开发的,由于纽曼USB来电通的硬件没有录音功能,于是硬件上使用了纽曼的另一个硬件产品来电小秘书,虽然是同一个厂家的产品,可是它们的API却是完全不兼容,更烦的是,来电小秘书API没有来电的回调接口,无法通过回调触发程序,也没有C#的Demo,很多功能只能通过一个不是那么详细的文档和一个Delphi的Demo摸索着做了,经历了一些挫折和困惑,终于完成了这个客户端程序。
首先,开发要做的就是与硬件的API进行沟通,依然通过C#的P/Invoke来完成,以下是来电小秘书的P/Invoke代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
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#中可以使用如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
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.jb51.net/article/53377.htm,可是无法上传成功,于是解读了一下代码,发现他将信息中的\r\n用空字符代替了,导致服务器无法识别,于是在更改了他的代码之后,问题解决了,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
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系统也要做一些修改,不过不是这篇文章的主要内容,关于服务器端的修改的小节,后续会有相关的报导。