把最近踩的坑总结一下(2)——二级MSoffice题库软件自动更新版本V2.0

     软件第一次可以正常运行,但是第二次只在任务管理器的进程菜单中出现应用程序的进程名称,而且主界面也无法显示。

        这个问题相当诡异,以至于开始的很长时间里不知道问题出在哪。在win7测试机上具体表现为:双击软件快捷方式后第1次界面可以显示,如果退出软件再启动时就没有任何反应,再次双击图标弹出重复运行警告。因为代码做了重复运行检测,说明程序已经运行,但是界面呢?为什么没有界面?也没有异常提示。打开任务管理器,程序集名称安静的在进程列表里躺着,岁月静好。真是见鬼了,手动干掉进程,再来。依然如此,没有任何界面。在自己的开发机上运行一切正常,主界面正常显示,更新提示可以正常弹出。在另外的一台win7和win8机器上这个问题时有时无。

        没办法,首先百度。还真有人碰到类似的问题,任务管理器里有进程,界面不显示。认真看了一下他的问题描述及解决办法,原因是在界面构造函数调用初始化函数时,目标机器上没有对应字体,造成控件渲染不下去,程序直接停在那里。对照他的解决办法对自己程序的控件初始化函数进行单步调试,一切正常,没有问题。而且,我的问题是界面有时可以出现,有时无法出现,所以应该不是界面初始化的问题。虽然这个答案对我解决问题没有帮助,但是贴主解决问题的思路值得学习,而且他程序有完整的日志功能,可以帮助分析解决问题,而我的程序根本没有日志功能。

        在开发机上调试运行时,功能正常,但是会出现System.InvalidOperationException类型的异常,提示线程间操作无效,从不是创建控件“FrmUkeMain”的线程访问它。解释一下,“FrmUkeMain”是我的主界面类,在主界面的Load事件中,使用了一个委托的BeginInvoke函数异步执行检查是否有可用的版本更新,使用回调函数的方式来显示结果,如果可以更新就弹出一个提示更新的窗口。因为使用MessageBox显示有时会被主界面挡住,用户稍不注意就看不见这个更新提示。所以我把这个提示做成了一个窗口,然后以showDialog的方式显示出来。一般情况下,如果窗口以showDialog的方式显示,会阻止调用窗口的消息响应,只有弹出窗口结束,调用窗口才能继续工作。只是这一次,虽然提示窗口显示正常显示出来,但是主窗口依然可以继续响应消息。(真是见鬼了,我当时压根没注意到弹出窗口和主窗口根本不在一个线程。)如果主窗口可以继续响应,那么弹出窗口仍然可能会被阻挡,问题依然存在。查看帮助文档发现showDialog还有一个重载方法,可以“将窗体显示为具有指定所有者的模式对话框”,这个方法也许管用。然后我就将主窗口的this引用传给了showDialog方法,再次运行达到预期效果。只是在调试时总会出现一开始所说的异常,然后我又按照网上的方式这个把窗体的CheckForIllegalCrossThreadCalls属性改为false,这样就不会用这个出现这个异常了。这里提示一下,正是从传this引用到关闭跨线程调用的异常检查这一系列的动作,才造成了后面的问题。所以说,有异常就要彻底解决,不要使用这种掩耳盗铃的解决方法。

       好了,在开发机上这一套动作下来,没有异常了,功能运行也正常,一切看似很完美。但是,在测试机器上界面时有时无。何解?在开发机上调试也没有什么眉目。最后没办法,索性把项目文件放到测试机器上调试。将断点设置在showDialog(this)调用的位置,然后单步执行。主界面可以弹出,但是一片空白,没有控件显示,也没有异常出现。来来回回试了几次都是这样,也没找到问题原因。最后索性不打断点,直接F5启动,跟之前一样窗口无显示,没有任何东西出现,就好像程序没有运行一样,但是任务管理器有承载进程。就这样等了一会,然后弹出这个下面这个窗口:

        “上下文切换死锁”,这是什么鬼?对CLR运行机制不太了解,但是隐隐感觉到应该是异步调用出了问题。先把异步调用代码注释掉,再次运行,窗口可以正常显示,除更新意外的其他功能正常,所以问题就位于异步调用代码中。异步调用代码中可能引起死锁的应该就是那个主窗口的this引用,把this引用去掉,然后开启异步功能。这次主窗口可以正常显示,除了更新提示窗口可能被遮挡外功能都正常。看来这个主窗口的this引用就是引起死锁的元凶了。至于为什么会引起死锁,应该和我在窗体的Load事件中使用异步并传入主窗体的this引用有关。其实之前编程中遇到控件跨线程调用,在调试时报异常,但是运行的时候一般可以正常运行,所以一开始也没当回事。现在算是给个提醒了,还是不要跨线程调用,非要在跨线程调用的话还是使用安全的方式,比如委托。

        好了,既然问题找到了,那就想办法解决。不使用委托的BeginInvoke方法来实现异步检查更新,改用BackgroudWorker类来实现,这样就不存在跨线程调用主线程控件的问题了。

       PS:在出现ContextSwitchDeadlock这个问题的时候,我在百度经验上看到一个解决办法是——将“调试”异常设置里的“ContextSwitchDeallock”这个异常引发设置选项不选中。呃。。。这真是一个眼不见心不烦的解决办法。这让我想到那句话:既然我们无法解决问题,那么我们就解决提出问题的人。

原文地址:https://www.cnblogs.com/zhang-15-506/p/7890717.html