C#调用C++的实现步骤

 更新时间:2024年11月29日 10:34:18   作者:qq_27663383  
本文主要介绍了C#调用C++的基本规则和方法,包括内存对齐、调用约定、基本数据类型的传递、结构体的传递以及数组的传递等,感兴趣的可以了解一下

1、内存对齐的规则

(适用于C++、C#)

首先需要了解C++、C#的内存对齐的规则:

结构体的数据成员,第一个成员的偏移量为0,后面的每个数据成员存储的起始位置要从自己大小的整数倍开始。

子结构体中的第一个成员偏移量应当是子结构体中最大成员的整数倍。

结构体总大小必须是其内部最大成员的整数倍。

以下为C#示例代码:

internal class Program
{
    struct Info
    {
        double dd;//32-40
        bool bo;//40-48
    }
    struct MyStruct
    {
        char c;//0-1
        decimal d;//8-16
        int a;//16-20
        double b;//24-32
        Info info;//32-
    }
    static unsafe void Main(string[] args)
    {
        Console.WriteLine(sizeof(MyStruct));//输出结果:48
    }
}

2、调用约定

  • _cdecl称之为c调用约定,参数从右至左的方式入栈,函数本身不清理栈,此工作由调用者负责,所以允许可变参数函数存在。我们自己编写的程序一般为_cdecl

  • _stdcall称之为标准调用约定,参数从右至左的方式入栈,函数本身清理栈,所以不允许可变参数函数存在。windowsAPI一般为_stdcall

3、C#传递基本数据类型到C++

C++与C#的基本类型对应关系如下表所示,数据通过值传递

C++基本类型C#基本类型
intint
charsbyte
shortshort
floatfloat
doubledouble
long longlong
boolbool
char*string(传值,需CharSet = CharSet.Ansi)
int*,double*ref int,ref double
int&,double&ref int,ref double

**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **

C++ 代码

//native.h文件
extern "C"
{
	__declspec(dllexport) void __cdecl TestBasicData(bool d1,char d2,short d3,
		int d4,long long d5,float d6,double d8);
}

//native.cpp文件
#include "native.h"
void __cdecl TestBasicData(bool d1, char d2, short d3, int d4, long long d5, float d6, double d8)
{
	return;//在此处添加断点,观察在C#中的数据传递到了C++代码中
}

C#代码

[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestBasicData(bool d1, sbyte d2, short d3,
                                       int d4, long d5, float d6, double d8);
static void Main(string[] args)
{
    TestBasicData(true, 64, 128, 123456, 123456789, 12.45f, 3.142592);
}

注意:使用VS调试过程中,需要在C#工程中勾选启动本地代码调试如下入所示,这样调试的时候才会进入C++代码。

在这里插入图片描述

建议在VS的解决方案属性中,将C#控制台项目依赖C++的dll项目,如下入所示。这样编译C#项目会自动编译C++项目。

在这里插入图片描述

4、C#传递基本数据类型的数组到C++

**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **

以下以传递int*为例

C++代码如下,生成NativeDll.dll文件

//native.h文件
#pragma once
extern "C"
{
	__declspec(dllexport) int __cdecl TestAddDoubles(int* d, int length);
}

//native.cpp文件
#include "native.h"
int __cdecl TestAddDoubles(int* d, int length)
{
	int re = 0;
	for (size_t i = 0; i < length; i++)
	{
		re += d[i];
		d[i] = 99;//改变d地址内的数据,在C#代码中也可以看到dpoint地址内的数据被改为99
	}
	return re;
}

C#代码如下

[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestAddDoubles(IntPtr d, int length);
static void Main(string[] args)
{
    IntPtr dpoint = Marshal.AllocHGlobal(sizeof(int)*4);//在非托管内存中创建4个int大小内存的指针
    unsafe
    {
        int* ptr = (int*)dpoint.ToPointer();
        ptr[0] = 1;
        ptr[1] = 3;
        ptr[2] = 5;
        ptr[3] = 7;
    }
    var re = TestAddDoubles(dpoint, 4);
    Console.WriteLine(re);//输出结果为16
    Marshal.FreeHGlobal(dpoint);//非托管内存需要在C#代码中释放
    Console.ReadLine();
}

5、C#传递基本类型组成的结构体给C++

5.1 按值传递基本类型组成的结构体

**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **

C++代码如下

//native.h文件
extern "C"
{
	struct Shape//注意:一定要与C#中的结构体对齐方式一致
	{
		int x;
		int y;
		int z;
		double area;
		double volume;
	};
	__declspec(dllexport) int __cdecl TestStructor(Shape p);
}

//native.cpp文件
#include"native.h"
int __cdecl TestStructor(Shape p)
{
	return sizeof(p);
}

C#代码如下

struct Shape//注意:一定要与C++中的结构体对齐方式一致
{
    public int x;
    public int y;
    public int z;
    public double area;
    public double volume;
}


[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestStructor(Shape shape);
static void Main(string[] args)
{
    Shape shape = new Shape() { x = 10, y = 20, z = 30, area = 123.45, volume = 3456.98 };
    var len = TestStructor(shape);//传值得方式将结构体传给C++
    Console.WriteLine(len);
}

5.2 按引用传递基本类型组成的结构体给C++结构体指针

**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **

C++代码如下

//native.h文件
extern "C"
{
	struct Shape//注意:一定要与C#中的结构体对齐方式一致
	{
		int x;
		int y;
		int z;
		double area;
		double volume;
	};
	__declspec(dllexport) int __cdecl TestStructorPointer(Shape* p);
}

//native.cpp文件
#include"native.h"
int __cdecl TestStructorPointer(Shape* p)
{
	int r = sizeof(*p);
	p->x = 100;
	p->y = 100;
	p->z = 100;
	p->area = 10.1;
	p->volume = 10.1;
	return r;
}

C#代码如下

struct Shape//注意:一定要与C++中的结构体对齐方式一致
{
    public int x;
    public int y;
    public int z;
    public double area;
    public double volume;
}


[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestStructorPointer(ref Shape shape);
static void Main(string[] args)
{
    Shape shape = new Shape() { x = 10, y = 20, z = 30, area = 123.45, volume = 3456.98 };
    var len = TestStructorPointer(ref shape);//ref方式将结构体传给C++,通过断点调试,查看C#中的shape也更改了
    Console.WriteLine(len);
}

6、C#传递元素有数组的结构体

**不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **

结构体是按值进行传递的。

C++代码如下

//native.h文件
#pragma once
extern "C"
{
	struct Student
	{
		char name[50];
		int age;
		double score;
	};
	__declspec(dllexport) int __cdecl TestStudentStructor(Student s);
}

//native.cpp文件
#include "native.h"
int __cdecl TestStudentStructor(Student s)
{
	int len = sizeof(s);
	return len;
}

C#中的结构体映射到C++内存中,要求结构体只能包含非托管类型

C#代码如下

unsafe struct Student//注意:一定要与C++中的结构体对齐方式一致
{
    public fixed byte name[50];
    public int age;
    public double score;
}

[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestStudentStructor(Student s);
static unsafe void Main(string[] args)
{
    Student s = new Student();
    s.age = 12;
    s.score = 123.4;
    var name = Encoding.GetEncoding("GB2312").GetBytes("abcd\0");//C++的字符串编码为GB2312,结尾添加\0
    Marshal.Copy(name, 0,new IntPtr(s.name),name.Length);
    var len = TestStudentStructor(s);
    Console.WriteLine(len);
}

7、C#传递元素有数组的结构体指针

** 不要将C#中托管堆上的数据,按照传引用的方式传递到C++中 **

C++代码如下

//native.h文件
#pragma once
extern "C"
{
	struct Student
	{
		char name[50];//结构体中含有char数组
		int age;
		double score;
	};
	__declspec(dllexport) int __cdecl TestStudentStructorPointer(Student* s);
}

//native.cpp文件
#include "native.h"
int __cdecl TestStudentStructorPointer(Student* s)//C++中改变s,C#中也会变
{
	int len = sizeof(*s);
	s->age = 1000;
	s->score = 1000.0;
	for (size_t i = 0; i < sizeof(s->name); i++)
	{
		s->name[i] = 123;
	}
	return len;
}

C#中的结构体映射到C++内存中,要求结构体只能包含非托管类型

C#代码如下

unsafe struct Student//注意:一定要与C++中的结构体对齐方式一致
{
    public fixed byte name[50];
    public int age;
    public double score;
}

[DllImport("NativeDll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestStudentStructorPointer(ref Student s);//使用ref
static  void Main(string[] args)
{
    Student s = new Student();
    s.age = 12;
    s.score = 123.4;
    var name = Encoding.GetEncoding("GB2312").GetBytes("abcd\0");//结尾添加\0
    unsafe
    {
        Marshal.Copy(name, 0, new IntPtr(s.name), name.Length);
    }
    var len = TestStudentStructorPointer(ref s);//ref方式将结构体传给C++,通过断点查看C#中的s也更改了
    Console.WriteLine(len);
}

到此这篇关于C#调用C++的实现步骤的文章就介绍到这了,更多相关C#调用C++内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 事务在c#中的使用

    事务在c#中的使用

    这篇文章介绍了事务在c#中的使用,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • C#通过html调用WinForm的方法

    C#通过html调用WinForm的方法

    这篇文章主要介绍了C#通过html调用WinForm的方法,涉及html页面中使用JavaScript访问C#的相关技巧,需要的朋友可以参考下
    2016-04-04
  • 一文详解C#中String字符串的常见操作与日期格式化方法

    一文详解C#中String字符串的常见操作与日期格式化方法

    这篇文章主要为大家详细介绍了C#中字符串的常用操作与日期格式化方法,主要包括字符串的4种创建方式,String类的常用属性和方法等内容,希望对大家有所帮助
    2026-02-02
  • C#实现块状链表的项目实践

    C#实现块状链表的项目实践

    这篇文章主要介绍了C#实现块状链表的项目实践,通过定义块和链表类,利用块内元素引用实现块与块之间的链接关系,从而实现对块状链表的遍历、插入和删除等操作,感兴趣的可以了解一下
    2023-11-11
  • C#中OpenCVSharp实现轮廓检测

    C#中OpenCVSharp实现轮廓检测

    这篇文章主要介绍了C#中OpenCVSharp实现轮廓检测,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • C#自定义组件实现表格的多层表头功能

    C#自定义组件实现表格的多层表头功能

    这篇文章主要为大家详细介绍了如何使用C#自定义组件实现表格的多层表头功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-12-12
  • C#控制台带参数程序源码编写实例讲解

    C#控制台带参数程序源码编写实例讲解

    像ipconfig /all 这样的CMD命令想必大家都知道,但是很多童鞋可能不知道怎么写这样的控制台带参数的程序,需要的朋友可以了解下
    2012-12-12
  • C#编写发送邮件组件

    C#编写发送邮件组件

    本文给大家分享的是使用C#编写的发送邮件的组件,非常的简单实用,有需要的小伙伴可以参考下。
    2015-06-06
  • C#纯技术之Class写入Json

    C#纯技术之Class写入Json

    这篇文章主要介绍了C#纯技术之Class写入Json问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • WPF实现播放RTSP视频流

    WPF实现播放RTSP视频流

    这篇文章主要为大家详细介绍了WPF实现播放RTSP视频流的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-01-01

最新评论