百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

C#和C++混合编程开发示例

bigegpt 2024-08-08 11:51 2 浏览

C#使用过程中经常会遇到和C++联合开发的过程,通过C++编写动态库,封装成dll后再C#中调用,在此做个记录,并供后期查看

一、新建C#控制台项目

打开VisualStudio,新建一个C#控制台项目,项目名称HelloWorldTest

点击下一步,一个空的默认c#项目创建完成

二、创建C++库

在解决方案上右键--添加--新建项目,建一个C++动态链接库工程,输入项目名称TestDll

创建完成后如下:

这里为了方便,我们直接在HelloWorldLib.cpp里定义函数

C++库导出有两种方式

1、以C语言接口的方式导出

这种方法就是在函数前面加上 extern "C" __declspec(dllexport)

加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。

#include "stdafx.h"
#include<iostream>

extern "C" __declspec(dllexport) void HelloWorld(char* name);
 
extern "C" __declspec(dllexport) void HelloWorld(char* name)
{
 std::cout << "Hello World " << name << std::endl;
}

2、以模块定义文件的方式导出

在工程上右键,选择添加-》新建项

然后选择代码-》模块定义文件

在Source.def中输入

LIBRARY

EXPORTS
HelloWorld

EXPORTS下面就是要导出的函数,这里不需要添加分号隔开,直接换行就行。

此时,我们函数的定义如下

#include "stdafx.h"
#include<iostream>
 
void HelloWorld(char* name);
 
void HelloWorld(char* name)
{
 std::cout <<"Hello World "<< name << std::endl;
}

编译,生成dll。这里需要注意的是,如果生成是64位的库,C#程序也要是64位的,否则会报错

在C#项目中添加引用:同时把C#代码修改为:

using System.Runtime.InteropServices;

namespace ConsoleApplication2
{
 class Program
 {
  [DllImport("HelloWorldLib.dll")]
  public static extern void HelloWorld(string name);

  //可以通过EntryPoint特性指定函数入口,然后为函数定义别名

  [DllImport("HelloWorldLib.dll", EntryPoint = "HelloWorld")]
  public static extern void CustomName(string name);
  static void Main(string[] args)
  {
   HelloWorld("LiLi");
   //跟上面是一样的
   CustomName("QiQi");
  }
 }
}

运行程序,结果如下:

这样就成功创建了一个C#可以调用的C++库

下面我们动态调用C++库,这里委托的作用就比较明显了。把委托比喻为C++的函数指针,一点也不为过。

我们在C++库中再新增一个函数GetYear(),用来获取当前年份。

int GetYear();

int GetYear()
{
 SYSTEMTIME tm;
 GetLocalTime(&tm);
 
 return tm.wYear;
}

记得在导出文件中(Source.def)增加GetYear。编译,生成新的DLL

再新建一个C#控制台程序

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication3
{

 class Program
 {
  [DllImport("kernel32.dll")]
  public static extern IntPtr LoadLibrary(string lpFileName);

  [DllImport("kernel32.dll")]
  public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

  [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]
  public static extern bool FreeLibrary(IntPtr hModule);

  //声明委托,这里的签名,需要跟C++库中的对应
  delegate int GetYearDelegate();

  static void Main(string[] args)
  {
   GetYearDelegate m_fGetYear;
   IntPtr hModule = LoadLibrary("HelloWorldLib.dll");
   if(hModule != IntPtr.Zero)
   {
    IntPtr hProc = GetProcAddress(hModule, "GetYear");
    if(hProc != IntPtr.Zero)
    {
     m_fGetYear = (GetYearDelegate)Marshal.GetDelegateForFunctionPointer(hProc, typeof(GetYearDelegate));

     //在这里可以调用
     int year = m_fGetYear();
     Console.WriteLine("年份是:" + year);
    }
   }
  }
 }
}

运行结果:

好的,前面函数里面涉及的都是简单数据类型,下面来介绍一下复杂数据类型。这里指的是结构体

在C++库中定义一个GetDate()的函数,代码如下。这里也要记得在导出文件中添加(Source.def)

struct MyDate
{
 int year;
 int month;
 int day;
};

MyDate GetDate();

MyDate GetDate()
{
 SYSTEMTIME tm;
 GetLocalTime(&tm);
 
 MyDate md;
 md.day = tm.wDay;
 md.month = tm.wMonth;
 md.year = tm.wYear;
 return md;
}
建一个C#控制台程序,完整代码如下
using System;
using System.Runtime.InteropServices;
 
namespace ConsoleApplication3
{
 struct MyDate
 {
  public int Year;
  public int Month;
  public int Day;
 }
 
 
 class Program
 {
  [DllImport("kernel32.dll")]
  public static extern IntPtr LoadLibrary(string lpFileName);
 
  [DllImport("kernel32.dll")]
  public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
 
  [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]
  public static extern bool FreeLibrary(IntPtr hModule);
 
  delegate IntPtr GetDateDelegate();
 
  static void Main(string[] args)
  {
   GetDateDelegate m_fGetDate;
   IntPtr hModule = LoadLibrary("HelloWorldLib.dll");
 
   if (hModule != IntPtr.Zero)
   {
    IntPtr hProc = GetProcAddress(hModule, "GetDate");
    if (hProc != IntPtr.Zero)
    {
     m_fGetDate = (GetDateDelegate)Marshal.GetDelegateForFunctionPointer(hProc, typeof(GetDateDelegate));
     IntPtr ptr = m_fGetDate();
     if(ptr != IntPtr.Zero)
     {
      MyDate md = (MyDate)Marshal.PtrToStructure(ptr, typeof(MyDate));
      Console.WriteLine("{0}年-{1}月-{2}日",md.Year,md.Month,md.Day);
     }
    }
   }
  }
 }
}

运行结果如下:

相关推荐

【Docker 新手入门指南】第十章:Dockerfile

Dockerfile是Docker镜像构建的核心配置文件,通过预定义的指令集实现镜像的自动化构建。以下从核心概念、指令详解、最佳实践三方面展开说明,帮助你系统掌握Dockerfile的使用逻...

Windows下最简单的ESP8266_ROTS_ESP-IDF环境搭建与腾讯云SDK编译

前言其实也没啥可说的,只是我感觉ESP-IDF对新手来说很不友好,很容易踩坑,尤其是对业余DIY爱好者搭建环境非常困难,即使有官方文档,或者网上的其他文档,但是还是很容易踩坑,多研究,记住两点就行了,...

python虚拟环境迁移(python虚拟环境conda)

主机A的虚拟环境向主机B迁移。前提条件:主机A和主机B已经安装了virtualenv1.主机A操作如下虚拟环境目录:venv进入虚拟环境:sourcevenv/bin/active(1)记录虚拟环...

Python爬虫进阶教程(二):线程、协程

简介线程线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能...

基于网络安全的Docker逃逸(docker)

如何判断当前机器是否为Docker容器环境Metasploit中的checkcontainer模块、(判断是否为虚拟机,checkvm模块)搭配学习教程1.检查根目录下是否存在.dockerenv文...

Python编程语言被纳入浙江高考,小学生都开始学了

今年9月份开始的新学期,浙江省三到九年级信息技术课将同步替换新教材。其中,新初二将新增Python编程课程内容。新高一信息技术编程语言由VB替换为Python,大数据、人工智能、程序设计与算法按照教材...

CentOS 7下安装Python 3.10的完整过程

1.安装相应的编译工具yum-ygroupinstall"Developmenttools"yum-yinstallzlib-develbzip2-develope...

如何在Ubuntu 20.04上部署Odoo 14

Odoo是世界上最受欢迎的多合一商务软件。它提供了一系列业务应用程序,包括CRM,网站,电子商务,计费,会计,制造,仓库,项目管理,库存等等,所有这些都无缝集成在一起。Odoo可以通过几种不同的方式进...

Ubuntu 系统安装 PyTorch 全流程指南

当前环境:Ubuntu22.04,显卡为GeForceRTX3080Ti1、下载显卡驱动驱动网站:https://www.nvidia.com/en-us/drivers/根据自己的显卡型号和...

spark+python环境搭建(python 环境搭建)

最近项目需要用到spark大数据相关技术,周末有空spark环境搭起来...目标spark,python运行环境部署在linux服务器个人通过vscode开发通过远程python解释器执行代码准备...

centos7.9安装最新python-3.11.1(centos安装python环境)

centos7.9安装最新python-3.11.1centos7.9默认安装的是python-2.7.5版本,安全扫描时会有很多漏洞,比如:Python命令注入漏洞(CVE-2015-2010...

Linux系统下,五大步骤安装Python

一、下载Python包网上教程大多是通过官方地址进行下载Python的,但由于国内网络环境问题,会导致下载很慢,所以这里建议通过国内镜像进行下载例如:淘宝镜像http://npm.taobao.or...

centos7上安装python3(centos7安装python3.7.2一键脚本)

centos7上默认安装的是python2,要使用python3则需要自行下载源码编译安装。1.安装依赖yum-ygroupinstall"Developmenttools"...

利用本地数据通过微调方式训练 本地DeepSeek-R1 蒸馏模型

网络上相应的教程基本都基于LLaMA-Factory进行,本文章主要顺着相应的教程一步步实现大模型的微调和训练。训练环境:可自行定义,mac、linux或者window之类的均可以,本文以ma...

【法器篇】天啦噜,库崩了没备份(天啦噜是什么意思?)

背景数据库没有做备份,一天突然由于断电或其他原因导致无法启动了,且设置了innodb_force_recovery=6都无法启动,里面的数据怎么才能恢复出来?本例采用解析建表语句+表空间传输的方式进行...