165 votos

¿Cómo el compilador de C# para detectar tipos COM?

EDIT: he escrito los resultados como una entrada de blog.


El compilador de C# de golosinas tipos COM algo por arte de magia. Por ejemplo, esta afirmación se ve normal...

Word.Application app = new Word.Application();

... hasta que te das cuenta de que Application es una interfaz. Llamar a un constructor en una interfaz? Yoiks! Esta realidad se traduce en una llamada a Type.GetTypeFromCLSID() y otro a Activator.CreateInstance.

Además, en C# 4, puede utilizar la no-ref argumentos para ref parámetros, y el compilador sólo añade una variable local para pasar por referencia, descartando los resultados:

// FileName parameter is *really* a ref parameter
app.ActiveDocument.SaveAs(FileName: "test.doc");

(Sí, hay un montón de argumentos que faltan. No son parámetros opcionales agradable? :)

Estoy tratando de investigar el comportamiento del compilador, y yo estoy fallando a falsas de la primera parte. Puedo hacer la segunda parte sin ningún problema:

using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

[ComImport, GuidAttribute("00012345-0000-0000-0000-000000000011")]
public interface Dummy
{
    void Foo(ref int x);
}

class Test
{
    static void Main()
    {
        Dummy dummy = null;
        dummy.Foo(10);
    }
}

Me gustaría ser capaz de escribir:

Dummy dummy = new Dummy();

sin embargo. Obviamente va a ir bang en tiempo de ejecución, pero eso está bien. Sólo estoy experimentando.

Las otras características añadidas por el compilador para vinculada COM Pia (CompilerGenerated y TypeIdentifier) no parecen hacer el truco... ¿qué es la magia de la salsa?

142voto

Michael Petrotta Puntos 35647

De ninguna manera soy un experto en esto, pero me encontré recientemente en lo que creo que quiere: la Coclase atributo de la clase.

[System.Runtime.InteropServices.CoClass(typeof(Test))]
public interface Dummy { }

Una coclase suministros de hormigón implementación(s) de uno o más las interfaces. En COM, hormigón las implementaciones pueden ser escritos en cualquier lenguaje de programación que soporta COM desarrollo de componentes, por ejemplo. Delphi, C++, Visual Basic, etc.

Ver mi respuesta a una pregunta similar sobre el Microsoft Speech API, que sea capaz de "crear una instancia de" la interfaz SpVoice (pero en realidad, estamos instanciando SPVoiceClass).

[CoClass(typeof(SpVoiceClass))]
public interface SpVoice : ISpeechVoice, _ISpeechVoiceEvents_Event { }

60voto

Eric Lippert Puntos 300275

Entre usted y Michael que casi he conseguido las piezas juntas. Creo que así es como funciona. (Yo no se escribir el código, así que yo podría ser un poco mal-diciendo esto, pero estoy bastante seguro de que esto es lo que pasa.)

Si:

  • "nuevos"ing un tipo de interfaz, y
  • el tipo de interfaz se conoce la coclase, y
  • usted ESTÁ utilizando el "no pia" característica de esta interfaz

a continuación, el código se genera como (IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID DE COCLASSTYPE))

Si:

  • "nuevos"ing un tipo de interfaz, y
  • el tipo de interfaz se conoce la coclase, y
  • usted NO ESTÁ utilizando el "no pia" característica de esta interfaz

a continuación, se genera el código como si hubieran dicho : "nueva COCLASSTYPE()".

Jon, siéntase libre de error a mí o a Sam directamente si tiene preguntas acerca de esta materia. FYI, Sam es el experto en esta característica.

35voto

Jon Skeet Puntos 692016

Bueno, esto es solo para poner un poco más de carne en la de Michael respuesta (es bienvenido a agregar si se quiere, en cuyo caso voy a eliminar este).

Buscar en el original PIA para Word.Application, hay tres tipos de involucrados (haciendo caso omiso de los eventos):

[ComImport, TypeLibType(...), Guid("..."), DefaultMember("Name")]
public interface _Application
{
     ...
}

[ComImport, Guid("..."), CoClass(typeof(ApplicationClass))]
public interface Application : _Application
{
}

[ComImport, ClassInterface(...), ComSourceInterfaces("..."), Guid("..."), 
 TypeLibType((short) 2), DefaultMember("Name")]
public class ApplicationClass : _Application, Application
{
}

Hay dos interfaces por las razones que Eric Lippert habla en otra respuesta. Y allí, como usted dijo, es el CoClass - tanto en términos de la propia clase y el atributo en Application de interfaz.

Ahora si usamos PIA vinculación en C# 4, algunos de esto está implícito en el binario resultante... pero no todo. Una aplicación con la que sólo se crea una instancia de Application termina con estos tipos:

[ComImport, TypeIdentifier, Guid("..."), CompilerGenerated]
public interface _Application

[ComImport, Guid("..."), CompilerGenerated, TypeIdentifier]
public interface Application : _Application

No ApplicationClass - presumiblemente debido a que se cargan de forma dinámica a partir de la real COM tipo en tiempo de ejecución.

Otra cosa interesante es la diferencia en el código entre la versión vinculada y no vinculada versión. Si usted descompilar la línea

Word.Application application = new Word.Application();

en la referencia de la versión termina como:

Application application = new ApplicationClass();

mientras que en el vinculado versión termina como

Application application = (Application) 
    Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("...")));

Así que parece que la "real" PIA necesidades de la CoClass de atributo, pero la versión enlazada no porque no es un CoClass el compilador puede en realidad de referencia. Tiene que hacerlo de forma dinámica.

Yo podría tratar de fingir una interfaz COM de utilizar esta información y ver si puedo conseguir que el compilador enlace...

27voto

Rasmus Faber Puntos 24195

Sólo para añadir un poco de confirmación a la respuesta de Michael:

El código siguiente compila y ejecuta:

public class Program
{
    public class Foo : IFoo
    {
    }

    [Guid("00000000-0000-0000-0000-000000000000")]
    [CoClass(typeof(Foo))]
    [ComImport]
    public interface IFoo
    {
    }

    static void Main(string[] args)
    {
        IFoo foo = new IFoo();
    }
}

Necesitas ambos el ComImportAttribute y el GuidAttribute para que funcione.

También tenga en cuenta la información cuando pasa el cursor del ratón sobre la new IFoo() : Intellisense correctamente se basa en la información: agradable!

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X