【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);
}