使用C#開發紐曼USB來電小秘書客戶端小結,
在前面用C#開發完CRM的來電彈屏之後,有些客戶有了新的要求,他們希望不但能夠實現來電彈屏,更希望能夠將呼入呼出的電話錄音並上傳到CRM服務器上,方便日後跟蹤記錄。於是便有了來電小秘書客戶端的開發。
來電小秘書客戶端的開發是基於紐曼USB來電通客戶端的基礎上進行開發的,由於紐曼USB來電通的硬件沒有錄音功能,於是硬件上使用了紐曼的另一個硬件產品來電小秘書,雖然是同一個廠家的產品,可是它們的API卻是完全不兼容,更煩的是,來電小秘書API沒有來電的回調接口,無法通過回調觸發程序,也沒有C#的Demo,很多功能只能通過一個不是那麼詳細的文檔和一個Delphi的Demo摸索著做了,經歷了一些挫折和困惑,終於完成了這個客戶端程序。
首先,開發要做的就是與硬件的API進行溝通,依然通過C#的P/Invoke來完成,以下是來電小秘書的P/Invoke代碼。
C#代碼
- 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#中可以使用如下代碼:
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用空字符代替了,導致服務器無法識別,於是我更改了他的代碼,解決了問題,代碼如下:
C#代碼
- 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語言中 ^怎使用
a1 = 0x01; //0000 0001
a2 = 0x00; //0000 0000
a3 = 0x03; //0000 0011
a4 = 0x02; //0000 0010
b1 = a1 ^ a2; //0000 0001
b2 = a1 ^ a3; //0000 0010
b3 = a1 ^ a4; //0000 0011
^異或運算符,位值相同為0,不同為1,見上示例.
//
簡單實際問題舉例:
======\=======\=======
======a=======b=======
上面是2條電路,2個開關分別為a和b,打開狀態:\[1],關閉狀態:/[0].
若同時打開或者關閉,兩條電路均不通.
若a打開[1],b關閉[0],電路1通電
======\=======/=======
若a關閉[0],b打開[1],電路2通電
======/=======\=======
綜上,電路在a,b狀態相同時不通[0],在a,b不同時通電[1].
c語言中for語句是怎使用的
1:for循環
for語句的格式為:
for (初始化語句; 條件語句; 控制語句)
{
語句1 ;
語句2 ;
....
語句n ;
}
for 語句的執行順序是:首先執行“初始化語句”;然後測試“條件語句”;若條件成立,則執行語句1到語句n;然後執行“控制”語句;接著再測試條件語句是否成立,如果成立則重復執行以上過程,直至條件不成立時才結束for循環。如:
for(i=0;i<10;i++)......;
int i,a[]=new int[10];
for (i=0,i<10;i++) a[i]= 0;
這段代碼把整型數組a中的所有元素都賦成0。
你可以在for循環的頭部說明你的變量,而且最後一個表達式可以省略,不過要確定在語句中對變量的值有所改變,如:
for(int i=0;i<=10;) i+=i;
for循環中,“初始化語句”、“條件語句”和“控制語句”都可以省略,但是其間的分號不能省略。例如:
int i =0 ;
for (; ; ;)
{
if i>10 break ;
i = i +1 ;
}
for循環中省略“條件語句”時,在for語句{}中必須包換轉句語句控制程序在某個條件滿足時跳出for循環,否則將形成死循環
2:while循環
while循環和for循環類似,其格式為:
while (條件語句)
{
語句1 ;
語句2 ;
....
語句n ;
}
執行while時,先測試“條件語句”,如果條件成立,則執行語句1到語句n,直至條件不成立時調處循環。
int i=0 ;
while (i<10)
{
i++ ;
System.out.println("Hey!.get me out of here!:);
}
3:do ... while 循環
do ... while 循環語句的格式為:
do
{
語句1 ;
語句2 ;
....
語句n ;
}
while (條件語句) ;
do ...while 語句的功能是首先執行語句1到語句n,然後進行條件測試,如果條件成立,則繼續執行語句1到語句n,否這跳出循環。如:
boolean test=false;
do
{
......
}
while(test);
這種控制並不是很常用,但有時卻非常重要,使用時注意結尾處while語句後的分號。