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