欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

DLL【动态链接库】加载方式、导出变量、导出类

程序员文章站 2022-06-25 18:45:06
...

一、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;
}
相关标签: DLL动态链接库