1.在编程的时候,启动程序常常会附带一些必须的dll文件和一些非必须的dll文件。非必须的dll文件作为该应用程序功能拓展使用,称为插件。所以插件和应用程序各为独立个体,但他们之间也应该有联系。
2.在编写能被应用程序使用的插件时,如果不仅仅是提供一些全局函(功能)给应用程序使用,一般还包含一些类。而这些类应当继承能被应用程序识别的类,一般为插件专门创建一个抽象类Base,而插件dll里面的类继承该base类。并在dll的代码里面提供一个创建dll里面实例的函数,以供在加载到dll后,利用该函数创建该实例,这样就能得到插件dll里面的实例的指针了,然后利用多态,完成插件功能的实现,比如:
//插件所需父类(抽象类)
class PluginBase
{
public:
virtual void DoFunction()=0;
virtual ~PluginBase(){}
};
//该类的.h文件应用程序和插件dll都应该包含,如果抽象类析构函数为虚函数,需要实现,否则其继承的子类无法创建,这是因为子类析构函数调用会调用父类析构,所以必须实现。
//插件实例声明
class TARGETDLL_EXPORT TargetDll:public QMainWindow ,public PluginBase
{
Q_OBJECT
public:
TargetDll();
~TargetDll();
virtual void DoFunction();
public slots:
void slot_Show();
private:
bool isStarting;
QTimer* m_timer;
QWidget* mCenWgt;
QLabel* mLabel;
};
//这里注意一下,如果是Qt类,使用到其Q_OBJECT相关功能,在多继承的时候,Qt相关的应放在前面,比如QMainWindow
//应该在插件父类PluginBase的前面。否则链接出错,这是Qt机制的特性。
//cpp实现
TargetDll::TargetDll()
{
m_timer=new QTimer(this);
mCenWgt=new QWidget(this);
mLabel=new QLabel(this);
mLabel->setText("JUSTTEST");
isStarting=false;
QVBoxLayout* vL=new QVBoxLayout(this);
vL->addWidget(mLabel);
mCenWgt->setLayout(vL);
this->setCentralWidget(mCenWgt);
connect(m_timer,SIGNAL(timeout()),this,SLOT(slot_Show()));
}
TargetDll::~TargetDll()
{
}
void TargetDll::DoFunction()
{
if(!isStarting)
{
m_timer->start();
isStarting=true;
this->show();
}
else
{
m_timer->stop();
isStarting=false;
this->hide();
}
}
void TargetDll::slot_Show()
{
QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间
QString str = time.toString("yyyy-MM-dd hh:mm:ss ddd"); //设置显示格式
QString strr= strr.fromLocal8Bit("当前时间:")+str;//调用中文显示
mLabel->setText(strr);
}
然后在插件的实现的cpp里面添加一个导出函数,用于dll解析时候使用,得到实例的指针:
extern "C" __declspec(dllexport) void * __cdecl CreateInstance()
{
//建立对象
PluginBase* pPlugin=NULL;
try
{
pPlugin=new TargetDll();
if (pPlugin==NULL) throw TEXT("创建失败");
return (void*)pPlugin;
}
catch (...) {}
//清理对象
if (pPlugin != NULL){
delete pPlugin;
}
return NULL;
}
将上诉文件生成的dll放在指定目录,接下来就是动态加载调用dll插件了,以Qt为例:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyPluginTest w;
w.show();
PluginBase* m_plugin=NULL;
//里面的路径自己安排
QLibrary* lib=new QLibrary("D:/code/MyPluginTest/Win32/Debug/TargetDll.dll");PluginBase* entry=NULL;
if(lib->load())
{
//解析创建实例函数,得到函数地址
PLUGIN_LIB_ENTRY func = (PLUGIN_LIB_ENTRY)lib->resolve("CreateInstance");
if(!func){
delete lib;
return false;
}
//call to get the entries.
try
{
entry = (PluginBase*)func();//调用解析到的函数,获取创建到的实例的指针
}
catch(...)
{
delete lib;
return false;
}
entry->DoFunction();//插件具体功能的实现(利用多态)
}
return a.exec();
}
效果如下:
小结:一般而言,插件会很多很多,应用程序会有一个插件管理器,插件管理器里面有一个容器,数据类型就是插件基础抽象类,首先应该判断某个目录下dll文件名,然后加载什么的:
QDir dir(“插件目录”);//只是示例,一般不用中文,中文可能出错
if(!dir.exists()){
if(!dir.mkpath(this->_plugins_dir))
{
throw new IOTP::IOTPException("can not make library directory %s!",
this->_plugins_dir.toLocal8Bit().data());
}
}
qDebug() << "Plugin home path:" << this->_plugins_dir;
foreach(QString file, QDir(this->_plugins_dir).entryList(QDir::Files))
{
file = file.toLower();
if(!file.endsWith(".dll"))
{//platform specific.
continue;
}
qDebug() << "Load library " << this->_plugins_dir + file;
QString errmsg;
//先判断是否dll,然后执行加载函数(这里理解意思就ok),主要就是获取实例或者等,然后放入定义的管理器里面等
if(!loadLibrary(this->_plugins_dir + file, &errmsg)){
qCritical() << "Failed, error: " << errmsg;
continue;
}
}
Qt提供的接口浅显易懂,界面开发也相对容易。以上主要是了解下思路,相信简单的插件框架,都能写了。