C#创建SetDllImportResolver委托报签名不兼容异常排查
执行代码时抛出ArgumentException异常,异常信息为:
Cannot bind to the target method because its signature is not compatible with that of the delegate type(无法绑定到目标方法,因其签名与委托类型签名不兼容)
触发异常的相关代码如下:
private const string ReferencedAssemblyName = "System.Private.CoreLib"; private delegate IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath); private delegate void SetDllImportResolverDelegate(Assembly assembly, DllImportResolver resolver); private static readonly SetDllImportResolverDelegate _setDllImportResolverCallback; static Program() { Assembly coreLibAssembly = AppDomain.CurrentDomain .GetAssemblies() .FirstOrDefault((assembly) => assembly.GetName().Name == ReferencedAssemblyName); if (coreLibAssembly != null) { MethodInfo[] methods = coreLibAssembly .GetTypes() .FirstOrDefault((type) => type.Name == "NativeLibrary") .GetMethods(BindingFlags.Public | BindingFlags.Static); foreach (MethodInfo method in methods) { if (method.Name == "SetDllImportResolver") _setDllImportResolverCallback = (SetDllImportResolverDelegate)method.CreateDelegate(typeof(SetDllImportResolverDelegate)); } } } static void Main() { _setDllImportResolverCallback(typeof(NativeLibrary).Assembly, (libraryName, assembly, searchPath) => { return NativeLibrary.Load("Kernel32"); }); }
异常触发场景:为System.Private.CoreLib中NativeLibrary类的SetDllImportResolver方法创建委托时抛出异常,自定义委托的结构签名与系统对应委托结构一致,需要修改代码使逻辑正常运行。
.NET运行时中,委托类型的匹配判定不看结构签名是否一致,只看运行时类型是否完全相等。
你自定义的DllImportResolver、SetDllImportResolverDelegate属于程序集内单独声明的新类型,和System.Runtime.InteropServices命名空间下系统内置的DllImportResolver委托、SetDllImportResolver方法的参数类型不是同一个运行时类型,因此创建委托时会判定签名不兼容抛出异常。
同时原有代码遍历所有同名方法创建委托的逻辑,也可能因为方法重载匹配到签名错误的重载,进一步触发异常。
- 删除代码中自定义的两个委托类型,直接使用系统内置的
DllImportResolver委托(位于System.Runtime.InteropServices命名空间),对应SetDllImportResolver的委托类型可以直接用通用Action<Assembly, DllImportResolver>匹配,无需额外声明。 - 反射获取方法时通过参数类型精确匹配目标方法,不要遍历所有同名方法盲目创建委托,避免匹配到错误的重载。
- 如果项目目标框架为.NET Core 3.0、.NET 5及以上版本,
NativeLibrary是公开暴露的类型,直接调用原生方法即可,不需要使用反射中转。
修复后的可运行代码:
using System.Reflection; using System.Runtime.InteropServices; private const string ReferencedAssemblyName = "System.Private.CoreLib"; private static readonly Action<Assembly, DllImportResolver> _setDllImportResolverCallback; static Program() { Assembly coreLibAssembly = AppDomain.CurrentDomain .GetAssemblies() .FirstOrDefault(assembly => assembly.GetName().Name == ReferencedAssemblyName); if (coreLibAssembly != null) { Type nativeLibraryType = coreLibAssembly .GetTypes() .FirstOrDefault(type => type.Name == "NativeLibrary"); if (nativeLibraryType != null) { // 按参数类型精确匹配目标方法,避免重载匹配错误 MethodInfo setResolverMethod = nativeLibraryType.GetMethod( "SetDllImportResolver", BindingFlags.Public | BindingFlags.Static, binder: null, new[] { typeof(Assembly), typeof(DllImportResolver) }, modifiers: null); if (setResolverMethod != null) { _setDllImportResolverCallback = setResolverMethod .CreateDelegate<Action<Assembly, DllImportResolver>>(); } } } } static void Main() { _setDllImportResolverCallback(typeof(NativeLibrary).Assembly, (libraryName, assembly, searchPath) => { return NativeLibrary.Load("Kernel32"); }); }
补充提示:反射逻辑仅适用于特殊场景下的兼容处理,常规开发直接调用
NativeLibrary.SetDllImportResolver(assembly, resolver)即可,不需要编写反射代码。
内容的提问来源于stack exchange,提问作者Redplcs

