C#调用C++的DLL

非托管调用

生成非托管DLL

1.使用VS2019创建C++新项目=》选择【空项目】
2.更改项目属性=》常规=》配置类型=》选择【动态库(.dll)】
3.添加源文件=》TestDLL.cpp

1
2
3
4
5
extern "C" __declspec(dllexport) void testCharAdd(char* buff, int buffSize) {
for (int i = 0; i < buffSize; ++i) {
++buff[i];
}
}

4.生成dll

DllImport调用DLL

1.使用VS创建C#新项目选择=》控制台应用程序

2.添加一个类 Usedll.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Runtime.InteropServices;
namespace test_csharp
{
class UseDLL
{
[DllImport("TestDLL.dll")]
public extern static void testCharAdd(ref byte buff, UInt32 buffSize);
}
class Program
{
static void Main(string[] args)
{
UInt32 buffsize = 5;
var buff = new byte[buffsize];
UseDLL.testCharAdd(ref buff[0], buffsize);
Console.WriteLine(BitConverter.ToString(buff));
}
}
}

CLR托管调用

(可以使用委托类封装或者直接改成委托类都可)

生成托管DLL

1.使用VS2019创建C++新项目=》选择【空项目】
2.更改项目属性=》高级=》公共语言运行时支持=》选择【公共语言运行时支持(/clr)】
3.更改项目属性=》C/C++=》语言=》符合模式=》选择【否(/permissive)】

4.添加头文件=》TestDLL.h

1
2
3
4
5
6
7
#pragma once
public ref class DllClass {
public:
DllClass() {};
~DllClass() {};
void testCharAdd(char* buff, int buffLen);
};

5.添加源文件=》TestDLL.cpp

1
2
3
4
5
6
#include "TestDLL.h"
void DllClass::testCharAdd(char* buff, int buffLen) {
for (int i = 0; i < buffLen; ++i) {
++buff[i];
}
}

6.生成dll

引用调用dll

1.使用VS创建C#新项目选择=》控制台应用程序

2.添加dll的引用=》依赖项=》添加项目引用=》浏览=》选择DLL=》确定

3.更改项目属性=》生成=》勾选【允许不安全代码(F)】

4.然后直接通过对象调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
namespace test_csharp
{
class Program
{
//如果用到指针,需要使用unsafe来修饰类或者函数
unsafe static void Main(string[] args)
{
var sbuff = new sbyte[5];
DllClass dc = new DllClass();
//使用fixed来防止CLR修改地址,使指针所指向的地址不能被改变
fixed (sbyte* pbuff = &sbuff[0])
{
dc.testCharAdd(pbuff, sbuff.Length);
}
byte[] buff = new byte[sbuff.Length];
Buffer.BlockCopy(sbuff, 0, buff, 0, sbuff.Length);
Console.WriteLine(BitConverter.ToString(buff));
}
}
}

动态加载

无需引用dll,利用反射进行动态加载调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace test_csharp
{
class Program
{
static void Main(string[] args)
{
var sbuff = new byte[5];
// 按照路径获取dll
Assembly assembly = Assembly.LoadFrom("TestDLL.dll");
// 获取dll中的类
Type dc = assembly.GetType("DllClass");
// 实例化该类
object obj = Activator.CreateInstance(dc);
// 获取该类中的方法,testCharAdd(sbyte*,int)
MethodInfo testCharAdd = dc.GetMethod("testCharAdd");
// 指针需要通过intPtr转换成object,无法通过sbyte*转换成object
IntPtr intPtr = Marshal.AllocHGlobal(sbuff.Length);
Marshal.Copy(sbuff, 0, intPtr, sbuff.Length);
// 反射调用,返回值就是原方法的返回值
testCharAdd.Invoke(obj, new object[] { intPtr, sbuff.Length });
Marshal.Copy(intPtr, sbuff, 0, sbuff.Length);
Marshal.FreeHGlobal(intPtr);
Console.WriteLine(BitConverter.ToString(sbuff));
}
}
}

C#和C++的数据类型对应表

C++ Type C# Type Size
BOOL bool 1 byte
BYTE byte 1 byte
CHAR byte 1 byte
DECIMAL Decimal 16 bytes
DOUBLE double 8 bytes
DWORD uint, UInt32 4 bytes
FLOAT float, single 4 bytes
INT, signed int int, Int32 4 bytes
INT16, signed short int short, Int16 2 bytes
INT32, signed int int, Int32 4 bytes
INT64 long, Int64 8 bytes
LONG int, Int32 4 bytes
LONG32, signed int int, Int32 4 bytes
LONG64 long, Int64 8 bytes
LONGLONG long, Int64 8 bytes
SHORT, signed short int short, Int16 2 bytes
UCHAR, unsigned char byte 1 byte
UINT, unsigned int uint, UInt32 4 bytes
UINT16, WORD ushort, UInt16 2 bytes
UINT32, unsigned int uint, UInt32 4 bytes
UINT64 ulong, UInt64 8 bytes
ULONG, unsigned long uint, UInt32 4 bytes
ULONG32 uint, UInt32 4 bytes
ULONG64 ulong, UInt64 8 bytes
ULONGLONG ulong, UInt64 8 bytes
WORD ushort 2 bytes
void*, pointers IntPtr x86=4 bytes, x64=8 bytes

注意:

C#与C++参数或者返回值数据类型不匹配,会报error:尝试读取或写入受保护的内存等

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!