【问题标题】:Is there any purpose of ComDefaultInterface for a COM Callable Wrapper?ComDefaultInterface 对 COM 可调用包装器有什么用途吗?
【发布时间】:2013-10-21 12:33:12
【问题描述】:

ComDefaultInterfaceAttribute 属性的用途是什么,如果ClassInterfaceType.None is marshaled 为IUnknown 或IDispatch 的托管对象,无论如何?

考虑以下 C# 类 AuthenticateHelper,它实现了 COM IAuthenticate:

[ComImport]
[Guid("79eac9d0-baf9-11ce-8c82-00aa004ba90b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAuthenticate
{
    [PreserveSig]
    int Authenticate(
        [In, Out] ref IntPtr phwnd,
        [In, Out, MarshalAs(UnmanagedType.LPWStr)] ref string pszUsername,
        [In, Out, MarshalAs(UnmanagedType.LPWStr)] ref string pszPassword);
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IAuthenticate))]
public class AuthenticateHelper: IAuthenticate
{
    public int Authenticate(ref IntPtr phwnd, ref string pszUsername, ref string pszPassword)
    {
        phwnd = IntPtr.Zero;
        pszUsername = String.Empty;
        pszPassword = String.Empty;
        return 0;
    }
}    

我刚刚了解到 .NET 互操作运行时将其对 IUnknown 的实现与此类类的 IAuthenticate 分开:

AuthenticateHelper ah = new AuthenticateHelper();
IntPtr unk1 = Marshal.GetComInterfaceForObject(ah, typeof(IAuthenticate));
IntPtr unk2 = Marshal.GetIUnknownForObject(ah);
Debug.Assert(unk1 == unk2); // will assert!

我了解到,在实现IServiceProvder 时,由于以下内容不起作用,它在从QueryService 返回时在客户端代码中崩溃:

[ComImport]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IServiceProvider
{
    [PreserveSig]
    int QueryService(
        [In] ref Guid guidService,
        [In] ref Guid riid,
        [Out, MarshalAs(UnmanagedType.Interface, IidParameterIndex=1)] out object ppvObject    
}

// ...

public readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");

AuthenticateHelper ah = new AuthenticateHelper();

int IServiceProvider.QueryService(ref Guid guidService, ref Guid riid, out object ppvObject)
{
    if (guidService == typeof(IAuthenticate).GUID && (riid == IID_IUnknown || riid == guidService))
    {
        ppvObject = this.ah; // same as ppvObject = (IAuthenticate)this.ah
        return S_OK;
    }
    ppvObject = null;
    return E_NOINTERFACE;
}

我天真地以为AuthenticateHelper 的实例会被编组为IAuthenticate,因为该类声明了[ComDefaultInterface(typeof(IAuthenticate))],所以IAuthenticate 是该类实现的唯一且默认的COM 接口 .但是,这不起作用,显然是因为该对象仍被编组为 IUnknown。

以下工作,但它改变了QueryService 的签名,使其对消费(而不是提供)对象不太友好:

[ComImport]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IServiceProvider
{
    [PreserveSig]
    int QueryService(
        [In] ref Guid guidService,
        [In] ref Guid riid,
        [Out] out IntPtr ppvObject);
}

// ...

int IServiceProvider.QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
{
    if (guidService == typeof(IAuthenticate).GUID && (riid == IID_IUnknown || riid == guidService))
    {
        ppvObject = Marshal.GetComInterfaceForObject(this.ah, typeof(IAuthenticate));
        return S_OK;
    }
    ppvObject = IntPtr.Zero;
    return E_NOINTERFACE;
}

那么,如果它不影响编组,我为什么要指定 ComDefaultInterface 呢?我看到的唯一其他用途是类型库生成。

调用我的IServiceProvider::QueryService 的托管实现的是非托管客户端COM 代码。有没有办法让QueryService 在我的示例中正常工作,而无需求助于GetComInterfaceForObject 之类的低级内容?

【问题讨论】:

  • 是的,只是类型库,它在 coclass 中列出的接口之一上设置 [default] 属性。帮助不支持接口的语言的编译器确定创建对象时要请求的接口,VB6 是标准示例。

标签: c# .net com marshalling com-interop


【解决方案1】:

ComDefaultInterface 属性只有在一个对象上实现了多个接口时才真正有用。在某些情况下,对象公开的“第一个”接口可能很重要,但语言实际上并未指定顺序。该属性强制您指定的接口首先发出,其他任何接口都以未指定的顺序出现。

它也适用于您从托管代码导出到 COM 的类,以便将您的类返回给它们的客户端不是 CoCreateObject 获得正确的“默认”接口(例如,如果您的类被标记如[ClassInterface(ClassInterfaceType.None)])。

对于您通过托管代码使用的导入类,或仅实现单个接口的类,该属性是无害的,但本质上是无用的。

此外,就您的最后一个问题而言,在完全托管代码中使用 COM 对象时,您很少需要求助于低级接口查询。如果您使用普通的as 和is 类型强制关键字,C# 编译器将自动处理QueryInterface 调用。在您的情况下,AuthenticationHelper 被创建为托管的 AuthenticationHelper 类,因为这是您所要求的;如果你知道你想要什么接口并且你知道它已经实现了,就问一下:

AuthenticateHelper ah = new AuthenticateHelper();
IAuthenticate ia = ah as IAuthenticate;

【讨论】:

  • 所以,如果我理解正确,我不同意以下陈述:它也适用于您从托管代码导出到 COM 的类,以便获得您的客户以CoCreateObject 以外的方式返回给他们的类获得正确的“默认”接口(例如,如果您的类被标记为[ClassInterface(ClassInterfaceType.None)])。显然,这在我的情况下不起作用。
  • 我不确定你到底做错了什么;我从未实现过IServiceProvider,所以我不知道为什么QueryService 与QueryInterface 有什么不同,但我可以告诉你ComDefaultInterface 在只有一个接口的类上毫无意义。跨度>
  • 我认为ComDefaultInterface 会指定默认情况下托管类如何编组到非托管客户端。我的意思是作为通用object 的传出封送处理,即[Out, MarshalAs(UnmanagedType.Interface)] out object result 签名,如QueryInterface。那对我不起作用。你是说没有Marshal.GetComInterfaceForObject/Marshal.GetIUnknownForObject/Marshal.QueryInterface 仍然有可能吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-06
  • 2010-09-28
相关资源
最近更新 更多