c# - ComDefaultInterface 对 COM Callable Wrapper 有什么用处吗?

标签 c# .net com marshalling com-interop

ComDefaultInterfaceAttribute的目的是什么?属性,如果托管对象具有 ClassInterfaceType.None is marshaled作为 IUnknownIDispatch,无论如何?

考虑以下 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 接口(interface)。但是,这没有用,显然是因为该对象仍被编码(marshal)为 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;
}

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

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

最佳答案

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

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

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

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

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

关于c# - ComDefaultInterface 对 COM Callable Wrapper 有什么用处吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19351840/

相关文章:

com - 如何: Call Managed C# Interface From Unmanaged C++ On WindowsCE Compact Framework

C# - 如何在运行时测试哪个 COM coclass 用于实例化接口(interface)?

c# - xml处理过程中的异常

C# 查找和替换 XML 属性

c# - 处理多个请求的 HttpListener

c# - 从 .NET 程序集加载项目设置

c# - 如何压缩一个目录的内容,除了一个子目录?

c# - 为什么设置静态方法会导致堆栈溢出?

c# - 在 .NET 中是否有用于长时间运行线程的线程调度程序?

.net - STA线程模式线程也会阻塞winform UI线程?