-
-
Save noncom/d6b39eb91f2051cb21dca56866d4bf0d to your computer and use it in GitHub Desktop.
csharp marshalling code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Dynamic; | |
using System.Runtime.InteropServices; | |
namespace Symmetry.Xenon.Model.Marshalling | |
{ | |
/** | |
* Base for Marshallers which need a buffer and a size - basically for wrapping binary data. | |
*/ | |
public abstract class CharArrayMarshaller : DualAllocationMarshaller | |
{ | |
public override object MarshalNativeToManaged(IntPtr pNativeData) | |
{ | |
if (pNativeData == IntPtr.Zero) | |
return null; | |
var array = (SizedArray) Marshal.PtrToStructure(pNativeData, typeof(SizedArray)); | |
if (array.Handle == IntPtr.Zero) | |
return null; | |
return UnmarshalArray(array); | |
} | |
public abstract override IntPtr MarshalManagedToNative(object ManagedObj); | |
protected override void FreeNativePointer(IntPtr pNativeData) | |
{ | |
DeleteSizedCharArray(pNativeData); | |
} | |
protected override void FreeManagedPointer(IntPtr pNativeData) | |
{ | |
Marshal.FreeHGlobal(pNativeData); | |
} | |
protected abstract object UnmarshalArray(SizedArray array); | |
public override int GetNativeDataSize() | |
{ | |
return IntPtr.Size; | |
} | |
[DllImport("Analytics.Model.dll", EntryPoint = "delete_sized_char_array", | |
CallingConvention = CallingConvention.Cdecl)] | |
private static extern void DeleteSizedCharArray(IntPtr pointer); | |
[StructLayout(LayoutKind.Sequential)] | |
protected struct SizedArray | |
{ | |
public IntPtr Handle; | |
public int Size; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Runtime.InteropServices; | |
using System.Threading; | |
namespace Symmetry.Xenon.Model.Marshalling | |
{ | |
/** | |
* Base for marshallers that need to free pointers allocated in manage code differently | |
* to pointers managed in native code. This is achieved by keeping a threadlocal set of | |
* pointers allocated during a call to MarshalManagedToNative. | |
*/ | |
public abstract class DualAllocationMarshaller : ICustomMarshaler | |
{ | |
private readonly ThreadLocal<HashSet<IntPtr>> _managedPointers = | |
new ThreadLocal<HashSet<IntPtr>>(() => new HashSet<IntPtr>()); | |
public abstract object MarshalNativeToManaged(IntPtr pNativeData); | |
public abstract IntPtr MarshalManagedToNative(object ManagedObj); | |
public void CleanUpNativeData(IntPtr pNativeData) | |
{ | |
if (pNativeData == IntPtr.Zero) | |
return; | |
if (_managedPointers.Value.Remove(pNativeData)) | |
{ | |
// allocated in managed code | |
FreeManagedPointer(pNativeData); | |
} | |
else | |
{ | |
// allocated in native code | |
FreeNativePointer(pNativeData); | |
} | |
} | |
public abstract void CleanUpManagedData(object ManagedObj); | |
public abstract int GetNativeDataSize(); | |
protected abstract void FreeNativePointer(IntPtr pNativeData); | |
protected abstract void FreeManagedPointer(IntPtr pNativeData); | |
protected void RegisterManagedPointer(IntPtr pNativeData) | |
{ | |
_managedPointers.Value.Add(pNativeData); | |
} | |
} | |
/** | |
* Base for marshallers that need to free pointers allocated in manage code differently | |
* to pointers managed in native code, while also keeping tracked of marshalled managed | |
* objects for additional cleanup. This is achieved by keeping a threadlocal dictionary of | |
* pointers allocated during a call to MarshalManagedToNative. | |
*/ | |
public abstract class TrackingDualAllocationMarshaller<TObj> | |
{ | |
private readonly ThreadLocal<IDictionary<IntPtr, TObj>> _managedPointers = | |
new ThreadLocal<IDictionary<IntPtr, TObj>>(() => new Dictionary<IntPtr, TObj>()); | |
public abstract object MarshalNativeToManaged(IntPtr pNativeData); | |
public abstract IntPtr MarshalManagedToNative(object ManagedObj); | |
public void CleanUpNativeData(IntPtr pNativeData) | |
{ | |
if (pNativeData == IntPtr.Zero) | |
return; | |
TObj obj; | |
if (_managedPointers.Value.TryGetValue(pNativeData, out obj)) | |
{ | |
_managedPointers.Value.Remove(pNativeData); | |
// allocated in managed code | |
FreeManagedPointer(pNativeData, obj); | |
} | |
else | |
{ | |
// allocated in native code | |
FreeNativePointer(pNativeData); | |
} | |
} | |
public abstract void CleanUpManagedData(object ManagedObj); | |
public abstract int GetNativeDataSize(); | |
protected abstract void FreeNativePointer(IntPtr pNativeData); | |
protected abstract void FreeManagedPointer(IntPtr pNativeData, TObj obj); | |
protected void RegisterManagedPointer(IntPtr pNativeData, TObj obj) | |
{ | |
_managedPointers.Value.Add(pNativeData, obj); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
using System.Threading; | |
namespace Symmetry.Xenon.Model.Marshalling | |
{ | |
/** | |
* Abstract base class for marshallers which need to hold a reference to a GCHandle for cleanup later. | |
* The marshalled data contains an additional pointer to the GCHandle at location -1. | |
* The GCHandle is freed during clearnup fo the managed pointer. | |
*/ | |
public abstract class GCHandleMarshaller<TMarsh, TObj> : DualAllocationMarshaller | |
where TMarsh : GCHandleMarshaller<TMarsh, TObj>, new() | |
{ | |
private static readonly ICustomMarshaler Instance = new TMarsh(); | |
private readonly GCHandleType _handleType; | |
protected GCHandleMarshaller(GCHandleType handleType) | |
{ | |
_handleType = handleType; | |
} | |
public static ICustomMarshaler GetInstance(string cookie) | |
{ | |
return Instance; | |
} | |
public override IntPtr MarshalManagedToNative(object ManagedObj) | |
{ | |
if (ManagedObj == null) | |
return IntPtr.Zero; | |
if (!(ManagedObj is TObj)) | |
throw new ArgumentException("Cannot marshal " + ManagedObj.GetType().Name + "!"); | |
TObj obj = (TObj) ManagedObj; | |
// Allocate enough space for the GCHandle pointer and the object. | |
IntPtr ptr = Marshal.AllocHGlobal(IntPtr.Size + AllocatedSize(obj)); | |
// Allocate a GCHandle for the object. | |
GCHandle gcHandle = GCHandle.Alloc(obj, _handleType); | |
// Write the pointer value to the allocated memory location | |
Marshal.WriteIntPtr(ptr, 0, GCHandle.ToIntPtr(gcHandle)); | |
// Advance past the stored GCHandle pointer, this is the pointer the client will see. | |
ptr += IntPtr.Size; | |
RuntimeHelpers.PrepareConstrainedRegions(); | |
try | |
{ | |
CopyData(ptr, obj); | |
} | |
catch | |
{ | |
// Make sure we clean up again if this fails. | |
FreeManagedPointer(ptr); | |
throw; | |
} | |
RegisterManagedPointer(ptr); | |
return ptr; | |
} | |
protected abstract int AllocatedSize(TObj obj); | |
protected abstract void CopyData(IntPtr dest, TObj obj); | |
protected override void FreeManagedPointer(IntPtr pNativeData) | |
{ | |
GCHandle gcHandle = GCHandle.FromIntPtr(Marshal.ReadIntPtr(pNativeData, -IntPtr.Size)); | |
TObj obj = (TObj) gcHandle.Target; | |
RuntimeHelpers.PrepareConstrainedRegions(); | |
try | |
{ | |
DoNativeCleanup(pNativeData, obj); | |
} | |
finally | |
{ | |
gcHandle.Free(); | |
Marshal.FreeHGlobal(pNativeData - IntPtr.Size); | |
} | |
} | |
protected abstract void DoNativeCleanup(IntPtr pNativeData, TObj obj); | |
public override int GetNativeDataSize() | |
{ | |
return IntPtr.Size; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Win32.SafeHandles; | |
using System; | |
namespace Symmetry.Xenon.Model.Handles | |
{ | |
public abstract class Handle : SafeHandleZeroOrMinusOneIsInvalid | |
{ | |
protected Handle() | |
: base(true) | |
{ | |
} | |
protected Handle(IntPtr handle, bool ownsHandle) | |
: base(ownsHandle) | |
{ | |
SetHandle(handle); | |
} | |
protected override bool ReleaseHandle() | |
{ | |
if (IsInvalid) | |
return false; | |
DeletePointer(); | |
handle = IntPtr.Zero; // In case this is called multiple times | |
return true; | |
} | |
/* | |
* You can't just have a method in C++ which takes a void* and deletes it so | |
* this needs to be overridden with the appropraite native method to call. | |
* The native method just needs to call delete on the pointer. | |
*/ | |
protected abstract void DeletePointer(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
using Symmetry.Xenon.Model.Marshalling; | |
namespace Symmetry.Xenon.Model.Handles | |
{ | |
/** | |
* Marshaller for arrays of SafeHandles and their | |
derivatives. | |
* Supports only managed to native for now - may need to be wrapped in a sized array. | |
*/ | |
public sealed class HandleArrayMarshaller : GCHandleMarshaller<HandleArrayMarshaller, SafeHandle[]> | |
{ | |
public HandleArrayMarshaller() | |
: base(GCHandleType.Normal) | |
{ | |
} | |
public override object MarshalNativeToManaged(IntPtr pNativeData) | |
{ | |
throw new NotImplementedException(); | |
} | |
protected override int AllocatedSize(SafeHandle[] obj) | |
{ | |
return obj.Length * IntPtr.Size; | |
} | |
protected override void CopyData(IntPtr dest, SafeHandle[] obj) | |
{ | |
bool success = false; | |
for (int i = 0; i < obj.Length; i++) | |
{ | |
SafeHandle handle = obj[i]; | |
if (handle != null && !handle.IsClosed && !handle.IsInvalid) | |
{ | |
// Add a reference count so that the handle does not get GCed. | |
handle.DangerousAddRef(ref success); | |
// Write the ith handle's pointer value to the allocated memory in position i | |
Marshal.WriteIntPtr(dest, i*IntPtr.Size, handle.DangerousGetHandle()); | |
} | |
} | |
} | |
public override void CleanUpManagedData(object ManagedObj) | |
{ | |
throw new NotImplementedException(); | |
} | |
protected override void FreeNativePointer(IntPtr pNativeData) | |
{ | |
throw new NotImplementedException(); | |
} | |
protected override void DoNativeCleanup(IntPtr pNativeData, SafeHandle[] obj) | |
{ | |
for (int i = 0; i < obj.Length; i++) | |
{ | |
SafeHandle handle = obj[i]; | |
if (handle != null && Marshal.ReadIntPtr(pNativeData, i*IntPtr.Size) != IntPtr.Zero) | |
{ | |
handle.DangerousRelease(); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Symmetry.Xenon.Model.Marshalling | |
{ | |
public class MarshallingException : Exception | |
{ | |
public MarshallingException(string message) | |
: base(message) | |
{ | |
} | |
public MarshallingException(object value, Type targetType, string reason) | |
: this(string.Format("Error marshalling {0} to type {1}: {2}", value, targetType, reason)) | |
{ | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using pb = Symmetry.Xenon.Model.Protobuf; | |
namespace Symmetry.Xenon.Model.Marshalling | |
{ | |
public static class Marshalling | |
{ | |
private static readonly DateTime StartTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified); | |
public static pb.XeDate Marshal(this DateTime dateTime) | |
{ | |
var year = dateTime.Year; | |
if (year < 1400 || year > 9999) | |
throw new MarshallingException(dateTime, typeof(pb.XeDate), "Year not in range 1400-9999."); | |
return new pb.XeDate | |
{ | |
Millis = (long) (dateTime - StartTime).TotalMilliseconds | |
}; | |
} | |
public static long ToTimestamp(this DateTime dateTime) | |
{ | |
return (long) (dateTime - StartTime).TotalMilliseconds; | |
} | |
public static DateTime Unmarshal(this pb.XeDate date) | |
{ | |
return StartTime.AddMilliseconds(date.Millis); | |
} | |
public static DateTime ToDate(this long date) | |
{ | |
return StartTime.AddMilliseconds(date); | |
} | |
public static pb.Schedule Marshal(this IEnumerable<DateTime> dates) | |
{ | |
var schedule = new pb.Schedule(); | |
schedule.Dates.Add(dates.Select(Marshal)); | |
return schedule; | |
} | |
public static pb.MonthSchedule Marshal(this IEnumerable<pb.YearMonth> months) | |
{ | |
var schedule = new pb.MonthSchedule(); | |
schedule.Months.Add(months); | |
return schedule; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
using Symmetry.Xenon.Model.Handles; | |
using static System.Runtime.InteropServices.CallingConvention; | |
namespace Symmetry.Xenon.Model.Marshalling | |
{ | |
public class CurrencyAmountArrayMarshaller : ValueArrayMarshaller<CurrencyAmountArrayMarshaller, CurrencyAmountImpl> | |
{ | |
protected override void FreeNativePointer(IntPtr pNativeData) | |
{ | |
DeleteCurrencyAmountArray(pNativeData); | |
} | |
[DllImport("Analytics.Model.dll", EntryPoint = "delete_sized_currency_amount_array", CallingConvention = Cdecl)] | |
private static extern void DeleteCurrencyAmountArray(IntPtr pointer); | |
} | |
/** | |
* Base for marshalling arrays of blittable types | |
*/ | |
public abstract class ValueArrayMarshaller : DualAllocationMarshaller | |
{ | |
protected static readonly int ArraySize = Marshal.SizeOf(typeof(SizedArray)); | |
protected override void FreeManagedPointer(IntPtr pNativeData) | |
{ | |
var array = (SizedArray)Marshal.PtrToStructure(pNativeData, typeof(SizedArray)); | |
Marshal.FreeHGlobal(array.Handle); | |
Marshal.FreeHGlobal(pNativeData); | |
} | |
public override void CleanUpManagedData(object ManagedObj) | |
{ | |
// No Op | |
} | |
public override int GetNativeDataSize() | |
{ | |
return ArraySize; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
protected struct SizedArray | |
{ | |
public IntPtr Handle; | |
public int Size; | |
} | |
} | |
public abstract class ValueArrayMarshaller<TMarsh, TObj> : ValueArrayMarshaller | |
where TMarsh : ValueArrayMarshaller<TMarsh, TObj>, new() | |
{ | |
private static readonly int ElementSize = Marshal.SizeOf(typeof(TObj)); | |
private static readonly ICustomMarshaler Instance = new TMarsh(); | |
public static ICustomMarshaler GetInstance(string cookie) | |
{ | |
return Instance; | |
} | |
public override object MarshalNativeToManaged(IntPtr pNativeData) | |
{ | |
if (pNativeData == IntPtr.Zero) | |
return null; | |
var array = (SizedArray) Marshal.PtrToStructure(pNativeData, typeof(SizedArray)); | |
if (array.Handle == IntPtr.Zero) | |
return null; | |
var result = new TObj[array.Size]; | |
var ptr = array.Handle; | |
for (var i = 0; i < array.Size; ++i) | |
{ | |
result[i] = (TObj) Marshal.PtrToStructure(ptr, typeof(TObj)); | |
ptr += ElementSize; | |
} | |
return result; | |
} | |
public override IntPtr MarshalManagedToNative(object ManagedObj) | |
{ | |
if (ManagedObj == null) | |
return IntPtr.Zero; | |
if (!(ManagedObj is TObj[])) | |
throw new ArgumentException("Cannot marshal " + ManagedObj.GetType().Name + "!"); | |
var managedArray = (TObj[]) ManagedObj; | |
var handle = Marshal.AllocHGlobal(managedArray.Length * ElementSize); | |
var elemPtr = handle; | |
foreach (var item in managedArray) | |
{ | |
Marshal.StructureToPtr(item, elemPtr, false); | |
elemPtr += ElementSize; | |
} | |
var array = new SizedArray | |
{ | |
Handle = handle, | |
Size = managedArray.Length | |
}; | |
var ptr = Marshal.AllocHGlobal(ArraySize); | |
Marshal.StructureToPtr(array, ptr, false); | |
return ptr; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment