Well, I found a possible solution:
I created a generic Invoke method that creates and caches all delegates for future use. Also
public void Select(uint target) { fixed (void* pThis = &this) { Generic.Invoke<Action<uint, uint>>(this.VTable[0xC0], CallingConvention.ThisCall) ((uint)pThis, target); } } [FieldOffset(0x00)] public uint* VTable;
Caches:
public static T Invoke<T>(uint addr, CallingConvention conv) where T : class { var type = typeof(T); if (!cache.Contains(type)) cache.Set<T>(type, NativeHelper.GetDelegateForFunctionPointer<T>(addr, conv)); return cache.Get<T>(type); }
And the function that creates the function (and works for general Func / Action)
public static T GetDelegateForFunctionPointer<T>(uint ptr, CallingConvention conv) where T : class { var delegateType = typeof(T); var method = delegateType.GetMethod("Invoke"); var returnType = method.ReturnType; var paramTypes = method .GetParameters() .Select((x) => x.ParameterType) .ToArray(); var invoke = new DynamicMethod("Invoke", returnType, paramTypes, typeof(Delegate)); var il = invoke.GetILGenerator(); for (int i = 0; i < paramTypes.Length; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Ldc_I4, ptr); il.EmitCalli(OpCodes.Calli, conv, returnType, paramTypes); il.Emit(OpCodes.Ret); return invoke.CreateDelegate(delegateType) as T; }
source share