COM 상호 운용 및 런타임 호출 가능 래퍼
RCW를 사용해 C#에서 COM 구성 요소를 사용하고, 형식 라이브러리를 가져오며, HRESULT 예외를 처리합니다.
COM 상호 운용 및 런타임 호출 가능 래퍼은(는) CoddyKit의 무료 C# Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 C# Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
COM 상호 운용이란 무엇인가요
COM(구성 요소 개체 모델)은 Microsoft의 레거시 바이너리 인터페이스 표준으로, 현재도 Office, Windows Shell, 레거시 DirectX API 및 많은 기업용 도구에서 사용됩니다. .NET은 관리되는 개체와 COM 인터페이스 사이를 변환하는 상호 운용 래퍼를 통해 COM 구성 요소를 사용할 수 있습니다.
런타임 호출 가능 래퍼(RCW)
.NET에서 COM 개체에 액세스하면 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에 포함된 형식 라이브러리)를 읽고, 강력한 형식의 RCW 클래스와 인터페이스 정의가 포함된 .NET 상호 운용 어셈블리를 생성합니다.
// 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 개체 사용
상호 운용 어셈블리를 참조하면 COM 형식은 일반적인 .NET 형식처럼 보입니다. 메서드 호출은 RCW를 거쳐 COM vtable 디스패치로 변환됩니다. 리소스 누수를 방지하려면 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는 GC로 수집될 때만 Release를 호출하므로 훨씬 나중에 호출될 수 있습니다. Marshal.ReleaseComObject()를 호출하면 RCW 참조 횟수가 즉시 감소하고 GC를 기다리지 않고 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을 사용한 지연 바인딩
형식 라이브러리나 상호 운용 어셈블리가 없다면 C#의 dynamic 키워드를 사용하여 지연 바인딩된 COM 디스패치(IDispatch)를 수행할 수 있습니다. 디스패치 ID를 런타임에 확인하므로 더 느리지만, 생성된 래퍼가 필요하지 않습니다.
// 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 개체는 연결 지점을 통해 이벤트를 발생시킵니다. 상호 운용 어셈블리는 이벤트 싱크 인터페이스를 생성합니다. 일반적인 .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와 같은 UI 관련 구성 요소는 단일 스레드 아파트먼트(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 선언
[ComImport], [Guid], [InterfaceType] 특성을 사용하여 COM 인터페이스를 수동으로 선언할 수 있습니다. 이는 형식 라이브러리가 없거나 대규모 COM API의 일부만 필요한 경우에 유용합니다.
[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);
}빠른 확인
.NET COM 상호 운용에서 런타임 호출 가능 래퍼(RCW)의 목적은 무엇인가요?
복습: COM 상호 운용 및 런타임 호출 가능 래퍼
핵심 요점:
- RCW = COM 개체를 감싸는 관리되는 프록시이며, CLR이 각 COM 개체에 대해 자동으로 하나를 생성합니다
tlbimp.exe또는 VS 참조를 사용하여 강력한 형식의 상호 운용 어셈블리를 생성합니다Marshal.ReleaseComObject()를 호출하여 COM 개체를 즉시 해제합니다(GC를 기다리지 않습니다)- COM 오류는 원래 HRESULT와 함께
COMException으로 나타납니다 dynamic을 사용하면 상호 운용 어셈블리 없이 지연 바인딩된 COM 디스패치를 수행할 수 있습니다- Office/UI COM 구성 요소에는 STA 스레드가 필요하므로
[STAThread]를 사용하거나 아파트먼트 상태를 설정합니다
자주 묻는 질문
“COM 상호 운용 및 런타임 호출 가능 래퍼” 강의는 무료인가요?
네 — “COM 상호 운용 및 런타임 호출 가능 래퍼” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C# Academy 강의 전체를 잠금 해제할 수 있습니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“COM 상호 운용 및 런타임 호출 가능 래퍼”에서 뭘 배우나요?
RCW를 사용해 C#에서 COM 구성 요소를 사용하고, 형식 라이브러리를 가져오며, HRESULT 예외를 처리합니다. 브라우저에서 직접 실행하는 실습 코드로 C# Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
C# Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 C# Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“COM 상호 운용 및 런타임 호출 가능 래퍼” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 C# Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 C# Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- P/Invoke 기초
- LibraryImport와 소스 생성 P/Invoke
- 안전하지 않은 코드, 포인터 및 고정 버퍼
- COM 상호 운용 및 런타임 호출 가능 래퍼