【问题标题】:WPF handling missing dllsWPF 处理丢失的 dll
【发布时间】:2012-11-27 14:31:53
【问题描述】:

我注意到在某些情况下,我不分发应用程序所需的特定 dll,应用程序不会提供任何错误,它只是不加载(显示在屏幕上)。它的行为就像你没有启动它一样。

有人知道为什么会这样吗?

编辑:重现步骤:

  1. 使用 WPF 应用项目和类库项目 (ReferenceDll) 创建解决方案。
  2. 在 WPF 应用项目中添加对类库项目的引用。
  3. 在类库中,将此方法添加到Class1

    public int Calculate(int a, int b) { 返回 a + b; }

  4. 将此代码放在 App.OnStartup 中:

    try
    {
        int result = new ReferenceDll.Class1().Calculate(2, 4);
    }
    catch (Exception ex)
    {
        File.WriteAllText(@"D:\Test\error.txt", ex.ToString());
    }
    
  5. 构建解决方案,然后从 bin 文件夹中删除 ReferenceDll。运行应用程序。

不会创建文件,不会显示应用程序。即使您在主视图中移动 Try catch 代码,在某些按钮单击事件中,Catch{} 也永远不会被触发,它会显示非信息性消息

AppName has stopped working.

并提供调试选项,这对最终用户没有用处。

【问题讨论】:

    标签: wpf .net-4.0


    【解决方案1】:

    如果您没有任何 Trace.WriteLine 语句,那么我建议您添加一些。添加一些到异常处理程序。您也可以使用以下免费工具来捕获跟踪并尝试以这种方式缩小范围。

    http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

    您还可以覆盖 app.xaml.cs 中的 Onstartup 方法并从那里实例化您的应用程序并捕获任何异常并输出以进行跟踪。

    更新

    我已经尝试了您在更新中指出的步骤,并且应用程序加载没有任何问题。我建议的下一件事是检查您已部署到的环境中的 .net 框架版本。如果您需要帮助,请查看此链接:

    http://msdn.microsoft.com/en-us/kb/kbarticle.aspx?id=318785

    更新

    将您的错误日志记录在类似于此问题答案中的处理程序中,您将看到错误。刚刚用你的例子试过这个,它奏效了。错误告诉你究竟出了什么问题:

    System.IO.FileNotFoundException:无法加载文件或程序集“ReferenceDll,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”或其依赖项之一。该系统找不到指定的文件。 文件名:'ReferenceDll,版本=1.0.0.0,文化=中性,PublicKeyToken=null' 在 WPFApp.App.OnStartup(StartupEventArgs e) 在 System.Windows.Application.<.ctor>b__1(未使用的对象) 在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(委托回调,对象 args,Int32 numArgs) 在 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

    警告:程序集绑定日志记录已关闭。 要启用程序集绑定失败日志记录,请将注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) 设置为 1。 注意:有一些与程序集绑定失败日志相关的性能损失。 要关闭此功能,请删除注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog

    WPF window crashes on startup, or it starts but hangs and does not render contents

    【讨论】:

    • 我认为重现步骤中缺少的是您需要手动删除引用的 dll,如原始问题中所述。
    • 这是第5步),从BIN文件夹中删除ReferenceDll,然后启动应用程序(当然是从bin文件夹)。
    • 是的,我的错,我昨晚尝试时错过了这个。感谢您指出@pangabiMC。
    • 不,我的错,我忘了添加它。 :)
    【解决方案2】:

    在辅助线程上抛出异常时可能会发生这种情况。见this page的备注部分:

    独立或浏览器托管的 WPF 应用程序使用 Application 类来检测未处理的异常(请参阅 DispatcherUnhandledException)。但是,Application 只能检测在 Application 类正在运行的同一线程上引发的未处理异常。通常,一个应用程序会有一个主用户界面 (UI) 线程,因此 Application 类的未处理异常检测行为就足够了。但是,主 UI 线程上的 Application 类不会自动检测辅助线程上引发的未处理异常。

    您可以尝试使用此事件来catch 检测异常并记录错误: AppDomain.UnhandledException Event

    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
    
    static void MyHandler(object sender, UnhandledExceptionEventArgs args) {
       Exception e = (Exception) args.ExceptionObject;
       Console.WriteLine("MyHandler caught : " + e.Message);
    }
    

    更新:

    除了线程问题之外,如果您将 try...catch 块放在错误的位置,也可能是原因。考虑这个例子:

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
    
            Do();
    
        }
    
        private void Do()
        {
            try
            {
                int result = new ClassLibrary1.Class1().Calculate(2, 4);
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("MyHandler caught by try...catch: " + ex.Message);
            }
        }
    }
    

    这将导致调用 Do() 的行出现异常,因为此处的 CLR 尝试在此时解析程序集。异常未被捕获,应用程序终止。

    但是如果你试试这个:

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
    
            try
            {
                Do();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("MyHandler caught by try...catch: " + ex.Message);
            }
        }
    
        private void Do()
        {
            int result = new ClassLibrary1.Class1().Calculate(2, 4);
        }
    }
    

    输出是:

    MyHandler 被 try...catch 捕获:无法加载文件或程序集“ClassLibrary1,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”或其依赖项之一。系统找不到指定的文件。

    请注意,当您在引用程序集的同一函数中订阅 UnhandledException 事件时,它不会被触发。这也有效:

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
    
            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += new UnhandledExceptionEventHandler(
                (sender, args) =>
                {
                    Exception ex = (Exception)args.ExceptionObject;
                    Console.WriteLine("MyHandler caught by UnhandledException handler: " + ex.Message);
                });
    
            Do();
        }
    
        private void Do()
        {
            int result = new ClassLibrary1.Class1().Calculate(2, 4);
        }
    

    结果:

    UnhandledException 处理程序捕获的 MyHandler:无法加载文件或程序集“ClassLibrary1,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”或其依赖项之一。系统找不到指定的文件。

    希望对你有帮助。

    【讨论】:

    • 第二个问题不是问题,我已经更新了问题并给出了重现的步骤。
    • .net 中的程序集在调用使用程序集的函数时按需加载。在您的情况下,它是 OnStartup,这就是未捕获异常的原因。尝试将对Calculate 函数的调用置于App 类中的方法CallCalculate 并从App.OnStartup 调用该方法。如果将 CallCalulate 放在 try catch 块中,则会捕获丢失的 dll 异常。
    • 如果您将此代码放在 App.Startup 中并在另一个函数中调用计算,我上面的解决方案也将起作用。这是一种在让应用死机之前让用户了解未处理异常的通用方式。
    • 在我的情况下,正如我所说,我在 MainView 上的按钮单击事件中复制了 try..catch 块,它立即显示消息:AppName 已停止工作,并提供调试它。此外,您的解决方案也将不起作用。您是否尝试过您的建议? UnhandledExcewptionEventHandler 在所有应用程序中都是必须的,所以我总是使用它。
    • 我明白你的意思,但我不能说我喜欢它。那么,这是否意味着避免这种情况的唯一方法是将对外部 dll 的调用包装到单独的方法中?用于构建 UI 的 3rd 方 dll 怎么样。我无法控制这些 dll,所以我无法执行 Try...catch... 那我该怎么办?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-23
    • 2017-06-28
    • 2013-06-18
    相关资源
    最近更新 更多