C#调用C++ dll教程
文章目录
- 一、创建C++ dll项目
- 二、C#程序员调用C++ dll
- 三、C++与C#数据类型对应
- 基本数据类型对应表
- C++指针类型与C#类型
在使用C#开发客户端时,有时需要调用C++ dll,本篇博客来介绍C#程序如何调用C++ dll。
一、创建C++ dll项目
首先使用VS2022创建C++ dll项目,具体步骤如下:
(1)选择Windows桌面向导,点击下一步, 取项目名,例如我的dll项目名是libMath

(2)选择动态项目,勾选导出符号

(3)编写动态代码,代码如下:
libMath.h
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LIBMATH_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// LIBMATH_API 函数视为是从 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#调用,为了方便导出类,添加了函数进行导出,并且需要加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++类通常包含成员函数、构造函数、析构函数等,而C#与C++在处理这些方面存在差异。一种可行的方法是在C++类中添加一些导出函数,这样它们可以通过C#调用。这些函数可以执行类的实例化、调用成员函数等操作。确保使用 extern “C” 以避免名称修饰, 因为C++函数在编译时,会在原有的函数名前后添加一些符号,例如add函数在编译后可能变成了@xxasd_sfdf_add_xxx之类的,但是使用extern "C" 后就是按照C语言的方式导出,函数名不变,例如我添加的一些类导出方法:
// 由于需要给C#调用,为了方便导出类,添加了函数进行导出,并且需要加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 的导出函数。
//#include "framework.h"
#include "libMath.h"// 这是已导出类的构造函数。
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字节与8字节的区分,这些都会导致在C#调用C++ dll代码失败或者crash。此外,确保你的应用程序具有访问和加载DLL所需的适当权限。
运行结果如下:

在C#中可以创建一个对应C++类的C#包装类,使用 DllImport 属性声明导出函数,例如下面的代码:
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类,并调用其中的方法,这将转发调用到C++类的实例。
MyClassWrapper myInstance = new MyClassWrapper();
myInstance.MyMethod();
这是一个简单的例子,实际情况可能更为复杂,特别是在处理类的继承、虚函数等方面。确保在进行实际应用时进行充分的测试,以确保互操作性正常运作。
三、C++与C#数据类型对应
C#在调用C++ DLL时,需要通过P/Invoke技术来完成。P/Invoke是.NET Framework用于调用非托管代码库的一种方式。在这个过程中,我们需要处理两种语言之间的数据类型转换,因为它们的数据类型不完全一致。
基本数据类型对应表
以下是C++和C#之间的一些常见数据类型的对应表(请注意,这并不是一个完全的列表,只是一些常见类型的示例):
| 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的函数调用。例如,如果我们有一个C++函数如下:
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);
}
对于更复杂的数据类型,如结构体和类,我们需要在C#中创建对应的类或结构体来匹配。例如,如果我们有一个C++结构体:
struct Person
{char* name;int age;
};
我们可以在C#中创建一个类来匹配它:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Person
{public string name;public int age;
}
注意我们使用了StructLayout特性并设置CharSet为CharSet.Ansi,因为C++中的char*类型对应于ANSI字符串。
在处理C++ DLL中的函数时,也需要注意函数调用的约定(cdecl,stdcall等)。默认情况下,C#假定被调用的函数使用stdcall调用约定。如果函数使用不同的调用约定,你需要在DllImport特性中指定它,例如:
[DllImport("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
在处理更复杂的情况,如回调函数,复杂的数据结构,和C++类时,可能需要更多的处理。处理这些情况通常需要对两种语言都有深入的理解,并且可能需要手动管理内存。
C++指针类型与C#类型
C++ 指针在C#中的相应类型取决于指针指向的数据类型和你如何打算使用它。这里有几种常见的情况:
-
指向简单类型的指针:如果指针指向一个简单的数值类型(如
int*、float*等),你可以在C#中使用IntPtr类型来表示它。你可以使用Marshal类中的方法(如Marshal.ReadInt32、Marshal.WriteInt32等)来读取或写入指针指向的数据。 -
指向字符串的指针:如果指针指向一个字符串(如
char*或wchar_t*),你可以在C#中使用string类型来表示它。在DllImport特性中,你可以使用MarshalAs特性来指定字符串的编码方式。 -
指向结构体的指针:如果指针指向一个结构体,你可以在C#中创建一个对应的类或结构体,并使用
ref关键字或者IntPtr类型来表示指针。使用ref关键字时,.NET运行时会自动处理内存管理。使用IntPtr时,你需要手动管理内存。
例如,假设你有一个C++函数如下:
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);
- 指向数组的指针:如果指针指向一个数组,你可以在C#中使用数组类型来表示它。你也可以使用
MarshalAs特性来指定数组的大小。
对于更复杂的情况,例如指向函数的指针(函数指针),你可能需要使用Marshal.GetDelegateForFunctionPointer方法将其转换为C#委托。
需要注意的是,当你使用IntPtr来管理指针时,你需要自己负责内存的分配和释放。你可以使用Marshal.AllocHGlobal和Marshal.FreeHGlobal方法来分配和释放非托管内存。
相关文章:
C#调用C++ dll教程
文章目录 一、创建C dll项目二、C#程序员调用C dll三、C与C#数据类型对应基本数据类型对应表C指针类型与C#类型 在使用C#开发客户端时,有时需要调用C dll,本篇博客来介绍C#程序如何调用C dll。 一、创建C dll项目 首先使用VS2022创建C dll项目…...
计算机毕设 深度学习 大数据 股票预测系统 - python lstm
文章目录 0 前言1 课题意义1.1 股票预测主流方法 2 什么是LSTM2.1 循环神经网络2.1 LSTM诞生 2 如何用LSTM做股票预测2.1 算法构建流程2.2 部分代码 3 实现效果3.1 数据3.2 预测结果项目运行展示开发环境数据获取 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要…...
97.qt qml-自定义Table之实现ctrl与shift多选
我们之前实现了:93.qt qml-自定义Table优化(新增:水平拖拽/缩放自适应/选择使能/自定义委托)-CSDN博客 实现选择使能的时候,我们只能一行行去点击选中,非常麻烦,所以本章我们实现ctrl多选与shift多选、 所以在Table控件新增两个属性: 1.实现介绍 ctrl多选实现原理:当我…...
运行软件报错mfc140.dll丢失?分享mfc140.dll丢失的解决方法
小伙伴们,你是否也有过这样的经历:每当碰到诸如" mfc140.dll 丢失 "之类的烦人错误时,你是不是会一头雾水,完全不知道从何下手去解决?不要担心,接下来咱就给你提供这样一篇实用教程,教…...
milvus数据库-连接
Milvus 支持 19530 和 9091 两个端口: 端口 19530 用于 gRPC 和 RESTful API。 这是您使用不同 Milvus SDK 或 HTTP 客户端连接到 Milvus 服务器时的默认端口。 端口 9091 用于 Kubernetes 内的指标收集、pprof 分析和运行状况探测。 它用作管理端口。 1.连接到数…...
ios + vue3 Teleport + inset 兼容性问题
目录 1,问题表现2,解决步骤1,teleport 的问题2,inset 的问题3,teleport 的问题之二 1,问题表现 使用 vue3 的 Teleport 实现的 dialog 弹窗,但是在 ios app 中嵌套的 h5 中无法打开。 直接在io…...
计蒜客T1654 数列分段(C语言实现)
【题目描述】对于给定的一个长度为n的正整数数列ai,现要将其分成连续的若干段,并且每段和不超过m(可以等于m),问最少能将其分成多少段使得满足要求。 【输入格式】第一行包含两个正整数n,m,表示…...
Linux进程——system函数、popen函数
system函数(执行shell 命令) 头文件 #include <stdlib.h> 函数定义 int system(const char * string); 函数说明 system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,…...
【智能家居】5、主流程设计以及外设框架编写与测试
目录 一、主流程设计 1、工厂模式结构体定义 (1)指令工厂 inputCmd.h (2)外设工厂 controlDevices.h 二、外设框架编写 1、创建外设工厂对象bathroomLight 2、编写相关函数框架 3、将浴室灯相关操作插入外设工厂链表等待被调…...
详解ssh远程登录服务
华子目录 简介概念功能 分类文字接口图形接口 文字接口ssh连接服务器浅浅介绍一下加密技术凯撒加密加密分类对称加密非对称加密非对称加密方法(也叫公钥加密) ssh两大类认证方式:连接加密技术简介密钥解析 ssh工作过程版本协商阶段密钥和算法…...
LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄
接着前两节的Langchain,继续实现Langchain中的Agent LangChain 实现给动物取名字,LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字 代码实现 # 从langchain库中导入模块 from langchain.llms import OpenAI # 从langchain.l…...
wpf devexpress绑定grid到总计和分组统计
此主题描述了如何在gridcontrol中的视图模型和显示定义总计和分组统计 在视图模型中指定统计 1、创建 SummaryItemType 枚举你想要在GridControl中显示的统计类型: public enum SummaryItemType { Max, Count, None } 2、创建一个grid统计描述类 public class S…...
嵌入式 Linux 移植与系统启动方法
1、Linux系统启动与U-Boot 所谓移植就是把程序代码从一种运行环境转移到另一种运行环境。对于内核移植来说,主要是从一种硬件平台转移到另一种硬件平台上运行。 体系结构级别的移植是指在不同体系结构平台上Linux内核的移植,例如,在ARM、MI…...
代码随想录算法训练营|五十六天
回文子串 647. 回文子串 - 力扣(LeetCode) dp含义:表示区间内[i,j]是否有回文子串,有true,没有false。 递推公式:当s[i]和s[j]不相等,false;相等时,情况一,…...
基于django水果蔬菜生鲜销售系统
基于django水果蔬菜生鲜销售系统 摘要 基于Django的水果蔬菜生鲜销售系统是一种利用Django框架开发的电子商务平台,旨在提供高效、便捷的购物体验,同时支持水果蔬菜生鲜产品的在线销售。该系统整合了用户管理、产品管理、购物车、订单管理等核心功能&…...
【数据结构】快速排序算法你会写几种?
👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:数据结构 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有帮助…...
C#访问修饰符
C#中的访问修饰符用于控制类型成员(如字段、属性、方法等)的访问级别。以下是C#中常用的访问修饰符: public:公共访问级别,没有任何访问限制。在任何其他类或程序集中都可以访问标记为 public 的成员。 private&#…...
anaconda中安装pytorch和TensorFlow环境并在不同环境中安装kernel
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...
记一次解决Pyqt6/Pyside6添加QTreeView或QTreeWidget导致窗口卡死(未响应)的新路历程,打死我都想不到是这个原因
文章目录 💢 问题 💢🏡 环境 🏡📄 代码💯 解决方案 💯⚓️ 相关链接 ⚓️💢 问题 💢 我在窗口中添加了一个 QTreeWidget控件 ,但是程序在运行期间,只要鼠标进入到 QTreeWidget控件 内进行操作,时间超过几秒中就会出现窗口 未响应卡死的 状态 🏡 环境 �…...
用照片预测人的年龄【图像回归】
在图像分类任务中,卷积神经网络 (CNN) 是非常强大的神经网络架构。 然而,鲜为人知的是,它们同样能够执行图像回归任务。 图像分类和图像回归任务之间的基本区别在于分类任务中的目标变量(我们试图预测的东西)不是连续…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...
