1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Runtime.InteropServices;
7 using System.Reflection;
13 [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
14 private static extern int MarshalPointer_In(HandleRef pintValue, int stackGuard);
16 [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
17 private static extern int MarshalPointer_InOut(HandleRef pintValue, int stackGuard);
19 [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
20 private static extern int MarshalPointer_Out(HandleRef pintValue, int stackGuard);
22 [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
23 private static extern int TestNoGC(HandleRef pintValue, Action gcCallback);
25 public unsafe static int Main(string[] args)
28 const int intManaged = 1000;
29 const int intNative = 2000;
30 const int intReturn = 3000;
31 const int stackGuard = 5000;
33 Console.WriteLine("MarshalPointer_In");
34 int int1 = intManaged;
36 HandleRef hr1 = new HandleRef(new Object(), (IntPtr)int1Ptr);
37 Assert.AreEqual(intReturn, MarshalPointer_In(hr1, stackGuard), "The return value is wrong");
38 Assert.AreEqual(intManaged, int1, "The parameter value is changed");
40 Console.WriteLine("MarshalPointer_InOut");
41 int int2 = intManaged;
43 HandleRef hr2 = new HandleRef(new Object(), (IntPtr)int2Ptr);
44 Assert.AreEqual(intReturn, MarshalPointer_InOut(hr2, stackGuard), "The return value is wrong");
45 Assert.AreEqual(intNative, int2, "The passed value is wrong");
47 Console.WriteLine("MarshalPointer_Out");
48 int int3 = intManaged;
50 HandleRef hr3 = new HandleRef(new Object(), (IntPtr)int3Ptr);
51 Assert.AreEqual(intReturn, MarshalPointer_Out(hr3, stackGuard), "The return value is wrong");
52 Assert.AreEqual(intNative, int3, "The passed value is wrong");
54 // Note that this scenario will always pass in a debug build because all values
55 // stay rooted until the end of the method.
56 Console.WriteLine("TestNoGC");
58 int* int4Ptr = (int*)Marshal.AllocHGlobal(sizeof(int)); // We don't free this memory so we don't have to worry about a GC run between freeing and return (possible in a GCStress mode).
59 Console.WriteLine("2");
60 *int4Ptr = intManaged;
61 CollectableClass collectableClass = new CollectableClass(int4Ptr);
62 HandleRef hr4 = new HandleRef(collectableClass, (IntPtr)int4Ptr);
63 Action gcCallback = () => { Console.WriteLine("GC callback now"); GC.Collect(2, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); GC.Collect(2, GCCollectionMode.Forced); };
64 Assert.AreEqual(intReturn, TestNoGC(hr4, gcCallback), "The return value is wrong");
65 Console.WriteLine("Native code finished");
68 } catch (Exception e){
69 Console.WriteLine($"Test Failure: {e}");
75 /// Class that will change a pointer passed to native code when this class gets finalized.
76 /// Native code can check whether the pointer changed during a P/Invoke
78 unsafe class CollectableClass
81 public CollectableClass(int* ptrToChange)
83 PtrToChange = ptrToChange;
88 Console.WriteLine("CollectableClass collected");
89 *PtrToChange = Int32.MaxValue;