【问题标题】:Implements vs Binary Compatibility实现与二进制兼容性
【发布时间】:2018-06-21 21:10:21
【问题描述】:

我有一个暴露类INewReport 的VB6 ActiveX DLL。我向这个类添加了一些新方法,并且能够重建它并保持二进制兼容性。

我有第二个 DLL,它公开了一个类 clsNewReport,它使用以下方法实现第一个类:

Implements RSInterfaces.INewReport

由于我向INewReport 添加了新方法,因此我还必须将这些新方法添加到clsNewReport

但是,当我尝试编译第二个 DLL 时,我得到了二进制兼容性错误“...类在版本兼容的组件中实现了一个接口,但在当前项目中没有” .

我不确定这里发生了什么。既然我只是添加到类中,为什么我不能保持与第二个 DLL 的二进制兼容性?有没有办法解决这个问题?

【问题讨论】:

  • VB6 中二进制兼容性的一个怪癖是您可以添加方法(这是怪癖),但必须附加它们。如果您碰巧在签名中已经存在的任何方法之前添加了任何方法(意思是,没有将它们放在代码窗口的底部),您将破坏兼容性。这可能是问题所在?
  • @BobRodes:听起来不太对劲。你有任何链接支持吗?
  • 看来我的记忆错了,对不起。您可以添加方法(这是怪癖),但不能删除或更改现有方法的签名。

标签: vba vb6


【解决方案1】:

我认为这是对正在发生的事情的正确解释,以及一些潜在的解决方法。

我编写了一个测试用例,重现了描述中的问题,然后使用 OLEView 从包含接口的新旧 DLL 中转储了 IDL。

这是来自 INewReport 的旧(左)和新 IDL 的差异:

重要区别:

  • interface _INewReport 的 UUID 已更改

  • 添加了一个名为 INewReport___v0 的 typedef,它引用了接口的原始 UUID

(我认为这也是问题中提到的代码所发生的情况。)

所以现在在客户端项目中,bincomp DLL 引用了原始接口 UUID;但该 UUID 仅匹配与最初不同的名称(INewReport___v0 而不是 INewReport)。我认为这就是 VB6 认为 bincomp 不匹配的原因。


如何解决这个问题?我无法在 VB6 中做任何事情,让您可以将更新的接口 DLL 与客户端代码一起使用,而不必破坏客户端代码的 bincomp。

一个(错误的)选项可能只是更改客户端 DLL 以使用项目兼容性...但在您的情况下可能会或可能不会接受。除非所有消费者也被重新编译,否则它可能会导致使用客户端 DLL 的任何内容中断。 (这可能会导致一连串损坏的 bincomp)。

更好但更复杂的选择是在 IDL 本身中定义接口,使用 MIDL 编译器生成类型库(TLB 文件),然后直接引用它。然后您就可以完全控制接口命名等。您可以使用从 OLEView 生成的 IDL 作为执行此操作的起点。

第二个选项假设接口类实际上只是一个接口,其中没有功能代码。


这是我如何设置一个案例来重现这个:

步骤 1. 原始接口定义 - 名为 INewReport 的类设置为二进制兼容:

Sub ProcA()    
End Sub    

Sub ProcB()    
End Sub

第 2 步。创建一个实现 INewReport 的测试客户端 DLL,也设置为二进制兼容:

Implements INewReport    

Sub INewReport_ProcA()    
End Sub

Sub INewReport_ProcB()    
End Sub

第 3 步:将ProcC 添加到INewReport 并重新编译(这也注册了新构建的 DLL):

(以上代码,加:)

Sub ProcC()    
End Sub    

第 4 步:尝试运行或编译测试客户端 DLL - 立即得到 OP 的错误。无需更改任何引用或任何内容。

【讨论】:

  • 感谢您的详细回复。你似乎完美地复制了这个问题,级联依赖确实是我遇到的问题(项目中有超过 100 个 DLL)。但我不确定我是否要手动编辑 IDL。 (我正在开发的旧 Windows XP 系统上什至没有可用的 OleView.exe。)我认为底线是,即使我没有破坏服务器 DLL 中的二进制兼容性,这也会破坏二进制兼容性在任何实现此接口的客户端 DLL 中。
【解决方案2】:

我能够使用类似于 DaveInCaz 的代码的方式重现您的问题。我尝试了很多方法来修复它,可能会重复你已经尝试过的事情。关于为什么会发生这种情况,我提出了一个可能的假设。它不能解决问题,但它可能会引发一些额外的问题。

引用This doc page:

为确保兼容性,Visual Basic 对您对默认界面所做的更改设置了某些限制。 Visual Basic 允许您添加新类,并通过添加属性和方法来增强任何现有类的默认接口。删除类、属性或方法,或更改现有属性或方法的参数,将导致 Visual Basic 发出不兼容警告。

另一个引用:

为了确保与多个接口的兼容性,您必须遵循的 ActiveX 规则很简单:接口一旦使用,就永远无法更改。标准接口的接口 ID 由定义接口的类型库固定。

所以,这是一个假设。第一个引用提到了 default 接口,这表明可能无法以任何方式更改自定义接口。第二个引用也暗示了这一点。您可以更改接口类,因为您本质上是在更改其默认接口。但是,当您尝试以实物方式更改实现类以反映接口中的更改时,您的实现引用指向的是接口的旧版本,该版本不再存在。当然,错误消息根本没有暗示这一点,因为它似乎是基于您没有尝试实现接口的想法。

我无法证明这一点,但看看 DaveInCaz 的回答,UUID 发生变化的事实似乎证实了这个想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-24
    • 2015-10-11
    • 2020-05-12
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 1970-01-01
    相关资源
    最近更新 更多