託管和非託管轉換新方法:Marshaling Library(zz) 【轉】

託管和非託管轉換新方法:Marshaling Library(zz)

託管和非託管轉換新方法:Marshaling Library(zz)html

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

 

 

1.VC++2008中新增長的庫:Marshaling Libraryc#

咱們一塊兒討論一下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);post

一些轉換須要分配內存,並且必須隨後刪除。這樣的轉換須要一個叫作context的對象,這個對象在它再也不須要的時候就刪除了。好比從一個託管的String^轉換到本地的char*就須要一個context,由於在轉換過程當中生成了String的一個臨時的副本。代碼以下:url

marshal_context context;spa

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;
}

數組做爲一種經常使用的數據結構,各類高級語言都提供了相應的支持,在這些高級語言之間交互操做的時候,數組也是傳送集合類型數據的重要結構,但願今天的內容能對你們有所幫助。

相關文章
相關標籤/搜索