首页 > 解决方案 > Assembly.Load 及其怪异...“找不到文件或程序集”错误

问题描述

我们的本地化团队正在尝试使用 LocBaml(.NET Framework 版本,4.6.1)来本地化一些资源。他们不断收到错误说“找不到文件或程序集......”所以,我查看了它,看到 x.resources.dll 文件必须与 x.dll 位于同一目录中的注释。(“x”只是表示某个名称)。试过了,还是一样的错误。然后我构建了一个调试版本并下载了 .NET 源代码。事实证明,.NET 内部发生了一些异常。如果我可以总结一下,尝试执行 Assembly.Load("x") 时失败了。所以我写了一个程序试图复制这种情况......

using System;
using System.Reflection;
using System.Linq;

namespace Nothing
{
   class Loader
   {
      public static void Main(string[] args)
      {
         try
         {
            if (args.Count() == 1)
            {
               Assembly asm = Assembly.Load(args[0]);

               Console.WriteLine($"Name of asembly: {asm.FullName}");
            }
            else
            {
               Console.WriteLine("Need to specify filename");
            }
         }
         catch (Exception x)
         {
            Console.WriteLine("Exception: {0}", x);
         }
      }
   }
}

该文件名为 AsmLoader.cs 并编译为 AsmLoader.exe。

从 x.dll 所在的目录,我输入了 \path\to\AsmLoader.exe x.dll 和 \path\to\AsmLoader.exe x。同样的错误,“找不到文件或程序集...”

查看异常的堆栈跟踪,发现“代码库”是堆栈上某个函数的参数。想了想,将 AsmLoader.exe 复制到与 x.dll 相同的目录下。

给 .\AsmLoader.exe x.dll 一个尝试..仍然是同样的错误。请记住,异常的参数只是“x”。试过 .\AsmLoader.exe x .... bingo ... 工作。为了咧嘴笑,将 LocBaml.exe 及其 .config 文件复制到同一目录,并尝试 .\LocBaml x.resources.dll ... ding, ding, ding... 成功。最后。

所以,现在,我只是告诉本地化团队将 LocBaml 复制到与文件相同的目录,一切都应该很好。

但是,我不禁觉得这可以通过代码以某种方式解决。如何更改示例中的代码,以便 AsmLoader.exe 不必与我要加载的 DLL 位于同一目录中?我什至更改了路径环境变量以确保 AsmLoader.exe 和两个 x.dll 目录都在路径中。那没用...

那么我需要改变什么才能让它在我的基础程序中工作......然后也许我可以为 LocBaml 做同样的事情......???

标签: c#.net

解决方案


好吧,我想出了一个将 AssemblyResolve 事件处理程序添加到当前应用程序域的解决方案。解决了这个简单的例子和​​我重建的 LocBaml ......

主要添加:

AppDomain.CurrentDomain.AssemblyResolve += LoadFromCurrentDirectory;

像这样实现 LoadFromCurrentDirectly:

static Assembly LoadFromCurrentDirectory(object sender, ResolveEventArgs args)
{
   string name = args.Name;

   bool bCheckVersion = false;
   int idx = name.IndexOf(',');
   if (idx != -1)
   {
      name = name.Substring(0, idx);
      bCheckVersion = true;
   }

   string sCurrentDir = Directory.GetCurrentDirectory();

   if (!name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !name.EndsWith(".exe"))
   {
      string[] exts = { ".dll", ".exe" };
      foreach( string ext in exts)
      {
         string tryPath = Path.Combine(sCurrentDir, name + ext);
         if (File.Exists(tryPath))
         {
            name = name += ext;
            break;
         }
      }
   }

   string path = Path.Combine(sCurrentDir, name);
   if (!string.IsNullOrEmpty(path) && File.Exists(path))
   {
      Assembly assembly = Assembly.LoadFrom(path);
      if (assembly != null & bCheckVersion)
      {
         if (assembly.FullName != args.Name)
            return null;
      }
      return assembly;
   }
   else
   {
      var reqAsm = args.RequestingAssembly;

      if (reqAsm != null)
      {
         string requestingName = reqAsm.GetName().FullName;
         Console.WriteLine($"Could not resolve {name}, {path}, requested by {requestingName}");
      }
      else
      {
         Console.WriteLine($"Could not resolve {args.Name}, {path}");
      }
   }
   return null;
}

我确信它可以被优化为添加一个全局目录列表,或者在搜索要加载的文件时使用路径。但是,对于我们的用例,效果很好。当我将它添加到我们的 LocBaml 代码中时,它为我们解决了加载问题......也摆脱了将 en\x.resources.dll 文件复制到我们的输出目录的必要性。只要我们从输出目录运行程序,LocBaml 就完成了解析。


推荐阅读