【发布时间】: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