2013-10-14 45 views
3

什麼是ComDefaultInterfaceAttribute屬性的目的,如果有ClassInterfaceType.Noneis marshaled爲任何IUnknownIDispatch,反正被管理的對象?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互操作運行時其執行IUnknownIAuthenticate這樣的類分開:

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這樣的低級別的東西?

+1

是的,只是類型庫,它設置在共類中列出的接口之一[默認]屬性。幫助不支持接口的語言的編譯器找出創建對象時要求的接口,VB6是標準示例。 –

回答

4

如果您在一個對象上實現了多個接口,那麼ComDefaultInterface屬性才真正有用。在某些情況下,對象暴露的「第一個」接口可能很重要,但實際上並未由語言指定順序。該屬性強制您指定的接口首先被髮射,其他任何接口都以非指定的順序發射。

這也意味着,你是從託管代碼到COM導出類,以便客戶端誰得到你的類歸還給他們的是除了CoCreateObject得到正確的「默認」的界面(例如,如果你的類被標記爲[ClassInterface(ClassInterfaceType.None)])。

對於通過託管代碼或僅實現單個界面的類來處理的導入類,該屬性是無害的,但本質上是無用的。另外,就最後一個問題而言,在完全託管代碼中使用COM對象時,很少需要求助於底層接口查詢。如果使用正常的asis類型的強制關鍵字,C#編譯器將自動處理QueryInterface調用。在你的情況下,AuthenticationHelper正在創建爲一個託管AuthenticationHelper類,因爲這就是你所要求的;如果你知道你想要什麼接口,你知道它的實現,要求是:

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

所以,如果我理解正確,我不會同意下面的說法:*它也適用於從託管代碼導出到COM的類,以便讓您的類的客戶以「 CoCreateObject'獲得正確的「默認」接口(例如,如果你的類被標記爲'[ClassInterface(ClassInterfaceType.None)]')*。顯然,這不適用於我的情況。 – Noseratio

+0

我不確定你在那裏做錯了什麼;我從來沒有實現過'IServiceProvider',所以我不知道爲什麼'QueryService'與QueryInterface'有什麼不同,但我可以告訴你'ComDefaultInterface'對於只有一個接口的類是沒有意義的。 –

+0

我以爲'ComDefaultInterface'會指定一個託管類在默認情況下如何封送到非託管客戶端。我的意思是將外向編組作爲通用'對象',即'[Out,MarshalAs(UnmanagedType.Interface)] out對象結果'簽名,如'QueryInterface'。這對我沒有用。你是說這仍然是可能的,沒有'Marshal.GetComInterfaceForObject/Marshal.GetIUnknownForObject/Marshal.QueryInterface'? – Noseratio

相關問題