6f6ed523eef0f8878a927895a07de3b662b72e7c
[platform/upstream/coreclr.git] / tests / src / Interop / PInvoke / Miscellaneous / HandleRef / HandleRefTest.cs
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.
4
5 using System.Runtime.InteropServices;
6 using System;
7 using System.Reflection;
8 using System.Text;
9 using TestLibrary;
10
11 class BoxedInt
12 {
13     public int MyInt;
14 }
15
16 class HandleRefTest
17 {
18     [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
19     private static extern int MarshalPointer_In(HandleRef pintValue, int stackGuard);
20
21     [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
22     private static extern int MarshalPointer_InOut(HandleRef pintValue, int stackGuard);
23
24     [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
25     private static extern int MarshalPointer_Out(HandleRef pintValue, int stackGuard);
26
27     [DllImport(@"HandleRefNative", CallingConvention = CallingConvention.Winapi)]
28     private static extern int TestNoGC(HandleRef pintValue, Action gcCallback);
29
30     public unsafe static int Main(string[] args)
31     {
32         try{
33             const int intManaged = 1000;
34             const int intNative = 2000;
35             const int intReturn = 3000;
36             const int stackGuard = 5000;
37
38             Console.WriteLine("MarshalPointer_In");
39             int int1 = intManaged;
40             int* int1Ptr = &int1;
41             HandleRef hr1 = new HandleRef(new Object(), (IntPtr)int1Ptr);
42             Assert.AreEqual(intReturn, MarshalPointer_In(hr1, stackGuard), "The return value is wrong");
43             Assert.AreEqual(intManaged, int1, "The parameter value is changed");
44             
45             Console.WriteLine("MarshalPointer_InOut");
46             int int2 = intManaged;
47             int* int2Ptr = &int2;
48             HandleRef hr2 = new HandleRef(new Object(), (IntPtr)int2Ptr);
49             Assert.AreEqual(intReturn, MarshalPointer_InOut(hr2, stackGuard), "The return value is wrong");
50             Assert.AreEqual(intNative, int2, "The passed value is wrong");
51             
52             Console.WriteLine("MarshalPointer_Out");
53             int int3 = intManaged;
54             int* int3Ptr = &int3;
55             HandleRef hr3 = new HandleRef(new Object(), (IntPtr)int3Ptr);
56             Assert.AreEqual(intReturn, MarshalPointer_Out(hr3, stackGuard), "The return value is wrong");
57             Assert.AreEqual(intNative, int3, "The passed value is wrong");
58
59             // Note that this scenario will always pass in a debug build because all values 
60             // stay rooted until the end of the method. 
61             Console.WriteLine("TestNoGC");
62
63             // Keep the int boxed and pinned to prevent it from getting collected.
64             // That way, we can safely reference it from finalizers that run on shutdown.
65             BoxedInt boxedInt = new BoxedInt();
66             GCHandle.Alloc(boxedInt, GCHandleType.Normal);
67             int* int4Ptr;
68             fixed (int* tempIntPtr = &boxedInt.MyInt)
69             {
70                 // Smuggle the pointer out of the fixed scope
71                 int4Ptr = tempIntPtr;
72             }
73             Console.WriteLine("2");
74             *int4Ptr = intManaged;
75             CollectableClass collectableClass = new CollectableClass(int4Ptr);
76             HandleRef hr4 = new HandleRef(collectableClass, (IntPtr)int4Ptr);
77             Action gcCallback = () => { Console.WriteLine("GC callback now"); GC.Collect(2, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); GC.Collect(2, GCCollectionMode.Forced); };
78             Assert.AreEqual(intReturn, TestNoGC(hr4, gcCallback), "The return value is wrong");
79             Console.WriteLine("Native code finished");
80
81             return 100;
82         } catch (Exception e){
83             Console.WriteLine($"Test Failure: {e}"); 
84             return 101; 
85         }
86     }
87
88     /// <summary>
89     /// Class that will change a pointer passed to native code when this class gets finalized.
90     /// Native code can check whether the pointer changed during a P/Invoke
91     /// </summary>
92     unsafe class CollectableClass
93     {
94         int* PtrToChange;
95         public CollectableClass(int* ptrToChange)
96         {
97             PtrToChange = ptrToChange;
98         }
99
100         ~CollectableClass()
101         {
102             Console.WriteLine("CollectableClass collected");
103             *PtrToChange = Int32.MaxValue;
104         }
105     }
106 }