如果使用
考虑以下实现COM
我刚学到,.NET互操作运行时将其对于
我天真地以为,由于该类声明了
以下方法可以解决问题,但它会改变
所以,如果
是非托管客户端COM代码调用我的托管
ClassInterfaceType.None
将托管对象作为IUnknown
或IDispatch
进行封送,那么ComDefaultInterfaceAttribute 属性的目的是什么?考虑以下实现COM
IAuthenticate
的C#类AuthenticateHelper
:[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!
我了解到,在实现IServiceProvider
时,由于以下方法无法正常工作,在从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;
}
我天真地以为,由于该类声明了
[ComDefaultInterface(typeof(IAuthenticate))]
,因此AuthenticateHelper
的实例将作为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
不影响封送,为什么我要指定它呢?我看到的唯一其他用途是用于类型库生成。是非托管客户端COM代码调用我的托管
IServiceProvider :: QueryService
实现。有没有办法使QueryService
在我的示例中工作,而不必诉诸于像GetComInterfaceForObject
这样的低级操作?