WinForm程序界面假死,寻求完美解决方案_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > WinForm程序界面假死,寻求完美解决方案

WinForm程序界面假死,寻求完美解决方案

 2013/8/2 10:08:59  funying  博客园  我要评论(0)
  • 摘要:故事的开端是这样的,小白是一个程序员,他确实也是一个小白,目前还在程序员发展的道路上,兢兢业业的小心求学。有一天,小白接到一个任务,完成一个Winform程序,附加一个功能就是可以读IC卡。小白终于有机会一展身手了!!不免内心兴奋。再联系了IC卡厂家,拿到开发SDK后,小白不久就碰到了以下难题:1、厂家的读卡器是通过API给定的事件ReadCard()驱动的,而读卡器在ReadCard事件驱动以后,可以在往后的3s以内侦测是否有IC卡片在附近:(1)3s内,有IC卡在设备附近,立即读卡
  • 标签:程序 解决方案 for 解决 完美 winform

   故事的开端是这样的,小白是一个程序员,他确实也是一个小白,目前还在程序员发展的道路上,兢兢业业的小心求学。

   有一天,小白接到一个任务,完成一个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();
                Thread.Sleep(1000);
                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;
            this.BeginInvoke(new Action(() =>
            {
                 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();
     }
 }

小白和他们小伙伴们再次惊呆了!!!界面启动,还是因为读卡器读卡的问题,界面假死!!

各位大哥、大姐、妹子、帅哥、老道、贫尼们,这是为虾米啊!!

小白正在为此寻找一个完美的解决方案。

各位大哥、大姐、妹子、帅哥、老道、贫尼们,你们别只是路过!小白请指教了!!

说两句吧。

发表评论
用户名: 匿名