我最近继承了一个在DLL中创建和实现COM coclass /接口的项目。该COM DLL由应用程序使用,该应用程序将GUI应用于COM类的接口。这是唯一使用COM DLL的应用程序。我刚接触COM并且很难找到好的文档。
我的部分任务涉及向界面添加一些方法/属性(以及删除一些不再需要的方法/属性)。我已经了解到我需要修改IDL才能完成此任务。现在我只是简单地将我的新方法和属性添加到界面的末尾,事情看起来效果很好。但是,当我删除其中一个属性时(例如),当我运行应用程序时,事情会很快变坏。
interface IMyComInterface : IDispatch
{
[id(1), helpstring("method CheckMessage")] HRESULT CheckMessage([in] VARIANT vMsg);
[id(2), helpstring("method CheckFolder")] HRESULT CheckFolder([in] VARIANT Folder, [out] VARIANT *pCount, [out, retval] VARIANT *pErrorCount);
//[propget, id(3), helpstring("property Flags")] HRESULT Flags([out, retval] VARIANT *pVal);
//[propput, id(3), helpstring("property Flags")] HRESULT Flags([in] VARIANT newVal);
[propget, id(4), helpstring("property MessageStore")] HRESULT MessageStore([out, retval] VARIANT *pVal);
[propput, id(4), helpstring("property MessageStore")] HRESULT MessageStore([in] VARIANT newVal);
[propget, id(5), helpstring("property Directory")] HRESULT Directory([out, retval] VARIANT *pVal);
[propput, id(5), helpstring("property Directory")] HRESULT Directory([in] VARIANT newVal);
}
我猜它与分解的ID号序列有关。我想我可以将所有东西都置于其下,事情会奏效。但我很好奇这种做法的正确方法。
非常感谢。
答案 0 :(得分:2)
如果GUI使用IDispatch::Invoke
来调用您的方法,它应该对接口中的更改具有弹性(只要保持ID相同),因为方法调用在运行时解析(后期绑定) 。 VB6程序和脚本语言通常以这种方式运行。
但是,如果GUI直接针对IMyComInterface
进行编译(最有可能的话,如果它是C ++或C#应用程序),重要的是界面中方法的确切顺序。方法调用在编译时解析(早期绑定),并且接口中方法的索引存储在COM客户端中。如果从IDL中删除方法,则客户端的所有函数调用都将被关闭。这就是为什么在最后添加新方法的原因,但删除中间的方法不会。
解决所有这些问题的最简单方法(因为您控制COM DLL和GUI)是确保在更改IDL后重新编译所有内容。但是,在一般情况下,您应该将每个接口视为“方法功能组的不可变合约”(source),并且在将COM接口发布到世界之后永远不会修改它。
答案 1 :(得分:1)
如果COM客户端是C ++,它可能会使用早期绑定(这意味着它依赖于保持相同的接口布局)而不是后期绑定。在这种情况下,DISPID是什么并不重要,因为DISPID用于后期绑定,而不是用于早期绑定。这就是为什么从接口中删除方法/属性会破坏客户端,除非你重新编译它。这也是为什么不建议更改COM接口,而是添加从旧接口继承的新接口并添加新功能的原因:
interface IMyComInterface : IDispatch
{
// existing methods/properties
}
interface IMyComInterface2 : IMyComInterface
{
// new methods/properties
}