在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
问题:我的dll别人没法用运行时库是个很复杂的东西,作为开发过程中dll制作需要了解的一部分,这里主要简单介绍一下如何选择编译选项。 在我们的开发过程中时常会遇到这样的问题: 1. 我的VS版本比较高(比如:VS2012),我想制作一个dll,封装了几个函数给别人用。 2. 打包后发现我的dll引用了msvcr110.dll或者msvcr110d.dll,这个dll别人电脑可能没有。 3. 于是别人使用时出现了诸如:“无法在DLL“XXXX.dll”中找到名为“XXXX()”的入口点”等问题。 最终结果就是,反复检查发现都没有错,用工具查看也发现函数确实已经导出了,但是别人就没法用。 这里可能就需要对编译选项进行修改了。 解释:如何避免上述问题在VS中打开:项目属性——>配置属性——>C/C++——>代码生成——>运行时。其中可以看到多个选项,如下图所示: 在微软的msdn中对CRT库进行了简单解释: https://msdn.microsoft.com/zh-cn/library/2kzt1wy3(VS.80).aspx https://msdn.microsoft.com/zh-cn/library/abx4dbyh(v=vs.110).aspx 下面是我黏贴的表格:
就从VS的dll库的编译选项来说就前面四项,/MD、/MDd、/MT和/MTd。其中/MDd、/MTd后面的“d”表示编译生成的是Debug版本,也就是用于编译生成Debug版本的程序;不加“d”表示是Release版本的程序,如果此时你把项目配置成Debug版本,编译会不通过。 动态链接多线程库(MD/MDd)动态链接的运行时库,此时将msvcrt.lib安置到obj文件中,它连接到dll的方式是静态链接,实际上工作的库是msvcrxx.dll。所有的 C 库函数保存在动态链接库 msvcrXX.dll中, 由msvcrXX.dll处理多线程问题。也就是说,这种编译方式下我们是通过msvcrXX.dll这个动态链接库去链接CRT。 此时我们编译的dll引用了msvcrXX.dll文件,这个文件在使用的时候必须能够被计算机查询到。比如:我的电脑编译引用了msvcr110.dll,那么我的dll给别人调用的时候,对方计算机一定要有msvcr110.dll,否则dll库就无法使用。因此这种编译方式,必须将msvcr110.dll一同携带过去,并且要在对方计算机上进行注册。 使用此方式编译时,使用Depends查看,我们可以看到dll 引用了msvcr110.dll,如下图:
静态链接多线程库(MT/MTd)静态链接多线程库,此时编译器把LIBCMT.lib 安置到OBJ文件中,让链接器使用LIBCMT.lib 处理外部符号。这种方式说简单点就是让我们的dll能够直接链接到CRT,无需引用msvcrXX.dll这个动态链接库。这样我们就不需要担心对方电脑是否拥有对应版本的msvcrXX.dll,因此这是一种方便的好办法。 在这种方式下,编译的dll通过Depends查看就是这样的: 注意:几个注意点光从上面来看,MT的方式很好用,但是必须要注意一个问题: 不要混合使用库的静态版本和动态版本。在一个进程中有多个库副本会导致问题,因为副本中的静态数据不与其他副本共享。(还应该避免在一个进程中混合使用这些库的调试版本和非调试版本)。 还有,在实际使用中能用MD就用MD的方式,因为这种方式软件更小,调用同一个dll时在内存中使用的是同一个副本。这就不会有堆空间释放问题: 不同的模块各自有一份C运行时库代码,各个C运行库会有各自的堆,导致了各个模块会有各自的堆。如果在A堆中申请空间,到B堆中释放就会有崩溃,在模块A申请的空间,必须在模块A中释放。
二、函数调用方式_cdecl与_stdcall的选择_stdcall调用
_stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈。形式如下: #define SW_REV extern "C" _declspec(dllexport) SW_REV int __stdcall add(int a, int b); int __stdcall add(int a, int b) { return a+b; }
_cdecl调用_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护。_cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。 在不加修饰的情况下,VC++默认使用这种调用方式,形式如下(默认,可省略): #define SW_REV extern "C" _declspec(dllexport) SW_REV int add(int a, int b); int add(int a, int b) { return a+b; } 什么时候区分?其实两种方式都一样,区别在于调用。例如:有些语言要求调用时只认_stdcall,那么就只能按照要求使用_stdcall版本的dll。很多情况下,调用方式都可选择,说白了,就是一定要保持一致,例如C#调用上述_stdcall方式的dll: [DllImport("SwLib.dll", EntryPoint = "add", CallingConvention = CallingConvention.StdCall)] private static extern int add(int a, int b);
这里CallingConvention需要选择CallingConvention.StdCall,如果我将其改成 CallingConvention.Cdecl,程序就会报错,指出堆栈调用不对称:
https://www.cnblogs.com/sleepwalker/p/5016610.html |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论