You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何动态加载非托管DLL并动态指定函数调用?

C# 动态加载非托管DLL并动态指定函数调用及参数

我需要在C#中动态加载非托管DLL,并动态指定函数调用及参数。以往找到的示例和我之前使用的方法都是在类顶部静态声明委托,不确定是否可以实现动态指定。以下是包含静态委托的可用示例,以及尝试动态定义委托但无法运行的代码:

using System;
using System.Runtime.InteropServices;
using System.IO;

class Program
{
  // 声明委托以匹配函数签名,我希望能动态完成这一步
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  private delegate int MyFunctionDelegate(int a, int b);

  static void Main()
  {
    try
    {
      // DLL路径
      string dllPath = @"C:\Projects_VS\Tests\DynamicLoadDLLTest\simpleCDLL.dll";
      // 要调用的函数名
      string functionName = "Add";

      if (!File.Exists(dllPath))
      {
        Console.WriteLine("缺少DLL文件。");
      }

      // 动态加载指定DLL
      IntPtr dllHandle = LoadLibrary(dllPath);

      if (dllHandle == IntPtr.Zero)
      {
        Console.WriteLine("加载DLL失败。");
        return;
      }

      // 获取函数地址
      IntPtr functionAddress = GetProcAddress(dllHandle, functionName);
      if (functionAddress == IntPtr.Zero)
      {
        Console.WriteLine("获取函数地址失败。");
        return;
      }

      // 当前可行的写法
      MyFunctionDelegate myFunction = Marshal.GetDelegateForFunctionPointer<MyFunctionDelegate>(functionAddress);
      int result = myFunction(10, 20);

      // 我想要实现的写法
      IntPtr functionPtr = GetProcAddress(dllHandle, "Add");
      delegate int DynamicFunctionDelegate(int parameter1, int parameter2); 
      DynamicFunctionDelegate myFunction = Marshal.GetDelegateForFunctionPointer<DynamicFunctionDelegate>(functionPtr);
      int result = myFunction(10, 20);

      Console.WriteLine("结果: " + result);
    }
    catch (Exception ex)
    {
      Console.WriteLine("发生错误: " + ex.Message);
    }
  }

  // 加载DLL的辅助方法
  [DllImport("kernel32.dll")]
  private static extern IntPtr LoadLibrary(string dllToLoad);

  // 获取函数地址的辅助方法
  [DllImport("kernel32.dll")]
  private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
}

更新:我找到另一个示例并修改,但无法调用DLL,请问这个方向是否可行?

static void Main()
{
  // DLL路径
  string dllPath = @"E:\Projects_VS\Tests\DynamicLoadDLLTest\SimpleCDLL.dll";
  // 要调用的函数名
  string functionName = "Add";
  object[] parameterValues = { 5, 10 };

  object r = DynamicDllFunctionInvoke(dllPath, functionName, parameterValues);

}


public static object DynamicDllFunctionInvoke(string DllPath, string entrypoint, object[] funcPrams)
{
  // 定义DLL函数的返回类型
  Type returnType = typeof(int);
  // 获取函数参数类型
  int n = funcPrams.Length;
  Type[] parameterTypes = new Type[n];
  for (int i =0; i<n; ++i)
  {
    parameterTypes[i] = funcPrams[i].GetType();
  }
  string entryPoint = entrypoint;

  // 创建动态程序集和模块
  AssemblyName asmName = new AssemblyName();
  asmName.Name = "tempDll";
  AssemblyBuilder dynamicAsm = AssemblyBuilder.DefineDynamicAssembly(asmName,
          AssemblyBuilderAccess.Run);
  ModuleBuilder dynamicMod =
    dynamicAsm.DefineDynamicModule("tempModule");

  // 动态构造全局PInvoke签名
  MethodBuilder dynamicMethod = dynamicMod.DefinePInvokeMethod(
    entryPoint, DllPath, MethodAttributes.Static | MethodAttributes.Public
    | MethodAttributes.PinvokeImpl, CallingConventions.Standard,
    returnType, parameterTypes, CallingConvention.Winapi,
    CharSet.Ansi);

  // 完成全局方法定义
  dynamicMod.CreateGlobalFunctions();

  // 获取PInvoke方法的MethodInfo
  MethodInfo mi = dynamicMod.GetMethod(entrypoint);
  // 调用静态方法并返回结果
  object retval = mi.Invoke(null, funcPrams);

  return retval;
}

问题解答

1. 动态定义委托的代码为何无法运行?

你在方法内部直接定义委托类型(delegate int DynamicFunctionDelegate(int parameter1, int parameter2);)是C#不允许的语法——委托类型只能在命名空间或类级别声明,不能在方法内部定义,这是代码编译失败的直接原因。

若要实现动态匹配函数签名的需求,不能直接在方法内定义委托,需通过反射动态生成委托类型,或使用你尝试的动态程序集方案。

2. 动态程序集的方向可行,但代码存在关键问题

动态生成P/Invoke方法的思路是正确的,但你的代码有以下错误需要修正:

问题1:调用约定不匹配

你的非托管DLL函数Add如果是C/C++默认的cdecl调用约定(与第一个示例中的[UnmanagedFunctionPointer(CallingConvention.Cdecl)]对应),但你在DefinePInvokeMethod中指定的是CallingConvention.Winapi(通常对应stdcall),会导致调用时栈不平衡,引发异常。

问题2:获取MethodInfo可能失败

dynamicMod.GetMethod(entrypoint)可能找不到方法,因为动态生成的P/Invoke方法名称可能因重载或命名规则被修改,建议直接使用dynamicMethod获取MethodInfo,而非通过名称查找。

问题3:缺少错误处理

未处理DLL不存在、函数找不到等场景,易出现未捕获异常。

修正后的代码

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

class Program
{
    static void Main()
    {
        string dllPath = @"E:\Projects_VS\Tests\DynamicLoadDLLTest\SimpleCDLL.dll";
        string functionName = "Add";
        object[] parameterValues = { 5, 10 };

        try
        {
            object r = DynamicDllFunctionInvoke(dllPath, functionName, typeof(int), new[] { typeof(int), typeof(int) }, parameterValues);
            Console.WriteLine($"结果: {r}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"错误: {ex.Message}");
        }
    }

    public static object DynamicDllFunctionInvoke(string dllPath, string entryPoint, Type returnType, Type[] parameterTypes, object[] funcParams)
    {
        // 检查DLL是否存在
        if (!System.IO.File.Exists(dllPath))
            throw new System.IO.FileNotFoundException("DLL文件不存在", dllPath);

        // 创建动态程序集和模块
        AssemblyName asmName = new AssemblyName("TempDllAssembly");
        AssemblyBuilder dynamicAsm = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
        ModuleBuilder dynamicMod = dynamicAsm.DefineDynamicModule("TempModule");

        // 定义P/Invoke方法,确保调用约定与非托管函数一致
        MethodBuilder dynamicMethod = dynamicMod.DefinePInvokeMethod(
            entryPoint,
            dllPath,
            MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.PinvokeImpl,
            CallingConventions.Standard,
            returnType,
            parameterTypes,
            CallingConvention.Cdecl, // 改为匹配非托管函数的调用约定
            CharSet.Ansi);

        // 完成方法定义
        dynamicMethod.SetImplementationFlags(dynamicMethod.GetImplementationFlags() | MethodImplAttributes.PreserveSig);
        dynamicMod.CreateGlobalFunctions();

        // 获取MethodInfo并调用
        MethodInfo mi = dynamicMod.GetMethod(entryPoint) ?? throw new MissingMethodException($"找不到函数 {entryPoint}");
        return mi.Invoke(null, funcParams);
    }
}

3. 更简洁的方案:反射动态生成委托类型

若不想使用动态程序集,可通过System.Reflection.Emit动态生成委托类型,结合Marshal.GetDelegateForFunctionPointer调用:

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

public static class DynamicDllHelper
{
    public static Delegate CreateDynamicDelegate(IntPtr functionPtr, Type returnType, Type[] parameterTypes, CallingConvention callingConvention)
    {
        // 创建动态程序集、模块和委托类型
        AssemblyName asmName = new AssemblyName("DynamicDelegateAssembly");
        AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
        ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("DynamicDelegateModule");

        // 定义委托类型:签名需与非托管函数一致
        TypeBuilder delegateTypeBuilder = modBuilder.DefineType(
            $"DynamicDelegate_{Guid.NewGuid():N}",
            TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class,
            typeof(MulticastDelegate));

        // 定义委托构造函数
        ConstructorBuilder ctorBuilder = delegateTypeBuilder.DefineConstructor(
            MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
            CallingConventions.Standard,
            new[] { typeof(object), typeof(IntPtr) });
        ctorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

        // 定义委托的Invoke方法
        MethodBuilder invokeBuilder = delegateTypeBuilder.DefineMethod(
            "Invoke",
            MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
            returnType,
            parameterTypes);
        invokeBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

        // 完成委托类型创建
        Type delegateType = delegateTypeBuilder.CreateType();

        // 应用UnmanagedFunctionPointer特性,指定调用约定
        ConstructorInfo attrCtor = typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) });
        CustomAttributeBuilder attrBuilder = new CustomAttributeBuilder(attrCtor, new object[] { callingConvention });
        delegateType.SetCustomAttribute(attrBuilder);

        // 创建委托实例
        return Marshal.GetDelegateForFunctionPointer(functionPtr, delegateType);
    }
}

// 使用示例
class Program
{
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibrary(string dllToLoad);

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

    static void Main()
    {
        string dllPath = @"C:\Projects_VS\Tests\DynamicLoadDLLTest\simpleCDLL.dll";
        string functionName = "Add";

        IntPtr dllHandle = LoadLibrary(dllPath);
        if (dllHandle == IntPtr.Zero)
        {
            Console.WriteLine("加载DLL失败");
            return;
        }

        IntPtr funcPtr = GetProcAddress(dllHandle, functionName);
        if (funcPtr == IntPtr.Zero)
        {
            Console.WriteLine("获取函数地址失败");
            return;
        }

        // 创建动态委托
        Delegate dynamicDelegate = DynamicDllHelper.CreateDynamicDelegate(funcPtr, typeof(int), new[] { typeof(int), typeof(int) }, CallingConvention.Cdecl);
        // 调用函数
        int result = (int)dynamicDelegate.DynamicInvoke(10, 20);
        Console.WriteLine($"结果: {result}");
    }
}

内容的提问来源于stack exchange,提问作者runfastman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 02:37:16