创建动态链接库 (DLL) 项目
-------参考MSDN和Repeaterbin的专栏
- 在菜单栏上,选择“文件”,“新建、“项目”。
- 在“新建项目”对话框左窗格中,展开“已安装”、“模板”、“Visual C++”,然后选择“Win32”。
- 在中间窗格中,选择“Win32 控制台应用程序”。
- 在“名称”框中为项目指定名称,例如,MathFuncsDll。 在“解决方案名称”框中为解决方案指定一个名称,例如 DynamicLibrary。 选择“确定”按钮。
- 在“Win32 应用程序向导”对话框的“概述”页上,选择“下一步”按钮。
- 在“应用程序设置”页上的“应用程序类型”下,选择“DLL”。
- 选择“完成”按钮创建项目。
向动态链接库添加类
- 若要为新类创建头文件,在菜单栏上依次选择“项目”、“添加新项...”。 在“添加新项”对话框中,在左窗格的“Visual C++”下,选择“代码”。 在中心窗格中,选择“头文件 (.h)”。 为头文件指定一个名称,例如 MathFuncsDll.h,然后选择“添加”按钮。 将显示一个空白头文件。
- 添加下列代码至头文件的开头:
- 添加下列代码至头文件的开头:
// MathFuncsDll.h
#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport)
#else
#define MATHFUNCSDLL_API __declspec(dllimport)
#endif
- 在MathFuncsDll.h中,添加一个名为“MyMathFuncs”的基础类,以执行常见的算术运算,如加、减、乘和除。 代码应类似如下:
namespace MathFuncs
{
// This class is exported from the MathFuncsDll.dll
class MyMathFuncs
{
public:
// Returns a + b
static MATHFUNCSDLL_API double Add(double a, double b);
// Returns a - b
static MATHFUNCSDLL_API double Subtract(double a, double b);
// Returns a * b
static MATHFUNCSDLL_API double Multiply(double a, double b);
// Returns a / b
// Throws const std::invalid_argument& if b is 0
static MATHFUNCSDLL_API double Divide(double a, double b);
};
}
- 当定义了 MATHFUNCSDLL_EXPORTS 符号时,MATHFUNCSDLL_API 符号将在此代码中的成员函数声明中设置 __declspec(dllexport) 修饰符。 此修饰符使函数能作为 DLL 导出,以供其他应用程序调用。 当 MATHFUNCSDLL_EXPORTS 未定义时,MATHFUNCSDLL_API 会在成员函数声明中定义 __declspec(dllimport) 修饰符。 此修饰符能够使编译器优化从 DLL 导入的用于其他应用程序的函数。 默认情况下,生成 MathFuncsDll 项目时会定义 MATHFUNCSDLL_EXPORTS。在“解决方案资源管理器”的“MathFuncsDll”项目中,在“源文件”文件夹中,打开“MathFuncsDll.cpp”。
- 在“解决方案资源管理器”的“MathFuncsDll”项目中,在“源文件”文件夹中,(存在)打开或(不存在)添加“MathFuncsDll.cpp”、
// MathFuncsDll.cpp : Defines the exported functions for the DLLapplication.
#include "stdafx.h"
#include "MathFuncsDll.h"
#include <stdexcept>
using namespace std;
namespace MathFuncs
{
double MyMathFuncs::Add(double a, double b)
{
return a + b;
}
double MyMathFuncs::Subtract(double a, double b)
{
return a - b;
}
double MyMathFuncs::Multiply(double a, double b)
{
return a * b;
}
double MyMathFuncs::Divide(double a, double b)
{
if (b == 0)
{
throw invalid_argument("b cannot be zero!");
}
return a / b;
}
}
备注:导出函数常用方式:
1,使用DEF文件从DLL中导出
.def 文件必须至少包含下列模块定义语句:
文件中的第一个语句必须是 LIBRARY 语句。 此语句将 .def 文件标识为属于 DLL。 LIBRARY 语句的后面是 DLL 的名称。 链接器将此名称放到 DLL 的导入库中。
EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。 通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。 当指定序号值时,序号值的范围必须是从 1 到N,其中 N 是 DLL 导出函数的个数。
LIBRARY BTREE
EXPORTS
Insert @1
Delete @2
Member @3
Min @4
2,使用__declspec(dllexport) 从DLL 导出
可以使用 __declspec(dllexport) 关键字从 DLL 导出数据、函数、类或类成员函数。 __declspec(dllexport) 会将导出指令添加到对象文件中,因此不需要使用 .def 文件。
若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边(如果指定了关键字)。
__declspec(dllexport) void __cdecl Function1(void);
若要导出类中的所有公共数据成员和成员函数,关键字必须出现在类名的左边,
class__declspec(dllexport) CExampleExport
{ ... classdefinition ... };
我们常用的导出定义:
#ifdef _EXPORTING
#defineAPI_DECLSPEC __declspec(dllexport)
#else
#defineAPI_DECLSPEC __declspec(dllimport)
#endif
class API_DECLSPECCBtt
{
public:
CBtt(void);
~CBtt(void);
public:
CString m_str;
static int GetValue()
{
return m_nValue;
}
private:
static int m_nValue;
};
注:单独使用__declspec(dllexport)已经可以实现DLL中库函数的导出,不使用 __declspec(dllimport)也能正确编译代码,但使用 __declspec(dllimport)使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport)才能导入 DLL 中使用的变量。
__declspec(dllimport)的实际用处:
SIMPLEDLL_EXPORT
SimpleDLLClass.h
#ifdefSIMPLEDLL_EXPORT
#define DLL_EXPORT__declspec(dllexport)
#else
#define DLL_EXPORT
#endif
class DLL_EXPORTSimpleDLLClass
{
public:
SimpleDLLClass();
virtual ~SimpleDLLClass();
virtual getValue() { return m_nValue;};
private:
int m_nValue;
};
SimpleDLLClass.cpp
#include"SimpleDLLClass.h"
SimpleDLLClass::SimpleDLLClass()
{
m_nValue=0;
}
SimpleDLLClass::~SimpleDLLClass()
{
}
然后你再使用这个DLL类,在你的项目中include SimpleDLLClass.h时,你的项目不用定义 SIMPLEDLL_EXPORT 所以,DLL_EXPORT 就不会存在了,这个时候,不会遇到问题。这正好对应MSDN上说的__declspec(dllimport)定义与否都可以正常使用。但,我们改一下SimpleDLLClass,把它的m_nValue改成static,然后在cpp文件中加一行
int SimpleDLLClass::m_nValue=0;
改完之后,再去LINK一下,你的APP,看结果如何,结果是LINK告诉你找不到这个m_nValue。
改一下SimpleDLLClass.h:
#ifdefSIMPLEDLL_EXPORT
#define DLL_EXPORT__declspec(dllexport)
#else
#define DLL_EXPORT__declspec(dllimport)
#endif
再LINK,一切正常。原来dllimport是为了更好的处理类中的静态成员变量的,如果没有静态成员变量,那么这个__declspec(dllimport)无所谓。