Existe algum propósito de ComDefaultInterface para um COM Callable Wrapper?

Qual é o propósito deComDefaultInterfaceAttribute atributo, se o objeto gerenciado comClassInterfaceType.None é empacotado como tambémIUnknown ouIDispatch, de qualquer forma?

Considere a seguinte classe C #AuthenticateHelper, que implementa COMIAuthenticate:

[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;
    }
}    

Acabei de aprender que o runtime de interoperabilidade .NET separa sua implementação deIUnknown deIAuthenticate para tal classe:

AuthenticateHelper ah = new AuthenticateHelper();
IntPtr unk1 = Marshal.GetComInterfaceForObject(ah, typeof(IAuthenticate));
IntPtr unk2 = Marshal.GetIUnknownForObject(ah);
Debug.Assert(unk1 == unk2); // will assert!

Eu aprendi que, ao implementarIServiceProvder, Porqueo seguinte não funcionou, estava caindo dentro do código do cliente ao retornar deQueryService:

[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;
}

Eu ingenuamente esperava que a instância deAuthenticateHelper seria empacotado comoIAuthenticate porque a classe declara[ComDefaultInterface(typeof(IAuthenticate))], assimIAuthenticate é a única e a interface COM padrão implementado por esta classe. No entanto, isso não funcionou, obviamente porque o objeto ainda é empacotado comoIUnknown.

Os seguintes trabalhos, mas muda a assinatura deQueryService e torna menos amigável para consumir (em vez de fornecer) objetos:

[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;
}

Então, por que eu especificariaComDefaultInterface em tudo, se isso não afeta o empacotamento? O único outro uso que vejo é para geração de biblioteca de tipos.

É o código COM do cliente não gerenciado que chama minha implementação gerenciada deIServiceProvider::QueryService. Existe uma maneira de fazerQueryService trabalhar no meu exemplo sem recorrer a coisas de baixo nível comoGetComInterfaceForObject?

questionAnswers(1)

yourAnswerToTheQuestion