目录
一、需求
二、C++项目
1.创建项目
2.C++代码
3.生成DLL
三、C#项目
1.新建项目
2.调用方式一
3.调用方式二
结束
平时我们写的 C# 类库,在 Visual Studio 中添加引用,然后调用 DLL 中的方法就好了,但是用C++生成的DLL并不行,一般的项目中,很少用到 C# 调用 C++ 代码的情况,但在上位机,工控行业很常见,视觉,人工智能行业中,算法很多都是 C++ 写的,而上位机大部分都是 C# 开发的,那么这篇文章就从头开始,教你如何从创建一个简单的C++项目,到C#的调用。
新建一个C++项目,选择动态链接库
项目的名字就默认的吧
创建成功后,会自动生成一些默认代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
这些代码可以直接删除,但是,#include "pch.h" 这个头文件不能删
讲代码替换如下:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
//BOOL APIENTRY DllMain( HMODULE hModule,
// DWORD ul_reason_for_call,
// LPVOID lpReserved
// )
//{
// switch (ul_reason_for_call)
// {
// case DLL_PROCESS_ATTACH:
// case DLL_THREAD_ATTACH:
// case DLL_THREAD_DETACH:
// case DLL_PROCESS_DETACH:
// break;
// }
// return TRUE;
//}
#include
#include "stdlib.h"
#include
extern "C" __declspec(dllexport) int Add(int x, int y)
{
return x + y;
}
extern "C" __declspec(dllexport) int Sub(int x, int y)
{
return x - y;
}
extern "C" __declspec(dllexport) int Multiply(int x, int y)
{
return x * y;
}
extern "C" __declspec(dllexport) int Divide(int x, int y)
{
return x / y;
}
在别的一些帖子,会提示你修改配置类型,那是因为他们创建的项目模板不是动态链接库,我们这里可以忽略这一步
点击项目生成
打开项目的Debug目录,你会发现,根本没有DLL文件
那是因为生成的DLL不在这里,我们找到项目的根目录,就会发现一个Debug目录
接下来,我们就在C#项目中进行调用,我们先创建一个C# 控制台项目,打开项目的Debug文件夹,将刚才生成的C++ DLL 放进去。
接下来写 C# 代码。
创建一个 DemoDelegate 类,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace 调用C加加DLL
{
public class DemoDelegate
{
[DllImport("DLL1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int x, int y);
[DllImport("DLL1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int Sub(int x, int y);
[DllImport("DLL1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int Multiply(int x, int y);
[DllImport("DLL1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int Divide(int x, int y);
}
}
程序入口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 调用C加加DLL
{
class Program
{
static void Main(string[] args)
{
int ret1 = DemoDelegate.Add(6, 6);
Console.WriteLine(ret1);
Console.ReadKey();
}
}
}
运行后,效果:
可以看到,计算的结果是对的
新建一个类 DllInvoke
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace 调用C加加DLL
{
public class DllInvoke
{
[DllImport("kernel32.dll", SetLastError = true)]
private extern static IntPtr LoadLibrary(String dllPath);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
private extern static IntPtr GetProcAddress(IntPtr lib, string funcName);
[DllImport("kernel32.dll", SetLastError = true)]
private extern static bool FreeLibrary(IntPtr lib);
private IntPtr hLib;
public DllInvoke(string dllPath)
{
hLib = LoadLibrary(dllPath);
if (hLib == IntPtr.Zero)
{
hLib = LoadLibraryEx(dllPath, IntPtr.Zero, 8);
}
if (hLib == IntPtr.Zero)
{
throw new Exception(string.Format("LoadLibrary {0} Error {1}!", dllPath, Marshal.GetLastWin32Error()));
}
}
///
/// 获取C++方法对应的C#委托实例
///
/// C++方法名
/// C#委托
///
public Delegate GetDelegate(string funcName, Type type)
{
IntPtr api = GetProcAddress(hLib, funcName);
Delegate del = (Delegate)Marshal.GetDelegateForFunctionPointer(api, type);
return del;
}
///
/// 获取C++方法对应的C#委托实例
///
/// C++函数地址
/// C#委托
///
public Delegate GetDelegateFromIntPtr(IntPtr address, Type t)
{
if (address == IntPtr.Zero)
return null;
else
return Marshal.GetDelegateForFunctionPointer(address, t);
}
///
/// 获取C++方法对应的C#委托实例
///
/// C++函数地址
/// C#委托
///
public Delegate GetDelegateFromInt(Int32 address, Type t)
{
if (address == 0)
return null;
else
return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace 调用C加加DLL
{
class Program
{
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
delegate int Add(int mid, int errid);
static void Main(string[] args)
{
//int ret1 = DemoDelegate.Add(6, 6);
//Console.WriteLine(ret1);
DllInvoke inv = new DllInvoke("D:\\Project\\C++\\Dll1\\Debug\\DLL1.dll");
Add add = (Add)inv.GetDelegate("Add", typeof(Add));
int result = add(2, 2);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
在上面代码中 Add 委托的上面有一个特性
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
主要是为了解决一个错误
“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配”
原因是C++和C#的数据类型不一致导致的。
如果这个帖子对你有用,欢迎 关注 + 点赞 + 留言,谢谢
end