想了一下,前一篇文章裡的方法似乎有點麻煩。雖然 C# 的實作有這樣的限制,但是我們應該可以用 System.Reflection.Emit.TypeBuilder 來動態產生一個使用 cdecl 的 delegate。理論上,透過 dynamic code generation 這種方式是可以突破任何語法上或是實作上的限制。因此參考下列的這兩篇文章,其實這問題是可以用更優雅的方式來解決:

下列的程式列出了新的作法,產生出來的 delegate 跟用 ildasm/perl/ilasm 是一樣的效果 (注意 new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) } 的使用),酷!

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;

namespace EmitCdeclDelegate
{
    class Program
    {
        static void Main(string[] args)
        {
            Program program = new Program();

            program.TestCdeclDelegate();
        }

        private void TestCdeclDelegate()
        {
            MethodInfo methodInfo = typeof(Program).GetMethod("CallBack");
            Type delegateType = CreateCustomDelegate(methodInfo);
            Delegate d = Delegate.CreateDelegate(delegateType, this, "CallBack");

            d.DynamicInvoke(new object[] {IntPtr.Zero, 0, null, null});
        }

        public unsafe int CallBack(IntPtr pArg, int argc, sbyte** argv, sbyte** columnNames)
        {
            return 0;
        }

        private Type CreateCustomDelegate(MethodInfo targetMethod)
        {
            AssemblyName assembly;
            AssemblyBuilder assemblyBuilder;
            ModuleBuilder modbuilder;
            TypeBuilder typeBuilder;
            MethodBuilder methodBuilder;

            assembly = new AssemblyName();
            assembly.Version = new Version(1, 0, 0, 0);
            assembly.Name = "SQLiteCallback";
            assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.RunAndSave);
            modbuilder = assemblyBuilder.DefineDynamicModule("SQLiteCallbackModule", "SQLiteCallback.exe", true);

            // Create a delegate that has the same signature as the method we would like to hook up to
            typeBuilder = modbuilder.DefineType("SQLiteCallback", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(System.MulticastDelegate));
            ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(object), typeof(int) });
            constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

            // Grab the parameters of the method
            ParameterInfo[] parameters = targetMethod.GetParameters();
            Type[] paramTypes = new Type[parameters.Length];

            for (int i = 0; i < parameters.Length; i++)
            {
                paramTypes[i] = parameters[i].ParameterType;
            }

            // Define the Invoke method for the delegate
            methodBuilder = typeBuilder.DefineMethod(
                "Invoke",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
                CallingConventions.Standard,
                targetMethod.ReturnType,
                null,
                new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) },
                paramTypes,
                null,
                null);
            methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

            // bake it!
            Type t = typeBuilder.CreateType();

            return t;
        }
    }
}