简介本文介绍如何快速入门Windows DLL开发,感兴趣的朋友可以参考一下。

一.用户模块

用户模块是由用户自己开发的、可以加入到最终用户(包括用户本人和其他使用该模块的人)应用程序中 提供某一特定功能的函数和类的集合

为了完成同样的工作,也可以向最终用户提供源程序。但是,使用用户模块有许多好处:

1.首先是省去用户管理源代码的烦恼,用户许多情况下往往并不关心模块的内部实现,他只是想把它作为一个黑匣子使用。

2.另外,模块的开发者有时候并不希望模块使用者看到源代码。

3.使用模块而不使用源代码还可以避免模块的函数名、变量名与最终用户的程序上的冲突。

用户模块可分为两大类:静态链接库和动态链接库。

静态链接库提供了函数的完整的目标代码,如果程序调用静态链接库中的 函数 ,则在 进行链接时连接程序将静态链接库中所包含的该函数的代码拷贝至运行文件中

动态链接库是一个可执行模块,其包含的函数可以由Windows应用程序调用以执行一些功能。动态链接库 主要为应用程序模块提供服务 。Windows内核的三个模块USER.EXE、KENERL.EXE和GDI.EXE实际上都是动态链接库,分别提供用户消息服务、进程管理、图形输出等服务。

动态链接库也包含了其所提供的函数的目标代码,但是在程序链接动态链接库中的函数时,链接程序并不将包含在动态链接库中的函数的目标代码拷贝至运行文件,而 只是简单地记录了函数的位置信息 (即包含于哪个动态链接库中以及在动态链接库中的位置)。有了这些信息后,程序在执行时,即可找到该函数的目标代码。因为只是在执行时才得到真正的链接,因此称为动态链接。提供函数在动态链接库中位置的信息存放在一个独立的文件中,这个文件就是引入库(IMPORT LIB)。

对比: 由于静态链接库将目标代码连接到应用程序中,当程序运行时,如果两个程序调用了同一静态库中的函数,内存中将出现该函数的多份拷贝。而动态链接库则更适合于多任务环境:当两个应用程序调用了同一动态链接库中的同一个函数时,内存中只保留该函数的一份拷贝,这样内存利用率更高。

二.动态链接库基本概念

在一些情况下,必须使用动态链接库:

  • 1.多个应用程序共享代码和数据:比如Office软件的各个组成部分有相似的外观和功能,这就是通过共享动态链接库实现的。
  • 2.在钩子程序过滤系统消息时必须使用动态链接库
  • 3.设备驱动程序必须是动态链接库
  • 4.如果要在对话框编辑器中使用自己定义的控件,也必须使用动态连接库
  • 5.动态链接库以一种自然的方式将一个大的应用程序划分为几个小的模块,有利于小组内部成员的分工与合作。而且,各个模块可以独立升级。如果小组中的一个成员开发了一组实用例程,他就可以把这些例程放在一个动态链接库中,让小组的其他成员使用。
  • 6.为了实现应用程序的国际化,往往需要使用动态链接库。使用动态链接库可以将针对某一国家、语言的信息存放在其中。对于不同的版本,使用不同的动态链接库。

在使用AppWizard生成应用程序时,我们可以指定资源文件使用的语言,这就是通过提供不同的动态链接库实现的。

VisualC++ 支持三种 DLL

  • Non-MFC DLL (非 MFC 动态库)
  • MFC Regular DLL ( MFC 规则 DLL )
  • MFC Extension DLL ( MFC 扩展 DLL )

非MFC动态库不采用 MFC 类库结构, 其导出函数为标准的 C接口, 能被非 MFC 或 MFC 编写的应用程序所调用;

MFC 规则 DLL 包含一个继承自CWinApp 的类,但其无消息循环 ;

MFC 扩展 DLL采用MFC的动态链接版本创建,它只能被用 MFC 类库所编写的应用程序所调用。

MFC支持两类动态链接库的创建:用户动态链接库、MFC扩展类库

2.1 动态链接库快速开发

第一步:实现文件 .h .cpp

导出整个class

在类的头文件中class和类名之间加上__declspec(dllexport);

同时在另外一份提供给客户端调用程序使用的类的头文件中class和类名之间加上__declspec(dllimport)。

这里提供更好的方式,头文件一份:

#ifdef DLLPROJECT_EXPORTS

#define MATH_LIBRARY_API __declspec(dllexport)

#else

#define MATH_LIBRARY_API __declspec(dllimport)

#endif

其中 DLLPROJECT_EXPORTS为DLL项目工程自定预定义的。

为了能让客户端程序和DLL程序公用该类的一份头文件,通常在类的头文件中使用宏和预编译指令来处理。

MathLibrary.h:

// MathLibrary.h

#ifndef MATHLIBRARY_H
#define MATHLIBRARY_H

// 定义类导出
#define MATH_LIBRARY_API __declspec(dllexport)

class MATH_LIBRARY_API CMathLibrary
{
public:
	CMathLibrary();
	~CMathLibrary();

	static  int Summary(int n);
	static  int Factorial(int n);
};

#endif

MathLibrary.cpp:

// MathLibrary.cpp

#include "stdafx.h"   
#include"MathLibrary.h"


CMathLibrary::CMathLibrary()
{
}

CMathLibrary::~CMathLibrary()
{
}

int CMathLibrary::Summary(int n)
{
	int sum = 0;
	int i;
	for (i = 1; i <= n; i++)
	{
		sum += i;
	}
	return sum;
}

int CMathLibrary::Factorial(int n)
{
	int Fact = 1;
	int i;
	for (i = 1; i <= n; i++)
	{
		Fact = Fact*i;
	}
	return Fact;
}

第二步:定义动态链接库的入口函数,VS会自动创建。

 // dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

编译完成后提交的文件:

  • .lib
  • .dll
  • .h (注意客户端的头文件声明导入:__declspec(dllimport))

补充: 导出这个类的某个或者某几个方法:

这时,需要将_declspec(dllexport)放到成员函数名前,如DLLTest.h:

#ifdef DLL_TEST_API
#else
#define DLL_TEST_API _declspec(dllimport)
#endif

class CDLLTest
{
public:
	CDLLTest();
	~CDLLTest();
	int  DLL_TEST_API  Add(int a, int b);
};

但是,如果仅仅是这样的话,当客户端程序#include这个头文件后,定义DLLTest这个类的一个对象后(静态方式链接DLL),客户端程序无法链接通过,会提示构造函数和析构函数无法解析, 此时,需要将构造函数和析构函数前也加上DLL_TEST_API宏即可

当然这里还有个问题就是类的函数在导出后,名字会发生变化,我们可以在函数名前再加上extern “C”,

如 extern “C” DLL_TEST_API int Add(int a ,int b);

但这只解决了C与C++调用时名字变更问题,可靠的方法还是增加一个模块定义文件def,在该文件中定义导出函数的名称,

2.2 动态链接库中导出函数的方法

  • 在函数声明中加上__declspec(dllexport)

类中:static __declspec(dllexport) double add(double a,double b)

C方式:extern "C"  int __declspec(dllexport)add(int x, int y);

  • 采用模块定义文件(.def)声明

LIBRARY "DLLProject"

EXPORTS

;The names of the DLL functions

Summary

Factorial

2.3 调用动态链接库中的函数的方法

1. 通过隐式链接

方法一:

项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件testdll.h所在的目录

项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件testdll.lib所在的目录

项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“testdll.lib”(若有多个 lib 则以空格隔开)

方法二:

或者直接在项目中添加.h ,然后引入.lib:

#include <iostream>

#include"MathLibrary.h"
#pragma comment(lib,"DLLProject.lib")

int _tmain(int argc, _TCHAR* argv[])
{
	std::cout << "Values: " << CMathLibrary::Factorial(5) << std::endl;

	return 0;
}

2. 显式链接

使用这种方式的应用程序可以在执行过程可以随时可以加载DLL,也可以随时卸载,更具灵活性,特别适合解释性语言。

#include <iostream>
#include <windows.h>
#include "StaticLibrary.h"

HINSTANCE ghMathsDLL = NULL; // 句柄实例
//Declare the Summary() function from the DLL.
typedef int(*SUMMARY)(int);
SUMMARY Summary;
typedef int(*FACTORIAL)(int);
FACTORIAL Factorial;

// 载入库
void LoadDLL()
{
	// 如果DLL已经载入,则返回
	if (ghMathsDLL != NULL)
	{
		return;
	}
	// 载入
	ghMathsDLL = LoadLibrary(_T("DLLProject.DLL"));
	// 载入失败
	if (ghMathsDLL == NULL)
	{
		std::cout << "Cannot load DLL file!" << std::endl;
	}
	// 获得DLL中Summary函数的地址
	Summary = (SUMMARY)GetProcAddress(ghMathsDLL, "Summary");
	// 获得DLL中Factorial函数的地址
	Factorial = (FACTORIAL)GetProcAddress(ghMathsDLL, "Factorial");
}

int _tmain(int argc, _TCHAR* argv[])
{
	//LoadDLL();
	std::cout << "Values: " << Factorial(5) << std::endl;

	// 释放
	FreeLibrary(ghMathsDLL);
	return 0;
}

LoadLibrary函数装入所需的动态链接库,并返回库的句柄。如果句柄小于32,则载入库失败,错误含义参见有关手册。GetProcAddress函数使用函数名字取得函数的地址。利用该函数地址,就可以访问动态链接库的函数了。

FreeLibrary通过检查动态链接库的引用计数器,判断是否还有别的程序在使用这个动态链接库。 如果没有,就从内存中移去该动态链接库;如果有,将动态链接库的使用计数器减1 。LoadLibrary则将引用计数加1。

三.静态链接库

3.1 创建静态库

使用VS创建一个StaticLibrary的静态库工程,然后在工程中加入以下两个文件。

 //StaticLibrary.h

#pragma once

extern "C"
{
	// 定义了Summary和Factorial两个函数,分别用于完成求和与阶乘。
	// 注意这里使用C风格的函数,需要加入extern “C”关键字,表明它是C风格的外部函数。
	int Summary(int n);
	int Factorial(int n);
}

// StaticLibrary.cpp

#include "stdafx.h"   

#include"StaticLibrary.h"

int Summary(int n)
{
	int sum = 0;
	int i;
	for (i = 1; i <= n; i++)
	{
		sum += i;
	}
	return sum;
}

int Factorial(int n)
{
	int Fact = 1;
	int i;
	for (i = 1; i <= n; i++)
	{
		Fact = Fact*i;
	}
	return Fact;
}

提示:用户在交付最终静态连接库时, 只需要提供.lib文件和头文件 ,不需要再提供库的源代码。

3.2 测试静态库

 #include "stdafx.h"
#include <iostream>
#include "StaticLibrary.h"

int _tmain(int argc, _TCHAR* argv[])
{
	std::cout << "Values: " << Factorial(5) << std::endl;
	return 0;
}


更多为你推荐