【C#】 使用Gsof.Native 动态调用 C动态库
一、背景
使用C# 开发客户端时候,我们经常会调用一些标准的动态库或是C的类库。
虽然C# 提供的PInvoke的方式,但因为使用的场景的多变,有些时候可能没办法,固定位置的调用,或是需要按需求调用不同的库。 设置当前目录的方式,有时会有加载不到的问题。
CLI的方式又需要用C++做再次的封装。
二、说明
- 支持根据路径动态加载DLL类库
- 支持using 销毁加载的类库
- 调用方便
github: https://github.com/gaoshang212/gsof/tree/master/Gsof.Native
nuget: https://www.nuget.org/packages/Gsof.Native/
接口说明:
NativeFactory 创建INative调用对象:
/// <summary>
/// 创建INative 对象
/// </summary>
/// <param name="p_fileName">文件路径</param>
/// <returns></returns>
public static INative Create(string p_fileName);
/// <summary>
/// 创建INative 对象
/// </summary>
/// <param name="p_fileName">文件路径</param>
/// <param name="p_calling">调用转换方式(同PInvoke CallingConvention)/param>
/// <returns></returns>
public static INative Create(string p_fileName, CallingConvention _calling);
/// <summary>
/// 销毁INative, 也可以调用 Native的Dispose方法
/// </summary>
/// <param name="p_native"></param>
public static void Free(INative p_native);
INative:
public interface INative : IDisposable
{
/// <summary>
/// 获取函数委托
/// </summary>
/// <typeparam name="TDelegate"></typeparam>
/// <returns></returns>
TDelegate GetFunction<TDelegate>();
/// <summary>
/// 函数委托调用方式
/// </summary>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <typeparam name="TDelegate">函数对应的委托类型</typeparam>
/// <param name="p_params">函数传参</param>
/// <returns></returns>
TResult Invoke<TResult, TDelegate>(params object[] p_params);
/// <summary>
/// 函数名调用
/// </summary>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="p_funName">函数名</param>
/// <param name="p_params">函数传参</param>
/// <returns></returns>
TResult Invoke<TResult>(string p_funName, params object[] p_params);
/// <summary>
/// 函数名调用
/// </summary>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="p_funName">函数名</param>
/// <param name="p_calling">调用转换方式(同PInvoke CallingConvention)</param>
/// <param name="p_params">函数传参</param>
/// <returns></returns>
TResult Invoke<TResult>(string p_funName, CallingConvention p_calling, params object[] p_params);
/// <summary>
/// 函数名调用(非泛型)
/// </summary>
/// <param name="p_funName">函数名</param>
/// <param name="p_retrunType">返回值类型</param>
/// <param name="p_params">函数传参</param>
/// <returns></returns>
object Invoke(string p_funName, Type p_retrunType, params object[] p_params);
/// <summary>
/// 函数委托调用方式
/// </summary>
/// <typeparam name="TDelegate">函数对应的委托类型</typeparam>
/// <param name="p_params">函数传参</param>
void Call<TDelegate>(params object[] p_params);
/// <summary>
/// 函数名调用
/// </summary>
/// <param name="p_funName">函数名</param>
/// <param name="p_params">函数传参</param>
void Call(string p_funName, params object[] p_params);
/// <summary>
/// 函数名调用
/// </summary>
/// <param name="p_funName">函数名</param>
/// <param name="p_calling">调用转换方式(同PInvoke CallingConvention)</param>
/// <param name="p_params">函数传参</param>
void Call(string p_funName, CallingConvention p_calling, params object[] p_params);
}
三、使用
libtest.dll 为 中包括一个test函数
int test(int input)
{
return input;
}
方法名调用
int input = 0;
int result = -1;
using (var native = NativeFactory.Create(@"../../libtest.dll"))
{
result = native.Invoke<int>("test", input);
}
dynamic 方式调用
- 优点:调用方便,简单类型调用时,不用做过多的定义。
- 缺点:4.0下性能不理想,4.5+性能好很多,但相较于委托的方式,还差些。
int input = 0;
int result = -1;
using (dynamic native = NativeFactory.Create(@"../../libtest.dll"))
{
result = native.test<int>(input);
}
委托方式调用
- 优化:效率高,没有了第一次动态构造委托的消耗,可获取到函数委托增加 重复调用消耗
- 缺点:如果函数较多,委托定义较为繁琐
[NativeFuncton("test")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Test(int p_sleep);
public void DelegateFunction()
{
int input = 0;
int result = -1;
using (var native = NativeFactory.Create(@"../../libtest.dll"))
{
// 直接调用
var result1 = native1.Invoke<int, Test>(input);
// 获取函数委托调用
var test = native.GetFunction<Test>();
result = test(input);
}
Assert.AreEqual(input, result);
}