c# - 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 做同样的事情......???
解决方案
好吧,我想出了一个将 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 就完成了解析。
推荐阅读
- python - Jira-Python 项目密钥
- java - Spring Batch - 重新触发服务激活器
- azure-devops - Azure DevOps VsTest 任务失败且没有错误
- javascript - 如何将 Formik 的 onSubmit 放在另一个组件上?
- java - 无法使用 Spring Boot 和 JPA 在 MySQL 8 DB 中插入数据
- javascript - 无法将 undefined 或 null 转换为对象 js
- node.js - 如何使用具有正确标头和 SHA512 散列令牌的节点生成正确的 TOTP?
- ffmpeg - ffmpeg:如何加速(并仅保留)输入视频的指定部分
- sql - 如何将一个表中的两列连接到 SAS 中基于匹配条件的不同表
- reactjs - 如何处理打字稿中的数字输入?