动态链接库导入库和静态链接库的区别:
目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。
静态库:静态库扩展名为.lib,静态链接(即代码会直接编译进可执行文件)。静态库是一个或多个obj文件的打包。
动态链接库导入库:
1.动态库不会编译进可执行文件中,多个程序引用动态库时,内存中实际只会有一份动态库的内容。用VC创建动态库时,实际生成两个文件,一个lib文件和一个dll文件,这个lib文件就是导入库。导入库是动态库的辅助库,导入库中不含代码,而是为链接程序提供信息,包含在.exe文件中建立动态链接时要用到的重定位表。导入库用于程序开发时,动态库用于程序运行时。
2. 在项目中使用动态库的方法:
方法一:隐式加载: ①在项目属性中添加附加依赖项。若库目录不在系统和当前目录,选中工程,单击右键,属性-->VC++目录,在库目录中添加库文件所在路径;②选中工程,单击右键,属性-->链接器-->输入,在"附加依赖项“右边选择编辑,输入要包含的静态库名称。
方法二:显式加载(又称运行时动态链接):在代码中使用LoadLibrary()显式打开dll文件,使用GetProcAddress获取函数地址然后使用,使用完之后用FreeLibrary显式释放dll文件。这种方式不需要导入库及.h文件。
使用隐式加载时,如果进程在启动时未找到dll,则操作系统将终止此进程。但使用显式加载时则进程不会被终止。
3. 在VC中生成动态库和导入库:
选中工程,单击右键,属性-->常规,在"目标文件扩展名"后输入.dll,在"配置类型"中选择"动态库(.dll)"
注意: 对于要导出给外部程序使用的函数及数据,均应具有_declspec(dllexport)。如果dll源码中没有一个_declspec(dllexport),则不会生成导入库。
4. DllMain函数:
DllMain函数为dll的入口函数,该函数不是必须的。该函数在dll被加载进程地址空间时运行(即隐式加载时,在main()函数执行之前就已经运行;显式加载时,在LoadLibrary时运行)。
5.确定要使用的链接方法:
有两种类型的链接:隐式链接和显式链接。
隐式链接
1.应用程序的代码调用导出 DLL 函数时发生隐式链接。当调用可执行文件的源代码被编译或被汇编时,DLL 函数调用在对象代码中生成一个外部函数引用。若要解析此外部引用,应用程序必须与 DLL 的创建者所提供的导入库(.LIB 文件)链接。
2.导入库仅包含加载 DLL 的代码和实现 DLL 函数调用的代码。在导入库中找到外部函数后,会通知链接器此函数的代码在DLL 中。要解析对 DLL 的外部引用,链接器只需向可执行文件中添加信息,通知系统在进程启动时应在何处查找 DLL 代码。
3.系统启动包含动态链接引用的程序时,它使用程序的可执行文件中的信息定位所需的 DLL。如果系统无法定位 DLL,它将终止进程并显示一个对话框来报告错误。否则,系统将 DLL 模块映射到进程的地址空间中。
4.如果任何 DLL 具有(用于初始化代码和终止代码的)入口点函数,操作系统将调用此函数。在传递到入口点函数的参数中,有一个指定用以指示 DLL 正在附带到进程的代码。如果入口点函数没有返回 TRUE,系统将终止进程并报告错误。最后,系统修改进程的可执行代码以提供 DLL 函数的起始地址。
5.与程序代码的其余部分一样,DLL 代码在进程启动时映射到进程的地址空间中,且仅当需要时才加载到内存中。因此,由 .def 文件用来在 Windows 的早期版本中控制加载的 PRELOAD 和 LOADONCALL 代码属性不再具有任何意义。
显式链接
大部分应用程序使用隐式链接,因为这是最易于使用的链接方法。但是有时也需要显式链接。下面是一些使用显式链接的常见原因:
1.直到运行时,应用程序才知道需要加载的DLL 的名称。例如,应用程序可能需要从配置文件获取 DLL 的名称和导出函数名。
2.如果在进程启动时未找到 DLL,操作系统将终止使用隐式链接的进程。同样是在此情况下,使用显式链接的进程则不会被终止,并可以尝试从错误中恢复。例如,进程可通知用户所发生的错误,并让用户指定 DLL 的其他路径。如果使用隐式链接的进程所链接到的 DLL 中有任何 DLL 具有失败的 DllMain 函数,该进程也会被终止。同样是在此情况下,使用显式链接的进程则不会被终止。
3.因为Windows 在应用程序加载时加载所有的DLL,故隐式链接到许多 DLL 的应用程序启动起来会比较慢。为提高启动性能,应用程序可隐式链接到那些加载后立即需要的 DLL,并等到在需要时显式链接到其他 DLL。
4.显式链接下不需将应用程序与导入库链接。如果DLL 中的更改导致导出序号更改,使用显式链接的应用程序不需重新链接(假设它们是用函数名而不是序号值调用 GetProcAddress),而使用隐式链接的应用程序必须重新链接到新的导入库。
下面是需要注意的显式链接的两个缺点:
1.如果 DLL 具有 DllMain 入口点函数,则操作系统在调用 LoadLibrary 的线程上下文中调用此函数。如果由于以前调用了LoadLibrary 但没有相应地调用 FreeLibrary 函数而导致 DLL 已经附加到进程,则不会调用此入口点函数。如果 DLL 使用 DllMain 函数为进程的每个线程执行初始化,显式链接会造成问题,因为调用 LoadLibrary(或 AfxLoadLibrary)时存在的线程将不会初始化。
2.如果 DLL 将静态作用域数据声明为__declspec(thread),则在显式链接时 DLL 会导致保护错误。用 LoadLibrary 加载 DLL 后,每当代码引用此数据时 DLL 就会导致保护错误。(静态作用域数据既包括全局静态项,也包括局部静态项。)因此,创建 DLL 时应避免使用线程本地存储区,或者应(在用户尝试动态加载时)告诉 DLL 用户潜在的缺陷。
隐式链接:
为隐式链接到 DLL,可执行文件必须从 DLL 的提供程序获取下列各项:
1.包含导出函数和/或 C++ 类的声明的头文件(.h 文件)。类、函数和数据均应具有 __declspec(dllimport),有关更多信息,请参见 dllexport, dllimport。
2.要链接的导入库(.LIB files)。(生成 DLL 时链接器创建导入库。)
3.实际的 DLL(.dll 文件)。
使用 DLL 的可执行文件必须包括头文件,此头文件包含每个源文件中的导出函数(或 C++ 类),而这些源文件包含对导出函数的调用。从编码的角度讲,导出函数的函数调用与任何其他函数调用一样。
若要生成调用可执行文件,必须与导入库链接。如果使用的是外部生成文件,请指定导入库的文件名,此导入库中列出了要链接到的其他对象 (.obj) 文件或库。
操作系统在加载调用可执行文件时,必须能够定位 DLL 文件。
显式链接:
在显式链接下,应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:
1.调用 LoadLibrary(或相似的函数)以加载DLL 和获取模块句柄。
2.调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。
3.使用完 DLL 后调用FreeLibrary。
C++的dllexport和dllimport
__declspec(dllexport)
声明一个导出函数,是说这个函数要从本DLL导出。我要给别人用。一般用于dll中省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类
__declspec(dllimport)
声明一个导入函数,是说这个函数是从别的DLL导入。我要用。一般用于使用某个dll的exe中不使用 __declspec(dllimport) 也能正确编译代码,但使用__declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
引用来自:http://blog.csdn.net/finewind/article/details/44959039
http://www.cnblogs.com/qinfengxiaoyue/archive/2012/05/27/2519703.html
http://blog.163.com/hbu_lijian/blog/static/1261291532013031101546987/