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

如何让C#类库依赖后续由最终应用指定的非托管DLL?

在C#中实现与非托管DLL的解耦依赖

方法1:动态P/Invoke(手动加载DLL)

核心思路是绕过静态的[DllImport],直接调用Win32 API动态加载DLL并获取函数指针,以此实现运行时指定DLL名称的效果。

实现步骤:

  • 定义与非托管函数签名完全匹配的委托类型
  • 在类库中暴露初始化入口,允许最终应用传入DLL路径/名称
  • 通过LoadLibrary加载目标DLL,再用GetProcAddress获取函数地址,转换为委托实例供类库内部调用

示例代码(类库部分):

using System;
using System.Runtime.InteropServices;

// 匹配非托管函数签名的委托
public delegate int MyApiFunction(int param);

public class MyApiWrapper : IDisposable
{
    private MyApiFunction _myFunction;
    private IntPtr _dllHandle = IntPtr.Zero;

    // 由最终应用调用,传入具体DLL名称/路径
    public void Initialize(string dllPath)
    {
        _dllHandle = LoadLibrary(dllPath);
        if (_dllHandle == IntPtr.Zero)
            throw new System.ComponentModel.Win32Exception();

        IntPtr funcPtr = GetProcAddress(_dllHandle, "MyApiFunction");
        if (funcPtr == IntPtr.Zero)
            throw new System.ComponentModel.Win32Exception();

        _myFunction = Marshal.GetDelegateForFunctionPointer<MyApiFunction>(funcPtr);
    }

    // 类库对外暴露的业务方法,内部调用动态获取的非托管函数
    public int CallMyFunction(int param)
    {
        return _myFunction?.Invoke(param) ?? throw new InvalidOperationException("API未完成初始化");
    }

    // Win32 API声明
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool FreeLibrary(IntPtr hModule);

    // 释放已加载的DLL资源
    public void Dispose()
    {
        if (_dllHandle != IntPtr.Zero)
        {
            FreeLibrary(_dllHandle);
            _dllHandle = IntPtr.Zero;
        }
    }
}

最终应用中的调用示例:

using (var wrapper = new MyApiWrapper())
{
    wrapper.Initialize("MySpecificImpl.dll"); // 在此指定具体的非托管DLL
    var result = wrapper.CallMyFunction(42);
    // 处理业务结果
}

方法2:抽象接口+依赖注入

通过定义抽象API接口让类库依赖,最终应用负责实现接口(包含指定具体DLL的P/Invoke逻辑),再通过依赖注入将实现注入到类库中,完全解耦类库与具体DLL的绑定。

实现步骤:

  1. 类库中定义抽象API接口:
public interface IMyApi
{
    int MyApiFunction(int param);
}
  1. 类库的业务逻辑依赖该抽象接口:
public class BusinessLogic
{
    private readonly IMyApi _api;

    public BusinessLogic(IMyApi api)
    {
        _api = api;
    }

    public void ProcessData(int input)
    {
        var result = _api.MyApiFunction(input);
        // 执行具体业务逻辑
    }
}
  1. 最终应用中实现接口,指定目标DLL:
public class MyApiImpl : IMyApi
{
    [DllImport("MySpecificImpl.dll")]
    private static extern int MyApiFunction(int param);

    public int MyApiFunction(int param)
    {
        return MyApiFunction(param);
    }
}
  1. 应用中注入实现并使用:
var apiImpl = new MyApiImpl();
var businessLogic = new BusinessLogic(apiImpl);
businessLogic.ProcessData(42);

这种方式符合C#面向对象设计原则,解耦彻底,还能轻松替换不同的DLL实现。

方法3:反射修改DllImport属性(不推荐)

如果想保留[DllImport]的形式,可以通过反射动态修改其DLL名称属性,但这属于hack手段,稳定性较差,仅作参考:

public class MyApiWrapper
{
    private static string _dllName;

    // 由应用传入DLL名称
    public static void SetDllName(string name)
    {
        _dllName = name;
    }

    [DllImport("", EntryPoint = "MyApiFunction", SetLastError = true)]
    private static extern int MyApiFunctionImpl(int param);

    public int CallMyFunction(int param)
    {
        // 反射修改DllImport的DLL名称
        var methodInfo = typeof(MyApiWrapper).GetMethod("MyApiFunctionImpl", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
        var dllImportAttr = (DllImportAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(DllImportAttribute));
        var field = typeof(DllImportAttribute).GetField("_dllName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        field.SetValue(dllImportAttr, _dllName);
        Marshal.Prelink(methodInfo);

        return MyApiFunctionImpl(param);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 15:11:13