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

有一天,小白接到一个任务,完成一个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 ( == initCOM)
{
// 读卡器读卡流程为3s,这里设置3.5s读卡一次
readerTimer = new System.Threading.Timer(readerDelegate, null, , );
}
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 ( == retCode)
{
//刷卡成功beep
obj.Extsys_BeepOK();
readerTimer.Change(-, -);
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, "系统提示", , );
if (dr == DialogResult.OK || dr == DialogResult.Cancel)
{
//amb.Dispose();
/*用户验证错误后,5s后启动读卡器*/
readerTimer.Change(, );
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(, );
}
}
else if (- != 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 = -;
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 ( == retCode)
{
//刷卡成功beep
obj.Extsys_BeepOK();
readerTimer.Change(-, -);
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, "系统提示", , );
if (dr == DialogResult.OK || dr == DialogResult.Cancel)
{
//amb.Dispose();
/*用户验证错误后,5s后启动读卡器*/
readerTimer.Change(, );
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(, );
}
}
else if (- != 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();
}
}

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

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

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

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

说两句吧。

WinForm程序界面假死,寻求完美解决方案的更多相关文章

  1. C# Winform 窗体界面”假死”后台线程阻塞 解决办法–BeginInvoke

    原文:C# Winform 窗体界面"假死"后台线程阻塞 解决办法–BeginInvoke 这个方法可以用在任何后台任务耗时较长,造成界面“假死”界面控件不更新的情况. 比如要要执 ...

  2. C#中异步及winform中界面假死

    c#中可以用BeginInvoke去启动异步调用,但是有两个BeginInvoke一个是controller的BeginInvoke还有一个是委托的BeginInvoke. 主要区别是controll ...

  3. WinForm查询大数据界面假死,使用异步调用解决

    用DataGridView无分页绑定一个几千条数据的查询,查询的时候界面直接卡死十几秒,用户体验非常不好,因此用异步操作解决界面卡死的问题原本场景:点击[查询]后,界面直接卡死优化场景:点击[查询]后 ...

  4. Qt多线程和GUI界面假死(run()是线程的入口,就像main()对于应用程序的作用。分析QThread::exec函数的源码,旧的QMutexLocker模式其实很好用,挡住别人进入抢占资源,可照抄)good

    QThread的常见特性: run()是线程的入口,就像main()对于应用程序的作用.QThread中对run()的默认实现调用了exec(),从而创建一个QEventLoop对象,由其处理该线程事 ...

  5. C#多线程解决界面卡死问题的完美解决方案

    C#多线程解决界面卡死问题的完美解决方案 文章转自http://www.sufeinet.com/thread-3556-1-1.html 问题描述: 当我们的界面需要在程序运行中不断更新数据时, 当 ...

  6. 解决VS在高DPI下设计出的Winform程序界面变形问题

    在目前高分屏流行的情况下,windows缩放与布局仍然设置为100%就显得太小(特别是笔记本),通常会调整为125%或150%, VS在缩放与布局设置为非100%的时候,就会自动启动DPI感知模式,以 ...

  7. 如何快速开发树形列表和分页查询整合的WInform程序界面

    我在做Winform界面的时候,一般都是统一化处理,界面顶部放置一些字段条件供查询,下面就是分页查询列表,展示相关的数据.但有时候碰到一些表字段内容分类比较多,有一些特别重要,如果放在一个树形列表来进 ...

  8. C#使用IrisSkin2.dll美化WinForm程序界面

    一.添加控件IrisSkin2.dll. 方法:         1.右键“工具箱”.“添加选项卡”,取名“皮肤”.         2.右键“皮肤”,“选择项”弹出对话框        3.点击“浏 ...

  9. 使用publisher模式控制频繁的UI输出,避免Winform界面假死

    http://www.cnblogs.com/Charltsing/p/publisher.html 最近测试task并发任务的效率与线程池的区别,发现了另外一个问题.task建立任务的速度很快,输出 ...

随机推荐

  1. DWZ (JUI) 教程 dwz框架 刷新dialog解决方案

    在DWZ中进行ajax表单提交后,通过回调函数来返回状态结果,以及返回是否需要刷新父页的navTabId.       DWZ给我们提供了两个回调函数,一个是子窗口为navTab的navTabAjax ...

  2. [Fiddler]Unable to Generate Certificate

    I'm using Fiddler2 (or trying) to capture SSL traffic for a windows desktop gadget hitting an https ...

  3. PHP实现前台同步显示后台任务进度

    一次批量发送几千条短信. 如果直接在后台循环执行虽然可行,但是前台操作用户就只能坐着空等,完全看不到后台执行结果,所以考虑能不能有一种办法可以在php后台执行过程中同时在前台显示后台执行任务进度呢. ...

  4. Exchange 2010先决条件

    为了方便大家一步到位的进行学习,已将各种角色安装所需的先决条件给与总结了,但注意系统需求是2008 R2     1.对于执行客户端访问.集线器传输及邮箱角色典型安装的服务器            ( ...

  5. 【学习笔记】【C语言】循环结构-do while

    用法: while (条件) {     } do {   } while(条件);   while和do-while的区别 1.很多情况下,while和do while可以互换   2.while特 ...

  6. OC5_@class关键字

    // // ClassB.h // OC5_@class关键字 // // Created by zhangxueming on 15/6/24. // Copyright (c) 2015年 zha ...

  7. Integer类的装箱和拆箱到底是怎样实现的?

    先解释一下装箱和拆箱: 装箱就是  自动将基本数据类型转换为包装器类型:拆箱就是  自动将包装器类型转换为基本数据类型. 下表是基本数据类型对应的包装器类型: int(4字节) Integer byt ...

  8. 判断Featureclass的类型

    一个Featureclass可以是Shapefile Feature Class.Personal Geodatabase Feature Class.File Geodatabase Feature ...

  9. band

    #include<iostream> #include<math.h> using namespace std; int calculate(double amount[],i ...

  10. MYSQL IN 与 EXISTS 的优化示例

    文章转载自:http://www.jb51.net/article/53127.htm 当B表的数据集必须小于A表的数据集时,用in优于exists,当A表的数据集系小于B表的数据集时,用exists ...