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


168

EDIT: ho scritto i risultati come post sul blog .


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

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

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

Inoltre, in C # 4, è possibile utilizzare argomenti non ref per i refparametri 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 molti argomenti. I parametri opzionali non sono belli? :)

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 poter scrivere:

Dummy dummy = new Dummy();

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

Gli altri attributi aggiunti dal compilatore per le PIA PIA collegate ( CompilerGeneratede TypeIdentifier) non sembrano fare il trucco ... qual è la salsa magica?


11
I parametri opzionali non sono belli? IMO, No, non sono carini. Microsoft sta cercando di correggere l'errore nelle interfacce COM di Office aggiungendo bloat a C #.
Mehrdad Afshari,

18
@Mehrdad: i parametri opzionali sono utili oltre COM, ovviamente. Devi stare attento con i valori predefiniti, ma tra questi e gli argomenti con nome, è molto più facile costruire un tipo immutabile utilizzabile.
Jon Skeet,

1
Vero. In particolare, i parametri con nome possono essere praticamente richiesti per l'interoperabilità con alcuni ambienti dinamici. Certo, senza dubbio, è una funzione utile ma ciò non significa che sia disponibile gratuitamente. Costa la semplicità (un obiettivo di progettazione esplicitamente dichiarato). Personalmente, penso che C # sia sorprendente per le funzionalità che il team ha lasciato (altrimenti, avrebbe potuto essere un clone di C ++). Il team di C # è fantastico, ma un ambiente aziendale difficilmente può essere privo di politica. Mi immagino Anders stesso non era molto contento di questo, come ha dichiarato nel suo discorso PDC'08: "ci ha portato anni dieci per tornare a dove eravamo."
Mehrdad Afshari,

7
Sono d'accordo che il team dovrà tenere d'occhio la complessità. Le cose dinamiche aggiungono molta complessità per poco valore per la maggior parte degli sviluppatori, ma alto valore per alcuni sviluppatori.
Jon Skeet,

1
Ho visto gli sviluppatori di framework iniziare a discutere dei suoi usi in molti luoghi. IMO è solo il momento di trovare un buon uso per dynamic... siamo troppo abituati alla tipizzazione statica / forte per capire perché è importante al di fuori di COM.
Chakrit,

Risposte:


145

Non sono un esperto in questo, ma di recente mi sono imbattuto in ciò che penso tu voglia: la classe di attributi CoClass .

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

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

Vedi la mia risposta a una domanda simile sull'API di Microsoft Speech , in cui sei in grado di "istanziare" l'interfaccia SpVoice(ma in realtà, stai istanziando SPVoiceClass).

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

2
Molto interessante - lo proverò più tardi. Tuttavia, i tipi di PIA collegati non hanno CoClass. Forse è qualcosa a che fare con il processo di collegamento - darò un'occhiata alla PIA originale ...
Jon Skeet,

64
+1 per essere fantastico scrivendo la risposta accettata quando anche Eric Lippert e Jon Skeet hanno risposto;) No, davvero, +1 per menzionare CoClass.
OregonGhost,

61

Tra te e Michael hai quasi messo insieme i pezzi. Penso che funzioni così. (Non ho scritto il codice, quindi potrei essere in qualche modo inesatto, ma sono abbastanza sicuro che sia così.)

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 stai usando la funzione "no pia" per questa interfaccia

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

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


36

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

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

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

Ora se usiamo il collegamento PIA in C # 4, parte di questo è incorporato nel binario risultante ... ma non tutto. Un'applicazione che crea solo un'istanza di Applicationfinisce 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 tipo COM reale al momento dell'esecuzione.

Un'altra cosa interessante è la differenza nel codice tra la versione collegata e la versione non collegata. Se si decompila la riga

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

nella versione referenziata 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 "reale" abbia bisogno CoClassdell'attributo, ma la versione collegata non lo fa perché non esiste un CoClasscompilatore a cui possa effettivamente fare riferimento il compilatore. Deve farlo in modo dinamico.

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


27

Solo per aggiungere un po 'di conferma alla risposta di Michael:

Il seguente codice viene compilato ed eseguito:

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

È necessario che sia ComImportAttributee GuidAttributeche funzioni.

Nota anche le informazioni quando passi il mouse sopra new IFoo(): Intellisense raccoglie correttamente le informazioni: Bello!


grazie, ci stavo provando ma mancava l' attributo ComImport , ma quando vado al codice sorgente stavo lavorando usando F12 mostra solo CoClass e Guid , perché?
Ehsan Sajjad,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.