在 C# 中,动态 COM 对象被要求提供未知接口 {B86A98CC-DCC0-3205-8777-7911A07DAAAF}

Dynamic COM object getting asked for unknown interface {B86A98CC-DCC0-3205-8777-7911A07DAAAF} in C#

已检测 IDispatch-based COM object accessed in C# via a dynamic 变量的 QueryInterface() 日志显示(除其他外)未知的 IID {B86A98CC-DCC0-3205-8777-7911A07DAAAF}。 Google、GitHub 和 microsoft.com 出现了。有谁知道这个接口可能是什么,如果知道的话,它可以用来做什么?

FWIW,对象是这样实例化的:

var type = Type.GetTypeFromProgID(THE_PROGID);
var obj = (dynamic)Activator.CreateInstance(type);

然后接口查询日志就直接dump了。在 LINQPad 我得到了这个:

099AC468 {00000000-0000-0000-C000-000000000046} IUnknown
-------- {C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4} IManagedObject
099AC420 {B196B283-BAB4-101A-B69C-00AA00341D07} IProvideClassInfo
-------- {AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90} IInspectable
-------- {ECC8691B-C1DB-4DC0-855E-65F6C551AF49} INoMarshal
-------- {94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90} IAgileObject
-------- {00000003-0000-0000-C000-000000000046} IMarshal
-------- {00000144-0000-0000-C000-000000000046} IRpcOptions
099AC42C {00020400-0000-0000-C000-000000000046} IDispatch
-------- {A6EF9860-C720-11D0-9337-00A0C90DCAA9} IDispatchEx
099AC42C {00020400-0000-0000-C000-000000000046} IDispatch
099AC42C {00020400-0000-0000-C000-000000000046} IDispatch
-------- {B86A98CC-DCC0-3205-8777-7911A07DAAAF}
-------- {00000038-0000-0000-C000-000000000046} IWeakReferenceSource
099AC42C {00020400-0000-0000-C000-000000000046} IDispatch

C# 单元测试用例(VS2015,Framework 4.5.2)中的相同代码给出了类似的结果:

0A41BFF0 {00000000-0000-0000-C000-000000000046} IUnknown
-------- {C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4} IManagedObject
0A41BFA8 {B196B283-BAB4-101A-B69C-00AA00341D07} IProvideClassInfo
-------- {AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90} IInspectable
-------- {ECC8691B-C1DB-4DC0-855E-65F6C551AF49} INoMarshal
-------- {94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90} IAgileObject
-------- {00000003-0000-0000-C000-000000000046} IMarshal
-------- {00000144-0000-0000-C000-000000000046} IRpcOptions
-------- {B86A98CC-DCC0-3205-8777-7911A07DAAAF}
-------- {B86A98CC-DCC0-3205-8777-7911A07DAAAF}
0A41BFB4 {00020400-0000-0000-C000-000000000046} IDispatch
-------- {A6EF9860-C720-11D0-9337-00A0C90DCAA9} IDispatchEx
0A41BFB4 {00020400-0000-0000-C000-000000000046} IDispatch

32 位和 64 位模式之间没有明显区别(当然,除了显示指针的宽度之外)。

对象本身派生自Delphi的TAutoObject, which explains how it could respond to the IProvideClassInfo query. However, the initial interface query sequence is basically the same for other objects that do not support this interface, regardless of whether they respond 0 or 1 to IDispatch::GetTypeInfoCount()

但是,每个成员访问不仅会导致对 IDispatch 的查询,还会导致 两次 对未知接口的查询:

099A5068 {00020400-0000-0000-C000-000000000046} IDispatch
-------- {B86A98CC-DCC0-3205-8777-7911A07DAAAF}
-------- {B86A98CC-DCC0-3205-8777-7911A07DAAAF}
099A5068 {00020400-0000-0000-C000-000000000046} IDispatch
-------- {B86A98CC-DCC0-3205-8777-7911A07DAAAF}
-------- {B86A98CC-DCC0-3205-8777-7911A07DAAAF}
... and so on ad infinitum ...

这是从一长串像这样重复的行中截取的;无法判断未知查询是在相应的 IDispatch 调用之前,将其括起来还是在它之后。

精确响应 Charlieface 的评论:在一长串 printf 语句中访问成员,如下所示:

Console.WriteLine("ApartmentType {0}", obj.ApartmentType);

如果我在将每个成员访问的结果传递给 Console.WriteLine() 之前将其转换为其真实数据类型,那么对未知接口的双重查询将完全消失:

Console.WriteLine("ApartmentType {0}", (string)obj.ApartmentType);

未知查询似乎是 CLR 问题,但不是 LINQPad 问题,因为它也出现在 VS2015 中。

有谁知道,{B86A98CC-DCC0-3205-8777-7911A07DAAAF} 代表哪个接口,它可以用来做什么?

发生这种情况是因为您使用了 dynamic 关键字,它会触发来自 .NET 的各种调用。对应的代码是这样的(在.NET reference source中找到):

/// <summary>
/// Creates a meta-object for the specified object. 
/// </summary>
/// <param name="value">The object to get a meta-object for.</param>
/// <param name="expression">The expression representing this <see cref="DynamicMetaObject"/> during the dynamic binding process.</param>
/// <returns>
/// If the given object implements <see cref="IDynamicMetaObjectProvider"/> and is not a remote object from outside the current AppDomain,
/// returns the object's specific meta-object returned by <see cref="IDynamicMetaObjectProvider.GetMetaObject"/>. Otherwise a plain new meta-object 
/// with no restrictions is created and returned.
/// </returns>
public static DynamicMetaObject Create(object value, Expression expression) {
    ContractUtils.RequiresNotNull(expression, "expression");

    IDynamicMetaObjectProvider ido = value as IDynamicMetaObjectProvider;
    if (ido != null && !RemotingServices.IsObjectOutOfAppDomain(value)) {
        var idoMetaObject = ido.GetMetaObject(expression);

        if (idoMetaObject == null ||
            !idoMetaObject.HasValue ||
            idoMetaObject.Value == null ||
            (object)idoMetaObject.Expression != (object)expression) {
            throw Error.InvalidMetaObjectCreated(ido.GetType());
        }

        return idoMetaObject;
    } else {
        return new DynamicMetaObject(expression, BindingRestrictions.Empty, value);
    }
}

{B86A98CC-DCC0-3205-8777-7911A07DAAAF}IDynamicMetaObjectProvider 的(自动生成的)IID,即:typeof(IDynamicMetaObjectProvider).GUID。它是一个纯托管接口,因此对本机代码没有用。

要找到它,只需查看调用堆栈,但要确保您的线程公寓类型与您的对象兼容,否则您将获得 RPC 远程处理“无用”堆栈。

在我的例子中,我必须在 .NET 方法上添加 STAThread 属性: