public void emitManagedDelegate(DelegatesBuilder tbDelegates) { // Create the delegate type TypeBuilder tb = tbDelegates.defineMulticastDelegate(method); // Create constructor for the delegate MethodAttributes ma = MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; ConstructorBuilder cb = tb.DefineConstructor(ma, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) }); cb.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); cb.DefineParameter(1, ParameterAttributes.In, "object"); cb.DefineParameter(2, ParameterAttributes.In, "method"); // Create Invoke method for the delegate Type[] paramTypes = method.GetParameters().Select(pi => pi.ParameterType).ToArray(); var mb = tb.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, method.ReturnType, paramTypes); mb.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); tManagedDelegate = tb.CreateType(); }
static Type createDelegate(DelegatesBuilder builder, MethodInfo method) { // Initially based on this: https://blogs.msdn.microsoft.com/joelpob/2004/02/15/creating-delegate-types-via-reflection-emit/ // Create the delegate type TypeBuilder tb = builder.defineMulticastDelegate(method); // Apply [UnmanagedFunctionPointer] using the value from RuntimeClass.defaultCallingConvention CustomAttributeBuilder cab = new CustomAttributeBuilder(ciFPAttribute, new object[1] { RuntimeClass.defaultCallingConvention }); tb.SetCustomAttribute(cab); // Create constructor for the delegate MethodAttributes ma = MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; ConstructorBuilder cb = tb.DefineConstructor(ma, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) }); cb.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); cb.DefineParameter(1, ParameterAttributes.In, "object"); cb.DefineParameter(2, ParameterAttributes.In, "method"); // Create Invoke method for the delegate. Appending one more parameter to the start, `[in] IntPtr pThis` ParameterInfo[] methodParams = method.GetParameters(); int nativeParamsCount = methodParams.Length + 1; int retValIndex = -1; RetValIndexAttribute rvi = method.GetCustomAttribute <RetValIndexAttribute>(); if (rvi != null) { retValIndex = rvi.index; nativeParamsCount++; } Type[] paramTypes = new Type[nativeParamsCount]; paramTypes[0] = typeof(IntPtr); int iNativeParam = 1; for (int i = 0; i < methodParams.Length; i++, iNativeParam++) { if (i == retValIndex) { retValIndex = -1; i--; paramTypes[iNativeParam] = nativeRetValArgType(method); continue; } ParameterInfo pi = methodParams[i]; Type tp = pi.ParameterType; iCustomMarshal cm = pi.customMarshaller(); if (null != cm) { tp = cm.getNativeType(pi); } paramTypes[iNativeParam] = tp; } if (retValIndex >= 0) { // User has specified [RetValIndex] value after the rest of the parameters paramTypes[iNativeParam] = nativeRetValArgType(method); } Type returnType; if (method.ReturnType != typeof(IntPtr) || null != rvi) { returnType = typeof(int); } else { returnType = typeof(IntPtr); } var mb = tb.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, returnType, paramTypes); mb.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); mb.DefineParameter(1, ParameterAttributes.In, "pThis"); iNativeParam = 2; // 2 because the first one is native this pointer, and MethodBuilder.DefineParameter API uses 1-based indices, the number 0 represents the return value of the method. retValIndex = rvi?.index ?? -1; for (int i = 0; i < methodParams.Length; i++, iNativeParam++) { if (i == retValIndex) { retValIndex = -1; i--; mb.DefineParameter(iNativeParam, ParameterAttributes.Out, "retVal"); continue; } ParameterInfo pi = methodParams[i]; ParameterBuilder pb = mb.DefineParameter(iNativeParam, pi.Attributes, pi.Name); ParamsMarshalling.buildDelegateParam(pi, pb, rvi?.index); } if (retValIndex >= 0) { // User has specified [RetValIndex] value after the rest of the parameters mb.DefineParameter(iNativeParam, ParameterAttributes.Out, "retVal"); } // The method has no code, it's pure virtual. return(tb.CreateType()); }