我有一个VB6 ActiveX DLL,它公开了类INewReport
。我向此类添加了一些新方法,并且能够重建它并保持二进制兼容性。
我有第二个DLL,它公开了类clsNewReport
,该类使用以下方法实现了第一个类:
Implements RSInterfaces.INewReport
自从我将新方法添加到INewReport
以来,我还必须将这些新方法添加到clsNewReport
。
但是,当我尝试编译第二个DLL时,出现二进制兼容性错误“ ...类在版本兼容组件中实现了接口,但在当前项目中未实现”
我不确定这里发生了什么。由于我仅添加到类中,为什么我不能与第二个DLL保持二进制兼容性?有什么办法解决吗?
答案 0 :(得分:2)
我认为这是对正在发生的情况以及一些可能的解决方法的正确解释。
我组成了一个测试用例,再现了描述中的问题,然后使用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编译器生成typelib(TLB文件),然后直接引用该接口。然后,您将可以完全控制接口的命名等。您可以使用从OLEView生成的IDL作为起点。
第二个选项假定接口类实际上只是一个接口,并且其中没有功能代码。
这是我设置案例以重现此事的方式:
步骤1。原始接口定义-名为INewReport
的类设置为二进制兼容:
Sub ProcA()
End Sub
Sub ProcB()
End Sub
步骤2。创建一个测试客户端DLL,它实现INewReport
,并且也设置为二进制兼容:
Implements INewReport
Sub INewReport_ProcA()
End Sub
Sub INewReport_ProcB()
End Sub
第3步:将ProcC
添加到INewReport
并重新编译(它还会注册新构建的DLL):
(以上代码,加号:)
Sub ProcC()
End Sub
第4步:尝试运行或编译测试客户端DLL-立即获得OP的错误。无需更改任何引用或任何内容。
答案 1 :(得分:1)
我能够使用类似于DaveInCaz的代码来重新创建您的问题。我尝试了很多方法来修复它,可能是重复您已经尝试过的事情。我提出了一个可能的假设,说明为什么会这样。它不能解决问题,但可能会带来一些其他启示。
为确保兼容性,Visual Basic对您对默认接口所做的更改施加了某些限制。 Visual Basic允许您添加新的类,并通过添加属性和方法来增强任何现有类的默认接口。删除类,属性或方法,或更改现有属性或方法的参数,将导致Visual Basic发出不兼容警告。
另一句话:
为确保与多个接口的兼容性必须遵循的ActiveX规则很简单:一旦使用了接口,就永远不能更改。标准接口的接口ID由定义接口的类型库固定。
因此,这是一个假设。第一个引号提到 default 接口,这表明可能无法以任何方式更改自定义接口。第二个引号也暗示了这一点。您可以更改接口类,因为实际上是在更改其默认接口。但是,当您尝试以实物方式更改实现类以反映接口中的更改时,实现参考指向的接口的旧版本已不再存在。当然,错误消息根本不会暗示这一点,因为它似乎是基于您没有尝试实现该接口的想法。
我还不能证明这一点,但是从DaveInCaz的答案来看,UUID已更改的事实似乎可以证明这一点。