C#与C++交互之——参数传递

13 篇文章 1 订阅

出发点:最近在做C#、C++的交互,期间出现不少的问题,边学边做


以下是《精通.NET互操作》读书笔记,该书详细讲述了.Net与C/C++的交互技术


托管代码和非托管代码的交互技术有3种:平台调用(PInvoke)、C++ Interop、COM Interop


平台调用(PInvoke)


其中,PInvoke最简单,但只能调用函数,不能调用类。但有一个折衷的办法,就是在C++里面定义一系列函数,里面调用相应的类,暴露给调用方(托管语言)的只有一系列的函数接口(API)。PInvoke本质就是调用dll,dll里面包含一系列C/C++ 的API。


PInvoke的过程:

1、获取非托管函数的信息(查看dll的内容或者头文件,得到API);

2、在托管代码中声明非托管函数,设置PInovke的属性(如函数入口点);

3、在托管代码中直接调用上一步声明的函数。


代码:

C++中使用 extern "C" _declspec(dllexport) 定义API;C#中DllImport方法。


既然是调用函数,少不了的就是参数传递。但由于托管语言是基于CLR的,而非托管语言则是本机代码(Native code),两者存在很大的差别,如数据类型不一致。这时候,在托管语言这一方,需要进行数据封送处理。封送指的就是托管内存和非托管内存之间传递数据的过程。

封送是双向的,由封送拆收器完成,其主要任务是:

1、数据类型转换。非托管数据类型到托管数据类型的转换(输出),或者,托管数据类型到管数据类型的转换(输入);

2、内存搬运。非托管内存复制到托管内存,或者,托管内存复制到非托管内存;

3、内存释放。


结构体的数据封送


API以结构体作为输入参数或输出参数。

必要的工作:

1、非托管函数定义一个结构体;

2、托管函数定义一个“等价”的结构体。

所以,封送结构体最大的难点在于:确保结构体在托管和非托管中是一致的,也就是参数的数据类型转换。


实例:

C++中定义结构体

typedef struct _DEMOSTRUCT
{
	int a;
        short b;
        float c;
        double d;
}DEMOSTRUCT,*pDEMOSTRUCT;
C#中定义等价结构体

private struct ManagedDemoStruct
{
	public	int a;
	public	short b;
	public	float c;
	public	double d;
}

等价的含义是,除了字段的名称可以不一样以外,以下内容必须保持一致:

1、字段声明顺序;

2、字段的类型;

3、字段在内存中的大小。


C++定义API

void Func(DEMOSTRUCT m_demoStruct)
{

	printf("\n int = %d, short = %d, 
            float = $f, double = %f \n",
	    m_demoStruct.a,
	    m_demoStruct.b,
	    m_demoStruct.c,
	    m_demoStruct.d,
	    );

	m_demoStruct.a +=10; //将是无效的
}
编译得到 myDLL.dll


C#中调用API

...

[DllImport("myDLL.dll",
	EntryPoint = "Func")]
private extern static void myAPI(ManagedDemoStruct argStruct)

private static void myTest()
{
	ManagedDemoStruct demoStruct
		= new ManagedDemoStruct();
	demoStruct.a = 10;
	demoStruct.b = 20;
	demoStruct.c = 3.5f;
	demoStruct.d = 6.8f;
	
	myAPI(demoStruct);
}
...

运行一遍myAPI后,demoStruct.a的值将依然为10?

以上例子仅仅是值传递,不能修改结构体的内容,想要实现修改就要用到结构体指针。对上述例子进行修改,参数改为传结构体指针。

C++

void Func(pDEMOSTRUCT p_demoStruct)
{

	printf("\n int = %d, short = %d, 
            float = $f, double = %f \n",
	    p_demoStruct->a,
	    m_demoStruct->b,
	    m_demoStruct->c,
	    m_demoStruct->d,
	    );

	p_demoStruct->a +=10; //有效的修改
}

C#

[DllImport("myDLL.dll",
	EntryPoint = "Func")]
private extern static void myAPI(ref ManagedDemoStruct argStruct)  //ref


总结


对于含有结构体参数的API,必须先在C++和C#中分别定义一个等价的结构体;在定义托管结构体时,可能需要使用StructLayout属性来指定对象中的内存布局。正确地声明托管结构体,是封送的关键。


带有传出参数的API,要使用指针传递参数,在C#里要声明ref

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++C#中,结构体是一种用户自定义的数据类型,可以包含多个不同类型的成员变量。在函数调用中,通过传递结构体指针作为参数,可以实现对结构体的修改。 在C++中,可以通过使用指针作为函数参数来传递结构体。示例代码如下: ```cpp #include <iostream> struct MyStruct { int num; char ch; }; void ModifyStruct(MyStruct* ptr) { ptr->num = 10; ptr->ch = 'A'; } int main() { MyStruct myStruct; ModifyStruct(&myStruct); std::cout << "Modified struct: " << myStruct.num << ", " << myStruct.ch << std::endl; return 0; } ``` 在上述代码中,定义了一个名为`MyStruct`的结构体,在`ModifyStruct`函数中,通过传递结构体指针`ptr`,可以直接修改结构体的成员变量。 在C#中,结构体是值类型,传递结构体参数时会进行值拷贝。为了实现类似于C++中的指针参数传递,可以使用`ref`关键字。示例代码如下: ```csharp using System; struct MyStruct { public int num; public char ch; } class Program { static void ModifyStruct(ref MyStruct myStruct) { myStruct.num = 10; myStruct.ch = 'A'; } static void Main(string[] args) { MyStruct myStruct; ModifyStruct(ref myStruct); Console.WriteLine("Modified struct: {0}, {1}", myStruct.num, myStruct.ch); } } ``` 在上述代码中,通过`ref`关键字将结构体参数传递给`ModifyStruct`函数,可以直接修改结构体的成员变量。 总结来说,C++C#都支持通过结构体指针参数传递来修改结构体的值。在C++中,可以直接使用指针作为参数;而在C#中,需要使用`ref`关键字来实现类似的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值