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

十日十乞001發表於2015-12-09

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

     原帖:

     http://topic.csdn.net/u/20110916/12/fcf63501-45ef-46d9-92d1-6ffa91086a80.html

     以及參考文章:

      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.  using namespace SmartDeviceDLL;  
  4.  int _tmain(int argc, _TCHAR* argv[])  
  5.  {  
  6.   printf("1111111111111\n");  
  7.    
  8.   SmartDeviceDLL::ICalculator ^pICalc=gcnew SmartDeviceDLL::Class1();  
  9.     
  10.   long lResult =0;  
  11.   lResult=pICalc->Add(5,10);  
  12.   wprintf(L"the result is %d\n",lResult);  
  13.     
  14.   printf("222222222222222222\n");  
  15.    
  16.   char c;  
  17.   scanf("%c",&c);  
  18.   return 0;  
  19.  }  

二、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.    
  12.      public class Class1: ICalculator  
  13.      {  
  14.          public int Add(int Number1, int Number2)  
  15.          {  
  16.              return Number1 * Number2;  
  17.          }  
  18.          public static int TestMethod(String s)  
  19.          {  
  20.              Console.WriteLine("Managed assembly: [0]", s);  
  21.              return s.Length;  
  22.          }  
  23.      }  
  24.  }<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.  using namespace 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.    
  14.   printf("222222222222222222\n");  
  15.   SmartDeviceDLL::ICalculatorPtr pICalc;  
  16.   printf("2.1\n");  
  17.   HRESULT hRes=pICalc.CreateInstance(__uuidof(Class1),NULL,CLSCTX_ALL);  
  18.   //HRESULT hRes=pICalc.CreateInstance(SmartDeviceDLL::CLSID_Class1);  
  19.   printf("2.2\n");  
  20.   if(hRes==S_OK)  
  21.   {  
  22.        printf("hRes ok\n");  
  23.        long lResult =0;  
  24.        pICalc->Add(5,10, &lResult);  
  25.        wprintf(L"the result is %d\n",lResult);  
  26.   }  
  27.   else  
  28.        printf("hRes failure\n");  
  29.    
  30.   printf("333333333333\n");  
  31.   CoUninitialize();  
  32.   printf("4444444444444444444\n");  
  33.   char c;  
  34.   scanf("%c",&c);  
  35.   return 0;  
  36.  }  

相關文章