本编所涉及到的工具以及框架:1、VisualStudio20222、。net6。0PInvok是什么?PInvoke全称为PlatformInvoke(平台调用),其实际上就是一种函数调用机制,通过PInvoke就可以实现调用非托管Dll中的函数。在开始之前,我们首先需要了解C中有关托管与非托管的区别托管(Collocation),即在程序运行时会自动释放内存;非托管,即在程序运行时不会自动释放内存。废话不多说,直接实操第一步:打开VS2022,新建一个C控制台应用右击解决方案,添加一个新建项,新建一个动态链接库(DLL),新建完之后需要右击当前项目属性CC预编译头选择不使用编译头在新建的DLL中我们新建一个头文件,用于编写我们的方法定义,然后再次新建一个C文件,后缀以。c结尾第二步:在我们DLL中的头文件(Native。h)中定义相关的Test方法,具体代码如下:pragmaonce定义一些宏ifdefcplusplusdefineEXTERNexternCelsedefineEXTERNendifdefineCallingConventioncdecl判断用户是否有输入,从而定义区分使用dllimport还是dllexportifdefDLLIMPORTdefineHEADEXTERNdeclspec(dllimport)elsedefineHEADEXTERNdeclspec(dllexport)endifHEADintCallingConventionSum(inta,intb);之后需要去实现头文件中的方法,在Native。c中实现,具体实现如下:includeNative。h导入头部文件includestdio。hHEADintAdd(inta,intb){returnab;}在这些步骤做完后,可以尝试生成解决方案,检查是否报错,没有报错之后,将进入项目文件中,检查是否生成DLL(。。x64Debug)第三步:在这里之后,就可以在C中去尝试调用刚刚所声明的方法,以便验证是否调用DLL成功,其具体实现如下:usingSystem。Runtime。InteropServices;classProgram{〔DllImport(C:MyprojectCCallCCSharpPInvokeDlldDebugNativeDll。dll)〕publicstaticexternintAdd(inta,intb);publicstaticvoidMain(string〔〕args){intsumAdd(23,45);Console。WriteLine(sum);Console。ReadKey();}} 运行结果为:68,证明我们成功调用了DLL动态链库C中通过PInvoke调用DLL动态链库的流程 通过上述一个简单的例子,我们大致了解到了在C中通过PInvoke调用DLL动态链库的流程,接下我们将对C中的代码块做一些改动,便于维护在改动中我们将用到NativeLibrary类中的一个方法,用于设置回调,解析从程序集进行的本机库导入,并实现通过设置DLL的相对路径进行加载,其方法如下:publicstaticvoidSetDllImportResolver(System。Reflection。Assemblyassembly,System。Runtime。InteropServices。DllImportResolverresolver);在使用这个方法前,先查看一下其参数a、assembly:主要是获取包含当前正在执行的代码的程序集(不过多讲解) b、resolber:此参数是我们要注重实现的,我们可以通过查看他的元代码,发现其实现的是一个委托,因此我们对其进行实现。 原始方法如下:publicdelegateIntPtrDllImportResolver(stringlibraryName,Assemblyassembly,DllImportSearchPath?searchPath);实现resolver方法:conststringNativeLibNativeDll。dll;staticIntPtrDllImportResolver(stringlibraryName,Assemblyassembly,DllImportSearchPath?searchPath){stringdllPath。Combine(newDirectoryInfo(Environment。CurrentDirectory)。Parent。Parent。Parent。Parent。ToString(),x64,Release,NativeDll。dll);此处为Dll的路径Console。WriteLine(dll);returnlibraryNameswitch{NativeLibNativeLibrary。Load(dll,assembly,searchPath),IntPtr。Zero};}该方法主要是用于区分在加载DLL时不一定只能是设置绝对路径,也可以使用相对路径对其加载,本区域代码是通过使用委托去实现加载相对路径对其DLL加载,这样做的好处是,便于以后需要更改DLL的路径时,只需要在这个方法中对其相对路径进行修改即可。更新C中的代码,其代码如下:usingSystem。Reflection;usingSystem。Runtime。InteropServices;classProgram{conststringNativeLibNativeDll。dll;〔DllImport(NativeLib)〕publicstaticexternintAdd(inta,intb);staticIntPtrDllImportResolver(stringlibraryName,Assemblyassembly,DllImportSearchPath?searchPath){stringdllPath。Combine(newDirectoryInfo(Environment。CurrentDirectory)。Parent。Parent。Parent。Parent。ToString(),x64,Release,NativeDll。dll);Console。WriteLine(dll);returnlibraryNameswitch{NativeLibNativeLibrary。Load(dll,assembly,searchPath),IntPtr。Zero};}publicstaticvoidMain(string〔〕args){NativeLibrary。SetDllImportResolver(Assembly。GetExecutingAssembly(),DllImportResolver);intsumAdd(23,45);Console。WriteLine(sum);Console。ReadKey();}}最后重新编译,检查其是否能顺利编译通过,最终我们的到的结果为:68至此,我们就完成了一个简单的C调用动态链接库的案例 下面将通过一个具体实例,讲述为什么要这样做?(本实例通过从性能方面进行对比)在DLL中的头文件中,加入如下代码:HEADvoidCBubbleSort(intarray,intlength);在。c文件中加入如下代码:HEADvoidCBubbleSort(intarray,intlength){inttemp0;for(inti0;ilength;i){for(intji1;jlength;j){if(array〔i〕array〔j〕){temparray〔i〕;array〔i〕array〔j〕;array〔j〕temp;}}}}C中的代码修改: usingSystem。Diagnostics;usingSystem。Reflection;usingSystem。Runtime。InteropServices;classProgram{conststringNativeLibNativeDll。dll;〔DllImport(NativeLib)〕publicunsafestaticexternvoidCBubbleSort(intarr,intlength);staticIntPtrDllImportResolver(stringlibraryName,Assemblyassembly,DllImportSearchPath?searchPath){stringdllPath。Combine(newDirectoryInfo(Environment。CurrentDirectory)。Parent。Parent。Parent。Parent。ToString(),x64,Release,NativeDll。dll);Console。WriteLine(dll);returnlibraryNameswitch{NativeLibNativeLibrary。Load(dll,assembly,searchPath),IntPtr。Zero};}publicunsafestaticvoidMain(string〔〕args){intnum1000;int〔〕arrnewint〔num〕;int〔〕cSharpResultnewint〔num〕;随机生成num数量个(010000)的数字RandomrandomnewRandom();for(inti0;iarr。Length;i){arr〔i〕random。Next(10000);}利用冒泡排序对其数组进行排序StopwatchswStopwatch。StartNew();Array。Copy(arr,cSharpResult,arr。Length);cSharpResultBubbleSort(cSharpResult);Console。WriteLine(34;C实现排序所耗时:{sw。ElapsedMilliseconds}ms);调用Dll中的冒泡排序算法NativeLibrary。SetDllImportResolver(Assembly。GetExecutingAssembly(),DllImportResolver);fixed(intptrarr〔0〕){sw。Restart();CBubbleSort(ptr,arr。Length);}Console。WriteLine(34;C实现排序所耗时:{sw。ElapsedMilliseconds}ms);Console。ReadKey();}冒泡排序算法publicstaticint〔〕BubbleSort(int〔〕array){inttemp0;for(inti0;iarray。Length;i){for(intji1;jarray。Length;j){if(array〔i〕array〔j〕){temparray〔i〕;array〔i〕array〔j〕;array〔j〕temp;}}}returnarray;}}执行结果:C实现排序所耗时:130msC实现排序所耗时:3ms在实现本案例中,可能在编译后,大家所看到的结果不是很出乎意料,但这只是一种案例,希望通过此案例的分析,能给大家带来一些意想不到的收获叭。最后 简单做一下总结叭,通过上述所描述的从第一步如何创建一个DLL到如何通过C去调用的一个简单实例,也应该能给正在查阅相关资料的你有所收获,也希望能给在这方面有所研究的你有一些相关的启发,同时也希望能给目前对这方面毫无了解的你有一个更进一步的学习。