【问题标题】:what are the things to note while adding a new interface in C++ COM (ATL)在 C++ COM (ATL) 中添加新接口时需要注意哪些事项
【发布时间】:2016-04-09 00:31:48
【问题描述】:

我添加了一个新接口 IAEx,它是从现有接口 IA(派生自 IDispatch)扩展而来的。

idl 有哪些需要改变的地方? 我已将 coclass 定义更改为从新定义继承。 我在idl中更改了coclass条目,之前是这样的。

(我需要默认界面作为新界面)

coclass CAx
    {

        [default] interface IA
        [default, source] dispinterface IAEvents;
    };

改成

coclass CAx
    {

        [default] interface IAEx
        [default, source] dispinterface IAEvents;
    };

我可以更改默认界面吗?

coclass 定义更改。 老一个

class ATL_NO_VTABLE CAx: 
...
public CCIDispatchImpl<IA, &IID_IA, &LIBID_CCALib>,

新的。

class ATL_NO_VTABLE CAx: 
...
public CCIDispatchImpl<IAEx, &IID_IAEx, &LIBID_CCALib>,

这样好吗?

COM MAP 条目修改: 老一:

COM_INTERFACE_ENTRY(IA)
COM_INTERFACE_ENTRY2(IDispatch,IA)

新的:

COM_INTERFACE_ENTRY(IAEx)
COM_INTERFACE_ENTRY2(IDispatch,IAEx)

我还需要在 COM MAP 中添加旧接口吗?

【问题讨论】:

  • “这样好吗?” 那么,它是否按预期工作?
  • 它有效,但我担心它是否会影响现有客户?
  • IAEx 是否继承 IA

标签: c++ com atl idl


【解决方案1】:

不,这是对客户端程序的重大改变。要记住的黄金法则 #1 是 names 在 COM 中并不重要,只有 uuids 很重要。规则 #2 是 COM 组件具有机器范围,修改组件会影响使用该组件的机器上的每个程序。另一种说法是 COM 有一个强大的 DLL Hell 问题。

因此,当您在机器上安装组件时,首先会发生的事情是使用它的每个程序都将停止工作。他们仍在寻找“IA”接口 uuid,但它已经不存在了。他们因 E_NOINTERFACE 而失败。避免这种情况的唯一方法是使用新类型库重新编译客户端程序,并在部署更新的 COM 组件的同时部署它们。这通常很难安排,因为他们没有共同的程序员或公司。通常只有用户才能做到这一点,他们很少知道如何正确地做到这一点或知道如何排除故障。

如果您希望您的更新向后兼容,那么您必须向您的 coclass 添加一个新接口。它不能是 [default] 接口,因为现有客户端程序期望旧接口作为默认接口。然而,这会导致一个新问题,使用 IDispatch 的客户端运行时通常只支持单个默认接口。通常是因为他们没有将接口作为主要语言结构的概念。换句话说,您的客户端程序员无法调用 IUnknown::QueryInterface(),因此根本无法使用您的新接口。所以不是一个通用的解决方案。

技术上可能会违反 COM 中的接口是不可变的规则。您可以将新方法添加到 IDispatch 接口的末尾。现有的客户端代码不知道它们,因此永远不会调用它们并继续在组件的旧版本和新版本中正常运行。假设您知道如何在不引起破坏性行为更改的情况下维护遗留方法,这通常比看起来更难。但是仍然存在 DLL Hell 问题,当客户端代码的更新版本遇到组件的旧版本时,世界就会崩溃。乍一看,这似乎不太可能,但当机器被更换或重新成像时,这往往会在很久以后出错。非常丑陋的场景,无法诊断运行时故障,并且最初涉及的任何人都不在身边或不记得细节。

唯一真正安全的方法是创建一个新版本。修改all uuid(LIBID、CLSID 和 IID)并更改 DLL 文件名。现在新旧版本可以共存,客户端程序员可以在闲暇时使用您的新版本。可能仍然存在部署问题,但故障很容易诊断,客户端程序失败并显示“类未注册”。

【讨论】:

  • 感谢您的回答。我的要求是这样的。现有接口派生自 IDispatch。客户端通过 IDispatch 调用访问接口。客户端使用 ITypeInfo 检查接口以获取接口和功能详细信息,他们只考虑默认接口。所以我的新接口需要默认才能列出新方法。我如何做到这一点?
  • 是的,我已经警告过你了。你写到最后了吗?
  • 不,那么 COM 的目的是什么 :-( 如果我必须像新的一样。?
【解决方案2】:

您应该在 COM MAP 中包含旧接口,并且如果客户端尝试为旧接口查询接口,它应该会收到有用的结果而不是错误。添加COM_INTERFACE_ENTRY2(IA, IAEx)

否则看起来你拥有一切。我们将这两个接口都添加到 IDL 文件的 coclass 条目中,但我不认为有任何东西真正使用它。即:

coclass Ax
{
    [default] interface IAEx;
    interface IA;
};

【讨论】:

  • 感谢您的回答。请说明如何做到这一点?像这样COM_INTERFACE_ENTRY(IAEx) COM_INTERFACE_ENTRY2(IDispatch,IAEx)
【解决方案3】:

好不好完全取决于目标是什么。更换接口本身就可以,如果您担心不破坏与现有客户端的兼容性,最重要的是您的服务器仍然实现旧接口IA。您应该在 COM MAP 上列出接口(因为您提到 IAEx 继承自 IA,您可能需要一个 COM_INTERFACE_ENTRY_IID 宏):

COM_INTERFACE_ENTRY(IAEx)
COM_INTERFACE_ENTRY_IID(__uuidof(IA), IAEx) //COM_INTERFACE_ENTRY(IA)
COM_INTERFACE_ENTRY2(IDispatch, IAEx)

这样,您的服务器同时实现了IAExIA。我宁愿反对从另一个继承接口,但你拥有你所拥有的。

更新coclass(正如 patthoyts 的回答所建议的那样)使更新更干净,也值得这样做,但是对于重建客户端而不是保持与现有构建代码的兼容性更重要:导入类型库信息的工具将能够看到这两个接口(如果他们关心的话)并且能够在那里处理非默认接口。

【讨论】:

    猜你喜欢
    • 2018-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    • 2013-02-03
    相关资源
    最近更新 更多