using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices.ComTypes;
+ using System.StubHelpers;
[Serializable]
public enum CustomQueryInterfaceMode
//====================================================================
// Read from memory
//====================================================================
- public static byte ReadByte([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs)
+ public static byte ReadByte(Object ptr, int ofs)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ return ReadValueSlow(ptr, ofs, (IntPtr nativeHome, int offset) => Marshal.ReadByte(nativeHome, offset));
}
public static unsafe byte ReadByte(IntPtr ptr, int ofs)
return ReadByte(ptr, 0);
}
- public static short ReadInt16([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs)
+ public static short ReadInt16(Object ptr, int ofs)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ return ReadValueSlow(ptr, ofs, (IntPtr nativeHome, int offset) => Marshal.ReadInt16(nativeHome, offset));
}
public static unsafe short ReadInt16(IntPtr ptr, int ofs)
return ReadInt16(ptr, 0);
}
- public static int ReadInt32([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs)
+ public static int ReadInt32(object ptr, int ofs)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ return ReadValueSlow(ptr, ofs, (IntPtr nativeHome, int offset) => Marshal.ReadInt32(nativeHome, offset));
}
public static unsafe int ReadInt32(IntPtr ptr, int ofs)
return ReadInt32(ptr, 0);
}
- public static IntPtr ReadIntPtr([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs)
+ public static IntPtr ReadIntPtr(Object ptr, int ofs)
{
#if BIT64
return (IntPtr)ReadInt64(ptr, ofs);
public static long ReadInt64([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ return ReadValueSlow(ptr, ofs, (IntPtr nativeHome, int offset) => Marshal.ReadInt64(nativeHome, offset));
}
public static unsafe long ReadInt64(IntPtr ptr, int ofs)
return ReadInt64(ptr, 0);
}
+ //====================================================================
+ // Read value from marshaled object (marshaled using AsAny)
+ // It's quite slow and can return back dangling pointers
+ // It's only there for backcompact
+ // I don't think we should spend time optimizing it
+ // People should really call the IntPtr overload instead
+ //====================================================================
+ private static unsafe T ReadValueSlow<T>(object ptr, int ofs, Func<IntPtr, int, T> readValueHelper)
+ {
+ // We AV on desktop if passing NULL. So this is technically a breaking change but is an improvement
+ if (ptr == null)
+ throw new ArgumentNullException(nameof(ptr));
+
+ int dwFlags =
+ (int)AsAnyMarshaler.AsAnyFlags.In |
+ (int)AsAnyMarshaler.AsAnyFlags.IsAnsi |
+ (int)AsAnyMarshaler.AsAnyFlags.IsBestFit;
+
+ MngdNativeArrayMarshaler.MarshalerState nativeArrayMarshalerState = new MngdNativeArrayMarshaler.MarshalerState();
+ AsAnyMarshaler marshaler = new AsAnyMarshaler(new IntPtr(&nativeArrayMarshalerState));
+
+ IntPtr pNativeHome = IntPtr.Zero;
+
+ try
+ {
+ pNativeHome = marshaler.ConvertToNative(ptr, dwFlags);
+ return readValueHelper(pNativeHome, ofs);
+ }
+ finally
+ {
+ marshaler.ClearNative(pNativeHome);
+ }
+ }
+
+
//====================================================================
// Write to memory
}
}
- public static void WriteByte([MarshalAs(UnmanagedType.AsAny), In, Out] Object ptr, int ofs, byte val)
+ public static void WriteByte(Object ptr, int ofs, byte val)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ WriteValueSlow(ptr, ofs, val, (IntPtr nativeHome, int offset, byte value) => Marshal.WriteByte(nativeHome, offset, value));
}
public static void WriteByte(IntPtr ptr, byte val)
}
}
- public static void WriteInt16([MarshalAs(UnmanagedType.AsAny), In, Out] Object ptr, int ofs, short val)
+ public static void WriteInt16(Object ptr, int ofs, short val)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ WriteValueSlow(ptr, ofs, val, (IntPtr nativeHome, int offset, short value) => Marshal.WriteInt16(nativeHome, offset, value));
}
public static void WriteInt16(IntPtr ptr, short val)
}
}
- public static void WriteInt32([MarshalAs(UnmanagedType.AsAny), In, Out] Object ptr, int ofs, int val)
+ public static void WriteInt32(Object ptr, int ofs, int val)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ WriteValueSlow(ptr, ofs, val, (IntPtr nativeHome, int offset, int value) => Marshal.WriteInt32(nativeHome, offset, value));
}
public static void WriteInt32(IntPtr ptr, int val)
#endif
}
- public static void WriteIntPtr([MarshalAs(UnmanagedType.AsAny), In, Out] Object ptr, int ofs, IntPtr val)
+ public static void WriteIntPtr(Object ptr, int ofs, IntPtr val)
{
#if BIT64
WriteInt64(ptr, ofs, (long)val);
}
}
- public static void WriteInt64([MarshalAs(UnmanagedType.AsAny), In, Out] Object ptr, int ofs, long val)
+ public static void WriteInt64(Object ptr, int ofs, long val)
{
- throw new PlatformNotSupportedException(); // https://github.com/dotnet/coreclr/issues/10442
+ WriteValueSlow(ptr, ofs, val, (IntPtr nativeHome, int offset, long value) => Marshal.WriteInt64(nativeHome, offset, value));
}
public static void WriteInt64(IntPtr ptr, long val)
WriteInt64(ptr, 0, val);
}
+ //====================================================================
+ // Write value into marshaled object (marshaled using AsAny) and
+ // propagate the value back
+ // It's quite slow and is only there for backcompact
+ // I don't think we should spend time optimizing it
+ // People should really call the IntPtr overload instead
+ //====================================================================
+ private static unsafe void WriteValueSlow<T>(object ptr, int ofs, T val, Action<IntPtr, int, T> writeValueHelper)
+ {
+ // We AV on desktop if passing NULL. So this is technically a breaking change but is an improvement
+ if (ptr == null)
+ throw new ArgumentNullException(nameof(ptr));
+
+ int dwFlags =
+ (int)AsAnyMarshaler.AsAnyFlags.In |
+ (int)AsAnyMarshaler.AsAnyFlags.Out |
+ (int)AsAnyMarshaler.AsAnyFlags.IsAnsi |
+ (int)AsAnyMarshaler.AsAnyFlags.IsBestFit;
+
+ MngdNativeArrayMarshaler.MarshalerState nativeArrayMarshalerState = new MngdNativeArrayMarshaler.MarshalerState();
+ AsAnyMarshaler marshaler = new AsAnyMarshaler(new IntPtr(&nativeArrayMarshalerState));
+
+ IntPtr pNativeHome = IntPtr.Zero;
+
+ try
+ {
+ pNativeHome = marshaler.ConvertToNative(ptr, dwFlags);
+ writeValueHelper(pNativeHome, ofs, val);
+ marshaler.ConvertToManaged(ptr, pNativeHome);
+ }
+ finally
+ {
+ marshaler.ClearNative(pNativeHome);
+ }
+ }
//====================================================================
// GetLastWin32Error
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+using CoreFXTestLibrary;
+
+internal struct BlittableStruct
+{
+ internal int a;
+ internal int b;
+ internal byte c;
+ internal short d;
+ internal IntPtr p;
+}
+
+internal struct StructWithReferenceTypes
+{
+ internal IntPtr ptr;
+ internal string str;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
+ internal int[] byValArr;
+}
+
+class Test
+{
+ static int Main(string[] args)
+ {
+ TestNegativeCases();
+ TestBlittableStruct();
+ TestStructWithReferenceType();
+
+ return 100;
+ }
+
+ static void TestNegativeCases()
+ {
+ Assert.Throws<ArgumentNullException>(() => { Marshal.WriteByte(null, 0, 0); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.WriteInt16(null, 0, 0); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.WriteInt32(null, 0, 0); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.WriteInt64(null, 0, 0); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.WriteIntPtr(null, 0, IntPtr.Zero); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.ReadByte(null, 0); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.ReadInt16(null, 0); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.ReadInt32(null, 0); });
+ Assert.Throws<ArgumentNullException>(() => { Marshal.ReadIntPtr(null, 0); });
+ }
+
+ static void TestBlittableStruct()
+ {
+ Console.WriteLine("TestBlittableStruct");
+
+ BlittableStruct blittableStruct = new BlittableStruct();
+ blittableStruct.a = 200;
+ blittableStruct.b = 300;
+ blittableStruct.c = 10;
+ blittableStruct.d = 123;
+ blittableStruct.p = new IntPtr(100);
+
+ object boxedBlittableStruct = (object)blittableStruct;
+
+ int offsetOfB = Marshal.OffsetOf<BlittableStruct>("b").ToInt32();
+ int offsetOfC = Marshal.OffsetOf<BlittableStruct>("c").ToInt32();
+ int offsetOfD = Marshal.OffsetOf<BlittableStruct>("d").ToInt32();
+ int offsetOfP = Marshal.OffsetOf<BlittableStruct>("p").ToInt32();
+
+ Assert.AreEqual(Marshal.ReadInt32(boxedBlittableStruct, 0), 200);
+ Assert.AreEqual(Marshal.ReadInt32(boxedBlittableStruct, offsetOfB), 300);
+ Assert.AreEqual(Marshal.ReadByte(boxedBlittableStruct, offsetOfC), 10);
+ Assert.AreEqual(Marshal.ReadInt16(boxedBlittableStruct, offsetOfD), 123);
+ Assert.AreEqual(Marshal.ReadIntPtr(boxedBlittableStruct, offsetOfP), new IntPtr(100));
+
+ Marshal.WriteInt32(boxedBlittableStruct, 0, 300);
+ Marshal.WriteInt32(boxedBlittableStruct, offsetOfB, 400);
+ Marshal.WriteByte(boxedBlittableStruct, offsetOfC, 20);
+ Marshal.WriteInt16(boxedBlittableStruct, offsetOfD, 144);
+
+ Marshal.WriteIntPtr(boxedBlittableStruct, offsetOfP, new IntPtr(500));
+
+ Assert.AreEqual(((BlittableStruct)boxedBlittableStruct).a, 300);
+ Assert.AreEqual(((BlittableStruct)boxedBlittableStruct).b, 400);
+ Assert.AreEqual(((BlittableStruct)boxedBlittableStruct).c, 20);
+ Assert.AreEqual(((BlittableStruct)boxedBlittableStruct).d, 144);
+ Assert.AreEqual(((BlittableStruct)boxedBlittableStruct).p, new IntPtr(500));
+ }
+
+ static void TestStructWithReferenceType()
+ {
+ Console.WriteLine("TestStructWithReferenceType");
+
+ StructWithReferenceTypes structWithReferenceTypes = new StructWithReferenceTypes();
+ structWithReferenceTypes.ptr = new IntPtr(100);
+ structWithReferenceTypes.str = "ABC";
+ structWithReferenceTypes.byValArr = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ object boxedStruct = (object)structWithReferenceTypes;
+
+ int offsetOfStr = Marshal.OffsetOf<StructWithReferenceTypes>("str").ToInt32();
+ int offsetOfByValArr = Marshal.OffsetOf<StructWithReferenceTypes>("byValArr").ToInt32();
+
+ Assert.AreEqual(Marshal.ReadInt32(boxedStruct, 0), 100);
+ Assert.AreNotEqual(Marshal.ReadIntPtr(boxedStruct, offsetOfStr), IntPtr.Zero);
+ Assert.AreEqual(Marshal.ReadInt32(boxedStruct, offsetOfByValArr + sizeof(int) * 2), 3);
+
+ Marshal.WriteInt32(boxedStruct, 0, 200);
+ Marshal.WriteInt32(boxedStruct, offsetOfByValArr + sizeof(int) * 9, 100);
+
+ Assert.AreEqual(((StructWithReferenceTypes)boxedStruct).ptr, new IntPtr(200));
+ Assert.AreEqual(((StructWithReferenceTypes)boxedStruct).byValArr[9], 100);
+ Assert.AreEqual(((StructWithReferenceTypes)boxedStruct).byValArr[8], 9);
+ Assert.AreEqual(((StructWithReferenceTypes)boxedStruct).str, "ABC");
+ }
+}
+