前期知识
- 对动态链接库基础概念的理解(详情参见博文深入浅出Visual C++动态链接库(Dll)编程 )
- 如何利用VS2013创建及使用DLL(详情参见博文vs2013创建及使用DLL)
将派生类封装成DLL并动态调用
如果源程序中C++的基类和由该基类生成的多个派生类在同一头文件和cpp文件下,要想将派生类编译成DLL使用,需要分别将每个派生类单独编译成DLL,再建立一个测试工程动态调用生成的DLL。
1.dll动态库的动态调用接口函数说明
LoadLibrary
函数原型:HMODUBLE WINAPI LoadLibrary(LPCTSTR lpFileName);(其中HMODUBLE通常是被载入模块的线性地址类型;LPCTSTR =const tchar *。)
功能描述:表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果LoadLibrary操作失败,返回NULL值;如果库已经被装载过,则LoadLibrary会返回同样的句柄。
参数中的lpFileName一般是库的全路径,这样LoadLibrary会直接装载该文件;如果只是指定了库名称,在LoadLibrary会在当前目录下查找。
GetProcAddress
函数原型:FARPROC WINAPI GetProcAddress (HMODUBLE hModule,LPCTSTR lpProcName);(其中FARPROC 通常代表函数指针)
功能描述:表示已获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。
参数中的hModule是由LoadLibrary加载库后返回的模块线性地址句柄;lpProcName是要调用的库函数名称。
FreeLibrary
函数原型: BOOL WINAPI FreeLibrary(HMODUBLE hModule)
功能描述:使用完 DLL 后调用 FreeLibrary卸载动态库。卸载成功返回true,否则返回false。
2. 将派生类封装成DLL并进行动态调用
建立一个普通测试项目
使用VS2013创建一个新项目,选择Win32——Win32控制台应用程序(此处需选择名称及位置,假设该处名称为dll_deal)
应用程序类型:控制台应用程序+附加选项:预编译头,完成以上步骤即可创建一个dll_deal项目。
修改项目的字符集属性
项目——dll_deal属性(最后一行就是)——配置属性——常规——字符集,设置为“未设置”。项目默认创建的字符集为“使用UNICODE字符集”。(如果字符集设置为UNICODE字符集的话,调试程序时无法自动实现 “char *”转换为“LPCWSTR”,需使用_T()或其它方法解决)
添加dll项目
文件——添加——新建项目——Win32——Win32控制台应用程序(此处填写名称dll1,位置默认)
应用程序类型:DLL+附加选项:空项目。
完成以上步骤即可在当前dll_deal项目中增加dll1项目。dll2项目也可参照dll1项目的添加即可。
在dll_deal、dll1和dll2项目中增加下面.h和.cpp源程序文件(其中dll_deal中的stdafx.h和stdafx.cpp为项目创建时默认生成,无需增加)。
说明:
dll_deal.h/dll1.h/dll2.h中定义相同的含有纯虚函数virtual void display() const = 0的基类。
dll1.cpp中定义继承类test1,实现虚函数virtual void display() const的定义,并实现一个创建类函数和一个销毁类指针函数。
dll2.cpp中定义继承类test2,实现虚函数virtual void display() const的定义,并实现一个创建类函数和一个销毁类指针函数。
dll_deal.cpp中实现调用不同动态库的display()方法。
dll_deal.h
#ifndef DLL_DEAL_H
#define DLL_DEAL_H
#include <iostream>
using namespace std;
class test_base {
public:
test_base(){}
virtual ~test_base() {}
void call_base()
{
cout << "call base" << endl;
}
virtual void display() const = 0;
};
// the types of the class factories
typedef test_base* create_t();
typedef void destroy_t(test_base*);
#endif
dll_deal.cpp
// dll_deal.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <Windows.h>
#include <string>
#include "dll_deal.h"
int main()
{
HINSTANCE hDll; //DLL句柄
string dll_name;
cout << "Please choose the dll_name(dll1.dll or dll2.dll):" << endl;
cin >> dll_name;
cout << endl;
hDll = LoadLibrary(dll_name.c_str());//加载DLL,需要将DLL放到工程目录下.
if (hDll != NULL)
{
cout << "LOAD DLL success!" << endl;
// load the symbols
create_t* create_test = (create_t*)GetProcAddress(hDll, "create");
if (create_test == NULL)
{
cout << "Cannot load symbol create: " << endl;
return 1;
}
destroy_t* destroy_test = (destroy_t*)GetProcAddress(hDll, "destroy");
if (destroy_test == NULL)
{
cout << "Cannot load symbol destroy: " << endl;
return 1;
}
// create an instance of the class
test_base* c_test = create_test();
// use the class
c_test->display();
// destroy the class
destroy_test(c_test);
// unload the? library
FreeLibrary(hDll);
}
else
{
cout << "Load DLL Error or DLL not exist!" << endl;
}
return 0;
}
dll1.h
#ifndef DLL1_H
#define DLL1_H
#include <iostream>
using namespace std;
class test_base {
public:
test_base(){}
virtual ~test_base() {}
void call_base() {
cout << "call base" << endl;
}
virtual void display() const = 0;
};
// the types of the class factories
typedef test_base* create_t();
typedef void destroy_t(test_base*);
#endif
dll1.cpp
#include "dll1.h"
class test1 : public test_base {
public:
virtual void display() const {
cout << "Running in test1.so Now" << endl;
}
};
// the class factories
extern "C" __declspec(dllexport)test_base* create() {
return new test1;
}
extern "C" __declspec(dllexport)void destroy(test_base* p) {
delete p;
}
dll2.h
#ifndef DLL2_H
#define DLL2_H
#include <iostream>
using namespace std;
class test_base {
public:
test_base(){}
virtual ~test_base() {}
void call_base() {
cout << "call base" << endl;
}
virtual void display() const = 0;
};
// the types of the class factories
typedef test_base* create_t();
typedef void destroy_t(test_base*);
#endif
dll2.cpp
#include "dll2.h"
class test2 : public test_base {
public:
virtual void display() const {
cout << "Running in test2.so Now" << endl;
}
};
// the class factories
extern "C" __declspec(dllexport)test_base* create() {
return new test2;
}
extern "C" __declspec(dllexport)void destroy(test_base* p) {
delete p;
}
各源程序中代码填充完成之后,在dll1项目中完成dll1.dll的生成;在dll2项目中完成dll2.dll的生成;在dll_deal项目中进行Debug,结果如下:
输入dll1.dll或者dll2.dll后,结果如下:
3.补充
如果想在测试项目中调用各派生类专有的成员函数,要调用的函数需在基类头文件中声明为虚函数,并在其他派生类的cpp文件中定义一个空的实现。