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#实现带行数和标尺的RichTextBox

    C#实现带行数和标尺的RichTextBox

    这篇文章主要为大家详细介绍了如何利用C#实现带行数和标尺的RichTextBox,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-12-12
  • C#创建不规则窗体的4种方式详解

    C#创建不规则窗体的4种方式详解

    在这里我们将实现的是C#创建不规则窗体的几种方式,包括自定义窗体,不规则图形等等。希望对大家有所帮助。
    2015-10-10
  • C#图像处理之头发检测的方法

    C#图像处理之头发检测的方法

    这篇文章主要介绍了C#图像处理之头发检测的方法,可实现针对图像中头发的检测功能,非常具有实用价值,需要的朋友可以参考下
    2015-04-04
  • C#用正则表达式Regex.Matches 方法检查字符串中重复出现的词

    C#用正则表达式Regex.Matches 方法检查字符串中重复出现的词

    使用正则表达式用Regex类的Matches方法,可以检查字符串中重复出现的词,Regex.Matches方法在输入字符串中搜索正则表达式的所有匹配项并返回所有匹配,本文给大家分享C#正则表达式检查重复出现的词,感兴趣的朋友一起看看吧
    2024-02-02
  • C#访问SqlServer设置链接超时的方法

    C#访问SqlServer设置链接超时的方法

    这篇文章主要介绍了C#访问SqlServer设置链接超时的方法,涉及CommandTimeout属性的相关设置技巧,非常简单实用,需要的朋友可以参考下
    2015-06-06
  • LZW数据压缩算法的原理分析

    LZW数据压缩算法的原理分析

    我希望通过本文的介绍,能给那些目前不太了解lzw算法和该算法在gif图像中应用,但渴望了解它的人一些启发和帮助。抛砖引玉而已,更希望兄弟们提出宝贵的意见。
    2016-06-06
  • Unity3D实现模型随机切割

    Unity3D实现模型随机切割

    这篇文章主要为大家详细介绍了Unity3D实现模型随机切割,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-03-03
  • 少见的C# RSA算法

    少见的C# RSA算法

    这篇文章主要介绍了非常少见的C# RSA算法,文章简单易懂,实例代码帮助大家更好的学习,感兴趣的朋友可以了解下
    2020-06-06
  • C# 中 Array和 ArrayList详解及区别

    C# 中 Array和 ArrayList详解及区别

    这篇文章主要介绍了C# 中 Array和 ArrayList详解及区别的相关资料,需要的朋友可以参考下
    2017-01-01
  • 详解C# 不能用于文件名的字符

    详解C# 不能用于文件名的字符

    在 Windows 有一些字符是不能作为文件名,尝试重命名一个文件,输入/ 就可以看到windows 提示的不能作为文件名的字符,那么具体是包括哪些符号不能作为文件名呢?下面小编给大家介绍下
    2018-02-02

最新评论