WPF 降低.net framework到4.0
1. 问题背景
由于xp系统上面最高只能安装.net framework 4.0,所以公司项目需要将原来项目的.net framework版本降低到4.0,具体的降版本很简单,只要把项目属性中的目标框架改成4.0,编译一下,解决一下出现的问题就可以了。但是在打包同事电脑上,登录界面都正常出来了,但是登录进去后,直接奔溃了。
2.解决问题
一开始检查了项目及其依赖文件的.net framework的版本,都是4.0,直接运行的是生成文件,中间没有更改任何文件,所以开始以下解决问题的道路。
(1)查找奔溃的dmp文件
程序的奔溃文件一般都在C:\Users\XXX\AppData\Local\CrashDumps目录下面,可惜的是,在同事电脑上面,没有发现崩溃文件。
(2)查找系统的事件日志
选中桌面计算机,右击,选择管理,在系统工具-》事件查看器-》自定义视图-》.NET Runtime, 双击,找到奔溃时间点的日志,找到如下的错误信息:
Application: XXXX.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AggregateException
Stack:
at System.Threading.Tasks.TaskExceptionHolder.Finalize()
因为项目中用到Task的地方只有一处,所以注释掉这段代码,在同事电脑上面试了一下,果然OK,程序正常运行。下面就是要看看具体是什么原因造成这个异常。
3. 问题原因
在stackoverflow上提到了这个问题的原因:
If you create a Task, and you don't ever call
task.Wait()
or try to retrieve the result of aTask<T>
, when the task is collected by the garbage collector, it will tear down your application during finalization. For details, see MSDN's page on Exception Handling in the TPL.The best option here is to "handle" the exception.
根据上面的英文,我的理解是:当你创建一个Task,没有调用过task.Wait()或者没有获取它的执行结果,(如果Task中出现了未处理的异常),当这个Task被GC回收时,在GC finalization阶段,会让当前应用程序崩溃。
进一步看MSDN中的Exception Handling (Task Parallel Library):
"Unhandled exceptions that are thrown by user code that is running inside a task are propagated back to the joining thread. ···Exceptions are propagated when you use one of the static or instance Task.Wait or Task(Of TResult).Wait methods···"
翻译:在一个task中运行的代码抛出的未处理异常会被回传给(创建该task的)主线程。···当你调用Task.Wait时,异常才会被回传(给主线程)。
分析:当我们遇到的情况是没调用Task.Wait,也就是异常没有被回传给主线程。下面的这句就提到了这个:
"If you do not wait on a task that propagates an exception, or access its Exception property, the exception is escalated according to the .NET exception policy when the task is garbage-collected."
译:如果你在一个task中没有等待异常被传播,或者访问它的异步特性,在task被GC回收时,该异常会遵循.NET异常策略被逐步升级。
分析:逐步升级的后果就是当前应用程序进程崩溃,对于ASP.NET程序来说,就是应用程序池崩溃。
4. 解决问题
最简单的方法就是捕获一下task中的异常,参考msdn中的代码大概如下:
1 var task1 = Task.Run(() => { throw new CustomException("task1 faulted."); }).ContinueWith( t => { Console.WriteLine("{0}: {1}", t.Exception.InnerException.GetType().Name, t.Exception.InnerException.Message); }, TaskContinuationOptions.OnlyOnFaulted); Thread.Sleep(500);