C# - Marshal 学习总结
生活随笔
收集整理的這篇文章主要介紹了
C# - Marshal 学习总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C#——Marshal.StructureToPtr方法簡介
http://blog.csdn.net/livelylittlefish/article/details/2423764? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
具體可以參考http://msdn.microsoft.com。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
Marshal.StructureToPtr方法簡介
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
1. 功能及位置
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
將數據從托管對象封送到非托管內存塊,屬于.NET Framework 類庫
命名空間:System.Runtime.InteropServices
程序集:mscorlib(在 mscorlib.dll 中)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
2. 語法
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
C#:
? ? ? [ComVisibleAttribute(true)] public static void StructureToPtr (Object?
structure,IntPtr ptr,bool fDeleteOld);
C++:
? ? ? [ComVisibleAttribute(true)]public: static void StructureToPtr (Object^ structure,?IntPtr ptr, bool fDeleteOld);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
3. 參數說明
structure:托管對象,包含要封送的數據。該對象必須是格式化類的實例。
ptr:指向非托管內存塊的指針,必須在調用此方法之前分配該指針。
fDeleteOld:設置為 true 可在執行Marshal.DestroyStructure方法前對 ptr 參數調用此方法。請注意
,傳遞 false 可導致內存泄漏。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
4. 異常
異常類型:ArgumentException
條件:structrue參數是泛型類型
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
5. 備注
StructureToPtr將結構的內容復制到 ptr 參數指向的預分配內存塊。如果 fDeleteOld 參數為 true,
則使用嵌入指
針上適當的刪除 API 來刪除最初由 ptr 指向的緩沖區,但該緩沖區必須包含有效數據。此方法為在鏡
像托管類中指
定的每個引用字段執行清理工作。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
假設 ptr 指向非托管內存塊。此內存塊的布局由相應的托管類 structure 描述。StructureToPtr將字
段值從結構封
送到指針。假設 ptr 塊包含引用字段,該字段指向當前包含“abc”的字符串緩沖區。假設托管端上相
應的字段是包含“vwxyz”的字符串。如果不另行通知它,StructureToPtr將分配一個新的非托管緩沖區
來保存“vwxyz”,并將它掛鉤到 ptr 塊。這將丟棄舊緩沖區“abc”使之漂移而不將其釋放回非托管堆
。最后,您將得到一個孤立的緩沖區,它表示在代碼中存在內存泄漏。如果將 fDeleteOld 參數設置為
真,則 StructureToPtr 在繼續為“vwxyz”分配新緩沖區之前釋放保存“abc”的緩沖區。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
6. 舉例
定義PERSON結構,并將該結構的一個變量拷貝到非托管內存,再將該內存中的PERSON還原為PERSON對象
,觀察其內容的變化。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
源代碼如下:
using System; using System.Text; using System.Runtime.InteropServices;namespace testStructureToPtr {public static class define //define some constant{ public const int MAX_LENGTH_OF_IDENTICARDID = 20; //maximum length of identicardidpublic const int MAX_LENGTH_OF_NAME = 50; //maximum length of namepublic const int MAX_LENGTH_OF_COUNTRY = 50; //maximum length of countrypublic const int MAX_LENGTH_OF_NATION = 50; //maximum length of nationpublic const int MAX_LENGTH_OF_BIRTHDAY = 8; //maximum length of birthdaypublic const int MAX_LENGTH_OF_ADDRESS = 200; //maximum length of address}public struct PERSON //person structure{//MarshalAs:指示如何在托管代碼和非托管代碼之間封送數據//UnmanagedType:指定如何將參數或字段封送到非托管內存塊[MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]public byte[] identicardid;[MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NAME)]public byte[] name;[MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_COUNTRY)]public byte[] country;[MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NATION)]public byte[] nation;[MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_BIRTHDAY)]public byte[] birthday;[MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_ADDRESS)]public byte[] address;}class testProgram{private static byte _fillChar = 0; //the fill character//convert string to byte array in Ascii with length is len public static byte[] CodeBytes(string str, int len){if (string.IsNullOrEmpty(str)){str = string.Empty;}byte[] result = new byte[len];byte[] strBytes = Encoding.Default.GetBytes(str);//copy the array converted into result, and fill the remaining bytes with 0for (int i = 0; i < len; i++)result[i] = ((i < strBytes.Length) ? strBytes[i] : _fillChar);return result;}//show the person informationpublic static void ShowPerson(PERSON person){Console.WriteLine("cardid :" + Encoding.ASCII.GetString (person.identicardid));Console.WriteLine("name :" + Encoding.ASCII.GetString(person.name));Console.WriteLine("country :" + Encoding.ASCII.GetString(person.country));Console.WriteLine("nation :" + Encoding.ASCII.GetString(person.nation));Console.WriteLine("birthday :" + Encoding.ASCII.GetString(person.birthday));Console.WriteLine("address :" + Encoding.ASCII.GetString(person.address));}static void Main(string[] args){PERSON person;person.identicardid = CodeBytes("123456198001011111", define.MAX_LENGTH_OF_IDENTICARDID);person.name = CodeBytes("jackson", define.MAX_LENGTH_OF_NAME);person.country = CodeBytes("China", define.MAX_LENGTH_OF_COUNTRY);person.nation = CodeBytes("HanZu", define.MAX_LENGTH_OF_NATION);person.birthday = CodeBytes("19800101", define.MAX_LENGTH_OF_BIRTHDAY);person.address = CodeBytes("Luoshan Road, Shanghai", define.MAX_LENGTH_OF_ADDRESS);int nSizeOfPerson = Marshal.SizeOf(person);IntPtr intPtr = Marshal.AllocHGlobal(nSizeOfPerson);Console.WriteLine("The person infomation is as follows:");ShowPerson(person);try{//將數據從托管對象封送到非托管內存塊,該內存塊開始地址為intPtrMarshal.StructureToPtr(person, intPtr, true);//將數據從非托管內存塊封送到新分配的指定類型的托管對象anotherPersonPERSON anotherPerson = (PERSON)Marshal.PtrToStructure(intPtr, typeof (PERSON));Console.WriteLine("The person after copied is as follows:");ShowPerson(anotherPerson);}catch (ArgumentException){throw;}finally{Marshal.FreeHGlobal(intPtr); //free tha memory}}} }
運行過程中的對象地址及其內容如下:
intPtr指向的內存塊的內容就是程序中對person對象所賦的初值,如下圖所示,共計378個字節:
對象person和another的地址及其identicardid成員的地址:
對象person的identicardid成員的內容,即程序中的值123456198001011111,最后的2個字節為0,圖中
顯示的是20個元素的ASCII碼的16進制數值:
運行結果如下:
========
c#中Marshal.Copy()方法的使用
http://blog.csdn.net/xiaobai1593/article/details/7065955c#中Marshal.Copy方法的使用
Marshal.copy()方法用來在托管對象(數組)和非托管對象(IntPtr)之間進行內容的復制
函數有很多重載,如下所示:
Copy(array<Byte>[]()[], Int32, IntPtr, Int32) 將一維的托管 8 位無符號整數數組中的數據復制到
非托管內存指針。
Copy(array<Char>[]()[], Int32, IntPtr, Int32) 將數據從一維的托管字符數組復制到非托管內存指
針。
Copy(array<Double>[]()[], Int32, IntPtr, Int32) 將數據從一維的托管雙精度浮點數組復制到非托
管內存指針。
Copy(array<Int16>[]()[], Int32, IntPtr, Int32) 將數據從一維的托管 16 位帶符號整數數組復制到
非托管內存指針。
Copy(array<Int32>[]()[], Int32, IntPtr, Int32) 將數據從一維的托管 32 位帶符號整數數組復制到
非托管內存指針。
Copy(array<Int64>[]()[], Int32, IntPtr, Int32) 將數據從一維的托管 64 位帶符號整數數組復制到
非托管內存指針。
Copy(IntPtr, array<Byte>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管 8 位無符號整
數數組。
Copy(IntPtr, array<Char>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管字符數組。
Copy(IntPtr, array<Double>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管雙精度浮點
數組。
Copy(IntPtr, array<Int16>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管 16 位帶符號
整數數組。
Copy(IntPtr, array<Int32>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管 32 位帶符號
整數數組。
Copy(IntPtr, array<Int64>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管 64 位帶符號
整數數組。
Copy(IntPtr, array<IntPtr>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管 IntPtr 數
組。
Copy(IntPtr, array<Single>[]()[], Int32, Int32) 將數據從非托管內存指針復制到托管單精度浮點
數組。
Copy(array<IntPtr>[]()[], Int32, IntPtr, Int32) 將數據從一維托管 IntPtr 數組復制到非托管內
存指針。
Copy(array<Single>[]()[], Int32, IntPtr, Int32) 將數據從一維的托管單精度浮點數組復制到非托
管內存指針。
這里需要注意的是兩個參數Int32類型的使用
Int32類型的兩個參數都是用來限定數組的,其中一個限定開始位置,一個限定長度
注意:長度是指數組元素的個數,而不是指字節數
示例:
string name = "xuwei"; ?
IntPtr pName = Marshal.AllocHGlobal(2*name.Length); ?
Marshal.Copy(name.ToCharArray(), 0, pName, name.Length); ?
char[] cName = new char[name.Length]; ?
Marshal.Copy(pName, cName, 0, name.Length); ?
易知name.Length=5
(1) 給pName指針分配了2*name.Length字節的空間
注意:Marshal.AllocHGlobal(Int32 cb)中的參數cb是分配的字節數
(2) 將name轉換的char[]中的內容復制到pName所指的內存中,所取長度為char的個數,即name.Length
(3) 給cName分配name.Length個char位置
(4) 將pName中的內容復制到cName數組中,長度同樣為name.Length
========
Marshal 類
https://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.marshal(VS.80).aspx.NET Framework 2.0 其他版本?
提供了一個方法集,這些方法用于分配非托管內存、復制非托管內存塊、將托管類型轉換為非托管類型
,此外還提供了在與非托管代碼交互時使用的其他雜項方法。
命名空間:System.Runtime.InteropServices
程序集:mscorlib(在 mscorlib.dll 中)
語法
C#C++VB
public static class Marshal
J#
public final class Marshal
JScript
public final class Marshal
備注
Marshal 類中定義的 static 方法對于處理非托管代碼至關重要。此類中定義的大多數方法通常由需要
在托管和非托管編程模型之間提供橋梁的開發人員使用。例如,StringToHGlobalAnsi 方法將 ANSI 字
符從指定的字符串(在托管堆中)復制到非托管堆中的緩沖區。該方法還分配大小正確的目標堆。
公共語言運行庫提供了特定的封送處理功能。有關封送處理行為的詳細信息,請參見 互操作封送處理。
示例
下面的代碼示例演示如何使用 Marshal 類所定義的各個方法。
C#C++
using System;
using System.Text;
using System.Runtime.InteropServices;
public struct Point
{
? ? public Int32 x, y;
}
public sealed class App
{
? ? static void Main()
? ? {
? ? ? ? // Demonstrate the use of public static fields of the Marshal class.
? ? ? ? Console.WriteLine("SystemDefaultCharSize={0}, SystemMaxDBCSCharSize={1}",
? ? ? ? ? ? Marshal.SystemDefaultCharSize, Marshal.SystemMaxDBCSCharSize);
? ? ? ? // Demonstrate the use of the SizeOf method of the Marshal class.
? ? ? ? Console.WriteLine("Number of bytes needed by a Point object: {0}",?
? ? ? ? ? ? Marshal.SizeOf(typeof(Point)));
? ? ? ? Point p = new Point();
? ? ? ? Console.WriteLine("Number of bytes needed by a Point object: {0}",
? ? ? ? ? ? Marshal.SizeOf(p));
? ? ? ??
? ? ? ? // Demonstrate how to call GlobalAlloc and?
? ? ? ? // GlobalFree using the Marshal class.
? ? ? ? IntPtr hglobal = Marshal.AllocHGlobal(100);
? ? ? ? Marshal.FreeHGlobal(hglobal);
? ? ? ? // Demonstrate how to use the Marshal class to get the Win32 error?
? ? ? ? // code when a Win32 method fails.
? ? ? ? Boolean f = CloseHandle(new IntPtr(-1));
? ? ? ? if (!f)
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("CloseHandle call failed with an error code of: {0}",?
? ? ? ? ? ? ? ? Marshal.GetLastWin32Error());
? ? ? ? } ?
? ? }
? ? // This is a platform invoke prototype. SetLastError is true, which allows?
? ? // the GetLastWin32Error method of the Marshal class to work correctly. ? ?
? ? [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
? ? static extern Boolean CloseHandle(IntPtr h);
? ??
}
// This code produces the following output.
//?
// SystemDefaultCharSize=2, SystemMaxDBCSCharSize=1
// Number of bytes needed by a Point object: 8
// Number of bytes needed by a Point object: 8
// CloseHandle call failed with an error code of: 6
繼承層次結構
System.Object?
? System.Runtime.InteropServices.Marshal
線程安全
此類型的任何公共靜態(Visual Basic 中的 Shared)成員都是線程安全的,但不保證所有實例成員都
是線程安全的。
平臺
Windows 98、Windows 2000 SP4、Windows CE、Windows Millennium Edition、Windows Mobile for?
Pocket PC、Windows Mobile for Smartphone、Windows Server 2003、Windows XP Media Center?
Edition、Windows XP Professional x64 Edition、Windows XP SP2、Windows XP Starter Edition
.NET Framework 并不是對每個平臺的所有版本都提供支持。有關受支持版本的列表,請參見系統要求。
版本信息
.NET Framework
受以下版本支持:2.0、1.1、1.0
.NET Compact Framework
受以下版本支持:2.0、1.0
請參見
參考
Marshal 成員
System.Runtime.InteropServices 命名空間
========
Marshal在C#中的應用(void *指針到IntPtr的轉化)
http://www.cnblogs.com/zhongxg/archive/2013/03/18/2965301.html? C#調用C語言的API時一般把void *指針轉換成IntPtr,但這經常遠遠不夠的。在C語言中void *是個萬
金油,尤其是一些老的c語言程序,所有的參數就一個void*指針,里面包羅萬象,然后在程序中來一個
switch,甚至多個switch來處理不同的參數。最近筆者就碰到了這個問題,不得不來研究一下怎么把void?
*指針轉換成IntPtr。
1.void *指針到IntPtr的簡單轉化。
c語言函數原型:
int SetConfig(int type, void *p);
這里假設p的所傳遞的參數式是結構體A:
struct A ? ? ? ? ? ?
{
? ? wchar_t osdbuffer[100]; ? ? ? ? ? ??
? ? unsigned short ix; ? ? ? ? ? ? ? ? ? ? ?
? ? unsigned short iy; ? ? ? ? ? ? ? ? ? ? ?
};
那么在C#中原型可以定義如下:
int SetConfig(int type, IntPtr p);
結構體A?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
? ? ? ? public struct A {
? ? ? ? ? ? [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
? ? ? ? ? ? public string osdbuffer; ? ? ? ?
? ? ? ? ? ? public ushort ix; ? ? ? ? ? ? ? ? ? ? ? //顯示坐標x
? ? ? ? ? ? public ushort iy; ? ? ? ? ? ? ? ? ? ? ? //顯示坐標y
? ? ? ? }
注意這里的CharSet,它由c中wchar_t決定的,如果c程序編譯時使用Unicode,這里就用
CharSet.Unicode,否則使用CharSet.Ansi。關于字符串的編碼問題如果不懂可以去網上查一下。至于怎
么知道C語言是用Unicode還是Ansi編譯,我是經過調用它的API測試出來的,調用成功了就說明他的編碼
和我的調用代碼一致。
這里還有一個很重要的問題,那就是內存在編譯時的分配問題。一般默認情況下,內存的分配是
4byte的整數倍,在這里我省略了,但為了便于理解,補充一下。結構體A完整一點的定義:(注意Pack的
值)
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode,Pack = 4)]
? ? ? ? public struct A {
? ? ? ? ? ? [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
? ? ? ? ? ? public string osdbuffer; ? ? ? ?
? ? ? ? ? ? public ushort ix; ? ? ? ? ? ? ? ? ? ? ? //顯示坐標x
? ? ? ? ? ? public ushort iy; ? ? ? ? ? ? ? ? ? ? ? //顯示坐標y
? ? ? ? }
c語言是用的是非托管代碼,c#使用的是托管代碼,c#的調用代碼如下:
復制代碼
? ? A s_a = new A();
int lenght = Marshal.SizeOf(s_a);
? ? IntPtr pA= Marshal.AllocHGlobal(lenght);
? ? Marshal.StructureToPtr(s_a, pA, true);
? ? int type = 1;
? ? int ret = SetConfig( type, pA);
? ? Marshal.FreeHGlobal(pA);
復制代碼
2.void *指針到IntPtr的復雜轉化。
在這里結構體A變得復雜一點,如果它內部包含一個指向另一個結構體B的指針
復制代碼
struct A ? ? ? ? ? ?
{
? ? wchar_t osdbuffer[100]; ? ? ? ? ? ?
? ? unsigned short ix; ? ? ? ? ? ?
? ? unsigned short iy;、
? ? B *pB; ? ? ? ? ? ?
};
struct B ? ? ? ? ? ?
{
? ? wchar_t title[20]; ? ??
};
復制代碼
在C#中你要做的也就稍微復雜一點,也就是說你不但要為A分配內存,也要為B分配內存
復制代碼
B s_b = new B();
//賦值省略
int lenght1 = Marshal.SizeOf(s_b);
? ? IntPtr pB= Marshal.AllocHGlobal(lenght1);
? ? Marshal.StructureToPtr(s_b, pB, true);
A s_a = new A();
s_a.pB = pB;
//其他賦值
//
int lenght2 = Marshal.SizeOf(s_a);
IntPtr pA= Marshal.AllocHGlobal(lenght2);
? ? Marshal.StructureToPtr(s_a, pA, true);
? ? int type = 1;?
? ? int ret = SetConfig( type, pA);
? ? Marshal.FreeHGlobal(pB);
? ? Marshal.FreeHGlobal(pA);
復制代碼
萬變不離其宗,只要掌握了原理,不管void *指針傳遞的參數有多么復雜,都可以搞定。
========
C# struct class 在Marshal.SizeOf 的區別
http://blog.csdn.net/norsd/article/details/7057049struct 和 class 的區別
最重要的就是 value 和 object 區別了.
但是最近發現另外一個有趣的現象,但是很重要.
一個工程中,因為需要各種結構,于是在開始使用了 struct , 但是覺得有點麻煩,值傳遞的話,消耗很多,
又不喜歡 ref (因為我喜歡寫泛型函數)
于是采用了 class , 接下來順風順水 , 直到.....
[csharp] view plain copy
[Serializable] // 指示可序列化 ?
?[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] //平鋪結構;Ansi字
符;結構成員各按1字節對齊 ?
?[RdMsgId(RDMSGID.E_FID_MC_MARKETINFO)] ?
?class MarketInfo ?
?{ ?
? ? ?[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] ?
? ? ?public MarketDataTick[] MarketDataTicks; ?
?} ?
照理說,Marshal.SizeOf( MarketInfo ) 應該是等于 sizeof(MarketDataTick) *5?
可是實際卻是: 4*5 = 20?
為什么? 因為 MarketDataTick 這個結構被設置為class , 改成 struct 就好辦了!
========
總結
以上是生活随笔為你收集整理的C# - Marshal 学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 大型对象堆学习总结
- 下一篇: VC++对话框学习总结