实现与二进制兼容性
Implements vs Binary Compatibility
我有一个公开 class INewReport
的 VB6 ActiveX DLL。我为此 class 添加了一些新方法,并且能够重建它并保持二进制兼容性。
我有第二个 DLL 公开了一个 class clsNewReport
,它实现了第一个 class 使用:
Implements RSInterfaces.INewReport
由于我向 INewReport
添加了新方法,所以我还必须将这些新方法添加到 clsNewReport
。
但是,当我尝试编译第二个 DLL 时,出现二进制兼容性错误 "...class 在版本兼容组件中实现了一个接口,但在当前项目.
我不确定这里发生了什么。因为我只添加到 class,为什么我不能保持与第二个 DLL 的二进制兼容性?有什么解决办法吗?
我认为这是对正在发生的事情的正确解释,以及一些潜在的解决方法。
我编写了一个测试用例,重现了描述中的问题,然后使用 OLEView 从包含接口的新旧 DLL 中转储了 IDL。
这是来自 INewReport 的旧(左)IDL 和新 IDL 的差异:
重要区别:
interface _INewReport
的 UUID 已 更改
添加了一个名为 INewReport___v0
的 typedef,它指的是 original 接口的 UUID
(我假设这也是问题中提到的代码所发生的情况。)
所以现在在客户端项目中bincomp DLL引用了原始接口UUID;但该 UUID 仅匹配与最初不同的名称(INewReport___v0
而不是 INewReport
)。我认为这就是 VB6 认为存在 bincomp 不匹配的原因。
如何解决这个问题?我无法在 VB6 中做任何事情,使您可以将更新的接口 DLL 与客户端代码一起使用,而不必破坏客户端代码的 bincomp。
一个(错误的)选项 可能只是更改客户端 DLL 以使用项目兼容性...但这在您的情况下可能会或可能不会被接受。它可能会导致使用客户端 DLL 的任何内容中断,除非所有使用者也都被重新编译。 (这可能会导致一连串损坏的 bincomp)。
更好但更复杂的选择 是在 IDL 本身中定义接口,使用 MIDL 编译器生成类型库(TLB 文件),然后直接引用它。然后您就可以完全控制接口命名等。您可以使用从 OLEView 生成的 IDL 作为起点来执行此操作。
第二个选项假定接口 class 实际上只是一个接口,其中没有功能代码。
以下是我设置案例以重现此内容的方法:
第 1 步。原始接口定义 - class 调用 INewReport
设置为二进制兼容:
Sub ProcA()
End Sub
Sub ProcB()
End Sub
第 2 步。创建实现 INewReport
的测试客户端 DLL,也设置为二进制兼容:
Implements INewReport
Sub INewReport_ProcA()
End Sub
Sub INewReport_ProcB()
End Sub
第三步:将ProcC
添加到INewReport
并重新编译(同时注册新建的DLL):
(以上代码,加上:)
Sub ProcC()
End Sub
第 4 步:尝试 运行 或编译测试客户端 DLL - 立即得到 OP 的错误。根本不需要更改任何引用或任何内容。
我能够使用类似于 DaveInCaz 的代码重现您的问题。我尝试了很多方法来修复它,可能会重复您已经尝试过的事情。我想出了一个可能的假设来解释为什么会这样。它不能解决问题,但它可能会给它带来一些额外的启发。
引用自This doc page:
To ensure compatibility, Visual Basic places certain restrictions on changes you make to default interfaces. Visual Basic allows you to add new classes, and to enhance the default interface of any existing class by adding properties and methods. Removing classes, properties, or methods, or changing the arguments of existing properties or methods, will cause Visual Basic to issue incompatibility warnings.
另一引述:
The ActiveX rule you must follow to ensure compatibility with multiple interfaces is simple: once an interface is in use, it can never be changed. The interface ID of a standard interface is fixed by the type library that defines the interface.
所以,这是一个假设。第一个引用提到了 default 接口,这表明可能无法以任何方式更改自定义接口。第二个引述也暗示了这一点。您可以更改界面 class,因为您实质上是在更改其默认界面。但是,当您尝试以实物方式更改实现 class 以反映接口中的更改时,您的实现引用指向不再存在的旧版本接口。当然,错误消息根本没有暗示这一点,因为它似乎是基于您没有尝试实现接口的想法。
我没能证明这一点,但看看 DaveInCaz 的回答,UUID 已经改变的事实似乎证实了这个想法。
我有一个公开 class INewReport
的 VB6 ActiveX DLL。我为此 class 添加了一些新方法,并且能够重建它并保持二进制兼容性。
我有第二个 DLL 公开了一个 class clsNewReport
,它实现了第一个 class 使用:
Implements RSInterfaces.INewReport
由于我向 INewReport
添加了新方法,所以我还必须将这些新方法添加到 clsNewReport
。
但是,当我尝试编译第二个 DLL 时,出现二进制兼容性错误 "...class 在版本兼容组件中实现了一个接口,但在当前项目.
我不确定这里发生了什么。因为我只添加到 class,为什么我不能保持与第二个 DLL 的二进制兼容性?有什么解决办法吗?
我认为这是对正在发生的事情的正确解释,以及一些潜在的解决方法。
我编写了一个测试用例,重现了描述中的问题,然后使用 OLEView 从包含接口的新旧 DLL 中转储了 IDL。
这是来自 INewReport 的旧(左)IDL 和新 IDL 的差异:
重要区别:
interface _INewReport
的 UUID 已 更改添加了一个名为
INewReport___v0
的 typedef,它指的是 original 接口的 UUID
(我假设这也是问题中提到的代码所发生的情况。)
所以现在在客户端项目中bincomp DLL引用了原始接口UUID;但该 UUID 仅匹配与最初不同的名称(INewReport___v0
而不是 INewReport
)。我认为这就是 VB6 认为存在 bincomp 不匹配的原因。
如何解决这个问题?我无法在 VB6 中做任何事情,使您可以将更新的接口 DLL 与客户端代码一起使用,而不必破坏客户端代码的 bincomp。
一个(错误的)选项 可能只是更改客户端 DLL 以使用项目兼容性...但这在您的情况下可能会或可能不会被接受。它可能会导致使用客户端 DLL 的任何内容中断,除非所有使用者也都被重新编译。 (这可能会导致一连串损坏的 bincomp)。
更好但更复杂的选择 是在 IDL 本身中定义接口,使用 MIDL 编译器生成类型库(TLB 文件),然后直接引用它。然后您就可以完全控制接口命名等。您可以使用从 OLEView 生成的 IDL 作为起点来执行此操作。
第二个选项假定接口 class 实际上只是一个接口,其中没有功能代码。
以下是我设置案例以重现此内容的方法:
第 1 步。原始接口定义 - class 调用 INewReport
设置为二进制兼容:
Sub ProcA()
End Sub
Sub ProcB()
End Sub
第 2 步。创建实现 INewReport
的测试客户端 DLL,也设置为二进制兼容:
Implements INewReport
Sub INewReport_ProcA()
End Sub
Sub INewReport_ProcB()
End Sub
第三步:将ProcC
添加到INewReport
并重新编译(同时注册新建的DLL):
(以上代码,加上:)
Sub ProcC()
End Sub
第 4 步:尝试 运行 或编译测试客户端 DLL - 立即得到 OP 的错误。根本不需要更改任何引用或任何内容。
我能够使用类似于 DaveInCaz 的代码重现您的问题。我尝试了很多方法来修复它,可能会重复您已经尝试过的事情。我想出了一个可能的假设来解释为什么会这样。它不能解决问题,但它可能会给它带来一些额外的启发。
引用自This doc page:
To ensure compatibility, Visual Basic places certain restrictions on changes you make to default interfaces. Visual Basic allows you to add new classes, and to enhance the default interface of any existing class by adding properties and methods. Removing classes, properties, or methods, or changing the arguments of existing properties or methods, will cause Visual Basic to issue incompatibility warnings.
另一引述:
The ActiveX rule you must follow to ensure compatibility with multiple interfaces is simple: once an interface is in use, it can never be changed. The interface ID of a standard interface is fixed by the type library that defines the interface.
所以,这是一个假设。第一个引用提到了 default 接口,这表明可能无法以任何方式更改自定义接口。第二个引述也暗示了这一点。您可以更改界面 class,因为您实质上是在更改其默认界面。但是,当您尝试以实物方式更改实现 class 以反映接口中的更改时,您的实现引用指向不再存在的旧版本接口。当然,错误消息根本没有暗示这一点,因为它似乎是基于您没有尝试实现接口的想法。
我没能证明这一点,但看看 DaveInCaz 的回答,UUID 已经改变的事实似乎证实了这个想法。