4

いずれにせよ、マネージ オブジェクトが または のいずれかとしてマーシャリングされている場合、ComDefaultInterfaceAttribute 属性の目的ですか?ClassInterfaceType.None IUnknownIDispatch

AuthenticateHelperCOM を実装する次の C# クラスを考えてみましょう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がマーシャリングされると素朴に予想していました。これは、このクラスによって実装されている唯一の既定の COM インターフェイスです。ただし、オブジェクトがまだ としてマーシャリングされるため、明らかに機能しませんでした。IAuthenticate[ComDefaultInterface(typeof(IAuthenticate))]IAuthenticateIUnknown

以下は機能しますが、署名を変更し、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::QueryServiceQueryServiceのような低レベルのものに頼らずに、私の例で機能させる方法はありGetComInterfaceForObjectますか?

4

1 に答える 1

5

このComDefaultInterface属性は、1 つのオブジェクトに複数のインターフェイスを実装している場合にのみ、本当に役立ちます。オブジェクトによって公開される「最初の」インターフェースは、場合によっては重要になる可能性がありますが、その順序は実際には言語によって指定されていません。この属性により、指定したインターフェイスが最初に発行され、その他のインターフェイスは指定されていない順序で発行されます。

また、マネージ コードから COM にエクスポートするクラスを対象としているため、返されたクラスを取得するクライアントはCoCreateObject、正しい「既定の」インターフェイスを取得するだけではありません (たとえば、クラスが としてマークされている場合[ClassInterface(ClassInterfaceType.None)])。

マネージ コードを介して操作するインポートされたクラス、または単一のインターフェイスのみを実装するクラスの場合、属性は無害ですが、本質的には役に立ちません。

また、最後の質問に関する限り、フル マネージド コードで COM オブジェクトを使用する場合、低レベルのインターフェイス クエリに頼る必要はほとんどありません。QueryInterface通常キーワードasis型強制キーワードを使用すると、C# コンパイラは呼び出しを自動的に処理します。あなたの場合、それがあなたが求めたものであるためAuthenticationHelper、管理されたクラスとして作成されています。AuthenticationHelper必要なインターフェースがわかっていて、それが実装されていることがわかっている場合は、それを求めてください。

AuthenticateHelper ah = new AuthenticateHelper();
IAuthenticate ia = ah as IAuthenticate;
于 2013-10-14T00:32:35.860 に答える