C#程序如何調用C++?dll詳細教程
在使用C#開發(fā)客戶端時,有時需要調用C++ dll,本篇博客來介紹C#程序如何調用C++ dll。
一、創(chuàng)建C++ dll項目
首先使用VS2022創(chuàng)建C++ dll項目,具體步驟如下:
(1)選擇Windows桌面向導,點擊下一步, 取項目名,例如我的dll項目名是libMath

(2)選擇動態(tài)項目,勾選導出符號

(3)編寫動態(tài)代碼,代碼如下:
libMath.h
// 下列 ifdef 塊是創(chuàng)建使從 DLL 導出更簡單的
// 宏的標準方法。此 DLL 中的所有文件都是用命令行上定義的 LIBMATH_EXPORTS
// 符號編譯的。在使用此 DLL 的
// 任何項目上不應定義此符號。這樣,源文件中包含此文件的任何其他項目都會將
// LIBMATH_API 函數(shù)視為是從 DLL 導入的,而此 DLL 則將用此宏定義的
// 符號視為是被導出的。
#ifdef LIBMATH_EXPORTS
#define LIBMATH_API __declspec(dllexport)
#else
#define LIBMATH_API __declspec(dllimport)
#endif
// 此類是從 dll 導出的
class LIBMATH_API ClibMath {
public:
ClibMath();
int Add(int a, int b);
int Sub(int a, int b);
};
// 由于需要給C#調用,為了方便導出類,添加了函數(shù)進行導出,并且需要加extern "C"
extern "C" {
LIBMATH_API ClibMath* CreateMyClass();
LIBMATH_API void DeleteMyClass(ClibMath* obj);
LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2);
LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2);
}
注意: 如果想導出C++類在C#中使用,由于語言語法差異,C++類在C#中無法使用,因為C++類通常包含成員函數(shù)、構造函數(shù)、析構函數(shù)等,而C#與C++在處理這些方面存在差異。一種可行的方法是在C++類中添加一些導出函數(shù),這樣它們可以通過C#調用。這些函數(shù)可以執(zhí)行類的實例化、調用成員函數(shù)等操作。確保使用 extern “C” 以避免名稱修飾, 因為C++函數(shù)在編譯時,會在原有的函數(shù)名前后添加一些符號,例如add函數(shù)在編譯后可能變成了@xxasd_sfdf_add_xxx之類的,但是使用extern "C" 后就是按照C語言的方式導出,函數(shù)名不變,例如我添加的一些類導出方法:
// 由于需要給C#調用,為了方便導出類,添加了函數(shù)進行導出,并且需要加extern "C"
extern "C" {
LIBMATH_API ClibMath* CreateMyClass();
LIBMATH_API void DeleteMyClass(ClibMath* obj);
LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2);
LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2);
}
libMath.cpp
// libMath.cpp : 定義 DLL 的導出函數(shù)。
//
#include "framework.h"
#include "libMath.h"
// 這是已導出類的構造函數(shù)。
ClibMath::ClibMath()
{
return;
}
int ClibMath::Add(int a, int b)
{
return a + b;
}
int ClibMath::Sub(int a, int b)
{
return a - b;
}
LIBMATH_API ClibMath* CreateMyClass() {
return new ClibMath();
}
LIBMATH_API void DeleteMyClass(ClibMath* obj) {
delete obj;
}
LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2) {
return obj->Add(num1, num2);
}
LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2) {
return obj->Sub(num1, num2);
}
二、C#程序員調用C++ dll
生成dll后可以用命令拷貝到C#項目的exe目錄,或者手動拷貝,然后在C#代碼中使用import導入即可,如下圖:

代碼如下:
/*
C# 調用 C++ dll
將libMath.dll放到CSharpCallCppDLL/bin/Debug目錄下
*/
using System.Runtime.InteropServices;
namespace CSharpCallCppDLL
{
internal class Program
{
private static IntPtr myClassInstance; // 定義C++類的實例,用于后面的調用
[DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateMyClass();
[DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void DeleteMyClass(IntPtr obj);
[DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int CallAdd(IntPtr obj, int num1, int num2);
[DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int CallSub(IntPtr obj, int num1, int num2);
static void Main(string[] args)
{
Console.WriteLine("測試C#調用C++");
myClassInstance = CreateMyClass();
int nRet = CallAdd(myClassInstance, 1, 2);
Console.WriteLine($"1 + 2 = {nRet}");
// 清理C++內存
DeleteMyClass(myClassInstance);
}
}
}
注意:由于C++的編譯特點,C++項目有Debug、Release、x86、x64之分,特別需要注意dll的放置位置,放錯了可能就鏈接失敗了,以及C++在x64于x86下的指針4字節(jié)與8字節(jié)的區(qū)分,這些都會導致在C#調用C++ dll代碼失敗或者crash。此外,確保你的應用程序具有訪問和加載DLL所需的適當權限。
運行結果如下:

在C#中可以創(chuàng)建一個對應C++類的C#包裝類,使用 DllImport 屬性聲明導出函數(shù),例如下面的代碼:
public class MyClassWrapper {
private IntPtr myClassInstance;
[DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateMyClass();
[DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void DeleteMyClass(IntPtr obj);
[DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void CallMyMethod(IntPtr obj);
public MyClassWrapper() {
myClassInstance = CreateMyClass();
}
~MyClassWrapper() {
DeleteMyClass(myClassInstance);
}
public void MyMethod() {
CallMyMethod(myClassInstance);
}
}
使用C#包裝類:
在C#中,可以實例化MyClassWrapper類,并調用其中的方法,這將轉發(fā)調用到C++類的實例。
MyClassWrapper myInstance = new MyClassWrapper(); myInstance.MyMethod();
這是一個簡單的例子,實際情況可能更為復雜,特別是在處理類的繼承、虛函數(shù)等方面。確保在進行實際應用時進行充分的測試,以確保互操作性正常運作。
三、C++與C#數(shù)據(jù)類型對應
C#在調用C++ DLL時,需要通過P/Invoke技術來完成。P/Invoke是.NET Framework用于調用非托管代碼庫的一種方式。在這個過程中,我們需要處理兩種語言之間的數(shù)據(jù)類型轉換,因為它們的數(shù)據(jù)類型不完全一致。
基本數(shù)據(jù)類型對應表
以下是C++和C#之間的一些常見數(shù)據(jù)類型的對應表(請注意,這并不是一個完全的列表,只是一些常見類型的示例):
| C++ | C# |
|---|---|
bool | bool |
char / BYTE | byte |
short | short |
int | int |
long | int |
float | float |
double | double |
char* (C-style string) | string |
wchar_t* (Unicode string) | string |
在C#中,我們使用DllImport特性來聲明對C++ DLL的函數(shù)調用。例如,如果我們有一個C++函數(shù)如下:
extern "C" __declspec(dllexport) int Add(int a, int b);
在C#中,我們可以這樣聲明和使用它:
[DllImport("MyLibrary.dll")]
public static extern int Add(int a, int b);
public void Main()
{
int result = Add(2, 3);
}
對于更復雜的數(shù)據(jù)類型,如結構體和類,我們需要在C#中創(chuàng)建對應的類或結構體來匹配。例如,如果我們有一個C++結構體:
struct Person
{
char* name;
int age;
};
我們可以在C#中創(chuàng)建一個類來匹配它:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Person
{
public string name;
public int age;
}
注意我們使用了StructLayout特性并設置CharSet為CharSet.Ansi,因為C++中的char*類型對應于ANSI字符串。
在處理C++ DLL中的函數(shù)時,也需要注意函數(shù)調用的約定(cdecl,stdcall等)。默認情況下,C#假定被調用的函數(shù)使用stdcall調用約定。如果函數(shù)使用不同的調用約定,你需要在DllImport特性中指定它,例如:
[DllImport("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
在處理更復雜的情況,如回調函數(shù),復雜的數(shù)據(jù)結構,和C++類時,可能需要更多的處理。處理這些情況通常需要對兩種語言都有深入的理解,并且可能需要手動管理內存。
C++指針類型與C#類型
C++ 指針在C#中的相應類型取決于指針指向的數(shù)據(jù)類型和你如何打算使用它。這里有幾種常見的情況:
指向簡單類型的指針:如果指針指向一個簡單的數(shù)值類型(如
int*、float*等),你可以在C#中使用IntPtr類型來表示它。你可以使用Marshal類中的方法(如Marshal.ReadInt32、Marshal.WriteInt32等)來讀取或寫入指針指向的數(shù)據(jù)。指向字符串的指針:如果指針指向一個字符串(如
char*或wchar_t*),你可以在C#中使用string類型來表示它。在DllImport特性中,你可以使用MarshalAs特性來指定字符串的編碼方式。指向結構體的指針:如果指針指向一個結構體,你可以在C#中創(chuàng)建一個對應的類或結構體,并使用
ref關鍵字或者IntPtr類型來表示指針。使用ref關鍵字時,.NET運行時會自動處理內存管理。使用IntPtr時,你需要手動管理內存。
例如,假設你有一個C++函數(shù)如下:
extern "C" __declspec(dllexport) void ModifyPerson(Person* person);
在C#中,你可以這樣使用它:
[DllImport("MyLibrary.dll")]
public static extern void ModifyPerson(ref Person person);
// 或者
[DllImport("MyLibrary.dll")]
public static extern void ModifyPerson(IntPtr personPtr);
- 指向數(shù)組的指針:如果指針指向一個數(shù)組,你可以在C#中使用數(shù)組類型來表示它。你也可以使用
MarshalAs特性來指定數(shù)組的大小。
對于更復雜的情況,例如指向函數(shù)的指針(函數(shù)指針),你可能需要使用Marshal.GetDelegateForFunctionPointer方法將其轉換為C#委托。
需要注意的是,當你使用IntPtr來管理指針時,你需要自己負責內存的分配和釋放。你可以使用Marshal.AllocHGlobal和Marshal.FreeHGlobal方法來分配和釋放非托管內存。
總結
到此這篇關于C#程序如何調用C++ dll的文章就介紹到這了,更多相關C#調用C++ dll內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
淺談c#.net中巧用ToString()將日期轉成想要的格式
有時候我們要對時間進行轉換,達到不同的顯示效果,更多的該怎么辦呢?2013-03-03

