C++ 为什么有必要在IDL接口的*末尾*添加新事件?

C++ 为什么有必要在IDL接口的*末尾*添加新事件?,c++,events,com,dll,idl,C++,Events,Com,Dll,Idl,我发现,当我向现有COM/IDL接口添加新事件时,有时会遇到奇怪的问题,除非它们添加到接口的末尾 例如,假设我有以下接口: interface IMyEvents { HRESULT FooCallback( [in] long MyParam1, [in] long MyParam2, [in] long MyParam3); HRESULT BarCallback( [in] long MyParam1,

我发现,当我向现有COM/IDL接口添加新事件时,有时会遇到奇怪的问题,除非它们添加到接口的末尾

例如,假设我有以下接口:

interface IMyEvents
{
    HRESULT FooCallback(
        [in] long MyParam1,
        [in] long MyParam2,
        [in] long MyParam3);

    HRESULT BarCallback(
        [in] long MyParam1,
        [in] BSTR MyParam2,
        [in] BSTR MyParam3);
};
现在假设我想添加一个新的回调事件,
NewCallback
。如果这样添加,则在COM上触发事件时,我不会遇到任何问题:

interface IMyEvents
{
    HRESULT FooCallback(
        [in] long MyParam1,
        [in] long MyParam2,
        [in] long MyParam3);

    HRESULT BarCallback(
        [in] long MyParam1,
        [in] BSTR MyParam2,
        [in] BSTR MyParam3);

    /* New event added to the end */
    HRESULT NewCallback(
        [in] BSTR MyParam1,
        [in] BSTR MyParam2,
        [in] BSTR MyParam3);
};
但是如果我这样添加它,当事件被触发时,我可能会遇到各种各样的问题(例如缓冲区溢出)

interface IMyEvents
{
    HRESULT FooCallback(
        [in] long MyParam1,
        [in] long MyParam2,
        [in] long MyParam3);

    /* New event added to the middle */
    HRESULT NewCallback(
        [in] BSTR MyParam1,
        [in] BSTR MyParam2,
        [in] BSTR MyParam3);

    HRESULT BarCallback(
        [in] long MyParam1,
        [in] BSTR MyParam2,
        [in] BSTR MyParam3);
};
我猜它与DLL入口点、地址偏移或类似的东西有关。或者可能是因为我没有正确地重新构建一些东西,而将它添加到末尾可以让它完全靠运气工作


有人能解释这种行为吗?

您不应该修改现有的COM接口。未使用更改编译的客户端不知道它,将继续调用,就像更改之前一样

结果是现有客户机使用一个长整数调用BarCallback,但取而代之的是认为这个长整数是BSTR的NewCallback。结果往往令人不快

在最后添加新函数时也会遇到类似的问题。旧的COM对象没有实现新的函数,当您尝试调用它时,它可能会崩溃


但是,如果您没有使用旧接口的现有客户端,请确保取消注册所有内容,并替换生成的对象、客户端、代理和存根

我认为这与在末尾添加代码无关

我记得在接口文件的中间添加了函数

但无论何时修改它,请确保取消注册dll并重新生成所有文件。(如前一篇文章所述)
在所有步骤中都要精确,因为在运行时调试这些东西很困难。

对不起,我原本的问题应该说得很清楚。COM接口的所有客户端都在我的控制之下,因此我可以根据更改重新构建它们。没有我无法重新编译的客户端(据我所知)。这是一个COM接口,在我工作的软件的各个组件内部使用。我猜您有一些二进制客户端或服务器没有正确更新或注册。我建议您创建一个干净的构建并在干净的机器上进行检查。即使您按照添加到文件末尾的规则控制所有代码,也可以大大减少由于忘记重新编译某些内容而带来的痛苦。这是一个很好的实践,也很容易遵循。顺便说一句,只有当客户端使用早期绑定时,这一切才是正确的。那些使用后期绑定的人将能够在运行时看到更改。