DLL【动态链接库】加载方式、导出变量、导出类
一、DLL【动态链接库】定义:DLL是一个包含可由多个程序同时使用的代码和数据的库。
1.为什么要使用DLL:提高软件开发效率。
2.加载DLL:加载时动态链接和运行时动态链接
/*示例代码TestDll.h头文件*/
#ifndef _TESTDLL_H_
#define _TESTDLL_H_
#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
extern "C" TESTDLL_API int Add (int a , int b);
extern "C" TESTDLL_API int GetMaxNumber(int a, int b);
#endif
/////////////////////////////////////////////////////////////////
/*示例代码estDll.cpp文件*/
#include "stdafx.h"
#include "TestDll.h"
extern "C" TESTDLL_API int Add(nt a , int b)
{
return a+b;
}
extern "C" TESTDLL_API int GetMaxNumber(int a,int b)
{
return a > b ? a:b;
}
二、DLL加载方式
1.加载时动态链接:应用程序像调用本地函数一样对导出的DLL函数进行显示调用。要使用加载时动态链接,需要在编译和链接应用程序时提供头文件和导入库文件(.lib)。当这样做的时候,链接器将向系统提供加载DLL所需的信息,并在加载时解析导出的DLL函数的位置;
//加载时动态链接
#include <windows.h>
#include <iostream>
//#include "TestDll.h"
using namespace std;
#pragma comment(lib, "TestDll.lib")
extern "C" _declspec(dllimport) int Add(int a, int b);
extern "C" _declspec(dllimport) int GetMaxNumber(int a, int b);
int main(int argc, char *argv[])
{
cout<<Add(2, 3)<<endl;
cout<<GetMaxNumber(9,18)<<endl;
return 0;
}
2.运行时动态链接:应用程序调用LoadLibrary函数或LoadLibraryEx函数以在运行时加载DLL。成功加载DLL后,可以使用GetProcAddress函数获得要调用的导出的DLL函数的地址。在使用运行时动态链接时,不需要使用导入库文件,结束后需调用FreeLibrary释放DLL。
//运行时动态链接
#include <windows.h>
#include <iostream>
using namespace std;
typedef int (*AddFunc)(int a, int b);
typedef int(*GetMaxNumFunc)int a, int b);
int main(int argc, char *argv[])
{
HMODULE hDll = LoadLibrary("TestDll.dll");
if (hDll != NULL)
{
AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
GetMaxNumFunc num = (GetMaxNumFunc)GetProcAddress(hDll, "GetMaxNumber");
if (add != NULL && num != NULL)
{
cout<<add(2, 3)<<endl;
cout<<GetMaxNumber(9,18)<<endl;
}
FreeLibrary(hDll);
}
}
3.选择加载方式的主要考虑因素:
A.启动性能如果应用程序的初始启动性能很重要,则应使用运行时动态链接;
B.易用性在加载时动态链接中,导出的DLL函数类似于本地函数,我们可以方便地进行这些函数的调用;
C.应用程序逻辑在运行时动态链接中,应用程序可以分支,以便按照需要加载不同的模块。
三、DllMain函数:DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DllMain函数也被调用。参数ul_reason_for_call指明了调用DllMain的原因,有以下四种情况:
1):DLL_PROCESS_ATTACH:当一个DLL被首次载入进程地址空间时,系统会调用该DLL的DllMain函数,传递的ul_reason_for_call参数值为DLL_PROCESS_ATTACH。这种情况只有首次映射DLL时才发生;
2):DLL_THREAD_ATTACH:该通知告诉所有的DLL执行线程的初始化。当进程创建一个新的线程时,系统会查看进程地址空间中所有的DLL文件映射,之后用DLL_THREAD_ATTACH来调用DLL中的DllMain函数。要注意的是,系统不会为进程的主线程使用值DLL_THREAD_ATTACH来调用DLL中的DllMain函数;
3):DLL_PROCESS_DETACH:当DLL从进程的地址空间解除映射时,参数ul_reason_for_call参数值为DLL_PROCESS_DETACH。当DLL处理DLL_PROCESS_DETACH时,DLL应该处理与进程相关的清理操作。如果进程的终结是因为系统中有某个线程调用了TerminateProcess来终结的,那么系统就不会用DLL_PROCESS_DETACH来调用DLL中的DllMain函数来执行进程的清理工作。这样就会造成数据丢失;
4):DLL_THREAD_DETACH:该通知告诉所有的DLL执行线程的清理工作。注意的是如果线程的终结是使用TerminateThread来完成的,那么系统将不会使用值DLL_THREAD_DETACH来执行线程的清理工作,这也就是说可能会造成数据丢失,所以不要使用TerminateThread来终结线程。
四、DLL函数导出:前面代码中是使用_declspec( dllexport )方式导出函数,另一种导出函数的方式,那就是使用导出文件(.def)。在DLL工程中,右键添加->新建项->“右上角搜索def”选择【模块定义文件.def】,该文件为链接器提供了有关被链接器程序的导出、属性及其它方面的信息。
注:具体如何使用,百度。
五、【extern “C”】
1.extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用,可以理解为“外部变量声明”。相反,static关键字则是只能在本模块中使用。
2.在一个文件内,如果外部变量不在文件的开头定义, 其有效范围只限定在从定义开始到文件的结束处。如果在定义前需要引用该变量,则要在引用之前用关键字”extern”对该变量做”外部变量声明”,表示该变量是一个已经定义的外部变量。有了这个声明,就可以从声明处起合理地使用该变量,代码理解如下:
/*
main函数中使用extern使用了函数外定义的int a 变量
*/
#include <iostream>
using namespace std;
int main()
{
extern int i;
cout<<i<<endl;
return 0;
}
int i= 100;
3.外部函数,在定义函数时,如果在最左端加关键字extern,表示此函数是外部函数。
4.在多文件的程序中,如果多个文件都要使用同一个外部变量,不能在各个文件中各定义一个外部变量,否则会出现“重复定义”的错误。正确的做法是在任意一个文件中定义外部变量,其它文件用extern对变量做“外部变量声明”。
六、DLL导出变量代码示例
/*导出变量示例 ExportVar.h 头文件 */
#ifndef _EXPORTVAR_H_
#define _EXPORTVAR_H_
#ifdef EXPORTVAR_EXPORTS
#define EXPORTVAR_API _declspec(dllexport)
#else
#define EXPORTVAR_API _declspec(dllimport)
#endif
extern EXPORTVAR_API int iVar ;
#endif
#include "stdafx.h"
/*导出变量示例 ExportVar.cpp 头文件 */
#include "ExportVar.h"
int iVar = 20;
/*main调用函数------加载时动态链接*/
#include <iostream>
#include "ExportVar.h"
using namespace std;
#pragma comment(lib, "ExportVar.lib")
int main(int argc, char *argv[])
{
cout<<iVar <<endl;
iVar = 1;
cout<<iVar <<endl;
}
输出结果如下:
20
1
七、DLL导出类代码示例
/*导出类示例 ExportClass.h 头文件 */
#ifndef _EXPORTCLASS_H_
#define _EXPORTCLASS_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifdef EXPORTCLASS_EXPORTS
#define EXPORTCLASS_API _declspec(dllexport)
#else
#define EXPORTCLASS_API _declspec(dllimport)
#endif
class EXPORTCLASS_API CTestClass
{
public:
static int Add(int a, int b);
void SetA(int a);
int GetA();
private:
int m_a;
};
#ifdef __cplusplus
}
#endif
#endif
/*导出变量示例 EXPORTCLASS.cpp 头文件 */
#include "stdafx.h"
#include "ExportClass.h"
int CTestClass::Add(int a, int b)
{
return (a + b);
}
void CTestClass::SetA(int a)
{
m_a = a;
}
int CTestClass::GetA()
{
return m_a;
}
/*main调用函数------加载时动态链接*/
#include <iostream>
#include "ExportClass.h"
using namespace std;
#pragma comment(lib, "EXPORTCLASS.lib")
int main()
{
CTestClass testObj;
testObj.SetA(10);
cout<<testObj.GetA()<<endl;
cout<<CTestClass::Add(3, 8)<<endl;
return 0;
}
上一篇: VS中类的导出