想了一下,前一篇文章裡的方法似乎有點麻煩。雖然 C# 的實作有這樣的限制,但是我們應該可以用 System.Reflection.Emit.TypeBuilder 來動態產生一個使用 cdecl 的 delegate。理論上,透過 dynamic code generation 這種方式是可以突破任何語法上或是實作上的限制。因此參考下列的這兩篇文章,其實這問題是可以用更優雅的方式來解決:
- Creating delegate types via Reflection.Emit, Joel Pobar
- Calling Code Dynamically, Eric Gunnerson
下列的程式列出了新的作法,產生出來的 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;
}
}
}