溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么

發(fā)布時間:2023-03-30 14:00:27 來源:億速云 閱讀:130 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么文章都會有所收獲,下面我們一起來看看吧。

    P/Invok是什么?

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

    1、Visual Studio 2022

    2、.net 6.0

    P/Invoke全稱為Platform Invoke(平臺調(diào)用),其實(shí)際上就是一種函數(shù)調(diào)用機(jī)制,通過P/Invoke就可以實(shí)現(xiàn)調(diào)用非托管Dll中的函數(shù)。

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

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

    非托管,即在程序運(yùn)行時不會自動釋放內(nèi)存。

    廢話不多說,直接實(shí)操

    第一步:

    • 打開VS2022,新建一個C#控制臺應(yīng)用

    P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么

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

    P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么

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

    P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么

    第二步:

    在我們DLL中的頭文件(Native.h)中定義相關(guān)的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);

    之后需要去實(shí)現(xiàn)頭文件中的方法,在Native.c中實(shí)現(xiàn),具體實(shí)現(xiàn)如下:

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

    P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么

    第三步:

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

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

    運(yùn)行結(jié)果為:68,證明我們成功調(diào)用了DLL動態(tài)鏈庫

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

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

    在改動中我們將用到NativeLibrary類中的一個方法,用于設(shè)置回調(diào),解析從程序集進(jìn)行的本機(jī)庫導(dǎo)入,并實(shí)現(xiàn)通過設(shè)置DLL的相對路徑進(jìn)行加載,其方法如下:

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

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

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

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

    實(shí)現(xiàn)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時不一定只能是設(shè)置絕對路徑,也可以使用相對路徑對其加載,本區(qū)域代碼是通過使用委托去實(shí)現(xiàn)加載相對路徑對其DLL加載,這樣做的好處是,便于以后需要更改DLL的路徑時,只需要在這個方法中對其相對路徑進(jìn)行修改即可。

    更新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();
        }
    }

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

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

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

    在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];
            //隨機(jī)生成num數(shù)量個(0-10000)的數(shù)字
            Random random = new Random();
            for (int i = 0; i < arr.Length; i++)
            {
                arr[i] = random.Next(10000);
            }
            //利用冒泡排序?qū)ζ鋽?shù)組進(jìn)行排序
            Stopwatch sw = Stopwatch.StartNew();
            Array.Copy(arr, cSharpResult, arr.Length);
            cSharpResult = BubbleSort(cSharpResult);
            Console.WriteLine($"\n C#實(shí)現(xiàn)排序所耗時:{sw.ElapsedMilliseconds}ms\n");
            // 調(diào)用Dll中的冒泡排序算法
            NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver);
            fixed (int* ptr = &arr[0])
            {
                sw.Restart();
                CBubbleSort(ptr, arr.Length);
            }
            Console.WriteLine($"\n C實(shí)現(xiàn)排序所耗時:{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í)行結(jié)果:

    C#實(shí)現(xiàn)排序所耗時: 130ms
    C實(shí)現(xiàn)排序所耗時:3ms

    關(guān)于“P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“P/Invoke之C#調(diào)用動態(tài)鏈接庫DLL的方法是什么”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    dll
    AI