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

來源:互聯網
上載者:User

標籤:

託管和非託管轉換新方法: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類型等概念做進一步的討論。


當Managed 程式碼需要和本地代碼互操作時,我們就進入了interop的領域。interop的情境形形色色,不變的是我們需要把資料從一個世界marshal到另一個世界。


在討論數組marshalling之前,請各位和我一起思考一個問題,什麼是數組?之所以要討論這個問題,原因在於不同的術語在不同的語境中含有不同的意 思。在使用c語言的時候,我認為數組就是一個指標。但是熟悉c#的朋友可能不同意我的觀點,數組是System.Array或者Object[]。我認為,這兩種回答都是出自語言領域的正確觀點。那麼如果有一個項目含有兩個模組,一個用本地代碼撰寫,另一個用Managed 程式碼撰寫,兩者之間的介面要求傳遞一個數組,這個”數組”包含著怎樣的語義呢?

我覺得有兩點是很重要的:


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) 【轉】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.