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

本文关键字:运行时 是否 程序集 何检查 检查 引用 | 更新日期: 2023-09-27 18:01:34

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

我有一个组件开始:

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

不幸的是,在加载组件时抛出了一个未处理的异常,远远早于我的try块。

正确的做法是什么?

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

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

如果没有找到程序集,则会触发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;
}

当函数被JIT时抛出异常。将代码改为:

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

你可能应该catch只特定的Exception

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

如果它被定义为主表单的一部分,则将其从自己的类中取出。

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

我认为我通过纠正代码中的作用域解决了这个问题。从本文开始:

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
}

虽然@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);