1. 程式人生 > >C#呼叫C++dll方法和注意事項

C#呼叫C++dll方法和注意事項

在實際C#開發專案中,存在如下兩種情況

  1. C#呼叫第三方庫,而第三方庫是使用C++編寫的;
  2. 牽涉到專案原始碼保密,C#程式碼容易被反編譯,因此抽取核心演算法部分使用C++編寫

這時候就涉及C#託管程式碼與C++非託管程式碼互相呼叫。

本文介紹C#呼叫C++的方法以及在C#呼叫C++產生問題的排查過程和經驗總結。

下面介紹C#如何靜態和動態呼叫C++庫;

一、C#中靜態呼叫C++動態連結

1. 建立 CppDemo,建立的時候選擇DLL動態庫。

2. 在DllDemo.cpp檔案中新增一個Add函式

extern "C" __declspec(dllexport) int Add(

int a,int b)

{

    return a+b;

}

3. 編譯生成DllDem.dll提供C#使用.

4. 新建C#工程,選擇控制檯測試程式InteropDemo

5. 在Program.cs中新增引用:using System.Runtime.InteropServices;

6. 在pulic class Program新增如下程式碼:

using System;

using System.Collections.Generic;

using System.Text;

using System.Runtime.InteropServices;

namespace

InteropDemo

{

    class Program

    {

        [DllImport("CppDemo.dll", EntryPoint = "Add", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]

        public static extern int Add(int a, int b); //

        static void Main(string[] args)

        {

            Console.WriteLine(

Add(1, 2));

            Console.Read();

        }

    }

}

C#中匯入C++庫並匯出需要使用的函式,這樣就可以實現C#靜態呼叫C++動態連結庫了。。

二、C# 中動態呼叫C++動態連結

C#除了可以靜態呼叫C++dll以外,通過簡單的封裝也可以實現C++一樣動態載入呼叫dll的功能,只要封裝C++中LoadLibrary, GetProcess, FreeLibrary這幾個函式就可以實現動態呼叫動態連結庫。實現過程如下:

1. 將kernel32中的幾個方法封裝成本地呼叫類LoadLibraryMethod Code
using System;

using System.Collections.Generic;

using System.Text;

using System.Runtime.InteropServices;

namespace InteropDemo

{

    public static class LoadLibraryMethod

    {

        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]

        public static extern int LoadLibrary(

            [MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]

        public static extern IntPtr GetProcAddress(int hModule,

            [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]

        public static extern bool FreeLibrary(int hModule);

    }

}

2. 使用LoadLibraryMethod類動態載入C++動態連結庫,獲得函式指標,並且將指標封裝成C#中的委託。原因很簡單,C#中已經不能使用指標了,如下:

using System;

using System.Collections.Generic;

using System.Text;

using System.Runtime.InteropServices;

namespace InteropDemo

{

class Program

{

static void Main(string[] args)

{

//1. 動態載入C++ Dll

int hModule = LoadLibraryMethod.LoadLibrary(@"c:\CppDemo.dll");

if (hModule == 0) return;

//2. 讀取函式指標

IntPtr intPtr = LoadLibraryMethod.GetProcAddress(hModule, "Add");

//3. 將函式指標封裝成委託

Add addFunction = (Add)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(Add));

//4. 測試

Console.WriteLine(addFunction(1, 2));

Console.Read();

}

/// <summary>

/// 函式指標

/// </summary>

/// <param name="a"></param>

/// <param name="b"></param>

/// <returns></returns>

delegate int Add(int a, int b);

}

}

三、 C#呼叫C++問題排查案例

在實際應用出現一個問題,我們提供一個C++DLL給第三方使用,第三方使用C#呼叫,C#在呼叫C++的時候總是崩潰,解決該問題經歷瞭如下排查過程:

  1. 使用Windbug進行除錯,由於C#託管呼叫C++導致C++dll庫崩潰堆疊找不到,無法確認崩潰位置。
  2. 在提供的DLL中的各個匯出函式入口和出口增加日誌,該方案可行,最終執行發現在DLL中回撥函式ResultCallback4C 呼叫前後的日誌有開始沒結束,確定是回撥函式返回時崩潰,由於自己demo呼叫沒有問題,懷疑ResultCallback4C回撥函式內部處理有問題。
  3. 確定可能是回撥函式有問題後,將回調註釋掉,發現正常,初步斷定是呼叫方回撥內部處理有問題。
  4. 第三方呼叫為確認呼叫沒問題,將C#中回撥函式處理邏輯全部註釋,發現仍然有問題,排除了第三方回撥內部問題。
  5. 經過上述排查後仍然未能解決問題,之後整理了下思路和思考了C++ C#的一些性質,懷疑到可能是函式呼叫約定的問題,果斷將呼叫約定由cdeclcall改為stdcall,編譯重新提供給第三方,更新之後發現沒有問題,最終確定第三方在定義回撥函式時候CallingConvention = CallingConvention.stdcall)使用的stdcall,而和我們我們定義的不一致,導致崩潰。

四、 注意事項

1. C#呼叫C++呼叫約定必須一致,當然C++本身也是一樣。

2. C++和c#中對應的資料結構大小一致.

3. c#引用函式的引數型別和c++函式中的引數型別一致。

相關推薦

C#呼叫C++dll方法注意事項

在實際C#開發專案中,存在如下兩種情況 C#呼叫第三方庫,而第三方庫是使用C++編寫的; 牽涉到專案原始碼保密,C#程式碼容易被反編譯,因此抽取核心演算法部分使用C++編寫 這時候就涉及C#託管程式碼與C++非託管程式碼互相呼叫。 本文介紹C#呼叫C++的方法以及在C#

C++中typename關鍵字的使用方法注意事項

目錄起因近日,看到這樣一行程式碼:typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor; 雖說已經有多年C++經驗,但上面這短短一行程式碼卻看得我頭皮發麻。看起來它

python2,python3子類呼叫父類初始化函式的方法注意事項

python2、python3: python子類呼叫父類初始化函式有兩種方式,以下程式碼在python2和python3都能執行: class A(object): def __init__(self, x): self.x = x # 方法

androidStudio JNI開發之c調java的流程注意事項

接著上一次的java調c,這次我們來看看c調java的具體步驟 接著上篇文章一直到在java檔案中新增native程式碼步驟 加入如下native方法 public native void call

java中介面(interface)及使用方法注意事項

1、介面:一種把類抽象的更徹底,接口裡只能包含抽象方法的“特殊類”。介面不關心類的內部狀態資料,定義的是一批類所遵守的規範。(它只規定這批類裡必須提供某些方法,提供這些方法就可以滿足實際要求)。 在JAVA程式語言中是一個抽象型別,是抽象方法的集合,介面通常以interface來宣告。一個類通過

Extjs整合struts2的jsonplugin的方法注意事項

      最近在做一個第三方表報監控的系統,要用的很多資料展示的應用,發現用extjs和struts2的jsonplugin的結合解決問題很棒,專案已經上線,現在寫下步驟以便查閱。 步驟1、在專案中新增struts2的庫。如下 步驟2、新增Google的jsonplug

NSBundle(獲取資源路徑方法)的相關使用方法注意事項

1、[NSBundle mainBundle],資料夾其實是Group,如左側的樹形檔案管理器 Build之後,檔案直接就複製到了根目錄下,於是讀取的方法,應該是這樣: NSString *earth = [[NSBundle mainBundle] pat

金錢草銅錢草怎麼養殖 金錢草的養殖方法注意事項

金錢草是一種常見的綠色觀葉植物,它也是一種中藥材,而且金錢草還能開出美麗的黃色小花,它既能美化環境,也能止血消腫功能解毒止痛,平時很多人都有養殖金錢草的打算,只是不知道它怎樣才能養好,今天小編就把它養殖方法寫出來告訴大家,並讓大家瞭解養殖錢草時要注意什麼。 金錢草怎麼養殖 金錢草的養殖

PDM匯出sql的方法注意事項(本人…

PDM生成sql的方法(應用oracle): 工具欄裡的Database--》Database Generation(Ctrl + G) Directory:匯出路徑 File name:匯出名(我寫的是myself.sql) 點選“確定”。 如果報錯:Generation aborted due to

python字串替換方法注意事項

方法有兩種: last_date = “1/2/3”   目標為"123" 之一:repalce date =last_date.replace('/','') 之二:re p = re.compile("/") date = p.sub('', last_date)

使用neo4j圖資料庫的import工具匯入資料 -方法注意事項

背景 最近我在嘗試儲存知識圖譜的過程中,接觸到了Neo4j圖資料庫,這裡我摘取了一段Neo4j的簡介: Neo4j是一個高效能的,NOSQL圖形資料庫,它將結構化資料儲存在網路上而不是表中。它是一個嵌入式的、基於磁碟的、具備完全的事務特性的Java持

使用 MPMoviePlayerController 出現的問題、解決方法注意事項

 在SDK3.2及SDK4.x中MPMoviePlayerController有下面這些改動,像實現豎屏播放不再需要使用私有API了。 - In 3.1 and earlier versions, MPMoviePlayerController was full-scre

mysql資料庫從window遷移的linux的方法注意事項

一般情況下Mysql從window遷移到linux的時候,網上都會有標準的教程如下: 1) 在windows平臺上進入/mysql/bin目錄(假設你的資料庫名字是mydata)       執行mysqldump 命令將你的資料庫匯出,具體命令如下:         

elasticsearch中client.transport.sniff的使用方法注意事項

(1)通過TransportClient這個介面,我們可以不啟動節點就可以和es叢集進行通訊,它需要指定es叢集中其中一臺或多臺機的ip地址和埠,例子如下:Client client = new TransportClient() .addTr

關於C#與Delphi DLL呼叫及回撥問題注意事項

1.Delphi封裝的函式API及定義的函式型別(用於回撥)中引數的修飾關鍵詞盡不使用const 2.Delphi封裝的函式API及定義的函式型別(用於回撥)中引數的修飾關鍵詞使用const,對應C#中申明需帶ref 3.Delphi封裝的函式API及定義的函式型別(用於回

C++】向量(vector) 基本使用方法注意事項

介紹: 向量(Vector)是一個封裝了動態大小陣列的順序容器(Sequence Container)。跟任意其它型別容器一樣,它能夠存放各種型別的物件。可以簡單的認為,向量是一個能夠存放任意型別的動態陣列。 特點: 1.順序序列 順序容器中的元素按照嚴格的線性順序排序。可以通過元素

關於 C#呼叫c庫,將C#的byte[]傳入C庫的方法C庫的char*向上傳入C#的回撥函式byte[] 的方法

需求         使用C#開發,因某種原因,寫了C庫Dll,使用C#抓圖資料傳入C,將C庫收到的圖片資料向上傳入C#回撥函式。   當前現狀         C#中將byte[]轉換成I

關於 C#呼叫CDll,有回撥函式時,只執行一次回撥函式就直接掛掉 的解決方法

錯誤         直接當機,如下圖:           錯誤原因        回撥函式宣告原因,跟堆疊有關係  

C#呼叫C++DLL方法

最近使用海康的某平臺SDK,但是提供的demo沒有C#版本,只有C++的,在轉換過程中遇到很多問題,簡單記錄一下. 目錄 1.引數為基本型別,例如 int,float,char等。 [C++] void fun(int value); v

C#呼叫MySQL資料庫方法1(使用MySql.Data.dll連線)

方法1、 1、檢查nuget包管理器是否為最新程式 vs2015使用nuget包管理器安裝失敗,在使用vs2015安裝一些nuget包的時候,出現了an error occurred while retrieving package for "Newtonsoft.Jso