【问题标题】:How do I expose a .netstandard2.0 library with COM for use in VB6?如何使用 COM 公开 .netstandard2.0 库以在 VB6 中使用?
【发布时间】:2020-06-17 19:28:00
【问题描述】:

我有一个 dotnet 核心库、一个框架 4.7.2 库和一个 vb6 应用程序。

我想编写一个通用库供他们所有人访问,因此选择 .netstandard2.0

我尝试了 .netstandard2.0 库和 vb6 之间的 4.7.2 框架包装库。

但是我遇到了assembly binding problems

看着the docs我明白了

在 .NET Core 中,与 .NET Framework 相比,将 .NET 对象公开给 COM 的过程已大大简化。

但是没有提到.netstandard2.0

尽管我的项目使用的是 .netstandard2.0,但我还是决定尝试遵循文档

我已按照有关生成 COM 主机的说明进行操作,在这种情况下,应该生成输出文件 ProjectName.dll、ProjectName.deps.json、ProjectName.runtimeconfig.json 和 ProjectName.comhost.dll。

但是 ProjectName.comhost.dll 和 ProjectName.runtimeconfig.json 不会创建。

我在dotnet standard issue 中看到,Microsoft 计划在“Preview 4”中提供工具支持

我正在运行 VS 16.4.5

[更新]

我决定尝试制作一个 .net 核心包装库并为 com 启用它。

我能够通过 nuget 包将我的 .netstandard 添加到包装库(我使用 azure devops 构建 .netstandard 库)

当我构建包装器库时,会在 bin\Debug\netcoreapp3.1 文件夹中创建 .dll、.deps.json、.pdb、.runtimeconfig.dev.json 和 .runtimeconfig.json 文件。

但是 bin\debug 文件夹中没有任何 .netstandard 库文件。

我将 .netstandard 库和 .netcore 包装器库复制到同一个文件夹并运行

regsvr32 MyCoreComWrapper.comhost.dll  

但是没有创建我需要能够从 VB6 使用的 .tlb 文件

我在文档中注意到以下内容

与 .NET Framework 不同,.NET Core 不支持 从 .NET Core 程序集生成 COM 类型库 (TLB)。这 指导是手动编写 IDL 文件或 C/C++ 标头 COM 接口的本机声明。

我找到了一些信息 on github,但我希望获得制作 .tlb 的分步指南

我考虑过使用后期绑定,但不确定如何将它与 com 库一起使用。

[更新]

我在GitHub 上放了一个示例项目,其中包括一些VB6 文件。 使用 VB6 引用框架库引用的 .tlb。

当我尝试运行时,我得到了

Could not load file or assembly 'Microsoft.EntityFrameworkCore, Version=3.1.2.0, 
Culture=neutral, PublicKeyToken=adb9793829ddae60' or one of its dependencies. The system cannot find the file specified.

所以我将框架测试项目中的所有文件复制到我的 vb6 文件夹中,重新构建并运行。

然后我得到了错误

Could not load file or assembly 'Microsoft.Extensions.DependencyInjection.Abstractions, Version=3.1.0.0, 
Culture=neutral, PublicKeyToken=adb9793829ddae60' or one of its dependencies. The system cannot find the file specified.

我看到文件 Microsoft.Extensions.DependencyInjection.dll 与文件版本 3.100.220.6706 一起存在

【问题讨论】:

标签: com vb6 com-interop .net-standard-2.0


【解决方案1】:

关于 .NET 标准,我可能错了,但我认为这在这里不适用,因为 COM 互操作的东西比 .NET 标准所针对的级别更高;对于 COM 互操作,我们只能讨论 .NET Core 或 .NET Framework。

如果你想生成一个类型库,你有几个选择。

到目前为止,最简单的方法就是使用 .NET Framework。您想要创建类型库的事实已经否定了 .NET Core 的优势,因为几个 COM,尤其是“自动化”功能仅适用于 Windows。至少在 .NET Core 5 出现之前,使用框架会很好。

也就是说,如果您有使用 .NET Core 的商业原因,但仍需要 COM 支持,包括类型库,那么基于 this GitHub comment,您应该能够编译自己的 IDL。请注意,这需要您安装 C++ 构建工具,因为 MIDL 编译器实际上并不是一个独立的东西,如果没有其他 C++ 构建工具,您就可以获得。

It is strongly suggested to have had read the documentation on how .NET Core handles COM activation.

假设拥有 C++ 构建工具对您来说不是障碍,那么步骤如下:

1) 创建一个.idl 文件,该文件以 IDL 格式定义所有 COM 接口。这需要在 .NET 接口和 COM 接口之间进行一些转换。下面是一个部分示例,说明您需要如何在 IDL 中定义的 C# 接口和 COM 接口之间进行转换:

[
  Guid("<some gooey>"),
  InterfaceType(ComInterfaceType.InterfaceIsDual)
]
public interface IFoo 
{
   string Bar { get; }
   string Baz(int Fizz);
}

将被翻译成 IDL:

[
  uuid(<assembly gooey>),
  version(1.0)
]
library myFoo
{
  [
    uuid(<some gooey>),
    object,
    dual
  ]
  interface IFoo : IDispatch {
    [propget] HRESULT Bar([out, retval] BSTR* retVal);
    HRESULT Baz([in] long Fizz, [out, retval] BSTR* retVal);
  }
}

一旦您定义了.idl 文件并且它是一个准确的表示,您就可以使用 MIDL 将.idl 文件编译成.tlb 文件。通常类似于midl foo.idl /tlb: foo.tlb。您应该使用MIDL language reference 来帮助您编写.idl 文件。作为一种快速入门方法,您可以将 C# 接口复制到 .NET 框架项目,使用 tlbexp,然后使用 oleview(可通过 Visual Studio 开发人员命令提示符获得)或 olewoo 查看生成的 IDL 文件让你开始。

下一步是创建注册表项,以便您的 CLSID 可以引用类型库。您需要将程序集的 GUID 放在手边,并且它还必须在 .idl 文件中用作 libraryuuid

使用IFoo 接口示例,您需要创建类似于以下的注册表(使用 .reg 格式以便于共享/理解并假设每个用户安装,而不是每个机器):

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}]
@="IFoo"

[HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}\ProxyStubClsid32]
@="{00020424-0000-0000-C000-000000000046}"

[HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}\TypeLib]
@="{assembly gooey}"
"Version"="1.0"

您还需要根据需要在CLSIDInterfaceTypeLibRecord 中创建注册表。 This article provides a good overview of all registry keys but keep in mind it's assuming .NET framework, not .NET Core, so not all keys are applicable, especially under the CLSID branch.

请注意,当您运行regsvr32 时,它通常会在CLSIDInterface 分支中创建密钥,但您需要在Interface 的分支下添加TypeLib 密钥,并且TypeLib 分支的条目。如果您想支持CreateObject 功能,您还需要创建ProgId 键。

最初,您可以只从一个可以手动更新和维护的.reg 文件开始,但如果您有多个对象,则需要自动执行此操作。这也可以通过DllRegisterServer 调用进行管理,这样当您执行regsvr32 时,它将负责注册密钥。另一方面,您现在正在使用注册码污染您的代码库。有些人选择使用安装程序来编写注册表项。

希望对您有所帮助!

【讨论】:

  • 谢谢。不幸的是,由于我遇到的程序集绑定问题,我无法使用框架库。我将在学习曲线上调查您的答案。
  • 天啊...我认为看这么多步骤不值得。
【解决方案2】:

问题是由于从 VB6(IDE 或编译的 .exe 文件)运行时程序集绑定解析失败。

解决方法如下:

  • 编译VB工程为例,假设编译后的文件为Project1.exe
  • 将所有 .NET 程序集(包括 x86 和 x64 目录,如果本地化版本很重要,还包括语言目录)复制到已编译的 VB6 文件旁边
  • 现在运行Project1.exe,你会得到这样的错误:

该错误显然是除Project1.exe 文件之外的程序集版本与引用程序集的版本之间的不匹配(不是您自己创建的引用,而是嵌入在这些程序集中的引用...)。当您启动一个 .NET 程序时,您不会看到这一点,因为解析是一个非常复杂的过程,它取决于很多参数(而且使用 .NET Core、Framework、Standard、nugets 等并没有变得更好)。

为了进一步检查它是否是不匹配错误,您还可以使用 SDK 中的Fuslogvw.exe (Assembly Binding Log Viewer) 工具。

现在我们知道这是一个程序集版本不匹配的问题,你可以做的是创建一个名为 Project1.exe.config 的文件,除了 Project1.exe 并添加 assembly binding redirects 到它。

配置它的最简单方法是将所有可能的版本重定向到包含您的程序的目录中存在的版本,因此在您的情况下(从今天开始,所有这些都可以发展......),它将是像这样的东西,可能对于您直接或间接引用的 每个 程序集:

<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      ...
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" />
        <!-- 3.1.2.0 is the version of the assembly you ship -->
        <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="3.1.2.0" />
      </dependentAssembly>
      ...
    </assemblyBinding>
  </runtime>
</configuration>

不幸的是,有很多附属程序集,使用正确的信息创建所有重定向有点乏味,所以我创建了一个工具来创建一个 .config 文件,其中自动为所有 .NET 程序集配置重定向给定目录:https://github.com/smourier/BindingRedirectGenerator

如果您希望它也适用于 VB6 IDE,则必须在 VB6.exe.config 文件中使用相同的过程,而不是 VB6.exe

【讨论】:

    【解决方案3】:

    给自己的提醒

    • 使用演示 UI 访问原始 DLL 以确认调用正常。 (如果你不能让它工作,请跳到使用 BindingRedirectGenerator 为单元测试项目制作 App.Config)

    • 在 com 可见项目中添加单元测试以确认 通话有效。

    • 将两个项目创建的所有 dll 复制到 发布文件夹

    对于每个 com 可见的 dll 以管理员身份运行

    c:\windows\microsoft.net\framework\v4.0.30319\regasm /verbose /codebase /tlb:MyLibrary.tlb c:\myproject\releasedlls\MyLibrary.dll
    
    • 安装 BindingRedirectGenerator 到 c:\brg 说

    • 在命令提示符下将目录更改为 c:\brg

        BindingRedirectGenerator c:\myproject\releasedlls App.config
      
    • 将 App.config 重命名为 MyVB6Project.exe.config 并将其复制到与 MyVB6Project.exe 相同的文件夹中

    如果要在vb6 ide中运行,记得设置vb6.exe文件夹的文件

    将整个过程放在一个脚本中以备将来使用(我使用了.bat)

    密切关注 nuget 在 app.config 中添加的内容

    注意构建时的黄色警告!

    【讨论】:

      猜你喜欢
      • 2010-09-21
      • 2010-11-17
      • 2011-01-01
      • 2012-03-09
      • 2018-02-17
      • 2015-01-22
      • 2021-08-26
      • 2016-12-21
      • 1970-01-01
      相关资源
      最近更新 更多