托管和非托管转换新方法: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());
l 把非托管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;
}
数组作为一种常用的数据结构,各种高级语言都提供了相应的支持,在这些高级语言之间交互操作的时候,数组也是传送集合类型数据的重要结构,希望今天的内容能对大家有所帮助。
托管和非托管转换新方法:Marshaling Library(zz) 【转】的更多相关文章
- C# 托管和非托管混合编程
在非托管模块中实现你比较重要的算法,然后通过 CLR 的平台互操作,来使托管代码调用它,这样程序仍然能够正常工作,但对非托管的本地代码进行反编译,就很困难. 最直接的实现托管与非托管编程的方法就是 ...
- [转]C# 之DLL调用(托管与非托管)
每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍.首先,您需要了解什么是托管,什么是非托管.一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX ...
- C#的托管与非托管大难点
托管代码与非托管代码 众所周知,我们正常编程所用的高级语言,是无法被计算机识别的.需要先将高级语言翻译为机器语言,才能被机器理解和运行.在标准C/C++中,编译过程是这样的:源代码首先经过预处理器,对 ...
- NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配
在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...
- C#的三大难点之二:托管与非托管
相关文章: C#的三大难点之前传:什么时候应该使用C#?C#的三大难点之一:byte与char,string与StringBuilderC#的三大难点之二:托管与非托管C#的三大难点之三:消息与事件 ...
- Oracle Data Provider for .NET的使用(托管与非托管(一))
目录 简单的概述 简单的使用 非托管系统要求 托管驱动系统要求 其它的注意事项 ODP.NET版本说明 安装ODP.NET 安装非托管驱动 非托管驱动绿色配置 简单的概述 ODP.NET的含义是 Or ...
- 重学c#系列——c# 托管和非托管资源(三)
前言 c# 托管和非托管比较重要,因为这涉及到资源的释放. 现在只要在计算机上运行的,无论玩出什么花来,整个什么概念,逃不过输入数据修改数据输出数据(计算机本质),这里面有个数据的输入,那么我们的内存 ...
- [.net 面向对象程序设计进阶] (8) 托管与非托管
本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收.掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常. 1.什么是托 ...
- C# using 三种使用方式 C#中托管与非托管 C#托管资源和非托管资源区别
1.using指令.using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间,类似于Java的import,这个功能也是最常用的,几乎每个cs的程序都会用到. ...
随机推荐
- JAVA 基础--开发环境IDEA 搭建
1.下载IDEA (500M+) 2.激活. 在网站http://idea.lanyus.com/中获取注册码,填入Activation code中: 然后点击Activate即可. 3.创建工程前 ...
- Pycharm Django开发(一)设置开发环境
一 由于我是一个对开发环境有强迫症的人,在装完PYTHON 2.6 3.3 3.4中,在创建Django工程的时候,会出现N个版本的python,那么在这里可以设置你喜欢和要使用的版本.
- MySQL基本命令和常用数据库对象
MySQL基本命令: 连接远程主机的MySQL服务(为了保证安全性,执行下面命令时,可以省略-p后面的密码,执行命令后系统会提示输入密码) mysql -p 密码 -u 用户名 -h 主机地址 --d ...
- Oralce重做日志(Redo Log)
1.简介 Oracle引入重做日志的目的:数据库的恢复. Oracle相关进程:重做日志写进程(LGWR). 重做日志性质:联机日志文件,oracle服务器运行时需要管理它们. 相关数据字典:v$lo ...
- Spring core resourc层结构体系及JDK与Spring对classpath中资源的获取方式及结果对比
1. Spring core resourc层结构体系 1.1. Resource相关结构体系 1.2. ResourceLoader相关体系 2. JDK与Spring对classpath中资源的获 ...
- Ubuntu 深度炼丹环境配置
Ubuntu 深度炼丹环境配置 深度炼丹最麻烦的就是环境配置了,下面过程记录了本人进行深度炼丹环境的配置. 首先是安装图形显卡驱动,按如下指令进行即可 sudo add-apt-repository ...
- Leetcode 424.替换后的最长重复字符
替换后的最长重复字符 给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次.在执行上述操作后,找到包含重复字母的最长子串的长度. 注意:字符串长度 和 ...
- 2018省赛赛第一次训练题解和ac代码
第一次就去拉了点思维很神奇的CF题目 2018省赛赛第一次训练 # Origin Title A CodeForces 607A Chain Reaction B CodeForces ...
- java反射的基本使用
反射机制是java中非常重要的功能,熟练使用反射功能对实际的开发有很大的帮助. 一,通过反射获取对象实例 使用的对象User package whroid.java.reflect; public c ...
- 一步一步,完成sparkMLlib对日志文件的处理(1)
https://blog.csdn.net/u012834750/article/details/81014997 初学第一天,当然是完成helloWorld啦,有点艰难,2个小时,在idea, ...