using System; using System.Runtime.InteropServices; using WhiteMagic; using WhiteMagic.Execution; using Xunit; namespace WhiteMagicTest.Execution; /// /// Tests for operating in-process. /// public class InProcessInvokerTests { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int AddDelegate(int a, int b); private static int NativeAdd(int a, int b) => a + b; [Fact] public void CreateFunction_calls_known_in_process_function() { using var reader = new InProcessReader(); var invoker = new InProcessInvoker(reader); var native = new AddDelegate(NativeAdd); IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(native); AddDelegate callable = invoker.CreateFunction(functionPointer); int result = callable(5, 7); Assert.Equal(12, result); GC.KeepAlive(native); } [Fact] public void CreateFunction_rejects_zero_address() { using var reader = new InProcessReader(); var invoker = new InProcessInvoker(reader); ArgumentException ex = Assert.Throws(() => invoker.CreateFunction(IntPtr.Zero)); Assert.Equal("address", ex.ParamName); } [Fact] public void Vtable_helper_reads_function_pointer_slot() { using var reader = new InProcessReader(); var invoker = new InProcessInvoker(reader); // Build a tiny fake vtable in a pinned buffer: two slots holding known pointers. IntPtr slot0 = Marshal.GetFunctionPointerForDelegate(new AddDelegate(NativeAdd)); IntPtr slot1 = new IntPtr(0x12345678); IntPtr[] vtable; if (reader.Is64Bit) { vtable = [slot0, slot1]; } else { vtable = [slot0, slot1]; } GCHandle pin = GCHandle.Alloc(vtable, GCHandleType.Pinned); try { IntPtr vTableAddress = pin.AddrOfPinnedObject(); Assert.Equal(slot0, invoker.ReadVTableFunction(vTableAddress, 0)); Assert.Equal(slot1, invoker.ReadVTableFunction(vTableAddress, 1)); } finally { pin.Free(); } } }