/// <summary> /// Marshal a managed object to an unmanaged chunk of memory. If <paramref name="structure"/> is not CustomMarshalable this /// function is the same as <see cref="Marshal.StructureToPtr"/>. /// </summary> /// <param name="structure">A managed object to marshal to unmanaged memory.</param> /// <param name="ptr">Pointer to unmanaged memory. This must be allocated before call</param> /// <param name="fDeleteOld">Indicates whether to delete old memory first</param> public static void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld) { if (!IsCustomMarshalObject(structure)) { Marshal.StructureToPtr(structure, ptr, fDeleteOld); return; } // first check that the struct has the struct layout attribute StructLayoutAttribute sla = structure.GetType().StructLayoutAttribute; if (sla.IsDefaultAttribute() || sla.Value == LayoutKind.Auto) { throw new ArgumentException("Structure must have StructLayoutAttribute with LayoutKind Explicit or Sequential", "structure"); } // iterate through all struct fields, handling customs, and using Marshal.StructToPtr for others uint extraDataOffset = 0; uint structBase = (uint)ptr.ToInt32(); uint structSize = (uint)Marshal.SizeOf(structure); foreach (FieldInfo field in structure.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) { uint fieldLoc = structBase + (uint)Marshal.OffsetOf(structure.GetType(), field.Name); if (field.IsDefined(typeof(CustomMarshalAsAttribute), true)) { byte[] bytes; CustomMarshalAsAttribute attr = (CustomMarshalAsAttribute)(field.GetCustomAttributes(typeof(CustomMarshalAsAttribute), true)[0]); switch (attr.Value) { case CustomUnmanagedType.LPStr: string val = (string)field.GetValue(structure) + '\0'; bytes = Encoding.ASCII.GetBytes(val); break; case CustomUnmanagedType.LPWStr: val = (string)field.GetValue(structure) + '\0'; bytes = Encoding.Unicode.GetBytes(val); break; default: throw new NotSupportedException("Operation not yet supported"); } uint dataLoc = structBase + structSize + extraDataOffset; Marshal.WriteIntPtr(new IntPtr(fieldLoc), new IntPtr(dataLoc)); // write the raw bytes to dataLoc for (int i = 0; i < bytes.Length; i++, extraDataOffset++) { Marshal.WriteByte(new IntPtr(dataLoc + (uint)i), bytes[i]); } } else { Marshal.StructureToPtr(field.GetValue(structure), new IntPtr(fieldLoc), fDeleteOld); } } }
/// <summary> /// Returns the runtime size of a CustomMarshalable object. If object is not CustomMarshalable, this function /// is the same as <see cref="Marshal.SizeOf"/> /// </summary> /// <param name="o">Object to calculate runtime size of</param> /// <returns>Size in bytes of <paramref name="o"/></returns> public static int SizeOf(object o) { if (!IsCustomMarshalObject(o)) { return(SizeOf(o.GetType())); } int objectSize = Marshal.SizeOf(o); foreach (FieldInfo field in o.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) { if (!field.IsDefined(typeof(CustomMarshalAttribute), true)) { continue; } foreach (object attribute in field.GetCustomAttributes(typeof(CustomMarshalAttribute), true)) { int size = 0; CustomMarshalAsAttribute attr = (CustomMarshalAsAttribute)attribute; switch (attr.Value) { case CustomUnmanagedType.LPStr: string val = (string)field.GetValue(o) + '\0'; size = Encoding.ASCII.GetByteCount(val); break; case CustomUnmanagedType.LPWStr: val = (string)field.GetValue(o) + '\0'; size = Encoding.Unicode.GetByteCount(val); break; default: throw new NotSupportedException("Operation not yet supported by CustomMarshaller"); } objectSize += size; } } return(objectSize); }
/// <summary> /// Marshal an unmanaged to pointer to a managed object. If <paramref name="structureType"/> is not CustomMarshalable, this /// function is the same as <see cref="Marshal.StructureToPtr"/>. /// </summary> /// <param name="ptr">Pointer to unmanaged memory</param> /// <param name="structureType">Type of managed object to instantiate</param> /// <returns>Managed instance of <paramref name="structureType"/> type</returns> public static object PtrToStructure(IntPtr ptr, Type structureType) { if (ptr == IntPtr.Zero) { return(null); } if (structureType == null) { throw new ArgumentNullException("structureType"); } if (structureType.IsGenericType) { throw new ArgumentException("Structure type must be non-generic", "structureType"); } if (!IsCustomMarshalType(structureType)) { return(Marshal.PtrToStructure(ptr, structureType)); } StructLayoutAttribute sla = structureType.StructLayoutAttribute; if (sla.IsDefaultAttribute() || sla.Value == LayoutKind.Auto) { throw new ArgumentException("Structure must have StructLayoutAttribute with LayoutKind Explicit or Sequential", "structure"); } object structure = Activator.CreateInstance(structureType); uint structBase = (uint)ptr.ToInt32(); uint structSize = (uint)Marshal.SizeOf(structure); foreach (FieldInfo field in structureType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) { uint fieldLoc = structBase + (uint)Marshal.OffsetOf(structureType, field.Name); if (field.IsDefined(typeof(CustomMarshalAsAttribute), true)) { IntPtr extraDataLoc = Marshal.ReadIntPtr(new IntPtr(fieldLoc)); CustomMarshalAsAttribute attr = (CustomMarshalAsAttribute)(field.GetCustomAttributes(typeof(CustomMarshalAsAttribute), true)[0]); switch (attr.Value) { case CustomUnmanagedType.LPStr: field.SetValue(structure, Marshal.PtrToStringAnsi(extraDataLoc)); break; case CustomUnmanagedType.LPWStr: field.SetValue(structure, Marshal.PtrToStringUni(extraDataLoc)); break; default: throw new NotSupportedException("Operation not currently supported"); } } else { field.SetValue(structure, Marshal.PtrToStructure(new IntPtr(fieldLoc), field.FieldType)); } } return(structure); }