dinamico - In che modo il compilatore C#rileva i tipi COM?



c# how to use dynamic (3)

EDIT: ho scritto i risultati come un post sul blog .

Il compilatore C # tratta i tipi di COM in qualche modo magicamente. Ad esempio, questa affermazione sembra normale ...

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

... finché non ti rendi conto che l' Application è un'interfaccia. Chiamare un costruttore su un'interfaccia? Yoik! Questo in realtà viene tradotto in una chiamata a Type.GetTypeFromCLSID() e un'altra a Activator.CreateInstance .

Inoltre, in C # 4, puoi usare gli argomenti non ref per i parametri ref e il compilatore aggiunge semplicemente una variabile locale da passare per riferimento, scartando i risultati:

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

(Sì, mancano alcuni argomenti. I parametri facoltativi non sono carini? :)

Sto cercando di indagare sul comportamento del compilatore e non riesco a simulare la prima parte. Posso fare la seconda parte senza problemi:

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

Mi piacerebbe essere in grado di scrivere:

Dummy dummy = new Dummy();

anche se. Ovviamente andrà bene al momento dell'esecuzione, ma va bene. Sto solo sperimentando.

Gli altri attributi aggiunti dal compilatore per i COM PIA collegati ( CompilerGenerated e TypeIdentifier ) non sembrano fare il trucco ... qual è la salsa magica?

https://src-bin.com


Answer #1

Non sono affatto un esperto in questo, ma sono inciampato di recente su ciò che penso tu voglia: la classe degli attributi CoClass .

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

Una coclasse fornisce un'implementazione concreta di una o più interfacce. In COM, tali implementazioni concrete possono essere scritte in qualsiasi linguaggio di programmazione che supporta lo sviluppo di componenti COM, ad esempio Delphi, C ++, Visual Basic, ecc.

Vedere la mia risposta a una domanda simile SpVoice Microsoft Speech , in cui è possibile "istanziare" l'interfaccia SpVoice (ma in realtà, si sta SPVoiceClass un'istanza di SPVoiceClass ).

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

Answer #2

Ok, questo è solo per dare un po 'più di carne alla risposta di Michael (lui è il benvenuto per aggiungerlo se vuole, nel qual caso rimuoverò questo).

Guardando il PIA originale per Word.Application, ci sono tre tipi coinvolti (ignorando gli eventi):

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

Ci sono due interfacce per le ragioni di cui Eric Lippert parla in un'altra risposta . E lì, come hai detto, c'è il CoClass - sia in termini di classe stessa che di attributo sull'interfaccia Application .

Ora, se utilizziamo il collegamento PIA in C # 4, parte di questo è incorporato nel file binario risultante ... ma non in tutto. Un'applicazione che crea solo un'istanza di Application termina con questi tipi:

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

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

No ApplicationClass - presumibilmente perché verrà caricato dinamicamente dal vero tipo COM al momento dell'esecuzione.

Un'altra cosa interessante è la differenza nel codice tra la versione collegata e la versione non collegata. Se decompilate la linea

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

nella versione di riferimento finisce come:

Application application = new ApplicationClass();

mentre nella versione collegata finisce come

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

Quindi sembra che il PIA "vero" abbia bisogno dell'attributo CoClass , ma la versione collegata non lo fa perché non c'è una CoClass il compilatore possa effettivamente fare riferimento. Deve farlo dinamicamente.

Potrei provare a simulare un'interfaccia COM usando queste informazioni e vedere se riesco a ottenere il compilatore per collegarlo ...


Answer #3

Tra te e Michael hai quasi messo insieme i pezzi. Penso che sia così che funziona. (Non ho scritto il codice, quindi potrei esserne un po 'fuorviante, ma sono abbastanza sicuro che sia così che va.)

Se:

  • sei "nuovo" in un tipo di interfaccia e
  • il tipo di interfaccia ha una coclasse nota e
  • stai usando la funzione "no pia" per questa interfaccia

quindi il codice viene generato come (IPIAINTERFACE) Activator.CreateInstance (Type.GetTypeFromClsid (GUID OF COCLASSTYPE))

Se:

  • sei "nuovo" in un tipo di interfaccia e
  • il tipo di interfaccia ha una coclasse nota e
  • NON si sta utilizzando la funzione "no pia" per questa interfaccia

quindi il codice viene generato come se avessi detto "nuovo COCLASSTYPE ()".

Jon, sentiti libero di biasimare me o Sam direttamente se hai domande su questa roba. Cordiali saluti, Sam è l'esperto di questa funzionalità.





c#-4.0