Marshal用法汇总
简介
(https://www.cnblogs.com/DragonX/p/3474251.html)
(https://www.cnblogs.com/lfls128/p/4939232.html)
在两个不同的实体(两个线程或者进程甚至机器、在Managed和Unmanaged之间、托管和非托管之间)进行方法调用和参数传递的时候,具体的调用方法和参数的内存格式可能需要一定的转换,这个转换的过程叫做Marshal。
Marshal就是把一个结构(类)序列化成一段内存,然后送到另一个进程(.net中Application domain)*另一个进程中的函数使用。
比如你的一个结构
struct{
Pen pen;
}s;
s是一个指向已有的Pen对象的引用,当你把s传给本进程中的一个函数f时,f可以很容易地找到pen的实际对象,但如果你把s传到另外一个进程时,甚至是另外一台机器上的进程时,这个进程就没办法找到pen的实际内容。Marshal技术则可以把pen对象中的所有实际内容按规则放到一个缓冲中,(所有的引用或指针都要转换成实际对象)然后把缓冲中的内容送到另一个进程,函数调用完成再用同样方式把结果返回来。
在RPC,Interop,COM中Marshal应用很多。
//特别注意:Marshal.PtrToStringAuto方法:分配托管 String,并从非托管内存中存储的字符串向其复制第一个空字符之前的所有字符。
例1、
public struct ImageDataMsg
{
public char DataType;
public int Srv_index;
public char ConvertType;
//这个个地方要指定长度,这样就可以得到结构体的正确长度了
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] VecLayer;//需要那几个图层。
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] GridLayer;//需要那几个栅格图层
public int Scale_index;//需要的是那个比例尺的图像
public int x_pos;
public int y_pos;
public int ClientArea_x;
public int ClientArea_y;
}
//使用这个方法将你的结构体转化为bytes数组
public static byte[] Struct2Bytes(ImageDataMsg obj)
{
int size = Marshal.SizeOf(obj);
byte[] bytes = new byte[size];
try
{
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(obj, ptr, false);
Marshal.Copy(ptr, bytes, 0, size);
Marshal.FreeHGlobal(ptr);
return bytes;
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
return bytes;
}
}
//使用这个方法将byte数组转化为结构体
public static object BytesToStuct2(byte[] bytes, ImageDataMsg type)
{
//得到结构体的大小
int size = Marshal.SizeOf(type);
//byte数组长度小于结构体的大小
if (size > bytes.Length)
{
//返回空
return null;
}
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将byte数组拷到分配好的内存空间
Marshal.Copy(bytes, 0, structPtr, size);
//将内存空间转换为目标结构体
object obj = Marshal.PtrToStructure(structPtr, typeof(ImageDataMsg));
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回结构体
return obj;
}
Marshal 类提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。
Marshal 类中有许多成员,但他们大多是公用事业援助COM代码进行互操作。
使用.NET,Visual Basic中有很大的权力,以至于处理在与 Windows 系统较低水平层次,并从外部库的非托管代码的工作。这权力是发现了三个新的工具:IntPtr的,NET的平台相关的内存地址代表性的GCHandle,从而你针和检索的管理内存堆的数据的地址; Marshal类的一站式。您的内存分配,清理,和操作的需要。
如果您决定与VB.NET直接内存操作,你需要了解的第一件事是IntPtr类型。
IntPtr 的是一个结构,它表示两个地址和处理(大多数处理 Windows 的指针的指针)。
IntPtr 的实例也依赖于平台的(或独立,取决于你的观点)。在32位系统,IntPtr 的是32位,而在64位系统 IntPtr 的是64位。这样做的好处是,你不需要更改或重新编译你的代码,或两个平台内的任何.NET Framework的公开的方式与地址和处理工作的功能。
使用 IntPtr 类型,这种功能是封送到非托管代码,仅仅作为内部地址编号,这意味着你可以通过一个 IntPtr 类型的变量的任何非托管代码的预计指针。因此,好消息是,虽然你不能在VB.NET中使用这样的: 如void * Dim MyPointer 这个工程精辟VB.NET:作为 IntPtr 的 DIM MyPointer
请注意,在Beta 1没有IntPtr类型??相反,工作指针和处理使用 Integer 类型。
IntPtr 类型有 ToInt32 方法的地址转换为一个整数,但可以将导致在64位系统的溢出异常。 IntPtr 也有 ToInt64 方法,但你必须保持跟踪的平台,如果你想要做的这些转换。StrPtr()和 VarPtr()NBSP在 VB.NET 这两个无证 VB 的6个功能变量的返回地址。相同的功能,可以在VB.NET的GCHandle类。
让我们来看看如何放到一个 IntPtr 变量的地址。您可以使用的 GCHandle 的类,它有一个 AddrOfPinnedObject 的方法返回一个变量一个IntPtr。
您必须"针"的数据,然后才可以得到这个地址。这可以防止不经意间移动数据的垃圾收集器,而你指的是原来的地址。这代码引脚一个变量,其地址显示在控制台窗口,并释放句柄:"昏暗的管理变量DIM MyString的字符串=阿德南塞缪尔? "针变量和创建"GC的处理实例昏暗的GH(MyString中,GCHandleType.Pinned)的GCHandle = GCHandle.Alloc "得到的变量的地址DIM AddrOfMyString作为IntPtr的= gh.AddrOfPinnedObject() Console.WriteLine(AddrOfMyString.ToString()) "*的处理,并脱离"变量gh.Free()说明AllocHGlobal和AllocCoTaskMem
上的本机堆内部使用GlobalAlloc函数分配内存AllocHGlobal; AllocCoTaskMem,这是类似的,而是使用的COM内存管理器(CoTaskMemAlloc来)。这两个函数有一个参数,分配的字节数。这两个函数返回一个IntPtr,新分配的缓冲区的基地址。释放内存,您使用的FreeHGlobal或FreeCoTaskMem方法,取决于分配方法您使用。这些函数有一个参数,新分配的缓冲区的分配函数返回地址。
使用WriteByte,WriteInt16,WriteInt32,或WriteInt64方法简单的数字数据写入到一个非托管的缓冲区。每个函数作为参数的写操作的目的地址和数值你想要写。这些功能也重载以允许可选的第三个参数指示从抵消所提供的地址,它可以是有用的,如果你试图填充数组元素或结构领域的内存,例如:"分配一些内存,并获得它的地址DIM MyPointer的IntPtr = Marshal.AllocHGlobal(4) "写入到该地址的数量255Marshal.WriteInt32(MyPointer,255) "一些更多的代码(调用"例如的非托管代码) "空闲内存Marshal.FreeHGlobal(MyPointer)
您也可以使用提供的地址,而不是你自己分配的非托管代码:DIM MyPointer作为IntPtr的新的IntPtr([插入整数地址_ 从这里非托管代码)] Marshal.WriteInt32(MyPointer,255)
反过来也有可能。你可以阅读简单的数字数据,从一个IntPtr地址使用的readByte ReadInt16,ReadInt32,并ReadInt64方法:DIM MyInteger为整数= Marshal.ReadInt32(MyPointer)字符串函数
阅读和写作字符串相似,读,写简单的数字数据,有一个小的例外:你不先分配内存,然后写一个字符串。相反,在非托管内存中创建一个字符串的行为分配的空间,并返回字符串的地址。写有七个方法:StringToBSTR,StringToCoTaskMemAnsi,StringToCoTaskMemUni,StringToCoTaskMemAuto,StringToHGlobalAnsi,StringToHGlobalUni和StringToHGlobalAuto。 StringToCoTaskMemxxx功能字符串数据写入到COM分配的内存,而StringToHGlobalxxx功能写入到本地的非托管堆。结束在ANSI的函数写单字节ANSI字符串。结束在统一的函数写双字节的Unicode字符串。功能,自动结束写入ANSI或Unicode字符串,这取决于操作系统:Windows 98和ME,NT为基础的平台(Windows NT 4.0中,2000年,和XP)的Unicode字符串的ANSI字符串。 StringToBSTR写一个自动化BSTR,这是类似于使用SysAllocString函数。这些函数接受一个字符串作为输入参数,并返回一个指针,得到的字符串:DIM MyStrPointer的IntPtr = Marshal.StringToHGlobalAuto(quot;您好Worldquot;)
四个读的方法呢??PtrToStringAnsi,PtrToStringUni,PtrToStringAuto,PtrToStringBSTR??在一个给定的地址读取数据,并创建一个托管String对象,其中包含的字符的副本。 PtrToStringAnsi如果非托管字符串是ANSI,PtrToStringUni如果非托管字符串是Unicode,或PtrToStringBSTR如果非托管字符串是BSTR类型。 PtrToStringAuto假定非托管字符串是一个Windows 98或ME系统的ANSI和Unicode的Windows NT 4.0,2000或XP平台上。 PtrToStringAuto实际上是调用PtrToStringAnsi或PtrToStringUni,取决于操作系统。每个函数重载接受一个可选的数字复制的字符。如果你没有提供的字符数,函数看起来为终止空字符:"复制整个字符串昏暗的MyString的字符串= Marshal.PtrToStringAuto(MyPointer)"复制前5个字符DIM MyString的字符串= Marshal.PtrToStringAuto(MyPointer,5)
拿着一个字符串要释放非托管内存缓冲区,可以调用FreeHGlobal或FreeCoTaskMem成员。要免费StringToBSTR创建一个BSTR,你叫FreeBSTR,这反过来又调用FreeSysString功能。 - ; StructureToPtr和PtrToStructure
你写的非托管内存结构(用户定义类型)使用的StructureToPtr方法。这种方法需要你有时间提前分配的内存缓冲区。它有三个参数:您要编写的结构,内存缓冲区的地址(IntPtr的),和一个删除标志。删除标志设置为True湿巾和释放任何现有的数据从缓冲区。这是非常重要的,因为你可能会导致内存泄漏不删除现有的缓冲空间。例如,如果结构的领域之一,是另一种结构或字符串的引用,数据被外地引用不会被释放,如果该参数设置为False。还要注意的是结构复制到非托管内存,使用特定的格式(你可以控制可选),和非托管的副本可能不像完全的托管表示。使用sizeof的方法来确定缓冲区所需的字节数:DIM MYVARIABLE为点MyVariable.X = 100MyVariable.Y = 250DIM MyPointer作为IntPtr的Marshal.AllocHGlobal(Marshal.SizeOf(MYVARIABLE))Marshal.StructureToPtr(MYVARIABLE,MyPointer,假)
使用PtrToStructure方法扭转的过程和读从非托管内存结构。您可以使用此方法可以作为一个函数返回一个结构的副本类型的基础上,或填补了结构参数作为一个子。请注意,PtrToStructure返回一个Object类型的引用,和Option Strict On时,您必须转换为结构类型(例如,所使用的CType):作为点DIM MyPointMyPoint = CTYPE(Marshal.PtrToStructure(MyPointer的GetType(点)),点)复制方法
读取和写入阵列中的数据,尤其是宝贵的,当你需要二进制数据流。 Copy方法读取和写入,根据您传递给它的参数。如果你想写入数据,你需要先分配一些缓冲区空间,就像你会使用字符串。接下来,您呼叫的复制方法,并通过数组本身,目的地址(IntPtr的分配),你要开始复制的元素的数组的索引,和缓冲区的大小:DIM MyData的(255)作为字节"在这里插入代码,以填补字节数组DIM BufferAddress的IntPtr = Marshal.AllocHGlobal(256)Marshal.Copy(MYDATA,0,BufferAddress,256)
要阅读从非托管内存中的数组,调用复制的方法,并通过缓冲区的地址(IntPtr的),你想从缓冲区中的数据填充数组,数组的索引,你要开始复制到了,你要复制的数据的大小:MyData的DIM(255)以字节 Marshal.Copy(BufferAddress,MYDATA,0,256)
Marshal类中有更多的方法,但他们大多是公用事业援助COM代码进行互操作。结论
这些功能也相当有助于封送处理以及asnbsp管理的数据; CopyMemory如非托管的内存也被称为RtlMoveMomry功能的一个很好的替代。
我总是乐于帮助,因此,如果您有任何疑问,或对我的文章建议,感到*。您还可以在MSN Messenger上达到我的屏幕名称千里马吗??
上一篇: shell的运算符
下一篇: 7-9 通讯录的录入与显示