欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

P/Invoke之C#調用動態(tài)鏈接庫DLL示例詳解

 更新時間:2023年03月30日 11:17:36   作者:百寶門  
這篇文章主要為大家介紹了P/Invoke之C#調用動態(tài)鏈接庫DLL示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

P/Invok是什么?

本編所涉及到的工具以及框架:

1、Visual Studio 2022

2、.net 6.0

P/Invoke全稱為Platform Invoke(平臺調用),其實際上就是一種函數調用機制,通過P/Invoke就可以實現調用非托管Dll中的函數。

在開始之前,我們首先需要了解C#中有關托管與非托管的區(qū)別

托管(Collocation),即在程序運行時會自動釋放內存;

非托管,即在程序運行時不會自動釋放內存。

廢話不多說,直接實操

第一步:

  • 打開VS2022,新建一個C#控制臺應用

1.png

  • 右擊解決方案,添加一個新建項,新建一個"動態(tài)鏈接庫(DLL)",新建完之后需要右擊當前項目--> 屬性 --> C/C++ --> 預編譯頭 --> 選擇"不使用編譯頭"

2.png

在新建的DLL中我們新建一個頭文件,用于編寫我們的方法定義,然后再次新建一個C++文件,后綴以.c 結尾

3.png

第二步:

在我們DLL中的頭文件(Native.h)中定義相關的Test方法,具體代碼如下:

#pragma once
// 定義一些宏
#ifdef __cplusplus
#define EXTERN extern "C"
#else
#define EXTERN
#endif
#define CallingConvention _cdecl
// 判斷用戶是否有輸入,從而定義區(qū)分使用dllimport還是dllexport
#ifdef DLL_IMPORT 
#define HEAD EXTERN __declspec(dllimport)
#else
#define  HEAD EXTERN __declspec(dllexport)
#endif
HEAD int CallingConvention Sum(int a, int b);

之后需要去實現頭文件中的方法,在Native.c中實現,具體實現如下:

#include "Native.h" // 導入頭部文件
#include "stdio.h"
HEAD int Add(int a, int b)
{
    return a+b;
}
  • 在這些步驟做完后,可以嘗試生成解決方案,檢查是否報錯,沒有報錯之后,將進入項目文件中,檢查是否生成DLL (../x64/Debug)

4.png

第三步:

在這里之后,就可以在C#中去嘗試調用剛剛所聲明的方法,以便驗證是否調用DLL成功,其具體實現如下:

using System.Runtime.InteropServices;
class Program
{
    [DllImport(@"C:\My_project\C#_Call_C\CSharp_P_Invoke_Dll\x64\Debug\NativeDll.dll")]
    public static extern int Add(int a, int b);
    public static void Main(string[] args)
    {
        int sum = Add(23, 45);
        Console.WriteLine(sum);
        Console.ReadKey();
    }
}

運行結果為:68,證明我們成功調用了DLL動態(tài)鏈庫

C#中通過P/Invoke調用DLL動態(tài)鏈庫的流程

  通過上述一個簡單的例子,我們大致了解到了在C#中通過P/Invoke調用DLL動態(tài)鏈庫的流程,接下我們將對C#中的代碼塊做一些改動,便于維護

在改動中我們將用到NativeLibrary類中的一個方法,用于設置回調,解析從程序集進行的本機庫導入,并實現通過設置DLL的相對路徑進行加載,其方法如下:

public static void SetDllImportResolver (System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportResolver resolver);

在使用這個方法前,先查看一下其參數

a、assembly: 主要是獲取包含當前正在執(zhí)行的代碼的程序集(不過多講解)
b、resolber: 此參數是我們要注重實現的,我們可以通過查看他的元代碼,發(fā)現其實現的是一個委托,因此我們對其進行實現。
原始方法如下:

public delegate IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath);

實現resolver方法:

const string NativeLib = "NativeDll.dll";
static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
    string dll = Path.Combine(new DirectoryInfo(Environment.CurrentDirectory).Parent.Parent.Parent.Parent.ToString(), "x64","Release", "NativeDll.dll"); // 此處為Dll的路徑
    //Console.WriteLine(dll);
    return libraryName switch
    {
        NativeLib => NativeLibrary.Load(dll, assembly, searchPath),
        _ => IntPtr.Zero
    };
}

該方法主要是用于區(qū)分在加載DLL時不一定只能是設置絕對路徑,也可以使用相對路徑對其加載,本區(qū)域代碼是通過使用委托去實現加載相對路徑對其DLL加載,這樣做的好處是,便于以后需要更改DLL的路徑時,只需要在這個方法中對其相對路徑進行修改即可。

更新C#中的代碼,其代碼如下:

using System.Reflection;
using System.Runtime.InteropServices;
class Program
{
    const string NativeLib = "NativeDll.dll";
    [DllImport(NativeLib)]
    public static extern int Add(int a, int b);
    static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
    {
        string dll = Path.Combine(new DirectoryInfo(Environment.CurrentDirectory).Parent.Parent.Parent.Parent.ToString(), "x64","Release", "NativeDll.dll");
        Console.WriteLine(dll);
        return libraryName switch
        {
            NativeLib => NativeLibrary.Load(dll, assembly, searchPath),
            _ => IntPtr.Zero
        };
    }
    public static void Main(string[] args)
    {
        NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver);
        int sum = Add(23, 45);
        Console.WriteLine(sum);
        Console.ReadKey();
    }
}

最后重新編譯,檢查其是否能順利編譯通過,最終我們的到的結果為:68

至此,我們就完成了一個簡單的C#調用動態(tài)鏈接庫的案例

  下面將通過一個具體實例,講述為什么要這樣做?(本實例通過從性能方面進行對比)

在DLL中的頭文件中,加入如下代碼:

HEAD void CBubbleSort(int* array, int length);

在.c文件中加入如下代碼:

HEAD void CBubbleSort(int* array, int length)
{
    int temp = 0;
    for (int i = 0; i < length; i++)
    {
        for (int j = i + 1; j < length; j++)
        {
            if (array[i] > array[j])
            {
                temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
    }
}

C#中的代碼修改:

using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
class Program
{
    const string NativeLib = "NativeDll.dll";
    [DllImport(NativeLib)]
    public unsafe static extern void CBubbleSort(int* arr, int length);
    static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
    {
        string dll = Path.Combine(new DirectoryInfo(Environment.CurrentDirectory).Parent.Parent.Parent.Parent.ToString(), "x64", "Release", "NativeDll.dll");
        //Console.WriteLine(dll);
        return libraryName switch
        {
            NativeLib => NativeLibrary.Load(dll, assembly, searchPath),
            _ => IntPtr.Zero
        };
    }
    public unsafe static void Main(string[] args)
    {
        int num = 1000;
        int[] arr = new int[num];
        int[] cSharpResult = new int[num];
        //隨機生成num數量個(0-10000)的數字
        Random random = new Random();
        for (int i = 0; i < arr.Length; i++)
        {
            arr[i] = random.Next(10000);
        }
        //利用冒泡排序對其數組進行排序
        Stopwatch sw = Stopwatch.StartNew();
        Array.Copy(arr, cSharpResult, arr.Length);
        cSharpResult = BubbleSort(cSharpResult);
        Console.WriteLine($"\n C#實現排序所耗時:{sw.ElapsedMilliseconds}ms\n");
        // 調用Dll中的冒泡排序算法
        NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver);
        fixed (int* ptr = &arr[0])
        {
            sw.Restart();
            CBubbleSort(ptr, arr.Length);
        }
        Console.WriteLine($"\n C實現排序所耗時:{sw.ElapsedMilliseconds}ms");
        Console.ReadKey();
    }
    //冒泡排序算法
    public static int[] BubbleSort(int[] array)
    {
        int temp = 0;
        for (int i = 0; i < array.Length; i++)
        {
            for (int j = i + 1; j < array.Length; j++)
            {
                if (array[i] > array[j])
                {
                    temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }
        return array;
    }
}

執(zhí)行結果:

C#實現排序所耗時: 130ms
C實現排序所耗時:3ms

在實現本案例中,可能在編譯后,大家所看到的結果不是很出乎意料,但這只是一種案例,希望通過此案例的分析,能給大家?guī)硪恍┮庀氩坏降氖斋@叭。

最后

簡單做一下總結叭,通過上述所描述的從第一步如何創(chuàng)建一個DLL到如何通過C#去調用的一個簡單實例,也應該能給正在查閱相關資料的你有所收獲,也希望能給在這方面有所研究的你有一些相關的啟發(fā),同時也希望能給目前對這方面毫無了解的你有一個更進一步的學習。

以上就是P/Invoke之C#調用動態(tài)鏈接庫DLL示例詳解的詳細內容,更多關于P/Invoke C#調用DLL的資料請關注腳本之家其它相關文章!

相關文章

最新評論