关于Marshal 类的整理
在两个不同的实体(两个线程或者进程甚至机器、在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应用很多。
类别 | 成员 |
---|---|
高级封送处理 |
GetManagedThunkForUnmanagedMethodPtr、GetUnmanagedThunkForManagedMethodPtr、NumParamBytes |
COM 库函数 |
BindToMoniker、GetActiveObject |
COM 实用工具 |
ChangeWrapperHandleStrength、CreateWrapperOfType、GetComObjectData、GetComSlotForMethodInfo、GetEndComSlot、 GetMethodInfoForComSlot、GetStartComSlot、ReleaseComObject、SetComObjectData |
数据转换 |
托管到非托管:Copy、GetComInterfaceForObject、GetIDispatchForObject、GetIUnknownForObject、StringToBSTR、StringToCoTaskMemAnsi、 StringToCoTaskMemAuto、StringToCoTaskMemUni、StringToHGlobalAnsi、StringToHGlobalAuto、StringToHGlobalUni、StructureToPtr、 UnsafeAddrOfPinnedArrayElement 非托管到托管:Copy、GetObjectForIUnknown、GetObjectForNativeVariant、GetObjectsForNativeVariants、GetTypedObjectForIUnknown、 GetTypeForITypeInfo、PtrToStringAnsi、PtrToStringAuto、PtrToStringBSTR、PtrToStringUni 属性:SystemDefaultCharSize、SystemMaxDBCSCharSize |
直接读取和写入 | ReadByte、ReadInt16、ReadInt32、ReadInt64、ReadIntPtr、WriteByte、WriteInt16、WriteInt32、WriteInt64、WriteIntPtr |
错误处理 | COM:GetHRForException、ThrowExceptionForHR
Win32:GetLastWin32Error、GetExceptionCode、GetExceptionPointers 两者:GetHRForLastWin32Error |
承载实用工具 | GetThreadFromFiberCookie |
IUnknown | AddRef、QueryInterface、Release |
内存管理 | COM:AllocCoTaskMem、ReAllocCoTaskMem、FreeCoTaskMem、FreeBSTR
Win32:AllocHGlobal、ReAllocHGlobal、FreeHGlobal 两者:DestroyStructure |
平台调用实用工具 | Prelink、PrelinkAll、GetHINSTANCE |
结构检查 | OffsetOf、SizeOf |
类型信息 |
GenerateGuidForType、GenerateProgIdForType、GetTypeInfoName、GetTypeLibGuid、GetTypeLibGuidForAssembly、GetTypeLibLcid、 GetTypeLibName、IsComObject、IsTypeVisibleFromCom |
特别注意: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;
}