2009-07-07 10 views
160

EDITAR: He escrito los resultados como blog post.¿Cómo detecta el compilador C# los tipos COM?


El compilador de C# trata a tipos COM algo mágico. Por ejemplo, esta afirmación parece normal ...

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

... hasta que se da cuenta de que Application es una interfaz. ¿Llamar a un constructor en una interfaz? Yoiks! Esto en realidad se traduce en una llamada a Type.GetTypeFromCLSID() y otra a Activator.CreateInstance.

Además, en C# 4, puede utilizar argumentos que no sean árbitro para ref parámetros, y el compilador simplemente 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 falta un montón de argumentos. ¿No son agradables los parámetros opcionales? :)

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

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

[ComImport, GuidAttribute("00-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 explotar en el momento de la ejecución, pero está bien. Solo estoy experimentando.

Los otros atributos agregados por el compilador para PIA COM vinculados (CompilerGenerated y TypeIdentifier) no parecen hacer el truco ... ¿cuál es la salsa mágica?

+0

Estás muy por delante de mí en este aspecto (la mayoría de las cosas), pero solo por clairificación, parece que lo que buscas es la funcionalidad de inyección de dependencia con una sintaxis más normalizada. ¿Sería correcto? –

+11

¿No son agradables los parámetros opcionales? OMI, no, no son agradables. Microsoft está intentando reparar el defecto en las interfaces COM de Office agregando bloat a C#. –

+0

Buena pregunta (+1). Me di cuenta lo mismo de llamar a un constructor en una interfaz. Se ve horrible en el código porque realmente confundirá a los desarrolladores que no están familiarizados con lo que está sucediendo. Siempre me pregunté qué estaba pasando detrás de escena para que funcione. Parece que compraré tu nuevo libro ;-) –

Respuesta

138

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

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

Un coclase concreto aplicación (s) de uno o más interfaces de . En COM, tales implementaciones concretas se pueden escribir en cualquier lenguaje de programación que admita el desarrollo de componentes COM , p. Delphi, C++, Visual Basic, etc.

Ver my answer to a similar question about the Microsoft Speech API, donde usted es capaz de "instancia" de la interfaz SpVoice (pero en realidad, estás instancias de SPVoiceClass).

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

Muy interesante: lo intentaré más tarde.Sin embargo, los tipos de PIA vinculados no tienen CoClass. Tal vez tenga algo que ver con el proceso de vinculación. Echaré un vistazo al PIA original ... –

+64

+1 por ser genial al escribir la respuesta aceptada cuando Eric Lippert y Jon Skeet también respondieron;) No, realmente, +1 por mencionar CoClass. – OregonGhost

60

Entre usted y Michael, casi tiene las piezas juntas. Creo que así es como funciona. (No escribí el código, por lo que podría estar mintiéndolo un poco, pero estoy bastante seguro de que así es como funciona.)

Si:

  • usted es "nueva" ing un tipo de interfaz, y
  • el tipo de interfaz tiene un coclase conocido, y
  • que está utilizando la característica de "no pia" para este interfaz

entonces el código se genera como (IPIAINTERFACE) Activator.CreateInstance (Type.GetTypeFromClsid (GUID DE COCLASSTYPE))

Si:

  • usted es "nueva" ing un tipo de interfaz, y
  • el tipo de interfaz tiene un coclase conocido, y
  • que no esté utilizando la función de "no pia" para esta interfaz

entonces se genera el código como si hubiera dicho "nueva COCLASSTYPE()".

Jon, siéntete libre de molestarme a mí o a Sam directamente si tienes preguntas sobre esto. FYI, Sam es el experto en esta característica.

35

Vale, esto es sólo para poner un poco más de carne sobre la respuesta de Michael (él es invitado a añadir en si él quiere, en cuyo caso voy a quitar éste).

Mirando el PIA original para Word.Application, hay tres tipos implicados (ignorando 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 para razones que Eric Lippert habla en another answer. Y allí, como dijiste, está el CoClass, tanto en términos de la clase en sí como del atributo en la interfaz Application.

Ahora si usamos enlaces PIA en C# 4, , algunos están incrustados en el binario resultante ... pero no en todos. Una aplicación que acaba de crea una instancia de Application termina con estos tipos:

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

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

Sin ApplicationClass - presumiblemente debido a que se cargarán de forma dinámica a partir del tipo COM reales en tiempo de ejecución.

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

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

en la versión referencia que termina como:

Application application = new ApplicationClass(); 

mientras que en la versión ligado que termina como

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

Por lo tanto, parece que el PIA "real" necesita el atributo CoClass, pero la versión vinculada no lo hace porque existe no es a CoClass el compilador realmente puede hacer referencia. Tiene que hacerlo dinámicamente.

Yo podría tratar de falsificar una interfaz COM utilizar esta información y ver si puedo conseguir el compilador para vincularlo ...

26

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

El siguiente código se compila y se 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(); 
    } 
} 

es necesario tanto el ComImportAttribute y la GuidAttribute para que funcione.

También tenga en cuenta la información al pasar el mouse sobre el new IFoo(): Intellisense recoge correctamente la información: ¡Agradable!

+0

gracias, lo estaba intentando, pero me faltaba el atributo ** ComInport **, pero cuando voy al código fuente, estaba trabajando con F12, solo muestra ** CoClass ** y ** Guid **, ¿por qué? –

Cuestiones relacionadas