Взаимодействие с COM и оболочки, вызываемые средой выполнения
Используйте компоненты COM из C# с помощью RCW, импортируйте библиотеки типов и обрабатывайте исключения HRESULT.
«Взаимодействие с COM и оболочки, вызываемые средой выполнения» — бесплатный урок C# Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Что такое COM Interop
COM (Component Object Model) — это устаревший двоичный стандарт интерфейсов Microsoft, который по-прежнему используется в Office, оболочке Windows, устаревших API DirectX и многих корпоративных инструментах. .NET может работать с COM-компонентами через interop-обёртки, преобразующие управляемые объекты и интерфейсы COM друг в друга.
Вызываемые средой выполнения обёртки (RCW)
Когда вы обращаетесь к объекту COM из .NET, CLR создаёт вызываемую средой выполнения обёртку (RCW) — управляемый прокси, оборачивающий объект COM. RCW автоматически обрабатывает подсчёт ссылок (AddRef/Release), работу потоков в апартаментах и маршалинг между типами COM и .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Импорт библиотек типов (TLB)
Инструмент tlbimp.exe (импортёр библиотеки типов) читает библиотеку типов COM (.tlb или библиотеку, встроенную в .dll) и создаёт сборку interop для .NET со строго типизированными классами RCW и определениями интерфейсов.
// 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Использование объекта COM через сборку interop
После добавления ссылки на сборку interop типы COM выглядят как обычные типы .NET. Вызов метода передаётся через RCW в диспетчеризацию виртуальной таблицы COM. Всегда правильно освобождайте объекты COM, чтобы избежать утечек ресурсов.
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 использует подсчёт ссылок — AddRef/Release. RCW вызывает Release только после сборки объекта сборщиком мусора, что может произойти значительно позже. Вызовите Marshal.ReleaseComObject(), чтобы немедленно уменьшить счётчик ссылок RCW и освободить объект COM, не дожидаясь сборщика мусора.
// 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 и исключения COM
Методы COM возвращают значения HRESULT, сообщающие об успехе или ошибке. RCW автоматически проверяет HRESULT и выбрасывает COMException (или более конкретное исключение), если значение указывает на ошибку. Обрабатывайте их обычной конструкцией 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}");
}Позднее связывание с dynamic
Если у вас нет библиотеки типов или сборки interop, можно использовать ключевое слово C# dynamic для позднего связывания с COM через IDispatch. Это медленнее: идентификаторы диспетчеризации разрешаются во время выполнения, — но сгенерированные обёртки не требуются.
// 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);Точки подключения событий COM
Объекты COM вызывают события через точки подключения. Сборка interop генерирует интерфейсы приёмников событий. Подписывайтесь на них с помощью обычных делегатов и событий .NET, а RCW берёт на себя взаимодействие с 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");Апартаменты COM: STA и MTA
Многим компонентам COM, особенно связанным с пользовательским интерфейсом, например Office, требуется однопоточный апартамент (STA). Всегда помечайте потоки, создающие STA-объекты COM, атрибутом [STAThread] (Main) или задавайте состояние апартамента потока до его запуска.
// 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 и ручные объявления COM
Интерфейсы COM можно объявлять вручную с помощью атрибутов [ComImport], [Guid] и [InterfaceType]. Это полезно, если библиотеки типов нет или вам нужна только часть большого API COM.
[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);
}Быстрая проверка
Для чего нужна вызываемая средой выполнения обёртка (RCW) в COM Interop .NET?
Повторение: COM Interop и вызываемые средой выполнения обёртки
Основные выводы:
- RCW — управляемый прокси, оборачивающий объект COM; CLR автоматически создаёт его для каждого объекта COM
- Используйте
tlbimp.exeили ссылки VS, чтобы создавать строго типизированные сборки interop - Вызывайте
Marshal.ReleaseComObject(), чтобы немедленно освободить объекты COM, не дожидаясь сборщика мусора - Ошибки COM проявляются как
COMExceptionс исходным значением HRESULT dynamicвключает позднюю диспетчеризацию COM без сборки interop- Для компонентов COM Office и пользовательского интерфейса требуется поток STA — используйте
[STAThread]или задайте состояние апартамента
Часто задаваемые вопросы
Урок «Взаимодействие с COM и оболочки, вызываемые средой выполнения» бесплатный?
Да — полный текст урока «Взаимодействие с COM и оболочки, вызываемые средой выполнения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Взаимодействие с COM и оболочки, вызываемые средой выполнения»?
Используйте компоненты COM из C# с помощью RCW, импортируйте библиотеки типов и обрабатывайте исключения HRESULT. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Взаимодействие с COM и оболочки, вызываемые средой выполнения»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы P/Invoke
- LibraryImport и P/Invoke из исходного кода
- Небезопасный код, указатели и буферы фиксированного размера
- Взаимодействие с COM и оболочки, вызываемые средой выполнения