.NET 與 COM 互通包含四大領域:P/Invoke、Reverse P/Invoke、COM Interop、Reverse COM Interop。本文專注於在傳統 COM 應用程式(VB6、C++、Delphi 等)中使用 .NET 組件的完整實作流程,解決遺留系統現代化遷移的關鍵技術難題。
| 類型 | 方向 | 核心機制 |
|---|---|---|
| P/Invoke | .NET → Native DLL | DllImport 屬性平台呼叫 |
| Reverse P/Invoke | Native → .NET 回呼 | 委派轉函數指標 |
| COM Interop | .NET → COM 組件 | RCW (Runtime Callable Wrapper) |
| Reverse COM Interop | COM → .NET 組件 | CCW (COM Callable Wrapper) + 類型程式庫 |
// 介面必須標記 ComVisible
[ComVisible(true)]
[Guid("IID_IA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IA
{
void Hello();
}
// 實作類別
[ComVisible(true)]
[Guid("CLSID_A")]
[ClassInterface(ClassInterfaceType.None)]
public class A : IA
{
public void Hello()
{
MessageBox.Show("Hello from .NET!");
}
}
關鍵動作:RegAsm 產生類型程式庫並註冊
csc /target:library A.cs // 編譯為 DLL
regasm A.dll /tlb /codebase // /tlb 產生 .tlb,/codebase 註冊路徑
註冊表關鍵變更:
HKEY_CLASSES_ROOT\CLSID\{CLSID_A}\InprocServer32 指向 mscoree.dll(CLR 載入器,俗稱 Shim)A.dll使用 oleview 檢視產生的 A.tlb,可見介面 IA 與類別 A 完整定義,確保 COM 端能正確識別。
C++ 範例(使用 #import 匯入類型程式庫):
// client.cpp
#define _WIN32_COM
#include
#include
#import "A.tlb" no_namespace named_guids raw_interfaces_only
int main() {
HRESULT hr;
// 0. 初始化 COM 程式庫
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr)) { printf("COM 初始化失敗: 0x%x\n", hr); return -1; }
// 1. 建立物件取得 IUnknown
IUnknown* pUnk = NULL;
hr = CoCreateInstance(CLSID_A, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnk);
if (FAILED(hr)) { printf("建立失敗: 0x%x\n", hr); return -1; }
// 2. QueryInterface 取得 IA 介面
IA* pIA = NULL;
hr = pUnk->QueryInterface(IID_IA, (void**)&pIA);
pUnk->Release(); // 釋放 IUnknown
if (FAILED(hr)) { printf("取得 IA 失敗: 0x%x\n", hr); return -1; }
// 3. 呼叫 .NET 方法
hr = pIA->Hello();
if (FAILED(hr)) { printf("呼叫失敗: 0x%x\n", hr); }
// 4. 清理
pIA->Release();
CoUninitialize();
return 0;
}
編譯:cl client.cpp(需連結 ole32.lib)
IUnknown、IDispatch 等標準 COM 介面| 屬性 | 用途 | 建議值 |
|---|---|---|
[ComVisible(true)] | 公開給 COM 可見 | 介面、類別、公開成員必加 |
[Guid("...")] | 固定 GUID | 介面用 IID_*,類別用 CLSID_*,切勿自動產生 |
[ClassInterface(ClassInterfaceType.None)] | 禁止自動產生介面 | 強制使用顯式介面,避免版本衝突 |
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | 指定介面類型 | 效能最佳,無 IDispatch 開銷 |