4

我正在创建一个简单的独立 .net winforms 应用程序。它引用Microsoft.SqlServer.SqlWmiManagement了客户端计算机上可能存在或不存在的 .Net Framework 4 程序集。如果该程序集不存在,那么在运行时我希望我的应用程序能够优雅地失败而不是崩溃。

我有一个开始的组件:

...
using Microsoft.SqlServer.SqlWmiManagement;
...
try {
    // do something that uses SqlWmiManagement
}
catch {
    // handle the missing assembly
}

不幸的是,在我的小尝试块之前,组件加载时会引发未处理的异常。

这样做的正确方法是什么?

没有安装程序,这应该是一个拖放的 exe。

4

5 回答 5

7

如果未找到程序集,则将触发 AssemblyResolve 事件。您可以尝试捕捉它并在那里退出应用程序。请参阅此 MSDN

public static void Main()
{
    // Note: AssemblyResolve occurs when the resolution of an assembly fails.
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}

private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
    if ( args.Name.Contains("SqlWmiManagement"))
    {
        // assembly not found
    }

    return null;
}
于 2013-02-28T23:45:20.013 回答
2

当函数是 JIT 时抛出异常。将您的代码更改为:

void DoSomethingThatUsesSqlWmiManagement_()
{
   ...
}
void DoSomethingThatUsesSqlWmiManagement()
{
   try
   { 
          DoSomethingThatUsesSqlWmiManagement_();
   }
   catch
   {   
            handle the missing assembly
   }
}

您可能应该catch只使用特定的Exception.

于 2013-02-28T23:44:10.703 回答
2

确保包含 Main() 函数的类不引用与程序集相关的任何内容。

如果它被定义为您的主要表单的一部分,请将其放在自己的类中。

然后,在方法的最开始使用 Assembly.Load()。您必须将 SqlWmiManagement.dll 程序集的完全限定名称作为参数传递。

于 2013-02-28T23:53:42.777 回答
2

我想我通过更正代码中的范围解决了这个问题。从这篇文章开始工作:

https://sites.google.com/site/craigandera/craigs-stuff/clr-workings/dealing-with-assembly-load-failure

我将需要的方法移到了Microsoft.SqlServer.SqlWmiManagement一个单独的类中。分离它意味着我可以从一个块内部调用它,该try {}块在导致程序集被隐式加载的范围之前开始。这意味着我可以从失败的程序集加载中捕获异常。

代替:

using Microsoft.SqlServer.SqlWmiManagement;

// .net implicitly loads assembly when current class is instantiated

// ... code ...

try {
   // problem method using missing assembly
} 
catch {
   // this is ineffective because the ass'y load already failed before the try block
}

我以前可以:

try {
    // invoke problem method in another class
    // implicitly loads assembly here instead, inside the try block 
} 
catch {
    // this now catches ass'y load failure
}
于 2013-03-01T20:33:20.723 回答
0

虽然 @CC Inc 的答案在Main()调用之后加载引用时有效,但对于使用静态对象或类的引用,ResolveEventHandler需要在Main()调用之前安装。你可以试试:

private static bool gAutoLoad = ExecuteBeforeMain();

private static bool ExecuteBeforeMain()
{
    // Note: AssemblyResolve only occurs when the resolution of an assembly fails.
    AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveErrorHandler;
    return true;
}

private static System.Reflection.Assembly AssemblyResolveErrorHandler(object sender, ResolveEventArgs args)
{ ... }

我不认为有ExecuteBeforeMain()足够早执行的保证(在尝试加载引用之前),但是当我包含我的记录器时它对我有用,如下所示:

ReferenceNamespace.LoggerClass.StaticActiveLogger.LogMethod(string);
于 2018-06-11T10:58:15.893 回答