0Pricing
C# Academy · Lezione

Interop COM e wrapper richiamabili dal runtime

Utilizzi componenti COM da C# con RCW, importi le librerie dei tipi e gestisca le eccezioni HRESULT.

Interop COM e wrapper richiamabili dal runtime è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Che cos'è COM Interop

COM (Component Object Model) è lo standard binario legacy di Microsoft per le interfacce, ancora utilizzato da Office, Windows Shell, dalle API legacy di DirectX e da molti strumenti aziendali. .NET può usare componenti COM tramite wrapper di interoperabilità che traducono tra oggetti gestiti e interfacce COM.

Runtime Callable Wrapper (RCW)

Quando si accede a un oggetto COM da .NET, il CLR crea un Runtime Callable Wrapper (RCW), ovvero un proxy gestito che avvolge l'oggetto COM. L'RCW gestisce automaticamente il conteggio dei riferimenti (AddRef/Release), l'esecuzione in apartment e il marshalling tra i tipi COM e .NET.

// The RCW is created automatically when you instantiate a COM class
// via a registered ProgID or CLSID

// Example: create an Excel Application COM object
Type excelType = Type.GetTypeFromProgID("Excel.Application")!;
object excelApp = Activator.CreateInstance(excelType)!;

// excelApp is an RCW — the CLR wraps the underlying IDispatch COM object
// COM AddRef is called; CLR tracks references
Console.WriteLine(excelApp.GetType().Name); // ApplicationClass

Importare le librerie dei tipi (TLB)

Lo strumento tlbimp.exe (Type Library Importer) legge una libreria dei tipi COM (.tlb o incorporata in .dll) e genera un assembly .NET di interoperabilità con classi RCW tipizzate e definizioni delle interfacce.

// From a Developer Command Prompt:
// tlbimp MyComLib.dll /out:MyComLib.Interop.dll

// Or reference via Visual Studio:
// Add Reference → COM → Microsoft Excel 16.0 Object Library
// → Generates Microsoft.Office.Interop.Excel.dll automatically

// The generated Interop assembly contains:
// - Interface types (matching COM vtable layout)
// - Co-class wrappers (implement the interfaces)
// - Enum types (from the type library)
// - Delegate types for COM event sinks

Usare un oggetto COM tramite un assembly di interoperabilità

Una volta referenziato l'assembly di interoperabilità, i tipi COM appaiono come normali tipi .NET. La chiamata a un metodo passa attraverso l'RCW fino alla distribuzione tramite vtable COM. Rilasciare sempre correttamente gli oggetti COM per evitare perdite di risorse.

using Microsoft.Office.Interop.Excel;

Application excel = new Application();
excel.Visible = false;

Workbooks books = excel.Workbooks;
Workbook wb = books.Add();
Worksheet ws = (Worksheet)wb.Sheets[1];

((Range)ws.Cells[1, 1]).Value = "Hello, COM!";
wb.SaveAs(@"C:\Temp\test.xlsx");
wb.Close();

// Release COM RCW explicitly
Marshal.ReleaseComObject(ws);
Marshal.ReleaseComObject(wb);
Marshal.ReleaseComObject(books);
excel.Quit();
Marshal.ReleaseComObject(excel);

Marshal.ReleaseComObject

COM usa il conteggio dei riferimenti: AddRef/Release. L'RCW chiama Release solo quando viene raccolto dal GC, evento che potrebbe verificarsi molto più tardi. Chiamare Marshal.ReleaseComObject() per diminuire immediatamente il conteggio dei riferimenti dell'RCW e rilasciare l'oggetto COM senza attendere il GC.

// Pattern: release COM objects in finally block
Application? excel = null;
Workbook? wb = null;
try
{
    excel = new Application();
    wb = excel.Workbooks.Add();
    // ... do work ...
}
finally
{
    if (wb   != null) Marshal.ReleaseComObject(wb);
    if (excel != null)
    {
        excel.Quit();
        Marshal.ReleaseComObject(excel);
    }
    // Force GC to clean up any remaining RCWs
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

HRESULT ed eccezioni COM

I metodi COM restituiscono valori HRESULT per segnalare il successo o l'errore. L'RCW controlla automaticamente l'HRESULT e genera una COMException (o un'eccezione più specifica) quando indica un errore. È possibile gestirle con il normale try/catch.

try
{
    // COM method that might fail
    Workbook wb = excel.Workbooks.Open(@"C:\missing.xlsx");
}
catch (COMException ex) when (ex.HResult == unchecked((int)0x800A03EC))
{
    // Excel-specific HRESULT for file not found
    Console.WriteLine($"Excel error: {ex.Message}");
}
catch (COMException ex)
{
    // General COM failure
    Console.WriteLine($"COM error 0x{ex.HResult:X8}: {ex.Message}");
}

Late binding con dynamic

Se non si dispone di una libreria dei tipi o di un assembly di interoperabilità, è possibile usare la parola chiave dynamic di C# per la distribuzione COM con associazione tardiva (IDispatch). Questa soluzione è più lenta, perché gli ID di distribuzione vengono risolti a runtime, ma non richiede wrapper generati.

// Late-bound COM via dynamic — no interop DLL needed
Type type = Type.GetTypeFromProgID("Word.Application")!;
dynamic word = Activator.CreateInstance(type)!;

word.Visible = false;
dynamic docs = word.Documents;
dynamic doc = docs.Add();

doc.Content.Text = "Late-bound COM example";
doc.SaveAs2(@"C:\Temp\test.docx");
doc.Close();
word.Quit();

Marshal.ReleaseComObject(doc);
Marshal.ReleaseComObject(docs);
Marshal.ReleaseComObject(word);

Sink degli eventi COM

Gli oggetti COM generano eventi tramite i connection point. L'assembly di interoperabilità genera le interfacce dei sink degli eventi. È possibile effettuare la sottoscrizione usando i normali delegate/eventi .NET; l'RCW gestisce il collegamento COM IConnectionPoint.

using Microsoft.Office.Interop.Excel;

Application excel = new Application();
excel.Visible = true;

// Subscribe to COM event via generated event wrapper
excel.WorkbookBeforeClose += (wb, ref cancel) =>
{
    Console.WriteLine($"Closing: {wb.Name}");
    // Set cancel = true to prevent close
};

excel.WorkbookOpen += (wb) =>
{
    Console.WriteLine($"Opened: {wb.Name}");
};

// Open a workbook to trigger events
excel.Workbooks.Open(@"C:\Temp\test.xlsx");

Apartment COM: STA e MTA

Molti componenti COM, soprattutto quelli relativi all'interfaccia utente come Office, richiedono un Single-Threaded Apartment (STA). Contrassegnare sempre con [STAThread] (Main) i thread che creano oggetti COM STA oppure impostare lo stato apartment del thread prima di avviarlo.

// Console apps default to MTA — COM Office automation requires STA
// Option 1: Mark Main with [STAThread]
[STAThread]
static void Main()
{
    var excel = new Microsoft.Office.Interop.Excel.Application();
    // ...
}

// Option 2: Run on an STA thread manually
Thread staThread = new Thread(() =>
{
    var excel = new Microsoft.Office.Interop.Excel.Application();
    // ...
    excel.Quit();
    Marshal.ReleaseComObject(excel);
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();

ComImport e dichiarazioni COM manuali

È possibile dichiarare manualmente le interfacce COM usando gli attributi [ComImport], [Guid] e [InterfaceType]: è utile quando non esiste una libreria dei tipi o quando serve solo un sottoinsieme di un'API COM di grandi dimensioni.

[ComImport]
[Guid("00000000-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IUnknown
{
    void QueryInterface(ref Guid riid, out IntPtr ppvObject);
    int  AddRef();
    int  Release();
}

// Declare a specific COM interface you want to consume:
[ComImport]
[Guid("0000010C-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IPersist
{
    void GetClassID(out Guid pClassID);
}

Verifica rapida

Qual è lo scopo di un Runtime Callable Wrapper (RCW) nell'interoperabilità COM di .NET?

Riepilogo: interoperabilità COM e Runtime Callable Wrapper

Concetti chiave:

  • RCW = proxy gestito che avvolge un oggetto COM; il CLR ne crea automaticamente uno per ogni oggetto COM
  • Usare tlbimp.exe o i riferimenti di Visual Studio per generare assembly di interoperabilità tipizzati
  • Chiamare Marshal.ReleaseComObject() per rilasciare immediatamente gli oggetti COM, senza attendere il GC
  • Gli errori COM vengono esposti come COMException con l'HRESULT originale
  • dynamic abilita la distribuzione COM con associazione tardiva senza un assembly di interoperabilità
  • I componenti COM di Office e dell'interfaccia utente richiedono un thread STA: usare [STAThread] o impostare lo stato apartment

Domande Frequenti

La lezione «Interop COM e wrapper richiamabili dal runtime» è gratuita?

Sì — il testo completo di «Interop COM e wrapper richiamabili dal runtime» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Interop COM e wrapper richiamabili dal runtime»?

Utilizzi componenti COM da C# con RCW, importi le librerie dei tipi e gestisca le eccezioni HRESULT. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Interop COM e wrapper richiamabili dal runtime»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Fondamenti di P/Invoke
  2. LibraryImport e P/Invoke generato dal codice sorgente
  3. Codice non sicuro, puntatori e buffer fissi
  4. Interop COM e wrapper richiamabili dal runtime
← Torna a C# Academy