什麼是ComDefaultInterfaceAttribute屬性的目的,如果有ClassInterfaceType.None
is marshaled爲任何IUnknown
或IDispatch
,反正被管理的對象?COM Callable Wrapper是否有任何ComDefaultInterface的用途?
考慮下面的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
時,因爲以下沒有工作,這是cras在從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
這樣的低級別的東西?
是的,只是類型庫,它設置在共類中列出的接口之一[默認]屬性。幫助不支持接口的語言的編譯器找出創建對象時要求的接口,VB6是標準示例。 –