在写C++程序时,时常须要将一个class写成DLL,供客户端程序调用。这样的DLL能够导出整个class,也能够导出这个class的某个方法。c++
1、导出整个class函数
方法很简单,只须要在类的头文件中class和类名之间加上_declspec(dllexport),同时在另一份提供给客户端调用程序使用的类的头文件中class和类名之间加上_declspec(dllimport)。为了能让客户端程序和DLL程序公用该类的一份头文件,一般在类的头文件中使用宏和预编译指令来处理。以下DLLTest.h:spa
#ifdef DLL_TEST_API #else #define DLL_TEST_API _declspec(dllimport) #endif Class DLL_TEST_API CDLLTest { Public: CDLLTest(); ~CDLLTest(); int Add(int a, int b); };
DLLTest.cpp以下:指针
#define DLL_TEST_API _declspec(dllexport) #include “DLLTest.h” ………………………………………
这样,在DLL编译时DLL_TEST_API被定义为_declspec(dllexport),并且客户端程序编译时它被定义为_declspec(dllimport)。code
2、导出这个类的某个或者某几个方法。对象
这时,须要将_declspec(dllexport)放到成员函数名前,如DLLTest.h:blog
#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,在该文件中定义导出函数的名称,咱们将在后面看到样例。编译器
DLL编写完成后,就只剩下客户端程序如何去调用该DLL了,静态方式调用DLL和动态方式调用DLL。编译
1、静态方式调用DLL
这个方法就简单了,将DLLTest.h头文件和DLLTest.lib,DLLTest.dll文件拷贝到客户端程序的当前目录下,在客户端程序中#include<DLLTest.h>,而后经过#pragma comment(lib,”DLLTest.lib”)的方式引入lib库,或者在客户端程序的工程属性里面增长对该lib文件的引入。
而后就能够在客户端程序中如同使用本地的一个class同样使用该DLL了,如:
CDLLTest dllTest; dllTest.Add(1,2);
2、动态方式调用DLL
动态调用这个DLL,就须要对这个class进行修改了。
首先,在DLLTest.cpp文件中增长一个全局函数,该函数能够返回这个class的一个实例,这样,客户端程序调用这个全局函数后,获得该class的实例,就能够调用该class的实例方法了。
extern “C” _declspec(dllexport) CDLLTest* GetInstance() { return new CDLLTest; }
注:extern “C” 只是解决了c与c++编译器之间的兼容问题,若是须要和其余编译器之间兼容,可靠的办法仍是增长一个.def文件,文件内容以下:
LIBRARY “DLLTest”
EXPORTS
GetInstance = GetInstance
这样就指定了DLL的函数导出后的名称仍然不变。
这样,客户端程序就能够经过该函数来获取class的一个实例了。以下:
先须要定义一个函数指针类型:
typedef CDllTestBase* (*pfGetInst)(); //注:CDllTestBase类后面会介绍。 HMOUDLE hMod = LoadLibrary( _T(“DLLTest.DLL”) ); if(hMod) { pfGetInst pfGetInstance = (pfGetInst)GetProcAddress(“GetInstance”); if( p ) { //经过基类指针指向派生类对象 CDllTestBase * pInst = pfGetInstance ();
if( NULL != pInst )
{ pInst->Add( 1,2);
} if( NULL != pInst ) { //释放对象 delete pInst;
} }
}
固然,这里仍是须要include这个DLL的头文件DLLTestBase.h,若是将以前所写的头文件DLLTest.h直接拷贝到客户端程序的当前目录下,并include进来的话,在编译链接时,是没法经过的,咱们须要对这个头文件进行修改,首先增长一个.h 文件DLLTestBase.h,在这个文件中咱们将须要在客户端程序中调用的函数都命名成纯虚函数,而后让CDLLTest类继承自CDLLTestBase类,DLLTestBase.h以下:
Class CDLLTestBase { Public: Virtual ~CDLLTestBase(){};//虚析构函数,且为内联函数 Virtual int Add(int a, int b) = 0; }
DLLTest.h修改后以下:
#include “DLLTestBase.h” Class CDLLTest : public CDLLTestBase { Public: CDLLTest(); ~CDLLTest(); int Add(int a, int b); };
注:这里的DLLTestBase须要提供一个虚析构函数,这样在客户端程序中就能够经过基类指针来释放派生类对象了。
这样,只须要将DLLTestBase.h拷贝到客户端程序的当前目录下,而后在客户端程序中#include”DLLTestBase.h”,就能够如上面介绍同样在客户端程序中调用DLL里面的方法了。