参考:【QT】QT中插件化开发及其简单使用_bailang_zhizun的博客-CSDN博客_qt插件开发
代码:qt插件的简单使用,插件创建,和使用-Flutter文档类资源-CSDN下载
QT插件程序开发流程
编写扩展 Qt 应用程序的插件,步骤如下:
(1)、声明一个继承自 QObject 和插件想要提供的接口的插件类
(2)、使用 Q_INTERFACES() 宏来告诉 Qt 元对象系统有关接口的情况
(3)、使用 Q_PLUGIN_METADATA() 宏导出插件
(4)、使用合适的 .pro 文件构建插件
抽象接口类:
#ifndef CALINTERFACE_H
#define CALINTERFACE_H
//定义接口
class CalInterface
{
public:
virtual ~CalInterface() {}
virtual int add(int a,int b) = 0;
};
#define CalInterface_iid "Examples.Plugin.CalInterface"
QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(CalInterface,CalInterface_iid)
QT_END_NAMESPACE
#endif // CALINTERFACE_H
创建了一个纯虚函数接口add,这里的DeclareInterface_iid 宏定义字符串一定要是唯一的,然后使用宏Q_DECLARE_INTERFACE来声明该接口
实现类:
#ifndef CALPLUGIN_H
#define CALPLUGIN_H
#include
#include
#include "calinterface.h"
class CalPlugin : public QObject,public CalInterface
{
Q_OBJECT
Q_INTERFACES(CalInterface)
Q_PLUGIN_METADATA(IID CalInterface_iid FILE "calplugin.json")
public:
explicit CalPlugin(QObject *parent = nullptr);
int add(int a,int b);
};
#endif // CALPLUGIN_H
注意,在头文件中一定要用Q_INTERFACES宏来声明接口类。
这里的qtplugin.json是为插件提供插件信息的,并使用Q_PLUGIN_METADATA声明(实例化该对象的)插件的元数据,元数据是插件的一部分。
Q_PLUGIN_METADATA这个宏所在的类必须是默认可构造的。
FILE 是可选的,并指向一个 Json 文件。Json 文件必须位于构建系统指定的包含目录之一中(在本工程中和.pro在同级目录下)。当无法找到指定的文件时,moc 会出现错误。如果不想为插件提供信息,当然不会有任何问题,只需保证 Json 文件为空就行
作用:
(1)读取json文件,将内容写入到qt_plugin_metadata
(2)提供插件的实例对象的句柄接口
展开:
#define Q_PLUGIN_METADATA(x) QT_ANNOTATE_CLASS(qt_plugin_metadata, x)
Q_PLUGIN_METADATA(IID CalInterface_iid FILE "calplugin.json")
与qt_plugin_metadata有关,在moc文件中查看该qt_plugin_metadata的信息,如下
作用:
(1)指定当前插件的元数据存储数据段
(2)qt_pluginMetaData数组 :存储了与插件相关的具体信息
#pragma section(".qtmetadata",read,shared)
# define QT_PLUGIN_METADATA_SECTION \
__declspec(allocate(".qtmetadata"))
#else
# define QT_PLUGIN_VERIFICATION_SECTION
# define QT_PLUGIN_METADATA_SECTION
#endif
__declspec(allocate 是让编译器分配一个名字为.qtmetadata的段
__declspec(allocate("segname"))和__declspec( selectany )(AC1)_Antoinette的博客-CSDN博客.qtmetadata的段的内容是:
QT_PLUGIN_METADATA_SECTION
static const unsigned char qt_pluginMetaData[] = {
'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ',
'q', 'b', 'j', 's', 0x01, 0x00, 0x00, 0x00,
0xa0, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x8c, 0x00, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00,
0x03, 0x00, 'I', 'I', 'D', 0x00, 0x00, 0x00,
0x1c, 0x00, 'E', 'x', 'a', 'm', 'p', 'l',
'e', 's', '.', 'P', 'l', 'u', 'g', 'i',
'n', '.', 'C', 'a', 'l', 'I', 'n', 't',
'e', 'r', 'f', 'a', 'c', 'e', 0x00, 0x00,
0x15, 0x09, 0x00, 0x00, 0x08, 0x00, 'M', 'e',
't', 'a', 'D', 'a', 't', 'a', 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x9b, 0x0c, 0x00, 0x00,
0x09, 0x00, 'c', 'l', 'a', 's', 's', 'N',
'a', 'm', 'e', 0x00, 0x09, 0x00, 'C', 'a',
'l', 'P', 'l', 'u', 'g', 'i', 'n', 0x00,
'1', 0x00, 0x00, 0x00, 0x05, 0x00, 'd', 'e',
'b', 'u', 'g', 0x00, 0x1a, '!', 0xa1, 0x00,
0x07, 0x00, 'v', 'e', 'r', 's', 'i', 'o',
'n', 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
'8', 0x00, 0x00, 0x00, 'T', 0x00, 0x00, 0x00,
'p', 0x00, 0x00, 0x00, '|', 0x00, 0x00, 0x00
};
QT_MOC_EXPORT_PLUGIN(CalPlugin, CalPlugin)
其中存储的信息:看上面的英文的组合:
QTMETADATA
IID
Examples.Plugin.CalInterface //定义的id
MetaData //插件的元数据:加载的json文件
className //插件的类名称
CalPlugin //接口类名称
debug //编译模式
Version //版本
# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \
Q_EXTERN_C Q_DECL_EXPORT \
const char *qt_plugin_query_metadata() \
{ return reinterpret_cast(qt_pluginMetaData); } \
Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \
Q_PLUGIN_INSTANCE(PLUGINCLASS)
#endif
作用:
1 获取当前插件对象的元数据
2 获取qt插件的实例句柄
Q_PLUGIN_INSTANCE(PLUGINCLASS) 也就是 QT_MOC_EXPORT_PLUGIN(CalPlugin, CalPlugin)
#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \
{ \
static QT_PREPEND_NAMESPACE(QPointer) _instance; \
if (!_instance) \
_instance = new IMPLEMENTATION; \
return _instance; \
}
在这里创建了插件实现类CalPlugin的实例对象
#define Q_INTERFACES(x) QT_ANNOTATE_CLASS(qt_interfaces, x)
其中传入的是插件的抽象接口类名称:
Q_INTERFACES(CalInterface)
在moc文件中,
void *CalPlugin::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_CalPlugin.stringdata0))
return static_cast(this);
if (!strcmp(_clname, "CalInterface"))
return static_cast< CalInterface*>(this);
if (!strcmp(_clname, "Examples.Plugin.CalInterface"))
return static_cast< CalInterface*>(this);
return QObject::qt_metacast(_clname);
}
当: if (!strcmp(_clname, "Examples.Plugin.CalInterface"))
return static_cast< CalInterface*>(this);
上面这两行当且使用了Q_INTERFACES才会有。
在将插件进行上行转换时,检查插件的抽象接口ID:Examples.Plugin.CalInterface
以确保插件的有效性
#ifndef Q_MOC_RUN
# define Q_DECLARE_INTERFACE(IFace, IId) \
//1 获取当前插件的抽象接口id
template <> inline const char *qobject_interface_iid() \
{ return IId; } \
//2 获取抽象接口对象 的指针,同时获取qt_metacast(IId)对插件接口进行验证,qt_metacast(IId)的作用在上面有讲述,当子类使用Q_INTERFACES宏定义时会产出moc 中 关于qt_metacast(IId)的代码来校验上行转换
template <> inline IFace *qobject_cast(QObject *object) \
{ return reinterpret_cast((object ? object->qt_metacast(IId) : Q_NULLPTR)); } \
template <> inline IFace *qobject_cast(const QObject *object) \
{ return reinterpret_cast((object ? const_cast(object)->qt_metacast(IId) : Q_NULLPTR)); }
#endif // Q_MOC_RUN
Q_DECLARE_INTERFACE 需要与Q_INTERFACES 宏成对出现,保证插件的有效性
QDir pluginsDir(qApp->applicationDirPath());
#if defined(Q_OS_WIN)
if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
if (pluginsDir.dirName() == "MacOS") {
pluginsDir.cdUp();
pluginsDir.cdUp();
pluginsDir.cdUp();
}
#endif
pluginsDir.cd("plugins");
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
qDebug() << "--->>>Lynn<<<---" << __FUNCTION__ << pluginLoader.errorString();
if (plugin) {
m_pInterface = qobject_cast(plugin);
if (m_pInterface)
return true;
}
}
return false;
主要使用 QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));来加载插件
QPluginLoader 提供对 Qt 插件的访问。Qt 插件存储在共享库(DLL)中,与使用 QLibrary 访问共享库相比,它具有以下优势:
如果插件尚未加载,则 instance() 函数会隐式地尝试加载插件。 QPluginLoader 的多个实例可用于访问同一个物理插件。加载后,插件将保留在内存中,直到卸载了 QPluginLoader 的所有实例,或者直到应用程序终止。
QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent)
: QObject(parent), d(0), did_load(false)
{
setFileName(fileName);
setLoadHints(QLibrary::PreventUnloadHint);
}
加载策略:
enum LoadHint {
ResolveAllSymbolsHint = 0x01,
ExportExternalSymbolsHint = 0x02,
LoadArchiveMemberHint = 0x04,
PreventUnloadHint = 0x08,
DeepBindHint = 0x10
};
QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent)
: QObject(parent), d(0), did_load(false)
{
setFileName(fileName);
setLoadHints(QLibrary::PreventUnloadHint);
}
void QPluginLoader::setFileName(const QString &fileName)
{
#if defined(QT_SHARED)
QLibrary::LoadHints lh = QLibrary::PreventUnloadHint;
if (d) {
lh = d->loadHints();
d->release();
d = 0;
did_load = false;
}
// 1 定位插件
const QString fn = locatePlugin(fileName);
//2 查找并创建插件
d = QLibraryPrivate::findOrCreate(fn, QString(), lh);
//3 更新插件状态
if (!fn.isEmpty())
d->updatePluginState();
#else
if (qt_debug_component()) {
qWarning("Cannot load %s into a statically linked Qt library.",
(const char*)QFile::encodeName(fileName));
}
Q_UNUSED(fileName);
#endif
}
查找并创建插件的过程:调用 QLibraryPrivate::findOrCreate-> QLibraryStore::findOrCreate
QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version,
QLibrary::LoadHints loadHints)
{
return QLibraryStore::findOrCreate(fileName, version, loadHints);
}
更新插件状态的过程:
void QLibraryPrivate::updatePluginState()
{
errorString.clear();
if (pluginState != MightBeAPlugin)
return;
bool success = false;
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (fileName.endsWith(QLatin1String(".debug"))) {
// refuse to load a file that ends in .debug
// these are the debug symbols from the libraries
// the problem is that they are valid shared library files
// and dlopen is known to crash while opening them
// pretend we didn't see the file
errorString = QLibrary::tr("The shared library was not found.");
pluginState = IsNotAPlugin;
return;
}
#endif
//1 插件句柄如果没有获取,则调用findPatternUnloaded获取插件
否则调用 qt_get_metadata
if (!pHnd) {
// scan for the plugin metadata without loading
success = findPatternUnloaded(fileName, this);
} else {
// library is already loaded (probably via QLibrary)
// simply get the target function and call it.
QtPluginQueryVerificationDataFunction getMetaData = NULL;
getMetaData = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_metadata");
success = qt_get_metadata(getMetaData, this);
}
if (!success) {
if (errorString.isEmpty()){
if (fileName.isEmpty())
errorString = QLibrary::tr("The shared library was not found.");
else
errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName);
}
pluginState = IsNotAPlugin;
return;
}
pluginState = IsNotAPlugin; // be pessimistic
//2 版本判断
uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
bool debug = metaData.value(QLatin1String("debug")).toBool();
if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
if (qt_debug_component()) {
qWarning("In %s:\n"
" Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
QFile::encodeName(fileName).constData(),
(qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
debug ? "debug" : "release");
}
errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
.arg(fileName)
.arg((qt_version&0xff0000) >> 16)
.arg((qt_version&0xff00) >> 8)
.arg(qt_version&0xff)
.arg(debug ? QLatin1String("debug") : QLatin1String("release"));
#ifndef QT_NO_DEBUG_PLUGIN_CHECK
} else if(debug != QLIBRARY_AS_DEBUG) {
//don't issue a qWarning since we will hopefully find a non-debug? --Sam
errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library."
" (Cannot mix debug and release libraries.)").arg(fileName);
#endif
} else {
pluginState = IsAPlugin;
}
}
static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
这将打开指定的库,将其映射到内存中,然后搜索 QT_PLUGIN_VERIFICATION_DATA。 这种方法的优点是我们无需实际加载库即可获取验证数据。这使我们能够更安全地检测不匹配。 如果版本信息不存在,或者如果信息无法读取。 如果版本信息存在并成功读取,则返回 true。
路径:C:\Qt\5.9.8\Src\qtbase\src\corelib\plugin\qlibrary_p.h 中QLibraryPrivate中声明了友元类QLibraryStore:
class QLibraryStore
{
public:
inline ~QLibraryStore();
static inline QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version, QLibrary::LoadHints loadHints);
static inline void releaseLibrary(QLibraryPrivate *lib);
static inline void cleanup();
private:
static inline QLibraryStore *instance();
// all members and instance() are protected by qt_library_mutex
typedef QMap LibraryMap;
LibraryMap libraryMap;
};
static QBasicMutex qt_library_mutex;
static QLibraryStore *qt_library_data = 0;
static bool qt_library_data_once;
QLibraryStore 中维持着一个 typedef QMap
LibraryMap libraryMap;表,映射了插件fileName和QLibraryPrivate的关系:是一一对应的关系;且QLibraryStore是作为一个单例使用,从instance 中创建,因此仅仅存在一份映射表;
......
// must be called with a locked mutex
QLibraryStore *QLibraryStore::instance()
{
if (Q_UNLIKELY(!qt_library_data_once && !qt_library_data)) {
// only create once per process lifetime
qt_library_data = new QLibraryStore;
qt_library_data_once = true;
}
return qt_library_data;
}
//创建插件
inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version,
QLibrary::LoadHints loadHints)
{
//1 上锁
QMutexLocker locker(&qt_library_mutex);
QLibraryStore *data = instance();
//2 在map中查找fileName 是否已经加载
// check if this library is already loaded
QLibraryPrivate *lib = 0;
if (Q_LIKELY(data)) {
lib = data->libraryMap.value(fileName);
if (lib)
lib->mergeLoadHints(loadHints);
}
//3 如果插件没有加载 则创建QLibraryPrivate
if (!lib)
lib = new QLibraryPrivate(fileName, version, loadHints);
// track this library
if (Q_LIKELY(data) && !fileName.isEmpty())
data->libraryMap.insert(fileName, lib);
//4 增加引用计数
lib->libraryRefCount.ref();
return lib;
}
//释放插件
inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib)
{
QMutexLocker locker(&qt_library_mutex);
QLibraryStore *data = instance();
if (lib->libraryRefCount.deref()) {
// still in use
return;
}
// no one else is using
Q_ASSERT(lib->libraryUnloadCount.load() == 0);
if (Q_LIKELY(data) && !lib->fileName.isEmpty()) {
//从映射表中移除
QLibraryPrivate *that = data->libraryMap.take(lib->fileName);
Q_ASSERT(lib == that);
Q_UNUSED(that);
}
delete lib;
}
返回插件的根组件对象。 必要时加载插件。 如果无法加载插件或无法实例化根组件对象,则该函数返回 nullptr。
如果根组件对象被销毁,调用此函数会创建一个新实例。
此函数返回的根组件在 QPluginLoader 被销毁时不会被删除。 如果您想确保根组件被删除,您应该在不再需要访问核心组件时立即调用 unload()。 当库最终被卸载时,根组件将被自动删除。
组件对象是一个 QObject。 使用 qobject_cast() 访问您感兴趣的接口。
QObject *QPluginLoader::instance()
{
if (!isLoaded() && !load())
return 0;
if (!d->inst && d->instance)
d->inst = d->instance();
return d->inst.data();
}
instance首先调用load
加载插件,如果插件加载成功则返回true; 否则返回假。 由于 instance() 总是在解析任何符号之前调用此函数,因此无需显式调用它。 在某些情况下,您可能希望提前加载插件,在这种情况下,您将使用此功能。
bool QPluginLoader::load()
{
if (!d || d->fileName.isEmpty())
return false;
if (did_load)
return d->pHnd && d->instance;
if (!d->isPlugin())
return false;
did_load = true;
return d->loadPlugin();
}
load 在检查文件名,是否已经加载,是否是一个插件后,开始加载插件:bool QLibraryPrivate::loadPlugin()
bool QLibraryPrivate::loadPlugin()
{
//1 实例存在,则引用+1
if (instance) {
libraryUnloadCount.ref();
return true;
}
// 2 不是插件 返回false
if (pluginState == IsNotAPlugin)
return false;
//3 加载插件
if (load()) {
// 4 解析动态库符号
instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance");
return instance;
}
//4 打印信息
if (qt_debug_component())
qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString;
pluginState = IsNotAPlugin;
return false;
}
其中第三步 加载插件的过程:
bool QLibraryPrivate::load()
{
//1 判断是否存在
if (pHnd) {
libraryUnloadCount.ref();
return true;
}
//2 判断文件名是否为空
if (fileName.isEmpty())
return false;
//3 调用相应平台的加载函数
bool ret = load_sys();
if (qt_debug_component()) {
if (ret) {
qDebug() << "loaded library" << fileName;
} else {
qDebug() << qUtf8Printable(errorString);
}
}
if (ret) {
//when loading a library we add a reference to it so that the QLibraryPrivate won't get deleted
//this allows to unload the library at a later time
libraryUnloadCount.ref();
libraryRefCount.ref();
installCoverageTool(this);
}
return ret;
}
其中第三步 //3 调用相应平台的加载函数load_sys ,调用windows平台的:LoadLibrary 加载动态库
bool QLibraryPrivate::load_sys()
{
#ifndef Q_OS_WINRT
//avoid 'Bad Image' message box
UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
#endif
// We make the following attempts at locating the library:
//
// Windows
// if (absolute)
// fileName
// fileName + ".dll"
// else
// fileName + ".dll"
// fileName
//
// NB If it's a plugin we do not ever try the ".dll" extension
QStringList attempts;
if (pluginState != IsAPlugin)
attempts.append(fileName + QLatin1String(".dll"));
// If the fileName is an absolute path we try that first, otherwise we
// use the system-specific suffix first
QFileSystemEntry fsEntry(fileName);
if (fsEntry.isAbsolute())
attempts.prepend(fileName);
else
attempts.append(fileName);
#ifdef Q_OS_WINRT
if (fileName.startsWith(QLatin1Char('/')))
attempts.prepend(QDir::rootPath() + fileName);
#endif
for (const QString &attempt : qAsConst(attempts)) {
#ifndef Q_OS_WINRT
//1 调用 windows api LoadLibrary 显式加载动态库
pHnd = LoadLibrary((wchar_t*)QDir::toNativeSeparators(attempt).utf16());
#else // Q_OS_WINRT
QString path = QDir::toNativeSeparators(QDir::current().relativeFilePath(attempt));
pHnd = LoadPackagedLibrary((LPCWSTR)path.utf16(), 0);
if (pHnd)
qualifiedFileName = attempt;
#endif // !Q_OS_WINRT
// If we have a handle or the last error is something other than "unable
// to find the module", then bail out
if (pHnd || ::GetLastError() != ERROR_MOD_NOT_FOUND)
break;
}
#ifndef Q_OS_WINRT
SetErrorMode(oldmode);
#endif
if (!pHnd) {
errorString = QLibrary::tr("Cannot load library %1: %2").arg(
QDir::toNativeSeparators(fileName)).arg(qt_error_string());
} else {
// Query the actual name of the library that was loaded
errorString.clear();
#ifndef Q_OS_WINRT
wchar_t buffer[MAX_PATH];
::GetModuleFileName(pHnd, buffer, MAX_PATH);
QString moduleFileName = QString::fromWCharArray(buffer);
moduleFileName.remove(0, 1 + moduleFileName.lastIndexOf(QLatin1Char('\\')));
const QDir dir(fsEntry.path());
if (dir.path() == QLatin1String("."))
qualifiedFileName = moduleFileName;
else
qualifiedFileName = dir.filePath(moduleFileName);
//2 如果是防止卸载策略
if (loadHints() & QLibrary::PreventUnloadHint) {
// prevent the unloading of this component
HMODULE hmod;
bool ok = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN |
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
reinterpret_cast(pHnd),
&hmod);
Q_ASSERT(!ok || hmod == pHnd);
Q_UNUSED(ok);
}
#endif // !Q_OS_WINRT
}
return (pHnd != 0);
}
bool QLibraryPrivate::unload(UnloadFlag flag)
{
if (!pHnd)
return false;
if (libraryUnloadCount.load() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to
delete inst.data();
if (flag == NoUnloadSys || unload_sys()) {
if (qt_debug_component())
qWarning() << "QLibraryPrivate::unload succeeded on" << fileName
<< (flag == NoUnloadSys ? "(faked)" : "");
//when the library is unloaded, we release the reference on it so that 'this'
//can get deleted
libraryRefCount.deref();
pHnd = 0;
instance = 0;
}
}
return (pHnd == 0);
}
widnow api:FreeLibrary
bool QLibraryPrivate::unload_sys()
{
if (!FreeLibrary(pHnd)) {
errorString = QLibrary::tr("Cannot unload library %1: %2").arg(
QDir::toNativeSeparators(fileName)).arg(qt_error_string());
return false;
}
errorString.clear();
return true;
}
其中:
typedef QObject *(*QtPluginInstanceFunction)();函数指针 返回值是QObject * 类型;
resolve 函数的定义: QFunctionPointer resolve(const char *symbol);路径:C:\Qt\5.9.8\Src\qtbase\src\corelib\plugin\qlibrary.h
qt_plugin_instance 也就是QT_MOC_EXPORT_PLUGIN 宏定义中的 :
moc_CalPlugin.cpp文件中的QT_MOC_EXPORT_PLUGIN(CalPlugin, CalPlugin);展开:
导出一个qt_plugin_instance 的接口,接口的实现是实例化一个插件类的对象
QLibraryPrivate::resolve接口
QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
{
if (!pHnd)
return 0;
return resolve_sys(symbol);
}
GetProcAddress 获取接口地址
QFunctionPointer QLibraryPrivate::resolve_sys(const char* symbol)
{
FARPROC address = GetProcAddress(pHnd, symbol);
if (!address) {
errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg(
QString::fromLatin1(symbol)).arg(
QDir::toNativeSeparators(fileName)).arg(qt_error_string());
} else {
errorString.clear();
}
return QFunctionPointer(address);
}