一、C\C++ 运行时库编译选项简单说明

问题:我的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

下面是我黏贴的表格:

选项 说明
/MD

使应用程序使用运行时库的多线程并特定于 DLL 的版本。定义 _MT_DLL,并使编译器将库名 MSVCRT.lib 放入 .obj 文件中。

用此选项编译的应用程序静态链接到 MSVCRT.lib。该库提供允许链接器解析外部引用的代码层。实际工作代码包含在 MSVCR80.DLL 中,该库必须在运行时对于与 MSVCRT.lib 链接的应用程序可用。

当在定义了 _STATIC_CPPLIB (/D_STATIC_CPPLIB) 的情况下使用 /MD 时,它将导致应用程序与静态多线程标准 C++ 库 (libcpmt.lib) 而非动态版本 (msvcprt.lib) 链接,同时仍通过 msvcrt.lib 动态链接到主 CRT。

/MDd 定义 _DEBUG_MT_DLL,并使应用程序使用运行时库的调试多线程并特定于 DLL 的版本。它还使编译器将库名 MSVCRTD.lib 放入 .obj 文件中。
/MT 使应用程序使用运行时库的多线程静态版本。定义 _MT 并使编译器将库名 LIBCMT.lib 放入 .obj 文件中,以便链接器使用 LIBCMT.lib 解析外部符号。
/MTd 定义 _DEBUG_MT。此选项还使编译器将库名 LIBCMTD.lib 放入 .obj 文件中,以便链接器使用 LIBCMTD.lib 解析外部符号。
/LD

创建 DLL。

将 /DLL 选项传递到链接器。链接器查找 DllMain 函数,但并不需要该函数。如果没有编写 DllMain 函数,链接器将插入返回 TRUE 的DllMain 函数。

链接 DLL 启动代码。

如果命令行上未指定导出 (.exp) 文件,则创建导入库 (.lib);将导入库链接到调用您的 DLL 的应用程序。

/Fe(命名 EXE 文件)解释为命名 DLL 而不是 .exe 文件;默认程序名成为 basename.dll 而不是 basename.exe。

除非显式指定 /MD,否则将暗指 /MT

/LDd

  创建调试 DLL。定义 _MT_DEBUG

就从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,程序就会报错,指出堆栈调用不对称:

 

PS:写这个东西真的好费劲,上次写到现在好久了,有好多想记录一下的东西,发现自己懒得写,好懒!

制作C/C++动态链接库(dll)若干注意事项的更多相关文章

  1. VC++动态链接库(DLL)编程深入浅出(zz)

    VC++动态链接库(DLL)编程深入浅出(zz) 1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用 ...

  2. 动态链接库dll,静态链接库lib, 导入库lib

    转载地址:http://www.cnblogs.com/chio/archive/2008/08/05/1261296.html 目前以lib后缀的库有两种,一种为静态链接库(Static Libar ...

  3. 动态链接库dll,导入库lib,静态链接库lib

    目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库” ...

  4. 动态链接库DLL导出函数并导入使用

    动态链接库DLL导出函数并导入使用 本文完全参考自<vs2008制作dll笔记,回带值样例>. 首先制作DLL文件,在vs2010中新建Win32控制台项目,选择DLL选项,简历头文件,源 ...

  5. ASP.net/C#中如何调用动态链接库DLL

    动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows最重要的组成要素之一,打开Windows系统文件夹,你会发现文件夹中有很多DLL文 ...

  6. VC++动态链接库(DLL)编程深入浅出(转帖:基础班)

    1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...

  7. VC-基础:VC++动态链接库(DLL)编程深入浅出

    1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...

  8. 编译可供C#调用的C/C++动态链接库dll文件

    编译可供C#调用的C/C++动态链接库dll文件,C语言控制台应用程序,探索生成dll过程 由于项目需求,需要公司另一个团队提供相关算法支持,是用C语言编译好的dll库提供给我们进行调用. 但是拿到d ...

  9. VS2010编写动态链接库DLL及单元测试用例,调用DLL测试正确性

    转自:http://blog.csdn.net/testcs_dn/article/details/27237509 本文将创建一个简单的动态链接库,并编写一个控制台应用程序使用该动态链接库,该动态链 ...

  10. 无法加载 DLL“rasapi32.dll”: 动态链接库(DLL)初始化例程失败。

    无法加载 DLL“rasapi32.dll”: 动态链接库(DLL)初始化例程失败. 在Asp.Net项目中使用WebClient或HttpWebRequest时出现以上错误 解决方案:把以下代码放在 ...

随机推荐

  1. 鼠标HOVER时区块动画旋转变色的CSS3样式掩码

    鼠标hover时区块动画旋转变色的css3样式掩码<!DOCTYPE html> <html> <head> <meta charset="utf- ...

  2. hadoop-2.7.1基于QMJ高可用安装配置

    1.修改主机名及hosts文件 10.205.22.185 nn1 (主)作用namenode,resourcemanager,datanode,JournalNode,zk,zkfc(hive,sq ...

  3. android自定义控件实现TextView按下后字体颜色改变

    今天跟大家分享一下Android自定义控件入门,先介绍一个简单的效果TextView,按下改变字体颜色,后期慢慢扩展更强大的功能 直接看图片             第一张是按下后截的图,功能很简单, ...

  4. js浏览器各种位置检测

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 一款兼容IE6并带有多图横向滚动的jquery特效

    一款兼容IE6并带有多图横向滚动的jquery特效,自动切换多个图片的jquery特效效果, 为大家分享这个的原因是,这款特效在兼容IE6上面很完美,实用性就广很多了. 适用浏览器:IE6.IE7.I ...

  6. jquery中each()函数

    今天在使用each函数时,发现写的js代码明明木有问题,为什么点击没有执行呢.js始终处于入门阶段,只好瞎鼓捣. 弄了半天,总算可以了.代码如下: <script type="text ...

  7. 【积硅计划】html标签

    一.基础     在html中,标签通常是由开始标签和结束标签组成,开始标签用<标签名>,结束标签用</标签名>      元素是指包括开始标签和结束标签在内的整体,内容是指出 ...

  8. php中的一些小细节(1)

    <?php $a=false; echo $a; //var_dump($a); ?> 输出结果为:     (即为空): 为什么会这样子? 查看官网对echo的相关资料得出: 结论:ec ...

  9. (转)Android如何编程设置APP安装位置(外部存储或内部存储)?

    Beginning with API Level 8, you can allow your application to be installed on the external storage ( ...

  10. Oracle中Clob类型处理解析:ORA-01461:仅可以插入LONG列的LONG值赋值

    感谢原作者:破剑冰-Oracle中Clob类型处理解析 上一篇分析:ORA-01461: 仅能绑定要插入 LONG 列的 LONG 值 最近为Clob字段在插入数据时发现当字符的字节数(一个半角字符一 ...