如何在运行时检查所引用的程序集是否可用?

4

我正在创建一个简单的、独立的 .net winforms 应用程序。它引用了 .Net Framework 4 的程序集 Microsoft.SqlServer.SqlWmiManagement,但可能客户端机器上没有该程序集。如果该程序集不存在,则在运行时我希望我的应用程序可以优雅地失败而不会崩溃。

我有一个启动组件:

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

很不幸,在组件加载之前就抛出了未处理的异常,远在我的小尝试块之前。

正确的做法是什么?

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

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;
}

到目前为止,这是我最喜欢的答案。 - caesay

2

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

如果该类是作为主窗体的一部分定义的,请将其移除到单独的类中。

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


谢谢,这个答案让我更接近目标了。 - onupdatecascade

2
当函数被JIT编译时,会抛出异常。请将代码更改为:
void DoSomethingThatUsesSqlWmiManagement_()
{
   ...
}
void DoSomethingThatUsesSqlWmiManagement()
{
   try
   { 
          DoSomethingThatUsesSqlWmiManagement_();
   }
   catch
   {   
            handle the missing assembly
   }
}

您可能只需要捕获特定的 Exception


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
}

0

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

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接