如何复用C#的DllImport定义以适配不同原生DLL?
解决跨DLL调用原生引用计数库的重复定义问题
核心问题分析
你的场景里,my_lib的引用计数基于每个DLL的独立堆管理,必须确保C#模块调用对应DLL的CONSTBUFFER_Create函数,不能混用不同DLL的实例,否则会引发内存泄漏或崩溃。DllImport是编译时绑定属性无法直接重写,但可以通过以下方式避免重复定义函数签名:
解决方案1:抽象基类+派生类复用签名
先定义抽象基类统一声明函数签名,再针对每个DLL创建派生类实现DllImport绑定,仅需写一次签名:
步骤1:定义抽象基类
public abstract class ConstBufferInteropBase { // 统一声明函数签名,无需DllImport public abstract IntPtr CONSTBUFFER_Create(byte[] source, uint size); }
步骤2:为每个DLL创建派生类
每个派生类仅指定对应DLL名称,复用基类签名:
// 对应my_dllA.dll的Interop类 public class ConstBufferInteropA : ConstBufferInteropBase { [DllImport("my_dllA.dll", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr CONSTBUFFER_Create(byte[] source, uint size); public override IntPtr CONSTBUFFER_Create(byte[] source, uint size) { return CONSTBUFFER_Create(source, size); } } // 对应my_dllB.dll的Interop类 public class ConstBufferInteropB : ConstBufferInteropBase { [DllImport("my_dllB.dll", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr CONSTBUFFER_Create(byte[] source, uint size); public override IntPtr CONSTBUFFER_Create(byte[] source, uint size) { return CONSTBUFFER_Create(source, size); } }
步骤3:模块中调用
模块A用ConstBufferInteropA,模块B用ConstBufferInteropB,确保调用对应DLL的函数:
// 模块A调用示例 var interopA = new ConstBufferInteropA(); IntPtr bufferHandle = interopA.CONSTBUFFER_Create(new byte[] { 0x01, 0x02 }, 2); // 模块B调用示例 var interopB = new ConstBufferInteropB(); IntPtr bufferHandleB = interopB.CONSTBUFFER_Create(new byte[] { 0x03, 0x04 }, 2);
解决方案2:部分类拆分签名与绑定
如果函数数量多,可将统一签名放在一个部分类文件,每个DLL的绑定逻辑单独放一个部分类文件:
统一签名的部分类文件
public abstract partial class ConstBufferInterop { public abstract IntPtr CONSTBUFFER_Create(byte[] source, uint size); // 其他原生函数签名也可在此统一声明 }
对应my_dllA的部分类文件
public partial class ConstBufferInteropA : ConstBufferInterop { [DllImport("my_dllA.dll", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr CONSTBUFFER_Create(byte[] source, uint size); public override IntPtr CONSTBUFFER_Create(byte[] source, uint size) { return CONSTBUFFER_Create(source, size); } }
注意事项
- 必须严格保证每个C#模块调用对应DLL的Interop类,禁止跨模块混用,否则仍会触发跨DLL引用计数内存问题。
CallingConvention需与原生库保持一致(如Cdecl),否则会导致栈损坏。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

