当前位置: 首页 > news >正文

托管和非托管转换新方法:Marshaling Library(zz) 【转】

托管和非托管转换新方法:Marshaling Library(zz)

托管和非托管转换新方法:Marshaling Library(zz)

http://hi.baidu.com/superql/blog/item/38e9c8073202fcc37a8947ac.html

 

 

1.VC++2008中新增加的库:Marshaling Library

我们一起讨论一下VC++2008中引入的新库——Marshaling Library。在这个类库之前我们使用的传统方法是固定指针(pin_ptr)。要使用Marshaling Library必须包含头文件<msclr/marshal.h>,使用命名空间msclr::interop,并使用marshal_as这个模板方法来执行转换,该模板方法需要两个参数:一是目标类型作为模板参数另一个就是这个方法的参数,就是要转换的对象

如果你要把一个const wchar_t* 类型转换成String^,你可以这样写:

const wchar_t* source;

String^ dest = marshal_as<String^>(source);

一些转换需要分配内存,而且必须随后删除。这样的转换需要一个叫做context的对象,这个对象在它不再需要的时候就删除了。比如从一个托管的String^转换到本地的char*就需要一个context,因为在转换过程中生成了String的一个临时的副本。代码如下:

marshal_context context;

const wchar_t* = context.marshal_as<const wchar_t*>(str);

在这个marshaling库中预定义好了很多转换,大部分都是字符串的类型转换。你还可以根据自己的需要,自行扩展。如果有兴趣的话,可以参考MSDN。

2.利用Marshaling Library进行互操作与以前的对比

通过一些具体的实例来看一看Marshaling Library给我们带来的便捷:

      以前的情况:在此之前我们是通过包含<vcclr.h>头文件,引用System::Runtime::InteropServices命名空间中的Marshal类的一些方法来进行类型的转换,这些方法一方面不容易记忆,另一方面转换不直接也容易出错。比如做字符串在托管和非托管之间进行转换:

l 把托管字符串转换成ANSI字符串

String^ s = gcnew String("sample string");

   IntPtr ip = Marshal::StringToHGlobalAnsi(s);

   const char* str = static_cast<const char*>(ip.ToPointer());

把非托管ANSI字符串转换成托管字符串

void ManagedStringFunc(s) {

String^ ms = Marshal::PtrToStringAnsi(static_cast<IntPtr>(s));

}

这里的s是char*的类型,如果是const char* 的话,还需要先去掉字符串变量的常量性

const char* tempString = const_cast<char*>(s);

因为static_const无法将const char*转换成System::IntPtr。

l   转换Unicode字符串的方式与上面类似,在将const wchar_t*转换成String^类型的时候也要先去掉其常量性。

      有了Marshal库以后:现在用Marshal库之后就可以marshal_as模板方法进行所有转换!

      marshal_as模板方法就提供了直接将ANSI,Unicode字符串包括进行托管和非托管转换的方法,记得要包含头文件和引用命名空间。示例代码如下:

#include <msclr/marshal.h>

using namespace msclr::interop;

l 把ANSI字符串char* ch转换成托管字符串

String^ s = marshal_as<String^>(ch);

l 把常量ANSI字符串const char* ch转换成托管字符串

String^ s = marshal_as<String^>(ch);

注意:这里就不再需要去除其常量性!

 

l 把托管字符串转换成非托管

    marshal_context context;

    return context.marshal_as<const char*>(str);

         注意:这时就需要一个context上下文对象

l Unicode字符串在托管和非托管类型之间的转换和ANSI字符串的转换类似。

    可以看出来,marshal_as不仅更加方便,还提供了更多的支持类型比如BSTR 和System::String^,std::string和System::String^等等。这就大大提高了开发人员的效率,使得转换信手拈来。

         如果想要扩展自定义类型,从本地到托管的话只需要实现下面一个模板方法即可:

namespace msclr {

   namespace interop {

      template<>

      inline TO marshal_as<TO, FROM> (const FROM& from) {

         // Insert conversion logic here, and return a TO parameter.

      }

   }

}

         如果需要从托管类型转换到本地类型,也是需要实现一个模板类,有兴趣的话可以参考msdn,便不在此赘述。

int intdat1[] = {1,2,3,4,5};
array<int>^ gcdat1 = gcnew array<int>(5);
Marshal::Copy((IntPtr)intdat1,gcdat1,0,5);

char* str = "abcdef";
array<Byte>^ byteArray =gcnew array<Byte>(6);
Marshal::Copy((IntPtr)str,byteArray,0,6);

         本文从数组的定义开始,介绍数组marshalling的三种方法,并对blittable类型等概念做进一步的讨论。


当托管代码需要和本地代码互操作时,我们就进入了interop的领域。interop的场景形形色色,不变的是我们需要把数据从一个世界marshal到另一个世界。


在讨论数组marshalling之前,请各位和我一起思考一个问题,什么是数组?之所以要讨论这个问题,原因在于不同的术语在不同的语境 中含有不同的意 思。在使用c语言的时候,我认为数组就是一个指针。但是熟悉c#的朋友可能不同意我的观点,数组是System.Array或者Object[]。我认 为,这两种回答都是出自语言领域的正确观点。那么如果有一个项目含有两个模块,一个用本地代码撰写,另一个用托管代码撰写,两者之间的接口要求传递一个数 组,这个”数组”包含着怎样的语义呢?

我觉得有两点是很重要的:


1. 如何访问数组元素。就好比c语言中的数组指针,c#中的数组引用,都是访问数组必不可少的线索。
2. 数组的大小。数组的大小不仅仅是System.Array.Length。它还可以包括诸如数组的维数,每个维上的启始边界和结束边界。


.NET在marshal数组的时候,很大程度上也是从以上两点出发,架起托管世界和本地代码之间的桥梁。根据操作的具体数据类型不同,数组marshal又可以分为以下两个大类,三个小类,我们分别介绍:


1. 数组作为参数传递
a) c/c++类型的数组
c类型的数组,也就是由指针指明存储空间首地址的数组,是一个自描述很 低的数据结构。尽管有些编译器支持在固定偏移量上写入数组的长度,但是因为各个编译 器处理的具体方法不同,没有一个标准让CLR来参考。所以我们在marshal一个c类型的数组的时候,不得不用其他方法考虑传递数组的大小,有以下两 种:

1) 约定指针数组长度
这种方法需要调用者和被调用者之间有一个约定,给出一个数组长度的固定值。在托管端声明一个interop方法的时候,只需要用SizeConst这个属性,把这个约定告诉CLR。CLR在进行Marshal的时候,会根据这个值,在本地堆上分配相应的空间。

public static extern void Ex(
[In, Out][MarshalAs(UnmanagedType.LPArray, SizeParamConst=3)]string[] a);


2)通过一个额外的参数指定数组长度
可能有的朋友觉得,约定一个静态的数组长度还不够灵活,希望能够动态的传递数组的长度,从而使 得数组marshalling不必受制于固定数组长度的限制。我们先来看普通的函数调用是如何解决这个问题的:caller和callee通过约定一个额 外的参数,来传递数组的长度,这可以被视作是一个调用者和被调用者的约定。由于marshalling需要CLR的参与,那么就需要把这个约定用CLR能 够理解的方式,进行扩充,形成一个三方约定。

CLR用属 性SizeParamIndex来描述此类约定。

public static extern void Ex2(
[In, Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]string[] a,
int len);


b) SafeArray
SafeArray是COM引入的数据类型,是一个自描述度很高的数据结构。他可以很清楚的告诉用户,该 数组的元素类型,数组包含了多少维,每一维的起始 位置和终止位置。所以marshal这类safearray的时候,只需要通过设定属性,告诉CLR,当前array对应的本地代码是safearray 即可。举例如下:


public void DumpSafeArrayStringIn(                                                         [In][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)]Object[] array);

大家可以看到,SafeArraySubType可以用来指定数组元素的类型


2. 数组作为字段传递
很久以来,对于interop,一直有这样的评价,简单数据结构的marshalling其实并不复杂,但 是一旦进入了struct或者class这种你 中有我,我中有你的层叠数据结构之后,marshalling就成了bug的温床。所以在这里,我们也要提提数组作为struct/class的一个字段 的方法。在这里首先要给这个stuct/class加一个限制,是byval。由于这个限制,大家可以想象的出,CLR在marshal的时候,做的事情 是类似于浅copy的内存复制,所以对数组marshal的时候,也就只支持固定长度的数组marshal。

public class StructIntArray
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public int[] array;
}


数组作为一种常用的数据结构,各种高级语言都提供了相应的支持,在这些高级语言之间交互操作的时候,数组也是传送集合类型数据的重要结构,希望今天的内容能对大家有所帮助。

相关文章:

  • 为什么要重写equals()方法与hashCode()方法
  • 【烈日炎炎战后端】计算机网络(4.2万字)
  • linux中c语言errno的使用
  • 【烈日炎炎战后端】操作系统(1.1万字)
  • for while (list each)的用法
  • 【烈日炎炎战后端】设计模式(1.1万字)
  • 【烈日炎炎战后端】 数据结构(0.7万字)
  • JavaScript学习总结——原型
  • 2的幂在约瑟夫环问题的应用
  • 【烈日炎炎战后端】MySQL理论(2.8万字)
  • Mysql5.6主从复制
  • 【烈日炎炎战后端】MySQL编程(3.6万字)
  • 【Mongodb】Master-Slave 复制
  • 解决前端文件修改后浏览器页面未更新的问题
  • 【烈日炎炎战后端】Redis(6.1万字)
  • codis proxy处理流程
  • conda常用的命令
  • emacs初体验
  • gcc介绍及安装
  • Java程序员幽默爆笑锦集
  • js算法-归并排序(merge_sort)
  • PHP变量
  • spring cloud gateway 源码解析(4)跨域问题处理
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 汉诺塔算法
  • 基于遗传算法的优化问题求解
  • 力扣(LeetCode)22
  • 前嗅ForeSpider采集配置界面介绍
  • 十年未变!安全,谁之责?(下)
  • 数据结构java版之冒泡排序及优化
  • 消息队列系列二(IOT中消息队列的应用)
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 智能合约Solidity教程-事件和日志(一)
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • # Maven错误Error executing Maven
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (1)Nginx简介和安装教程
  • (LeetCode 49)Anagrams
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • .htaccess配置重写url引擎
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Core中Emit的使用
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • /usr/bin/env: node: No such file or directory
  • @Responsebody与@RequestBody
  • [ 云计算 | AWS ] 对比分析:Amazon SNS 与 SQS 消息服务的异同与选择
  • []指针
  • [ACL2022] Text Smoothing: 一种在文本分类任务上的数据增强方法
  • [android] 请求码和结果码的作用
  • [AR Foundation] 人脸检测的流程
  • [BetterExplained]书写是为了更好的思考(转载)