【问题标题】:DispID must be unique across interfaces?DispID 必须跨接口唯一?
【发布时间】:2013-04-19 10:15:54
【问题描述】:

我将 COM 与旧的 VB6 应用程序一起使用。

我将代码更改为在接口中使用 DispID,因为它似乎比使用 [ClassInterface(ClassInterfaceType.AutoDual)] 更好。

但是是否允许从 DispID(1) 开始计算每个接口,即使一个类使用两个接口?

这样工作稳定吗?还是我误会了什么?

[ComVisible(true)]
[Guid("9E1125A6-...")]
public interface IMyInterface1
{
    [DispId(1)]
    string Name1 { get; }
}

[ComVisible(true)]
[Guid("123425A6-...")]
public interface IMyInterface2
{
    [DispId(1)]
    string Name2 { get; }
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
class MyClass : IMyInterface1, IMyInterface2
{
    public string Name1 { get { return "Name1"; } }
    public string Name2 { get { return "Name2"; } }
}

【问题讨论】:

  • 您列出的第一个接口是 VB6 在后期绑定时唯一可以看到的接口。它将是标记为 [Default] 接口的接口。所以不是一个真正的问题,因为其他接口无论如何都不可用。虽然这可能是一个真正的问题;)

标签: c# com


【解决方案1】:

是否允许从 DispID(1) 开始计数的每个接口,即使一个类使用两个接口?

DISPID 仅在接口内必须是唯一的。最好使用两个接口,每个接口都有自己的(不同的)DISPID 1 属性,即使这两个接口都是由同一个 COM 对象实现的。

然而,由于提到了 VB6,您需要记住,VB6 不喜欢在同一个 COM 对象上实现的 2+ 个调度接口,并且可能只“看到”第一个/主要的。也就是说,问题不是 DISPID 冲突(这根本不是问题),而是 VB6 无法与暴露 2+ 双接口的对象正常工作。发生这种情况的原因在 MSDN 中的 Multiple Dual Interfaces 中进行了描述:

由于只公开了一个 IDispatch 接口,因此只能通过 IDispatch 接口访问您的对象的客户端将无法访问任何其他接口中的方法或属性。

遗憾的是,VB6 就是这种情况。与更高级的环境不同,它以“任何其他接口中的方法或属性”无法访问的方式查询接口。不过,分配不同的 DISPID 并无济于事。

【讨论】:

    【解决方案2】:

    每个 COM 对象只有一个 IDispatch 实现,因此如果您希望像 IDispatch::Invoke 这样的调用成功,则需要每个 COM 对象具有唯一的 DISPID。

    编辑:事实上,正如汉斯在评论中指出的那样,在仔细考虑之后,这个问题已经无关紧要了。因为您将 ClassInterfaceType 定义为 None,这意味着 .NET 只会使第一个接口 IMyInterface1 dispids 可用(默认情况下,但您可以使用ComDefaultInterfaceAttribute Class 属性配置默认接口)。

    如果您使用 ClassInterfaceType 作为 AutoDual 或 AutoDispatch,则会自动生成 DISPID,而不会使用手动定义的。

    .NET 不合并或合并接口,因此在这种“.NET 暴露为 COM”的情况下,dispid 不同的事实并不重要,因为只使用了一组 DISPID(用于默认接口) .请注意,如果您在同一个类上定义了两次相同的 DISPID 集,它将编译得很好,但 regasm 会抱怨并忽略重复的。

    这是一个证实了这一切的小型 C++ 程序:

    int _tmain(int argc, _TCHAR* argv[])
    {
        CoInitialize(NULL);
        IDispatch *pDispatch;
        CoCreateInstance(__uuidof(MyClass), NULL, CLSCTX_ALL, IID_IDispatch, (void**)&pDispatch);
        DISPID dispid;
        LPOLESTR name1 = L"Name1";
        LPOLESTR name2 = L"Name2";
        HRESULT hr;
        hr = pDispatch->GetIDsOfNames(IID_NULL, &name1, 1, 0, &dispid);
        printf("Name1:%i hr=0x%08X\n", dispid, hr);
        hr = pDispatch->GetIDsOfNames(IID_NULL, &name2, 1, 0, &dispid);
        printf("Name2:%i hr=0x%08X\n", dispid, hr);
        pDispatch->Release();
        CoUninitialize();
        return 0;
    }
    

    它会输出这个:

    Name1:1 hr=0x00000000 (S_OK)
    Name2:-1 hr=0x80020006 (DISP_E_UNKNOWNNAME)
    

    如果你更改为 AutoDispatch 或 AutoDual,它会输出这个(ids 是使用某种算法计算的):

    Name1:1610743812 hr=0x00000000
    Name2:1610743813 hr=0x00000000
    

    【讨论】:

    • 每个 COM 对象没有一个 IDispatch 的限制。好吧,IDispatch 可以是一个,但对象实现了从 IDispatch 派生的接口,并且它们的数量不受限制(当然 DISPID 被分配给那些派生接口的方法,而不是 IDispacth)。
    • 我认为我所说的没有与你所说的相反。但是由于同一个 COM 对象将始终返回同一个 IDispatch 指针,我几乎看不出在使用 IDispatch 接口时如何使用具有相同 DISPID 的两个方法。没有任何东西在物理上强制执行该规则。如果它有效(而且我从未说过 OP 的代码不会按原样工作,两次声明相同的 Dispid),那是因为代码最终没有使用 IDispatch 接口。
    • 问题是很少有应用程序查询 IDispatch。相反,它们直接查询 IDispatch 派生的接口(例如,它们是通过类型库发现的),因此没有多个 IDispatch 实现的问题,并且它们都可以完美使用。不仅仅是 VB6,它更像是一个例外(不寻常的限制)。
    • 一个 COM 对象没有多个 IDispatch 实现。 COM 要求一个接口 -> 一个指针。如果它有效,那是因为许多客户端根本不使用 IDispatch,他们不使用 GetIDsOfNames 也不 Invoke,他们直接使用 V-Table 和目标接口,无论它是否来自 IDispatch(包括 VB6)。但是尝试具有相同 ID 的 VBScript,或者在 VB6 中使用 Object 声明,您将无法使用具有相同 ID 的两个函数。 support.microsoft.com/kb/245115
    • 我用 2+ IDispatch 接口做了 COM 对象百万次。你在这里不正确。如果您查看msdn.microsoft.com/en-us/library/ds84x0xt%28v=vs.80%29.aspx,您将看到一个属性由 IID + DISPID 标识,而不仅仅是 DISPID。我的 multi-dispatch COM 对象甚至可以通过支持IDispatchEx 与 JS/VBS 一起工作,但这是另一回事。 .NET 和 Delphi 等环境可以轻松且完美地处理实现多个 IDispatch 派生接口的 COM 对象。
    【解决方案3】:

    DISPID 必须是唯一的 这里 8 是 DISPID。我重复。它必须是唯一的

    【讨论】:

      猜你喜欢
      • 2017-09-20
      • 2012-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多