首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 建立 COM 元件供 C++/VB6 呼叫:Interop 完整實戰教學

     編輯:關於C#

C# 透過 COM Interop 可將 Managed 組件暴露為傳統 COM 物件,讓 C++、VB6、VBA、Delphi 等非受控代碼呼叫。本文完整示範:強名稱簽章、介面定義 (DispId)、類別實作、RegAsm 註冊、類型程式庫 (.tlb) 產生、C++ 智慧指標呼叫等端到端流程。

架構概覽


[C# Class Library] 
   → 介面 (Interface) + 類別 (Class) + 屬性 (ComVisible, Guid, ClassInterface, ComSourceInterfaces)
   → 編譯產生 .dll
   → RegAsm /tlb /codebase 註冊並產生 .tlb
   → [C++ Client] #import ".tlb" → 智慧指標呼叫

C# COM 元件專案設定

  1. 建立 Class Library (.NET Framework) 專案 (非 .NET Core/Standard,需完整 COM Interop 支援)。
  2. 專案屬性 → Build → 勾選 Register for COM Interop (開發機自動註冊)。
  3. 專案屬性 → Signing → 勾選 Sign the assembly → 新建強名稱金鑰 (.snk)。
  4. AssemblyInfo.cs 確認 [assembly: ComVisible(true)] (或類別層級控制)。

介面與類別設計規範

規則說明
介面/類別/成員必須為 public
介面需 Guid[Guid("...")] 由 guidgen.exe 或 VS 工具產生
成員需 DispId[DispId(n)] 明確指定派發識別碼,順序即為 v-table 順序
類別介面類型[ClassInterface(ClassInterfaceType.None)] 禁止自動產生介面,強制實作明確介面 (推薦)
事件介面[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] + [ComSourceInterfaces(typeof(Events))]
參數/回傳型別限 COM 可轉換型別 (基本型別、陣列、字串、DateTime、Decimal、IPicture/IFont 等)

完整程式碼範例:資料庫操作 COM 物件


using System;
using System.Runtime.InteropServices;
using System.Data.SqlClient;

namespace DatabaseComObject
{
    // 1. 類別介面 (供 COM 呼叫)
    [Guid("694C1820-04B6-4988-928F-FD858B95C880")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)] // Dual: 支援早期/晚期繫結
    public interface IDbComInterface
    {
        [DispId(1)] void Init(string userId, string password);
        [DispId(2)] bool ExecuteSelectCommand(string sql);
        [DispId(3)] bool NextRow();
        [DispId(4)] void ExecuteNonSelectCommand(string sql);
        [DispId(5)] string GetColumnData(int index);
    }

    // 2. 事件介面 (可選)
    [Guid("47C976E0-C208-4740-AC42-41212D3C34F0")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IDbComEvents
    {
        // [DispId(1)] void OnError(string message); // 範例
    }

    // 3. 實作類別
    [Guid("9E5E5FB2-219D-4EE7-AB27-E4DBED8E123E")]
    [ClassInterface(ClassInterfaceType.None)] // 關鍵:不自動產生介面
    [ComSourceInterfaces(typeof(IDbComEvents))] // 事件來源
    [ComVisible(true)]
    public class DbComClass : IDbComInterface
    {
        private SqlConnection conn;
        private SqlDataReader reader;

        public void Init(string userId, string password)
        {
            string cs = $"Data Source=.;Initial Catalog=Northwind;User ID={userId};Password={password};Connect Timeout=30";
            conn = new SqlConnection(cs);
            conn.Open();
            // MessageBox 需參考 System.Windows.Forms,純 COM 元件建議改用事件/回呼
        }

        public bool ExecuteSelectCommand(string sql)
        {
            reader?.Close();
            using (var cmd = new SqlCommand(sql, conn))
            {
                reader = cmd.ExecuteReader();
            }
            return true;
        }

        public bool NextRow() => reader?.Read() ?? false;

        public string GetColumnData(int index)
        {
            object val = reader?.GetValue(index);
            return val?.ToString() ?? "";
        }

        public void ExecuteNonSelectCommand(string sql)
        {
            using (var cmd = new SqlCommand(sql, conn))
                cmd.ExecuteNonQuery();
        }
    }
}

註冊與部署步驟

開發機 (Visual Studio 自動)

建置專案即自動執行 RegAsm /codebase 註冊至 HKCR。

目標機器 (手動部署)


# 1. 複製 .dll 至目標資料夾 (如 C:\Program Files\MyCom\)
# 2. 以管理員身分執行開發人員命令提示字元
RegAsm.exe "C:\Program Files\MyCom\DatabaseComObject.dll" /tlb /codebase
# 產生 .tlb 並寫入登錄表
# 3. (選擇性) GAC 安裝強名稱組件
gacutil /i "C:\Program Files\MyCom\DatabaseComObject.dll"

C++ 客戶端呼叫 (#import 智慧指標)


// Stdafx.h 或對應 cpp
#import "C:\Program Files\MyCom\DatabaseComObject.tlb" no_namespace named_guids

// 使用
CoInitialize(NULL); // 單執行緒單元 (STA) 必須

DatabaseComObject::IDbComInterfacePtr dbCom(__uuidof(DatabaseComObject::DbComClass));
dbCom->Init("scott", "tiger");

char cmd[1024];
sprintf_s(cmd, "SELECT CompanyName, ContactName FROM Customers WHERE CustomerID = '%s'", "ALFKI");
dbCom->ExecuteSelectCommand(cmd);

if (dbCom->NextRow())
{
    _bstr_t company = dbCom->GetColumnData(0);
    _bstr_t contact = dbCom->GetColumnData(1);
    // 轉 CString: CString str = (LPCTSTR)company;
}

CoUninitialize();

VB6 / VBA 呼叫範例


Dim db As DatabaseComObject.DbComClass
Set db = New DatabaseComObject.DbComClass
db.Init "scott", "tiger"
db.ExecuteSelectCommand "SELECT * FROM Categories"
Do While db.NextRow
    Debug.Print db.GetColumnData(0) & " " & db.GetColumnData(1)
Loop

常見問題與除錯

現代化替代方案

  • .NET Core 3.1+ / .NET 5+:支援 ComCallWrapper 但 不支援 產生可供非受控呼叫的 COM 伺服器 (無 RegAsm 等效工具)。
  • 跨語言互通:建議改用 gRPC、Named Pipes、HTTP/REST、Message Queue 等進程間通訊機制。
  • 既有 COM 遷移:維護 .NET Framework 專案作為 COM 橋接層,核心邏輯移至 .NET Core 庫,透過 IPC 通訊。

掌握 Guid/DispId/ClassInterfaceType.None/RegAsm /tlb /codebase 這組關鍵鍵,即可將 C# 業務邏輯無縫橋接至傳統 C++/VB6 生態系,延續既有資產壽命並平滑邁向現代架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
現象原因解決
Class not registered (0x80040154)未註冊或位元數不符 (x86/x64)目標平台對應註冊:32-bit 用 RegAsm (Framework), 64-bit 用 RegAsm (Framework64)
Type mismatch / Invalid argument參數型別非 COM 可轉換檢查陣列維度、DateTime 格式、避免泛型/介面參數
事件不觸發事件介面未標記 IDispatch / Sink 未建立確認 InterfaceIsIDispatch、ComSourceInterfaces
部署後呼叫失敗強名稱驗證失敗 / 相依組件缺失GAC 安裝或 /codebase、確保相依 DLL 同目錄