非托管调用
生成非托管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:尝试读取或写入受保护的内存等