【问题标题】:COM-Interop assembly not finding a native (.Net) dependancy when called from Vb从 Vb 调用时,COM-Interop 程序集未找到本机 (.Net) 依赖项
【发布时间】:2016-10-10 08:10:12
【问题描述】:

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

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

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

“无法加载文件或程序集 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' 或其依赖项之一。系统找不到指定的文件。”

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

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

【问题讨论】:

  • 所以您在 COM 中注册的不是 Json.Net,而是另一个使用 Json.Net 的 .Net 程序集?
  • 此错误是否来自您的 C# 互操作程序集?您可能希望使用 Process Monitor 之类的工具来精确观察您的应用程序在尝试查找 Newtonsoft.Json.dll 文件时所查找的位置。
  • @Liam 是的。使用标准 Newtonsoft.Json 程序集的 COM 互操作 DLL。
  • 我认为您需要将 Newtonsoft dll 与 COM dll 放在同一目录中。这是一个依赖项,因此需要能够访问它。那就是说我的 COM 生锈了
  • @Liam COM DLL 所需的任何 DLL 都会在构建项目时由 VS2012 自动复制到那里。所以,是的,它们与 COM DLL 在同一个文件夹中。

标签: c# json.net dependency-management com-interop


【解决方案1】:

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

在您的 COM 可见 C# 库中,我们可以告诉 .NET 运行时环境在 C# 库的目录中搜索 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);
};

有关此解决方法的说明:

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

  • 您修改整个过程的行为,而不仅仅是您的库。如果您的 VB6 进程使用另一个使用 JSON 的库,那么您的“重定向”也会影响另一个库。

【讨论】:

  • 我故意省略了这一点,重要的是要指出其中的缺陷。首先是先有鸡还是先有蛋的问题,您的代码开始运行之前 的抖动可能需要DLL。更糟糕的是,它会导致 more DLL Hell,现在您的代码负责破坏另一个使用该库的 COM 服务器。当其他人也这样做时,它就会停止工作。
  • @HansPassant:嗯,有时这种解决方法是两害相权取其轻。我们将它用于基于 Office-VBA 的项目,您建议的解决方案(GAC 安装或将 DLL 放入 EXE 目录)需要管理权限 - 这不是一个选项。无论如何,我宁愿在这里发布解决方法,我可以在其中附加警告并确保干净且侵入性最小的代码,而不是让读者继续从某些 MSDN 线程中搜索和复制并粘贴一些写得不好的解决方案。
  • 这个方法很好用,谢谢你提供这个。我不想在 GAC 中存储另一个程序集。
【解决方案2】:

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

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

这是一种温和的 DLL Hell,只是一个普通的文件未找到事故。比讨厌的那种要温和得多,找到一个名称正确但版本错误的文件。一般来说,Newtonsoft.Json.dll 的一个严重问题,大约有 35 个版本。有这么多版本并且它是一个如此受欢迎的库也引发了另一种讨厌,即使用另一个也使用 DLL 的 COM 服务器的程序。但几乎不可避免地会出现不同的版本。在您宣布项目完成后很长时间内往往会发生。其中一个会输,50-50 赔率就是你。最终用户的几率为 100%。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多