微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

在 C# 中通过 IDispatch 变量访问的基于 dynamic 的检测的 COM 对象的 QueryInterface() 日志显示(除其他外)未知 IID {B86A98CC-DCC0-3205-8777-7911A07DAAAF}。谷歌、GitHub 和 microsoft.com 出现了 zilch。有谁知道这个接口是什么,如果知道,它可以用来做什么?

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

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

然后接口查询日志马上就被转储了。在 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,它解释了它如何响应 IProvideClassInfo 查询。但是,对于其他不支持该接口的对象,无论是响应 0 还是 1 到 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 属性:

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。