静态库和动态库的使用包括两个方面,1是使用已有的库(调用过程),2是编写一个库供别人使用(创建过程)。这里不讲述过多的原理,只说明如何编写,以及不正确编写时会遇见的问题。

//注:本文先从简单到复杂,动态库的部分先说明了静态链接方式,比较简单,若想看动态链接过程会遇到的问题可直接跳过。

后面说明动态链接方式有关extern "C” 、名字改变、 __stdcall 的影响的问题。

1.静态库

1)创建过程

在VS环境下创建一个 “Win32 Static Libarary” 工程StaticLib,添加头文件lib.h和源文件lib.cpp

//----lib.h----------
int add(int a,int b); //-----lib.cpp-------
#include "lib.h" int add(int a,int b)
{
return a+b;
}

  Build之后会发现Debug下生成了StaticLib.lib  静态库文件。   将lib.h和StaicLib.lib给别人,别人就可以使用库中的函数add了。

2)调用过程

新建一个简单的控制台工程,只有一个StaticLibCall.cpp 。 将lib.h和StaticLib.lib放在同目录下。

#include "lib.h"
#pragma comment(lib,"StaticLib.lib") #include <iostream>
using namespace std; int main()
{
int t = add(2,3);
cout<<t<<endl;
getchar();
}

  编译链接运行成功。

此处若在add(2,3)设置断点,调试态 F11进入函数可以跳入到StaticLib.cpp中进行执行。可以知道静态库在调用过程中是会和源文件一起编译链接的。

其中  #pragma comment(lib,"StaticLib.lib") 是用来说明静态库调用,也可以在VS界面上设置:依次选择tools、options、directories、library files添加。

  标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。F11同样可以进入对应的**.c文件中。

  

2. 动态库

动态库比静态库的创建和调用都复杂。因为动态库中的函数分为两种,1种为内部函数,只供库内部使用。 2种为导出函数,只有声明为导出函数,才可以给别人使用。

创建时声明导出方式有2种:  1.__declspec(dllexport) 方式

2.DEF文件方式

使用时链接动态库方式也有2种:  1.静态链接方式 :同静态库的调用方式   #pragma comment(lib,"***.lib")

2.动态链接方式:使用Win32系列函数:LoadLibrary(...)  GetProcAddress(...)  FreddLibrary(...)

所以,组合起来有4种方式完成动态库的 ”创建和使用“ 的过程。 由于不同交叉的方式中注意的问题不同,所以分别说明。

1)__declspec(dllexport) 方式导出方式,静态方式链接

A.创建:  在VS环境下创建一个 “Win32 DLL ” 工程AddDll,添加头文件dll.h和源文件dll.cpp

//-----dll.h---------
__declspec(dllexport) int add(int x, int y); //-----dll.cpp---------
#include "dll.h" __declspec(dllexport) int add(int x, int y)
{
return x + y;
}

  Build之后发现,Debug中生成了AddDll.lib和AddDll.dll文件。  将.h .lib .dll提供给别人,别人就可以使用动态库中的add函数了。

B. 静态方式调用:

新建一个控制台工程,只有一个AddDllCall.cpp 文件。 并且将 dll.h放在同目录下,AddDll.lib  AddDll.dll 放在该工程的Debug下。

注意:因为动态库是在运行时才调用,所以必须放在运行时的目录下,否则会找不到dll库。

//---------AddDllCall.cpp----- 

#include <iostream>
using namespace std ;
#include "dll.h"
#pragma comment(lib,"AddDll.lib") int _tmain(int argc, _TCHAR* argv[])
{
int t = add(2,3);
cout<<t<<endl;
getchar();
return 0;
}

  运行成功。

注:有些人说,导出方式为__declspec(dllexport)时,调用时的函数声明需要为__declspec(dllimport) add(int a,int b);这里测验发现并没有这个问题

调用时#include ”dll.h" 只有一句话 int add(int a,int b);正常的函数声明。

 2)DEF文件导出函数,静态链接方式调用

A.创建:  在VS环境下创建一个 “Win32 DLL ” 工程AddDll,添加头文件dll.h和源文件dll.cpp ,并添加DEF文件:dll.def 文件

//-----dll.h---------
int add(int x, int y); //-----dll.cpp---------
#include "dll.h" int add(int x, int y)
{
return x + y;
} //----dll.def--------
LIBRARY BlogUse
EXPORTS add @ 1

  编译链接生成AddDll.lib AddDll.dll . 将*.h *.lib *.dll提供给别人。

B. 静态方式调用:

新建一个控制台工程,只有一个AddDllCall.cpp 文件。 并且将 dll.h,AddDll.lib 放在同目录下。  AddDll.dll 放在该工程的Debug下。

注意:此处与上处不同,这里的 AddDll.lib 必须和.h一起放在AddDllCall.cpp的同目录下,否则会找不到lib.或者显示指定路径。

代码同上,只是放了不同的这三个文件。

3)__declspec(dllexport) 方式导出函数,动态链接方式使用动态库

A.创建: 同1)中一样 在VS环境下创建一个 “Win32 DLL ” 工程AddDll,添加头文件dll.h和源文件dll.cpp 。代码也一样

B.动态链接方式:

新建一个控制台工程,只有一个AddDllCall.cpp 文件。 同样将 lib 和 dll放在Debug下。代码不多解释,可以参看文章最后给出的链接。

#include <iostream>
using namespace std;
#include "dll.h"
//int add(int x, int y); typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
#pragma warning(disable:4996) int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hDll; //DLL句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary(TEXT("..//Debug//AddDll.dll")); if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, "add");
if (addFun != NULL)
{
int result = addFun(2, 3);
cout<< result <<endl;
}
FreeLibrary(hDll);
}
getchar();
return 0;
}

  运行,发现没反应,没有输出想要的5。这里强调,调用方式没有任何问题,声明函数,再获得函数地址,用指针调用,没有任何问题。

那问题出在哪里了呢???

调试状态发现addFun句柄返回了null,也就是 GetProcAddress(hDll, "add"); 没有正确的函数。这是为什么呢?通过函数名“add"查找函数,有问题吗?

了解过这块的人会发现,很多动态库的导出函数 都有extern "C" __decl~~~ 这里extern "C" 是什么意思呢?是不是这个的原因呢?

 没错,问题就出在extern "C"上。  因为没有加extern "C"之前,动态库里的add函数根本不叫”add".

回到创建的代码,使用工具Depends.  ( 该工具找不到可以在命令行 cmd 下 输入Depend.exe 就弹出来了。)

发现导出的函数不叫"add",而是一大串 ?add@YAHHH@Z 。这是因为C语言和C++编译方式不同造成的。

所以GetProcAddress用 "add"去查找时,根本找不到函数。

将创建的代码其他都不变,就把add函数的声明和定义前,加上extern "C" 。Build之后再看

导出的函数名变成了 “add". 使用这个lib和dll会发现GetProcAddress 正常执行了,也输出了5.

所以在用__declspec(dllexport)方式导出函数时,一般都添加extern ”C". 且给对方提供的头文件中函数的声明为import 

externa " C" __declspec(import)  int add(int a,int b);

  注意:extern "C"只解决了C和C++语方之间调用的问题,它只能用于导出全局函数这种情况而不能导出一个类的成员函数。

另外如果导出函数的调用约定发生改变,即使使用了extern "C",编译后的函数名还是会发生改编。比如我们加入_stdcall关键字说明调用约定为C调用约定.

将创建当中的函数声明和定义修改为如下:

extern "C"  __declspec(dllexport)  int __stdcall add(int x, int y)
{
return x + y;
}

  查看发现,函数名还是改变了。

关于__stdcall 和 __cdecl可以参看编译原理的东西。windows下多用__stdcall的方式,CALLBACK,WINAPI看宏定义发现都是__stdcall的重定义。

而C/c++语言默认__cdecl的方式。

调用方式不同不仅影响函数名的变化,最主要影响函数栈和回收等问题。接下的DEF文件中我们也会看到。

4)DEF文件方式导出函数,动态方式调用

A.创建   同2)中,提供 .h .lib .dll

B.使用  同3)中使用过程

使用Depend工具查看创建的文件,发现,add依然叫add. 所以调用时用add函数名可以找到,正确执行。

但是当加上__stdcall调用方式后,发现,add依然叫add 。

但是提供给调用函数调用时发现,编译链接通过,运行的时错误如下:

调试发现,add函数正确执行了,并返回了5 ,但是main函数返回的时候,出现了如上运行错误。

这是因为,向上面所说的,__stdcall等调用方式不仅影响函数名字,更重要的是影响 函数栈的调用方式和回收方式。具体参考,编译原理 的 运行时刻环境 一章。

因此,两种导出方式都无法解决__stdcall调用方式的问题。DLL文件与调用方必须是同一种方式,否则会函数不匹配 或者 函数栈出错。

总结: 综上对比,可以发现,静态调用方式很简单,不用考虑很多。因为静态方式是由编译器完成了一起编译,因此使用就像使用内部函数一样。

动态方式是用系统APII来加载、卸载DLL以及获取DLL中导出函数的地址,由程序决定加载和释放的位置。

动态调用方式必须使用LoadLibrary GetProcAddress函数用指针调用,而不能直接使用 add(2,3).

DEF导出方式更简单,不用写太多,考虑太多。

另外,还有导出变量和导出类的内容。此处不详说。

只说明,导出变量需注意,使用的时候,得到的是地址,使用内容必须 cout<< *(int*) g_value <<endl;

导出类的时候,两边都需要有类的定义,创建方用__declspec(export)  调用方用 __declspec(import)  .

参考: http://blog.csdn.net/friday5pm/article/details/1532226

http://blog.csdn.net/anye3000/article/details/7481481

//--------------呼,写了好几个小番茄啊,测了很多问题,应该还有一些问题没有考虑全。希望再接再厉,总结自己的收货,提高自己也帮助别人,开森!!

Windows下静态库、动态库的创建和调用过程的更多相关文章

  1. Golang调用windows下的dll动态库中的函数

    Golang调用windows下的dll动态库中的函数 使用syscall调用. package main import ( "fmt" "syscall" & ...

  2. Golang调用windows下的dll动态库中的函数 Golang 编译成 DLL 文件

    Golang调用windows下的dll动态库中的函数 package main import ( "fmt" "syscall" "time&quo ...

  3. Windows下静态库与动态库的创建与使用

    Windows下静态库与动态库的创建与使用 学习内容:本博客介绍了Windows下使用Visual C++ 6.0制作与使用静态库与动态库的方法. --------CONTENTS-------- 一 ...

  4. windows库的创建和使用:静态库+动态库

    windows库的创建和使用:静态库+动态库   一.静态库的创建和使用 1. 静态库创建 (1)首先创建projecttest,測试代码例如以下: 1) test.h void test_print ...

  5. linux静态与动态库创建及使用实例

    一,gcc基础语法: 基本语法结构:(由以下四部分组成) gcc -o 可执行文件名 依赖文件集(*.c/*.o) 依赖库文件及其头文件集(由-I或-L与-l指明) gcc 依赖文件集(*.c/*.o ...

  6. Linux中创建和使用静态库&动态库

    库本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行 Linux下库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同. 静态库的代码在 ...

  7. C/C++ 跨平台交叉编译、静态库/动态库编译、MinGW、Cygwin、CodeBlocks使用原理及链接参数选项

    目录 . 引言 . 交叉编译 . Cygwin简介 . 静态库编译及使用 . 动态库编译及使用 . MinGW简介 . CodeBlocks简介 0. 引言 UNIX是一个注册商标,是要满足一大堆条件 ...

  8. 如何在WINDOWS下编译BOOST C++库 .

    如何在WINDOWS下编译BOOST C++库 cheungmine 2008-6-25   写出来,怕自己以后忘记了,也为初学者参考.使用VC8.0和boost1.35.0.   1)下载boost ...

  9. Linux 静态库&动态库调用

    1.什么是库在windows平台和linux平台下都大量存在着库.本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行.由于windows和linux的本质不同,因此二者库的二进制是不 ...

随机推荐

  1. (转载)管道命令和xargs的区别(经典解释)

    一直弄不懂,管道不就是把前一个命令的结果作为参数给下一个命令吗,那在 | 后面加不加xargs有什么区别 NewUserFF 写道:懒蜗牛Gentoo 写道:管道是实现“将前面的标准输出作为后面的标准 ...

  2. JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架

    1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...

  3. SQL使用链接服务器执行远程数据库上的存储过程

    原文:SQL使用链接服务器执行远程数据库上的存储过程 --创建链接服务器 exec sp_addlinkedserver'server_tmp','','SQLOLEDB','远程服务器名或ip地址' ...

  4. 利用jquery.form.js实现将form提交转为ajax方式提交的方法(带上传的表单提交)

    提供一种方法就是利用jquery.form.js. (1)这个框架集合form提交.验证.上传的功能. 核心方法 -- ajaxForm() 和 ajaxSubmit() $('#myForm').a ...

  5. sed命令使用示例

    sed -i '/mirrorlist/d' CentOS-Base-163.repo 把有mirrorlist的行删除sed -i '/\[addons\]/,/^$/d' CentOS-Base- ...

  6. 设计模式之单例模式(php实现)

    github地址:https://github.com/ZQCard/design_pattern 单例模式:顾名思义就就是创建单个实例的模式. 优点:保证一个类仅有一个实例,并提供一个访问它的全局访 ...

  7. homebrew代理设置

    方法一 brew用curl下载,所以给curl挂上socks5的代理即可. 在~/.curlrc文件中输入代理地址即可. socks5 = "127.0.0.1:1080" 方法二 ...

  8. JComboBox添加item的赋值类型问题!不一致的话会导致不能更改jcombobox的选择值

    在用swing做页面的时候,往往需要设置字体样式,那么,如何用一种方法设置字体之后,在后面的其他页面就不需要再次设置字体了呢? 下面这个方法就可以解决了: JComboBox在对它进行添加子项的时候, ...

  9. 在eclipse中使用Lombok

    1.下载Lombok.jar http://projectlombok.googlecode.com/files/lombok.jar2.运行Lombok.jar: java -jar  D:\001 ...

  10. 13 Basic Cat Command Examples in Linux

    FROM: http://www.tecmint.com/13-basic-cat-command-examples-in-linux/ The cat (short for “concatenate ...