故事的開端是這樣的,小白是一個程序員,他確實也是一個小白,目前還在程序員發展的道路上,兢兢業業的小心求學。
有一天,小白接到一個任務,完成一個Winform程序,附加一個功能就是可以讀IC卡。
小白終於有機會一展身手了!!不免內心興奮。
再聯系了IC卡廠家,拿到開發SDK后,小白不久就碰到了以下難題:
1、廠家的讀卡器是通過API給定的事件ReadCard()驅動的,而讀卡器在ReadCard事件驅動以后,可以在往后的3s以內偵測是否有IC卡片在附近:
(1)3s內,有IC卡在設備附近,立即讀卡,返回讀卡狀態。
(2)3s內如果沒有IC卡在設備附近,則讀卡器等待3s后返回“-3”表示無卡。
2、小白在程序中,對讀卡器獲取的值需要做進一步處理,如Winform登錄。
小白是這樣構想實現他的程序的:
1、設定一個定時器。定時器定時的驅動讀卡設備進行讀卡。
2、獲取讀卡結果以后,在對界面上的內容進行更新。
小白按照這樣的思路寫了這樣的代碼
private void FormLogin_Load(object sender, EventArgs e) { TimerCallback readerDelegate = new TimerCallback(CardReaderDoing);//設定托管 var task2 = System.Threading.Tasks.Task.Factory.StartNew(new Action(() => { try { int initCOM = InitCardThird(); if (0 == initCOM) { // 讀卡器讀卡流程為3s,這里設置3.5s讀卡一次 readerTimer = new System.Threading.Timer(readerDelegate, null, 1000, 3500); } else { string err = "打開串口異常" + initCOM.ToString(); InvokeHelper.Set(label_tips, "Visible", true); InvokeHelper.Set(label_tips, "Text", err); InvokeHelper.Set(label_tips, "ForeColor", Color.Red); } } catch (Exception ex) { string strDateInfo = "出現應用程序未處理的異常:" + DateTime.Now.ToString() + "\r\n"; string str = string.Format(strDateInfo + "Application UnhandledException:{0};\n\r堆棧信息:{1}", ex.Message, ex.StackTrace); log.Error(str); } })); }
接下來是定時事件中小白的處理方法:
private void CardReaderDoing(object stateinfo) { try { int retCode = obj.ReadCard();if (1 == retCode) { //刷卡成功beep obj.Extsys_BeepOK(); readerTimer.Change(-1, -1); string studentCode = obj.GetCardNo(); string userName = obj.GetName(); UserInfo.StudentCode = studentCode; UserInfo.StudentName = userName; UserInfo.AuthToken = CommonHelper.WebMethod.GetAutherizeToken(BaseConfigInfoProvider.ConfigInfo.LeoAppDomain, UserInfo.StudentCode); bool getAuthErrored = CheckObj.CheckErrored(UserInfo.AuthToken); if (getAuthErrored) { string err = CheckObj.CheckAndReturn(UserInfo.AuthToken, "CH"); //DialogResult dr = AutoClosedMessageBox.Show(err, "系統提示", 20, 15); AutoClosedMessageBox amb = new AutoClosedMessageBox(); DialogResult dr = amb.Show(err, "系統提示", 20, 15); if (dr == DialogResult.OK || dr == DialogResult.Cancel) { //amb.Dispose(); /*用戶驗證錯誤后,5s后啟動讀卡器*/ readerTimer.Change(5000, 3500); InvokeHelper.Set(label_ReadNO, "Text", "請刷學生卡"); } } /*用戶驗證成功*/ else { InvokeHelper.Set(label_ReadNO, "Text", studentCode); /*設置最近預約*/ GetCurrentSession(); InvokeHelper.Set(this, "Visible", false); //顯示主窗體 FormMain frm = new FormMain(); DialogResult dr = frm.ShowDialog(); InvokeHelper.Set(this, "Visible", true); InvokeHelper.Set(label_ReadNO, "Text", "請刷卡"); readerTimer.Change(1000, 3500); } } else if (-3 != retCode) { obj.Extsys_BeepERR(); string err = CommonHelper.ErrorDefinition.GetErrMsgByCode(retCode); string err_brief = "Code:" + retCode.ToString(); InvokeHelper.Set(label_ReadNO, "Text", err); InvokeHelper.Set(label_tips, "Text", err_brief); if (readerTimer != null) { readerTimer.Dispose(); } } else { //-3,no card } } catch (Exception ex) { string strDateInfo = "出現應用程序未處理的異常:" + DateTime.Now.ToString() + "\r\n"; string str = string.Format(strDateInfo + "Application UnhandledException:{0};\n\r堆棧信息:{1}", ex.Message, ex.StackTrace); log.Error(str); } }
小白發現了大問題!!
由於讀卡事件來自與第三方SDK,obj.ReadCard()每次調用,如果在無卡狀態下,需要等待3s才能返回讀卡狀態。因此再此出導致了界面阻塞,在運行起來的時候,界面假死了!!
有人告訴小白說,“你應該在 CardReaderDoing事件里,將obj.ReadCard()事件用異步的方式進行處理!比如用Task或者新開一個線程去處理。”也有人告訴小白說,“你試試委托方式唄!”,小白收到大家幫忙的建議很是開心,但是嘗試完后,小白和小白的伙伴們都驚呆了!答案是:官人,不可以!!!!
小白只能怪自己才疏學淺,於是想換個方式試試。BackgroundWork以前小白是用過的,而且可以通過異步的方式,讓IO和界面UI線程分開?
於是小白有了下面的代碼:
//初始worker private void InitializeBackgoundWorker() { this.backgroundWorker1.WorkerReportsProgress = true; this.backgroundWorker1.WorkerSupportsCancellation = true; this.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); //this.backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged); this.backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); } //異步過程處理讀卡 void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; //get result from card reader e.Result = ReadCardResult(worker,e); if (worker.CancellationPending) { e.Cancel = true; } } //read card and return result private int ReadCardResult(BackgroundWorker worker, DoWorkEventArgs e) { int retCode = -3; retCode = obj.ReadCard(); return retCode; } //worker完成 void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { string strDateInfo = "出現應用程序未處理的異常:" + DateTime.Now.ToString() + "\r\n"; string str = string.Format(strDateInfo + "Application UnhandledException:{0};\n\r堆棧信息:{1}", e.Error.Message, e.Error.StackTrace); log.Error(str); //MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { //do nothing } else { int retCode =(int)e.Result; try { if (1 == retCode) { //刷卡成功beep obj.Extsys_BeepOK(); readerTimer.Change(-1, -1); string studentCode = obj.GetCardNo(); string userName = obj.GetName(); UserInfo.StudentCode = studentCode; UserInfo.StudentName = userName; UserInfo.AuthToken = CommonHelper.WebMethod.GetAutherizeToken(BaseConfigInfoProvider.ConfigInfo.LeoAppDomain, UserInfo.StudentCode); bool getAuthErrored = CheckObj.CheckErrored(UserInfo.AuthToken); if (getAuthErrored) { string err = CheckObj.CheckAndReturn(UserInfo.AuthToken, "CH"); //DialogResult dr = AutoClosedMessageBox.Show(err, "系統提示", 20, 15); AutoClosedMessageBox amb = new AutoClosedMessageBox(); DialogResult dr = amb.Show(err, "系統提示", 20, 15); if (dr == DialogResult.OK || dr == DialogResult.Cancel) { //amb.Dispose(); /*用戶驗證錯誤后,5s后啟動讀卡器*/ readerTimer.Change(5000, 3500); InvokeHelper.Set(label_ReadNO, "Text", "請刷學生卡"); } } /*用戶驗證成功*/ else { InvokeHelper.Set(label_ReadNO, "Text", studentCode); /*設置最近預約*/ GetCurrentSession(); InvokeHelper.Set(this, "Visible", false); //顯示主窗體 FormMain frm = new FormMain(); DialogResult dr = frm.ShowDialog(); InvokeHelper.Set(this, "Visible", true); InvokeHelper.Set(label_ReadNO, "Text", "請刷學生卡"); readerTimer.Change(1000, 3500); } } else if (-3 != retCode) { obj.Extsys_BeepERR(); string err = CommonHelper.ErrorDefinition.GetErrMsgByCode(retCode); string err_brief = "Code:" + retCode.ToString(); InvokeHelper.Set(label_ReadNO, "Text", err); InvokeHelper.Set(label_tips, "Text", err_brief); if (readerTimer != null) { readerTimer.Dispose(); } } else { //-3,no card } } catch (Exception ex) { string strDateInfo = "出現應用程序未處理的異常:" + DateTime.Now.ToString() + "\r\n"; string str = string.Format(strDateInfo + "Application UnhandledException:{0};\n\r堆棧信息:{1}", ex.Message, ex.StackTrace); log.Error(str); } } }
而小白通過修改定時器里的代碼是:
private void CardReaderDoing(object stateinfo) { if (!backgroundWorker1.IsBusy) { backgroundWorker1.RunWorkerAsync(); } }
小白和他們小伙伴們再次驚呆了!!!界面啟動,還是因為讀卡器讀卡的問題,界面假死!!
各位大哥、大姐、妹子、帥哥、老道、貧尼們,這是為蝦米啊!!
小白正在為此尋找一個完美的解決方案。
各位大哥、大姐、妹子、帥哥、老道、貧尼們,你們別只是路過!小白請指教了!!
說兩句吧。
