1. 程式人生 > >VC++ 呼叫 C#生成DLL的兩種方法

VC++ 呼叫 C#生成DLL的兩種方法

今年在C++呼叫對方用C#寫的DLL時,遇到託管和非託管的問題。

     原帖:

     以及參考文章:

VisualC、Delphi或者VB等程式語言來編寫的DLL檔案,在編譯完成以後,產生DLL檔案已經是一個可以直接供計算機使用的二進位制檔案,而Visual C#生成的DLL不是獨立執行的程式,是某個程式的一個部分,只能由所屬的程式呼叫,使用者不能也不需要開啟它,Visual C#編譯器生成的託管程式碼雖然也是二進位制檔案,但不是可以直接供計算機使用的原始程式碼,實際上是一種中間語言(IL)程式碼,需要經過"下一代視窗服務"( Next Generation Windows Services,簡寫為NGWS ) runtime的即時編譯器(即JIT)進行編譯。用Visual C#生成的DLL檔案已經和以前的DLL檔案有了本質上的區別。用Visual C#生成的DLL檔案在程式設計中更多的表現為一種類(Class)或者類庫(Class Library)。

        如果想在VC++的非託管程式碼中呼叫已經用VC#生成的託管的DLL,從兩個方向進行調整可以產生兩種辦法:(visual studio 2008)(下面方法都是對於同一平臺下,即C#跟VC++都是在windows平臺下)

一、對VC++的環境中進行修改使其支援託管程式碼:

      vc++呼叫端增加公共語言執行時【clr】的支援以執行C#的程式:解決方案-》Properties(右鍵)-》Configuration Properties(展開左樹)->General(開啟子節點)->Common Language Runtime support(選中選項)->【Common Language Runtime support(/clr)】(選中)

 OK,現在就可以引入托管的動態連線庫來用了,不過在呼叫時還是得注意語法(new->gcnew,....),例如下: 
  1. #include "stdafx.h"
  2.  #using "SmartDeviceDLL.dll"
  3.  usingnamespace SmartDeviceDLL;  
  4.  int _tmain(int argc, _TCHAR* argv[])  
  5.  {  
  6.   printf("1111111111111\n");  
  7.   SmartDeviceDLL::ICalculator ^pICalc=gcnew SmartDeviceDLL::Class1();  
  8.   long
     lResult =0;  
  9.   lResult=pICalc->Add(5,10);  
  10.   wprintf(L"the result is %d\n",lResult);  
  11.   printf("222222222222222222\n");  
  12.   char c;  
  13.   scanf("%c",&c);  
  14.   return 0;  
  15.  }  

二、C#生成DLL端編譯成COM介面,供VC++以託管格式呼叫(命令的執行都是在visual studio command prompt (命令視窗)中)

1.新建一個C#的動態連線庫(在 模板 ,下單擊 類庫): 
  1. using System;  
  2.  using System.Linq;  
  3.  using System.Collections.Generic;  
  4.  using System.Text;  
  5.  namespace SmartDeviceDLL  
  6.  {  
  7.      public interface ICalculator  
  8.      {  
  9.          int Add(int Number1, int Number2);  
  10.      }  
  11.      publicclass Class1: ICalculator  
  12.      {  
  13.          publicint Add(int Number1, int Number2)  
  14.          {  
  15.              return Number1 * Number2;  
  16.          }  
  17.          publicstaticint TestMethod(String s)  
  18.          {  
  19.              Console.WriteLine("Managed assembly: [0]", s);  
  20.              return s.Length;  
  21.          }  
  22.      }  
  23.  }<span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span>  

2.為程式集建立一個強命名的類庫,並在AssemblyInfo.cs檔案中用AssemblyKeyFile屬性指向它:
    1)、使用強命名工具(Strong Name Utility)產生金鑰對:
       sn -k MyKeyFile.snk
    2)、在AssemblyInfo.cs檔案中用AssemblyKeyFile屬性指向它:
      即在專案的檔案AssemblyInfo.cs中將[assembly: ComVisible(false)]用以下內容替換:

  1. [assembly: ComVisible(true)]   
  2. [assembly: AssemblyDelaySign(false)]   
  3. [assembly: AssemblyKeyFile("MyKeyFile.snk")] //指向剛生成的檔案(可用無漢字的絕對路徑)
   3)、重新編譯,產生的程式集就是經過簽名後的程式集了 

3.把生成的庫檔案加入全域性程式集快取(Global Assembly Cache, .NET [Compact]Framework支援一個工具,通常位於:C\Wndows\Assembly下)以便可以從任何 COM 客戶端啟用它,可以使用工具GACUtil.exe,指定/i命令開關將一個程式集安裝到GAC中,同樣可以使用/u命令開關將一個程式集從GAC中解除安裝。注意:安裝的程式集必須是強命名程式集:         

  1. GACUTIL /i SmartDeviceDLL.dll   //可用SmartDeviceDLL.dll的絕對路徑,Wince平臺直接在命令視窗中用CGACUTIL(Compact)

4.用下面的命令為COM註冊剛才的程式集,生成COM庫檔案(程式集註冊工具讀取程式集中的元資料,並將所需的項新增到登錄檔中,登錄檔允許 COM 客戶程式以透明方式建立 .NET Framework 類。類一經註冊,任何 COM 客戶程式都可以使用它,就好像該類是一個 COM 類。類僅在安裝程式集時註冊一次。程式集中的類例項直到被實際註冊時,才能從 COM 中建立)

  1. //下面命令註冊 SmartDeviceDLL.dll 中包含的所有公共類,並生成和註冊型別庫 SmartDeviceDLL.tlb,該型別庫包含 SmartDeviceDLL.dll 中定義的所有公共型別的定義 
  2.   REGASM SmartDeviceDLL.dll /tlb:SmartDeviceDLL.tlb  
  3. //或者可以選中:解決方案->Properties(右鍵)->Build->【Register for COM interop】(Wince平臺的DLL此選項不可選)

5.建立非託管的VC++呼叫程式(此處用Win32 Console Project為例): 

  1. #include "stdafx.h"
  2.  #import "SmartDeviceDLL.tlb" named_guids  raw_interfaces_only
  3.  usingnamespace SmartDeviceDLL;  
  4.  int _tmain(int argc, _TCHAR* argv[])  
  5.  {  
  6.   printf("1111111111111\n");   
  7.   //初始化COM以及產生智慧指標
  8.   HRESULT hr=CoInitializeEx(NULL,COINIT_MULTITHREADED);  
  9.   if(hr!=S_OK)  
  10.    printf("hr failed\n");  
  11.   else
  12.    printf("hr ok\n");  
  13.   printf("222222222222222222\n");  
  14.   SmartDeviceDLL::ICalculatorPtr pICalc;  
  15.   printf("2.1\n");  
  16.   HRESULT hRes=pICalc.CreateInstance(__uuidof(Class1),NULL,CLSCTX_ALL);  
  17.   //HRESULT hRes=pICalc.CreateInstance(SmartDeviceDLL::CLSID_Class1);
  18.   printf("2.2\n");  
  19.   if(hRes==S_OK)  
  20.   {  
  21.        printf("hRes ok\n");  
  22.        long lResult =0;  
  23.        pICalc->Add(5,10, &lResult);  
  24.        wprintf(L"the result is %d\n",lResult);  
  25.   }  
  26.   else
  27.        printf("hRes failure\n");  
  28.   printf("333333333333\n");  
  29.   CoUninitialize();  
  30.   printf("4444444444444444444\n");  
  31.   char c;  
  32.   scanf("%c",&c);  
  33.   return 0;  
  34.  }