从 Vb 调用时,COM-Interop 程序集未找到本机 (.Net) 依赖项

COM-Interop assembly not finding a native (.Net) dependancy when called from Vb

我有一个从 Visual Basic 6 应用程序调用的 C# COM-Interop 程序集。此程序集发出 HTTP 请求以发送和检索 JSON.

程序集在使用 C# 测试客户端进行测试时工作正常。

但是,在 VB6 应用程序中使用它时,会返回以下错误:

"Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified."

Newtonsoft.Json.dll 与 COM-Interop DLL (TLB) 位于同一文件夹中。

是否需要显式加载 Newtonsoft.Json.dll?或者可能放在 GAC 中?

这是一个标准的 DLL Hell 问题,它是由 Regasm.exe 使用 /codepage 选项引起的。或者,更常见的是,项目 > 属性 > 构建选项卡 > "Register for COM interop" 复选框。两者做同样的事情,他们将 DLL 的路径写入注册表。当您忙于开发和测试项目时,这是一个非常好的选择,它避免了每次进行更改时都必须将 DLL 重新注册到 GAC 中。

但是它而不是所做的是帮助 CLR 找到任何依赖项。正常的探测规则仍然有效,它会在存储 EXE 的目录中查找 appname.exe.config 文件。首先查看 GAC,然后查看依赖项的 EXE 路径。配置仍然在 DLL Hell 的通常受害者的控制之下,谁必须维护 EXE。经常是最终用户。因此,明确地说,它 而不是 查找存储 [ComVisible] DLL 的目录。

这是一种轻微的 DLL 地狱,只是一种普通的文件未找到的事故。比讨厌的那种温和得多,找到一个名称正确但版本错误的文件。一般Newtonsoft.Json.dll的强问题,野外有35个左右的版本。拥有如此多的版本并且它是一个如此受欢迎的库也带来了另一种麻烦,即使用另一个也使用 DLL 的 COM 服务器的程序。但几乎不可避免地会出现不同的版本。往往在您宣布项目完成后很长时间内发生。他们中的一个人会输,有 50-50 的几率是你。最终用户有 100% 的几率。

是的,GAC 解决了这个问题。每个图书馆都得到他们要求的版本。理想情况下,Newtonsoft 会使用将 DLL 部署到 GAC 中的安装程序为您解决此问题。但这并不是开源库编写者想要提供的那种承诺。他们想要(并且需要)让它成为你的问题。 Microsoft 这样做,但他们也有 Windows 更新以确保部署关键错误和安全修复程序。并且有大量人员致力于确保任何新修订始终向后兼容原始版本,因此版本号不必更改。

请注意,您可以利用 Microsoft 的承诺。您还可以使用 DataContractJsonSerializer 和 JavascriptSerializer 类 来完成这项工作。框架的一部分,他们很少弄错。

同时,请记住只是一个找不到文件的问题。您不必在您的开发机器上使用 GAC,如果您不这样做会更好,将文件复制到正确的位置以使 CLR 满意同样容易。这是与您的 VB6 测试程序相同的目录。并且,如果您想使用 VB6 调试器,请将 VB6 的额外怪癖放入 C:\Program Files (x86)\Visual Studio\VB6。部署时请使用 GAC。

Hans 很好地解释了为什么 会发生这种情况。让我提供一个解决方法,使这项工作 无需 在 GAC 中注册 Json DLL 或将其复制到 VB6 EXE 目录。

在您的 COM 可见的 C# 库中,我们可以告诉 .NET 运行时环境在 C# 库的目录而不是 "usual" 路径中搜索 Json DLL。我们通过将我们自己的处理程序附加到 AssemblyResolve 事件来做到这一点:

AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
{
    // We only want this workaround for one particular DLL
    if (e.Name != "Newtonsoft.Json")
        return null;

    var myLibraryFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    var path = Path.Combine(myLibraryFolder, "Newtonsoft.Json.dll");

    return Assembly.LoadFrom(path);
};

关于此解决方法的说明:

  • 只有在您的 C# 库 执行任何可能导致加载 JSON 库 抖动的操作之前,此代码才有效。例如,您的库和 VB6 进程中的任何其他 .NET 库都必须在执行此代码之前调用 JSON 库中引用类型的任何方法。

  • 您修改了整个过程的行为,而不仅仅是您的库。如果您的 VB6 进程使用另一个使用 JSON 的库,您的 "redirect" 也会影响另一个库。