最近項目涉及第三方接口調用。第三方是用C#實現的WCF服務。而我們的程序是使用的BCB6開發。因此,打算將與WCF的通訊包含在C#的類庫中,給BCB6調用。BCB6 是無法直接調用C#的DLL,但可以通過C#編寫一個COM組件,然后BCB調用這個COM組件來調用。
-  
          用C#編寫COM
大體步驟是
- 新建一個類庫項目
 
 
|   
  |  
           
-  
          設置Assemblyinfo.cs

 -  
          設置項目屬性

 - 編寫代碼
 
|   using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; //1添加Rumtime.InteropServices 程序集引用 using System.Windows.Forms; //2為了演示,所以加入了forms引用 
 namespace ComImpTest { [Guid("3CD116D4-18A1-4504-921B-57C053BAD618")] //3主菜單->工具->創建GUID產生 public interface IComInterface //4必須先定義接口 { [DispId(1)] //5每一個函數或者屬性都需要指定DispID void Hello(string name); [DispId(2)] int Add(int x,int y); [DispId(3)] string COMINFO{get;} 
 } 
 [Guid("1A8EB38C-E732-49B2-A897-B40FFD744E3D")] public class ComImp : IComInterface //6編寫一個實現接口的類 { public void Hello(string name) { Console.WriteLine("你好"+name); MessageBox.Show("你好"+name,"C# dll 彈出界面"); } 
 public int Add(int x, int y) { return x + y; } 
 public string COMINFO { get { return "com 測試"; } } } }  |  
           
-  
          編譯得到DLL 和 TLB

 
-  
          BCB編寫調用
-  
            首先BCB的開發環境中需要安裝.NET FrameWork ,這里都是用的.NET FrameWork 4.0,安裝之后,
在如圖的目錄會得到RegAsm.exe(一定要用安裝后得到的RegAsm 從其他地方復制的RegASM可能存在版本不兼容),
這個在注冊COM的時候要用到。

 - 建立一個BCB控制台工程
 -  
            吧RegAsm.exe(我們是為了注冊方便),前面編譯得到的 ComImpTest.tlb 、ComImpTest.dll 復制到BCB工程的EXE
輸出目錄。(這也不是必須的,本質上COM只要能注冊,然后BCB功能能找到TLB創建代理類即可)

 -  
            用管理員啟動命令行,注冊ComImpTest.dll

其本質是吧這個COM類注冊到了注冊表中。向系統進行了發布。
 -  
            用BCB 產生代理類
打開BCB 工程,選擇主菜單 projectàimport type library



得到代理類

包含這個頭文件,我們就可以調用了。
但是這里不知道是BCB的bug,還是兼容性的問題,BCB產生的ComImp代理類的GUID是錯誤的。

正確的GUID 應該是編寫C#dll 時產生的Guid
 
 -  
            
 
|   [Guid("1A8EB38C-E732-49B2-A897-B40FFD744E3D")]  |  
           
我們只需要替換這個Guid即可,如果不這么做,調用時HRESULT會返回找不到實現接口。

-  
          BCB 調用COM的代碼
- 在控制台工程中,加入代理類的頭文件 ComImpTest_tlb.h
 - 代碼如下
 
 
|   //--------------------------------------------------------------------------- #pragma hdrstop #include <iostream> #include <vcl.h> //注意,iostream要在 vcl.h 前面定義,否則cout<<AnsiString 報錯 #include "Comimptest_tlb.h" using namespace std; //--------------------------------------------------------------------------- 
 #pragma argsused int main(int argc, char* argv[]) { HRESULT hr; //初始化COM CoInitialize ( NULL ); //創建智能指針命名空間在頭文件Comimptest_tlb.h中可以查找到 //接口智能指針的定義也能查找到 Comimptest_tlb::IComInterfacePtr ptr; 
 //創建實例,ComImp 的聲明,也能在頭文件中查找到。 hr = ptr.CreateInstance(__uuidof (Comimptest_tlb::ComImp)); 
 if(hr == S_OK) { //調用ADD方法 cout << ptr->Add (1, 2)<<endl; //調用Hello方法,在bcb中Widestring 對應 c#的 string WideString name = "Zakk wylde 和奧茲"; ptr->Hello(name.c_bstr()); //調用COMINFO屬性,這里COM進行了名字改變,這些函數都可以在頭文件中查到原型定義。 BSTR outp = NULL; ptr->get_COMINFO(&outp); //因為是寬字符,所以要轉換為AnsiString進行輸出 cout<<AnsiString(outp)<<endl; } 
 CoUninitialize (); system("pause"); return 0; }  |  
           


4 用vc調用COM
用VC調用C#的COM 就沒有這么多事情。下面是VC調用的代碼,
將ComImpTest.tlb 和 ComImpTest.dll 拷貝到 VC工程的debug目錄下。然后用import指令(BCB 無法用import識別,估計是不兼容)
// testCOM3.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include <iostream> #include <Windows.h> using namespace std; #import "../debug/ComImpTest.tlb" int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr; //初始化COM CoInitialize ( NULL ); //創建智能指針 ComImpTest::IComInterfacePtr ptr; //創建實例 hr = ptr.CreateInstance(__uuidof (ComImpTest::ComImp)); if(hr == S_OK) { cout << ptr->Add (1,2)<<endl; ptr->Hello(L"VC++ 調用"); cout<<ptr->COMINFO<<endl; } CoUninitialize (); system("pause"); return 0; } //---------------------------------------------------------------------------

