QT 有着独特的插件管理方法便于使用 , 调理清晰 . 完全可以替代 WIN32 下的动态库 , 静态库 . 不过 ,QT 也支持动态库和静态库加载 . 见 QLibrary, 最终 ,QLibrary 调用 WIN32 下的 LoadLibrary, GetProcAddress函数 .
Qt 插件的使用方法 :
[1]project_main_1 工程中定义接口
class interface__1
{
public :
void __func1() = 0 ;
void __func2() = 0 ;
void __func3() = 0 ;
} ;
class interface__2
{
public :
void __func4() = 0 ;
void __func5() = 0 ;
void __func6() = 0 ;
} ;
[2]project_plugin_1 工程中实现接口
class derive__1: public interface__1,interface__2
{
public :
void __func1();
void __func2();
void __func3();
void __func4();
void __func5();
void __func6();
} ;
[3]project_main_1 中使用 QPluginLoader,QPluginLoader 内部实现也是使用 LoadLibrary, GetProcAddress , 稍后会有说明
用法 1:
QobjectList objList = QpluginLoader::staticInstances();
for ( int i = 0 ; i < objList.size(); i ++ )
{
interface__1 * inter1 = qobject_cast < interface__1 *> (objList[i]);
interface__2 * inter1 = qobject_cast < interface__2 *> (objList[i]);
}
用法 2:
QpluginLoader pl(“plugin path”);
Qobject * plugin = pl.instance();
这里可以看出 , 充分的使用了对象对象的多态 . 那么 , 是 QpluginLoader 是如何实现的呢 ?
看下面细节 .
Qt 的类几乎所有的都有一个 QT_class+private 的类 , 用来实现具体逻辑 , 暴露给我们的类定义通用的接口 . QpluginLoader 的内部类是 QLibraryPrivate, 与 QLibrary 是同一个 .
[1] 如何加载
bool QLibraryPrivate::loadPlugin()
{
if (instance) {
libraryUnloadCount. ref ();
return true ;
}
if (load()) { // 这里最终调用load_sys()
instance = (QtPluginInstanceFunction)resolve( " qt_plugin_instance " ); // 注意这里的 qt_plugin_instance,插件里面必然导出该函数名称
return instance;
}
return false ;
}
bool QLibraryPrivate::load_sys()
{
#ifdef Q_OS_WINCE
QString attempt = QFileInfo(fileName).absoluteFilePath();
#else
QString attempt = fileName;
#endif
// avoid 'Bad Image' message box
UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
pHnd = LoadLibrary((wchar_t * )QDir::toNativeSeparators(attempt).utf16());
if (pluginState != IsAPlugin) {
if ( ! pHnd && ::GetLastError() == ERROR_MOD_NOT_FOUND) {
attempt += QLatin1String( " .dll " );
pHnd = LoadLibrary((wchar_t * )QDir::toNativeSeparators(attempt).utf16());
}
}
SetErrorMode(oldmode);
if ( ! pHnd) {
errorString = QLibrary::tr( " Cannot load library %1: %2 " ).arg(fileName).arg(qt_error_string());
}
if (pHnd) {
errorString.clear();
wchar_t buffer[MAX_PATH];
::GetModuleFileName(pHnd, buffer, MAX_PATH);
attempt = QString::fromWCharArray(buffer);
const QDir dir = QFileInfo(fileName).dir();
const QString realfilename = attempt.mid(attempt.lastIndexOf(QLatin1Char( ' \\ ' )) + 1 );
if (dir.path() == QLatin1String( " . " ))
qualifiedFileName = realfilename;
else
qualifiedFileName = dir.filePath(realfilename);
}
return (pHnd != 0 );
}
[2] qt_plugin_instance 是定义导出的呢 ?
在实现接口时 , 必须加上 Q_EXPORT_PLUGIN2 , Q_EXPORT_PLUGIN2
( PluginName , ClassName )
宏定义 :
# define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \
Q_PLUGIN_VERIFICATION_DATA \
Q_EXTERN_C Q_DECL_EXPORT \
const char * Q_STANDARD_CALL qt_plugin_query_verification_data() \
{ return qt_plugin_verification_data; } \
Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) * Q_STANDARD_CALL qt_plugin_instance() \
Q_PLUGIN_INSTANCE(PLUGINCLASS)
其中
# define Q_PLUGIN_VERIFICATION_DATA \
static const char * qt_plugin_verification_data = \
" pattern= "" QT_PLUGIN_VERIFICATION_DATA "" \n " \
" version= " QT_VERSION_STR " \n " \
" debug= " QPLUGIN_DEBUG_STR " \n " \
" buildkey= " QT_BUILD_KEY;
#define Q_EXTERN_C extern
#define Q_DECL_EXPORT __declspec(dllexport)
#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \
{ \
static QT_PREPEND_NAMESPACE(QPointer) < QT_PREPEND_NAMESPACE(QObject) > _instance; \
if ( ! _instance) \
_instance = new IMPLEMENTATION; \
return _instance; \
}
去掉宏之后,是2个函数.
static const char * qt_plugin_verification_data = " pattern= "" QT_PLUGIN_VERIFICATION_DATA "" \n " " version= " QT_VERSION_STR " \n "
" debug= " QPLUGIN_DEBUG_STR " \n "
" buildkey= " QT_BUILD_KEY;
extern __declspec(dllexport) qt_plugin_query_verification_data()
{
return qt_plugin_verification_data;
}
extern __declspec(dllexport) QObject * qt_plugin_instance()
{
Qpoint < QOjbect > _instance;
if ( ! _instance)
_instance = new PLUGINCLASS;
return _instance;
}
[3] instance 是 typedef QObject *(* QtPluginInstanceFunction )();
这样就实现了 QT 的插件 . 但是还没完 .
在定义接口时 , 还应加上 Q_DECLARE_INTERFACE, This
macro associates the given Identifier (a string literal) to the interface class called ClassName. The Identifier must be unique.
# define Q_DECLARE_INTERFACE(IFace, IId) \
template <> inline const char * qobject_interface_iid < IFace *> () \
{ return IId; } \
template <> inline IFace * qobject_cast < IFace *> (QObject * object ) \
{ return reinterpret_cast < IFace *> (( object ? object -> qt_metacast(IId) : 0 )); } \
template <> inline IFace * qobject_cast < IFace *> ( const QObject * object ) \
{ return reinterpret_cast < IFace *> (( object ? const_cast < QObject *> ( object ) -> qt_metacast(IId) : 0 )); }
#endif // Q_MOC_RUN