由於經常有舊專案要新增與新專案相同的功能,但舊專案使用 BCB5 開發,新專案使用 C# .Net 開發,開發環境的不同,使得相同功能需要重複撰寫,造成重工的問題,而且有些新功能在 BCB5 本身不支援,因此必須使用 C# .Net 來開發給 BCB5 使用,但 BCB5 並無法直接使用 C# .Net 建立的 DLL,必須以 COM 元件的方式提供使用,所以,特以此篇做 Demo 說明。
C# .Net 如何建立 COM 元件
開發環境說明:
- 作業系統:Win10
- IDE:VS2012
- .Net Framework:4.5
作為 COM 元件的專案,有以下幾點須注意:
- AssemblyInfo.cs - [assembly: ComVisible(true)]
- 屬性 -> 建置 -> 輸出 -> 勾選 "註冊 COM Interop"
- 宣告 interface 提供給接口 class 繼承。
- 接口的 interface 與 class 需給予 GUID。(工具 -> 建立 GUID)
另外一點,因為需要註冊 COM 元件,所以 VS2012 必須選擇以 "系統管理員身分執行" 的方式開啟。
以下範例以 COM_4_BCB_DEMO 為方案名稱,內有兩個專案,分別為 COM4BCB 與 FuncFactory,其中 COM4BCB 為接口類別,FuncFactory 為實作類別,也就是說 COM4BCB.dll 必須參考到 FuncFactory.dll,但在 BCB5 只看得到 COM4BCB interface 提供的介面函式,會這樣做是因為這樣必較接近實際專案的開發模式,總不能為了做成 COM 元件,就把所有的東西都寫在一個專案內吧,這樣也太不實際了。
簡述步驟與程式碼:
- 建立 Windows 類別庫
- 設定 COM4BCB 專案屬性
- 設定 COM4BCB 專案的 AssemblyInfo.cs -> assembly: ComVisible(true)
- 新增 FuncFactory 專案
- 程式碼內容如下:
//COM4BCB.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using FuncFactory; using System.Runtime.InteropServices; //作為 COM 元件的專案必需引用 namespace COM4BCB { [Guid("8791D490-F0B1-4198-9582-2DDE3EA4AD64")] public interface ICOM4BCBInterface { [DispId(1)] int Add(int x, int y); [DispId(2)] int Sub(int x, int y); [DispId(3)] string GetComInfo(); [DispId(4)] void ShowMsg(string name); } [Guid("009E0FB6-BF2E-4C32-BAAF-5BD38E7B1CCC")] public class COM4BCBDemo : ICOM4BCBInterface { private DemoFunc demo = new DemoFunc(); public int Add(int x, int y) { return demo.Add(x, y); } public int Sub(int x, int y) { return demo.Sub(x, y); } public string GetComInfo() { return demo.GetComInfo(); } public void ShowMsg(string s) { demo.ShowMsg(s); } } }
//FuncFactory.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace FuncFactory { public class DemoFunc { public DemoFunc() { } public int Add(int x, int y) { return (x + y); } public int Sub(int x, int y) { return (x - y); } public string GetComInfo() { return "測試於 BCB5 呼叫使用 C# .Net DLL。"; } public void ShowMsg(string s) { MessageBox.Show(s); } } }
- 設定 GUID。
- 建置方案後,可於 COM_4_BCB_DEMO\COM4BCB\bin\Debug\ 底下得到 COM4BCB.dll, COM4BCB.tlb 與 FuncFactory.dll,將這三個檔案放置於 BCB 專案執行檔同目錄底下。
註冊 COM 元件
- 複製 C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe 檔案至 BCB 專案執行檔同目錄底下。
- 建立批次檔 RegAsm.bat 於 BCB 專案執行檔同目錄底下,批次檔內容可參考下方所示:
@echo off %~dp0RegAsm.exe COM4BCB.dll pause
- 以系統管理員的身分執行,執行後出現以下畫面代表註冊成功。
BCB5 如何引用 COM 元件
測試環境說明:
- 作業系統:Win10
- IDE:BCB5
- .Net Framework:4.5 (務必安裝 Microsoft .NET Framework,且版本需與 COM 的開發環境版本相同 )
簡述步驟與程式碼:
- 產生 COM 代理類別 Unit:開啟 BCB5 IDE 選擇 Project -> Import Type Library -> 選擇 COM4BCB.tlb -> 如下圖選擇 -> Create Unit -> 產生 COM4BCB_TLB.cpp 與 COM4BCB_TLB.h (還有一些阿浬阿渣的檔案)。
- 開啟 COM4BCB_TLB.h 觀察 GUID 的內容,會發現與 C# .Net 設定的不同,所以必須手動修改成與 C# .Net 設定相同的 GUID 後才可使用。
- 建立新的 BCB5 專案,本例專案名稱為 ComImport,將方才產生的 Unit - COM4BCB_TLB 加入專案中,並於引用的 Form 或 Unit 中加入 #include "COM4BCB_TLB.h"。
- 本利使用的 Sample Code 如下:
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { HRESULT hr; Com4bcb_tlb::ICOM4BCBInterfacePtr ptr; CoInitialize ( NULL ); //初始化 COM 元件 hr = ptr.CreateInstance(__uuidof (Com4bcb_tlb::COM4BCBDemo)); //建立 COM 物件 if(hr == S_OK) //若 COM 物件建立失敗會回傳小於 0 的值(錯誤碼) { AnsiString s0 = ptr->GetComInfo(); ptr->ShowMsg(WideString(s0)); AnsiString s1 = "1 加 2 等於 " + IntToStr(ptr->Add(1, 2)); ptr->ShowMsg(WideString(s1)); AnsiString s2 = "1 減 2 等於 " + IntToStr(ptr->Sub(1, 2)); ptr->ShowMsg(WideString(s2)); } }
-
執行結果如下所示:
結語
之後如果 C# .Net 的實作內容有變更,只要 interface 沒有變,直接替換 dll 即可,程式不用再重新編譯,這是使用 dll 的好處,更重要的是專案只要維護 C# 版本即可,不用重複撰寫相同功能的 Code,只為了配合不同的 IDE 開發環境,這才是最重要的。
參考