在VS2010上使用C#调用非托管C++生成的DLL文件(图文讲解)

背景 

     在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件的时候,通过C#来开发应用软件时,就需要利用DllImport特性进行方法调用。本篇文章将引导你快速理解这个调用的过程。

步骤

1. 创建一个CSharpInvokeCPP的解决方案:

image

2. 创建一个C++的动态库项目:

image

3. 在应用程序设置中,选择“DLL”,其他按照默认选项:

image

最后点击完成,得到如图所示项目:

image

      我们可以看到这里有一些文件,其中dllmain.cpp作为定义DLL应用程序的入口点,它的作用跟exe文件有个main或者WinMain入口函数是一样的,它就是作为DLL的一个入口函数,实际上它是个可选的文件。它是在静态链接时或动态链接时调用LoadLibrary和FreeLibrary时都会被调用。详细内容可以参考(DllMain详解-CSDN博客)。

4. 现在我们打开CSharpInvokeCPP.CPPDemo.cpp文件:

现在我们加入以下内容:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// CSharpInvokeCPP.CPPDemo.cpp : 定义 DLL 应用程序的导出函数。

//

#include "stdafx.h"

extern "C" __declspec(dllexportint Add(int x, int y)

{

    return x + y;

}

extern "C" __declspec(dllexportint Sub(int x, int y)

{

    return x - y;

}

extern "C" __declspec(dllexportint Multiply(int x, int y)

{

    return x * y;

}

extern "C" __declspec(dllexportint Divide(int x, int y)

{

    return x / y;

}

      extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。而被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。

      __declspec(dllexport)的目的是为了将对应的函数放入到DLL动态库中。

      extern "C" __declspec(dllexport)加起来的目的是为了使用DllImport调用非托管C++的DLL文件。因为使用DllImport只能调用由C语言函数做成的DLL。

5. 编译项目程序,最后在Debug目录生成CSharpInvokeCPP.CPPDemo.dll和CSharpInvokeCPP.CPPDemo.lib

image

我们用反编译工具PE Explorer查看下该DLL里面的方法:

image

可以发现对外的公共函数上包含这四种“加减乘除”方法。

6. 现在来演示下如何利用C#项目来调用非托管C++的DLL,首先创建C#控制台应用程序:

image

7. 在CSharpInvokeCSharp.CSharpDemo项目上新建一个CPPDLL类,编写以下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class CPPDLL

{

    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]

    public static extern int Add(int x, int y);

    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]

    public static extern int Sub(int x, int y);

    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]

    public static extern int Multiply(int x, int y);

    [DllImport("CSharpInvokeCPP.CPPDemo.dll")]

    public static extern int Divide(int x, int y);

}

DllImport作为C#中对C++的DLL类的导入入口特征,并通过static extern对extern “C”进行对应。

8. 另外,记得把CPPDemo中生成的DLL文件拷贝到CSharpDemo的bin目录下,你也可以通过设置【项目属性】->【配置属性】->【常规】中的输出目录:

image

这样编译项目后,生成的文件就自动输出到CSharpDemo中了。

9. 然后在Main入口编写测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

static void Main(string[] args)

{

    int result = CPPDLL.Add(10, 20);

    Console.WriteLine("10 + 20 = {0}", result);

    result = CPPDLL.Sub(30, 12);

    Console.WriteLine("30 - 12 = {0}", result);

    result = CPPDLL.Multiply(5, 4);

    Console.WriteLine("5 * 4 = {0}", result);

    result = CPPDLL.Divide(30, 5);

    Console.WriteLine("30 / 5 = {0}", result);

    Console.ReadLine();

}

运行结果:

image

方法得到调用。

10. 以上的方法只能通过静态方法对于C++中的函数进行调用。那么怎样通过静态方法去调用C++中一个类对象中的方法呢?现在我在CPPDemo项目中添加一个头文件userinfo.h:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

class UserInfo {

private:

    char* m_Name;

    int m_Age;

public:

    UserInfo(char* name, int age)

    {

        m_Name = name;

        m_Age = age;

    }

    virtual ~UserInfo(){ }

    int GetAge() { return m_Age; }

    char* GetName() { return m_Name; }

};

在CSharpInvokeCPP.CPPDemo.cpp中,添加一些代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

#include "malloc.h"

#include "userinfo.h"

typedef struct {

    char name[32];

    int age;

} User; 

UserInfo* userInfo;

extern "C" __declspec(dllexport) User* Create(char* name, int age)   

{  

    User* user = (User*)malloc(sizeof(User));

    userInfo = new UserInfo(name, age);

    strcpy(user->name, userInfo->GetName()); 

    user->age = userInfo->GetAge();

    return user;

}

这里声明一个结构,包括name和age,这个结构是用于和C#方面的结构作个映射。

注意:代码中的User*是个指针,返回也是一个对象指针,这样做为了防止方法作用域结束后的局部变量的释放。

strcpy是个复制char数组的函数。

11. 在CSharpDemo项目中CPPDLL类中补充代码:

1

2

3

4

5

6

7

8

9

10

11

[DllImport("CSharpInvokeCPP.CPPDemo.dll")]

public static extern IntPtr Create(string name, int age);

[StructLayout(LayoutKind.Sequential)]

public struct User

{

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]

    public string Name;

    public int Age;

}

其中这里的结构User就和C++中的User对应。

12. 在Program.cs中补充代码:

1

2

3

IntPtr ptr = CPPDLL.Create("李平", 27);

<strong><font color="#ff0000">CPPDLL.User user = (CPPDLL.User)Marshal.PtrToStructure(ptr, typeof(CPPDLL.User));</font></strong>

Console.WriteLine("Name: {0}, Age: {1}", user.Name, user.Age);

注意:红色字体部分,这里结构指针首先转换成IntPtr句柄,然后通过Marshal.PtrToStructrue转换成你所需要的结构。

运行结果:

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/224600.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 8特性:Lambda表达式、函数式接口与Stream API的深度探索

一、引言 随着编程范式的不断演变&#xff0c;Java语言也在不断地发展和创新。Java 8的发布&#xff0c;为开发者们带来了诸多全新的特性&#xff0c;其中包括Lambda表达式、函数式接口以及Stream API。这些特性使得Java语言的编程更加简洁、优雅&#xff0c;同时也提高了代码…

mybatis多表映射-对多关联

1、建库建表 create database mybatis-example; use mybatis-example; create table t_book (bid varchar(20) primary key,bname varchar(20),stuid varchar(20) ); insert into t_book values(b001,Java,s001); insert into t_book values(b002,Python,s002); insert into …

docker部署go gin框架 Windows环境

目录 文章目的是什么 环境介绍 Windows 环境下 docker 部署 go gin 详细步骤 运行容器时因为挂载文件可能会出现的问题 直接部署gin&#xff08;跳过运行容器时因为挂载文件可能会出现的问题&#xff09; 文章目的是什么 假设我们学习了 go 语言&#xff0c;在 Windows(本…

6.rk3588获取摄像头和激光雷达数据(用线程根据时间同步)

文件夹结构如下&#xff1a; 如果没有特殊说明&#xff0c;我们将py文件写在该路径里面。 保存数据的路径如下&#xff1a; ---img_lidar_save ---2023-12-13&#xff08;根据日期自动生成当天保存数据的文件夹) ---camera_data(相机数据文件夹&#xff09; ---image(保存相加…

[蓝桥杯刷题]合并区间、最长不连续子序列、最长不重复数组长度

前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录: 成功的关键在于对目标的持久追求。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法 数据库 文章目录 前言合并区间问题&#x1f4d5;现实应用大致思路代码实现代码讲解 最长不连续子序列&a…

jvisualvm手动安装VisualGC插件

前言 笔者近期排查问题需要查看GC的情况&#xff0c;于是用到了jvisualvm这个工具&#xff0c;查阅网上资料发现它有一个名为VisualGC的插件非常好用&#xff0c;于是笔者以此文记录一下VisualGC插件的安装步骤。 安装步骤 下载插件 首先我们要到官网 https://visualvm.gi…

未势能源受邀参加中国氢能100人论坛并发表演讲

12月12日-14日&#xff0c;“2023氢能嘉年华暨中国氢能100人论坛年会”在苏州举办&#xff0c;行业内专家学者、氢能头部企业代表等齐聚现场&#xff0c;聚焦氢能在化工、钢铁、交通等领域发展&#xff0c;共同探讨我国氢能产业初期前进之路。 未势能源液氢总工程师黄欢明受邀…

DevOps搭建(六)-安装Maven详细步骤

1、官网下载 下载地址&#xff1a; Maven – Download Apache Maven 2、上传压缩包到服务器 把下载好的压缩包上传到服务器上。 3、解压压缩包 解压压缩包到安装目录/usr/local/ tar -zxvf apache-maven-3.9.3-bin.tar.gz -C /usr/local/ 切换到/usr/local目录下ls命令看…

基于FPGA的视频接口之高速IO(光纤)

简介 对于高速IO口配置光纤,现在目前大部分开发板都有配置,且也有说明,在此根据自己的工作经验以及对于各开发板的说明归纳 通过高速IO接口,以及硬件配置,可以实现对于光纤的收发功能,由于GTX的速率在500Mbs到10Gbps之间,但通道高速io可配置光纤10G硬件,物理通完成,则…

SpringIOC之Jsr330ScopeMetadataResolver

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

科东软件Intewell操作系统:以“鸿”鹄之志,创未来之“道”

打造自主可控的新型工业操作系统是我国加速新型工业化进程&#xff0c;在新一轮科技革命中实现“换道超车”的重要契机。科东软件期待凭借鸿道Intewell新型工业操作系统的创新与应用&#xff0c;打造100%自主可控的工业网络和工业控制底层技术&#xff0c;为我国新型工业化贡献…

顶级Web应用程序测试工具列表

今天主要列举Web应用程序的工具。 今天的列表仅仅提供索引功能&#xff0c;具体要使用的同学&#xff0c;可以自行搜索哦。 通过web应用程序测试&#xff0c;在web应用程序公开发布之前&#xff0c;会发现网站功能、安全性、可访问性、可用性、兼容性和性能等问题。 Web应用程…

模板方法模式(行为型)

目录 一、前言 二、模板模式 三、带钩子的模板模式 四、总结 一、前言 模板方法模式是一种行为型设计模式&#xff0c;它定义了一个操作中的算法框架&#xff0c;将一些步骤延迟到子类中实现。这种模式是基于“开闭原则”的设计思想&#xff0c;即对扩展开放&#xff0c;对…

JAVA:乘除窗体的实现

目录 题目要求&#xff1a; 窗口的实现&#xff1a; try 和 catch 的用法&#xff1a; 思路大意&#xff1a; 关键代码的实现&#xff1a; 题目要求&#xff1a; 使用 try 和catch 方法完成乘法除法的异常处理和窗体的实现&#xff0c;如下图所示&#xff1a; 窗口的实…

西瓜视频RenderThread引起的闪退问题攻坚历程

背景 影响 西瓜之前存在过一类RenderThread闪退&#xff0c;从堆栈上看&#xff0c;全部都是系统so调用&#xff0c;给人的第一印象像是一个系统bug&#xff0c;无从下手。闪退集中在Android 5~6上&#xff0c;表现为打开直播间立即闪退。该问题在2022年占据Native Crash Top5&…

Linux(19):基础系统设定与备份策略

系统基本设定 网络设定&#xff08;手动设定与 DHCP 自动取得&#xff09; 通常网络参数的取得方式常见的有底下这几种&#xff1a; 1.手动设定固定 IP 常见于学术网络的服务器设定、公司行号内的特定座位等。这种方式你必须要取得底下的几个参数才能够让你的 Linux 上网的: …

SpringBoot基础使用及对其他项目进行整合

目录 一、简介 1-讲述 2-特点 二、创建配置 1.创建 2.配置 3.代码生成 三、项目整合 每篇一获 一、简介 1-讲述 众所周知 Spring 应用需要进行大量的配置&#xff0c;各种 XML 配置和注解配置让人眼花缭乱&#xff0c;且极容易出错&#xff0c;因此 Spring 一度被称…

i春秋云镜之Initial

首先拿到目标IP&#xff1a;39.99.156.72 通过Fscan进行扫描发现存在Thinkphp RCE漏洞。 ./fscan_amd64 -h 39.99.156.72然后通过利用工具进行RCE。 我们进行getshell之后通过蚁剑进行连接。 反弹shell并转换成python 交互式shell。 rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/b…

《儿童绘本》期刊杂志发表论文投稿

《儿童绘本》杂志是由国家新闻出版管理部门批准&#xff0c;由吉林省舆林报刊发展有限责任公司主管主办&#xff0c;国内外公开发行的全国优秀期刊。办刊宗旨&#xff1a;以“普及绘本知识、推动儿童阅读”为理念&#xff0c;带动家庭亲子阅读&#xff0c;推动阅读教育及图画书…

C语言刷题数组------数组交换

输入一个长度为 10的整数数组 X[10]&#xff0c;将里面的非正整数全部替换为 1&#xff0c;输出替换完成后的数组。 输入格式 输入包含 10个整数&#xff0c;每个整数占一行。输出格式 输出新数组中的所有元素&#xff0c;每个元素占一行。输出格式为 X[i] x&#xff0c;其中…