3

我们有一个用 VB.NET 开发的 COM 组件(我们称之为 MyLib),供我们的 Access 应用程序(我们称之为 MyApp)使用。为此,我们需要使用生成的 MyLib.dll 和 MyLib.tlb 文件进行 COM 注册。当我将这两个文件安装到我们的 MyApp 文件夹中时,一切正常,COM 函数被正确调用。但是,当我将 dll 安装到一个公共文件夹时,我在标题中提到了一个错误 - 我这样做的原因是因为我们希望允许在同一台机器上安装不同版本的 MyApp。所以如果 COM 组件没有改变,我们当然希望在这些不同的版本之间共享它,让 Windows 来做引用计数。

我不确定应该将 MyLib.tlb 文件放在 MyApp 的安装文件夹中,还是与 MyLib.dll 相同的公共文件夹中。但无论如何,我尝试了两个位置,它们都给出了相同的错误。我尝试比较将 MyLib.* 放入 MyApp 文件夹的情况和将 MyLib.dll 放入公共文件夹的情况之间的注册表文件。除了我试图在 HKCR\Wow6432nodes\CLSID{MYCLSID}\InprocServer32 下注册的类的 CodeBase 之外,我看不到任何区别。我不明白的另一件事是在这两种情况下,在 HKCR\Wow6432nodes\CLSID{MYCLSID} 下都没有名为 TypeLib 的子键,

我不完全了解 COM 参考如何用于 Access 应用程序,所以如果我错了,请纠正我。谁能告诉我我做错了什么?注册共享 COM dll 的正确方法是什么(是否将 dll 和 tlb 都放入共享文件夹)?谢谢!

更新:

关于 COM 注册,我使用 WIX 创建 Windows 安装程序和 heat.exe 从 dll 和 tlb 文件中获取信息。生成的文件包括Class、ProgID、TypeLib 和Registry 标签的信息。正如我所提到的,从 WIX 配置的角度来看,我的两种情况之间的唯一区别是我放置 MyLib.dll 文件的位置(我假设将 MyLib.tlb 文件放在 MyApp 安装文件夹中是正确的方法,再次,请更正如果我错了),当我将 dll 和 tlb 文件都放入应用程序的安装文件夹时,它确实有效。这是注册后的一些注册表结构

首先我有 HKCR\CLSID\{MYCLSIDs},它们中的每一个都代表我的一个 COM 类。在名为“InprocServer32”的子项中,我有 Assembly、Class、CodeBase、RuntimeVersion、threadingModel。CodeBase要么是普通文件夹(不工作)要么是MyApp的安装文件夹(工作),这是我放置dll的不同位置。我认为 {MYCLSIDs} 下会有另一个子键 TypeLib,因为 Access 只看到 TypeLib,我认为应该有一些从 TypeLib 到实际 dll 的链接,但是,在这两种情况下,这个子键都丢失了,但在第二种情况它仍然有效。它有问题吗?

其次我有 HKLM\Software\Classes\CLSID\{MYCLSIDs},这个键的结构和上面描述的一样。

第三,HKCR\{MYPROGIDs},这些只是我班级的 ProgID

第四,HKCR\Typelib\{LibID},包含来自tlb文件的信息,这个ID来自COM组件项目的Assembly GUID。

最后是 HKEY_CLASSES_ROOT\Interface\{InterfaceID},有一个名为 ProxyStubClsid32 的子键,其值为 {00020424-0000-0000-C000-000000000046},一个名为 TypeLib 的子键,其值为我的 LibID。

似乎唯一的区别是第一项中的 CodeBase(我可能是错的,但这就是我所看到的)。如果您发现任何问题,请告诉我。谢谢!

第二次更新:

将 MyLib.dll 安装到共享文件夹后,COM 调用失败。但是,如果我将 SHARED_FOLDER\MyLib.dll 的所有 CodeBase 值替换为 INSTALLDIR\MyLib.dll,并将 MyLib.dll 复制到 INSTALLDIR,它实际上可以工作。反之亦然,在我将 MyLib.dll 安装到 INSTALLDIR 后(在这种情况下 COM 正在工作),我将 CodeBase 值从 INSTALLDIR\MyLib.dll 更改为 SHARED_FOLDER\MyLib.dll,并复制到 SHARED_FOLDER,这次它失败了。所以看来,正是安装位置的问题,和我对COM的理解相反。而且我认为 SHARED_FOLDER 没有权限问题(我可能是错的),因为它位于我的安装程序创建的文件夹中。

4

3 回答 3

7

我不完全了解 COM 参考对 Access 应用程序的工作原理,

com 对象在 windows 上的工作方式与 1990 年出现这种东西以来是一样的。所以我们谈论的是一种肉和土豆的支柱或技术,它已经存在了 22 年(而且早在 .net 中如何创建对象之前就已经存在了)。

因此,对于 Access、Delphi、FoxPro 或 VB6 或任何能够使用 com 对象的系统,com 的工作方式都是相同的。

接下来重要的是要记住,对于此类 com 对象,计算机上此类对象的注册表本质上是 GLOBAL。

与 .net 中放置在同一文件夹中的对象的动态链接概念没有任何关系。

所以way.net 的工作方式不需要全局注册(实际上你不需要任何注册!)。

但是,在.net 中,您可以根据需要在全局程序集中注册一个对象(GAC)。所以这是您的选择,但这样的选择不是标准的 windows com 对象,而是 .net 对象的选择。因此,那些.net 对象与我们 22 年来拥有的标准 windows com 对象几乎没有任何关系。

所以 .net 中的大多数对象实际上是 dir 的本地对象,但这不是标准窗口 com 对象的选择。

因此,要将 .net 对象或程序集正确转换为可用且工作的 windows com 对象,则该对象必须将 GLOBAL 注册到计算机。这意味着您不使用 regsvr32,但实际上将使用名为 regasm 的 .net 注册工具(实际上,在为您构建 com 对象桥之后,它最终会为您执行 regsvr32)。另请记住,您需要在此处编译正确的位版本。

如果您部署到其他计算机,则将您的项目设置为生成 2.0 对象,这样您就很有可能在目标计算机上安装了 .net 库。因此,在部署期间,您必须使用 regasm:

这个:C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe

最重要的是不要忘记将 /codebase 添加到 regasm.exe 的命令行使用中,因为您必须在 GAC(全局程序集缓存)中注册 .net 对象。如果您不这样做,那么支持 windows com 对象的非 .net 应用程序将看不到 noir 能够将程序集用作标准 windows com 对象。

因此,在谈论非 .net 对象时,没有注册 ActiveX 的概念或通常在文件夹基础上称为 windows com 对象的概念——它本质上总是全局的。

因此,将 .dll 放入哪个目录或文件夹尚无定论。正确注册该 .dll 后,所有支持 com 对象的系统都可以使用它。

这也意味着在 .net 中的注册过程中,您必须使用全局程序集注册表选项(否则它将不起作用)。

于 2012-07-27T06:31:54.327 回答
2

原来,MyLib.dll 正在使用一些其他的库,这些库仍然安装在 MyApp 安装文件夹中。因此,如果 MyLib.dll 安装在共享文件夹中,它会尝试在相同的库中查找这些库,这当然会失败。当我也将这些库安装在共享文件夹中时,它正在工作。

顺便说一句,我发现 fulogvw.exe 在跟踪程序集加载问题时非常有用。例如,在我的失败日志中,它说无法在 SHARED_FOLDER 中加载文件 xxx.dll,xxx.dll 是 MyLib.dll 正在使用的某个库,我不知道 MyLib.dll 需要它,直到我看到日志。

于 2012-08-01T12:50:55.670 回答
0

我遇到了同样的问题,尝试了所有建议,但没有任何效果。然后我决定使用/reg标志regasm来寻找线索,这让我发现了原因——我的 .net 程序集版本增加了,所以当它注册时,在注册表中创建了新的分支,并且一定发生了一些损坏同时。

这也意味着我无法回滚到以前的工作版本,tlb因为注册表总是会尝试使用最新的。

解决此问题的两种方法是从分支中手动删除所有恶意注册表项CLSID,将程序集的版本号重置为以前的编号,重新构建并重新注册。

或者,在我的情况下,更简单的方法 - 刷新 .net 程序集中的所有互操作指南。只需为每个类和接口以及程序集本身创建新的。然后重建,并使用 regasm 重新注册 tlb,dll 现在有效地成为具有新注册表项的新库。
显然,然后需要删除并阅读 VB6 中的引用,以便将其视为一个新库,然后一切正常。

于 2021-08-30T15:09:25.653 回答