未按本规范编译出来的 DLL,主程序会直接拒绝加载。
一、编译环境与依赖
| 项目 | 硬性要求 |
|---|---|
| Qt 版本 | 必须与主程序完全一致(Qt 6.11.1,套件目录 6.11.1/mingw_64) |
| 编译器 | MinGW-w64 GCC 13.1(win32 线程模型, SEH)64-bit;禁止 MSVC |
| 目标架构 | x64 仅此一种;ARM64 客户端不提供插件市场 |
| 编译模式 | Release + x64(Debug / 32-bit 一律拒绝) |
| Qt 链接方式 | 动态链接 Qt,禁止静态编译 |
| C++ 标准 | C++17 或更高 |
| 字符集 | Unicode(_UNICODE,Qt 默认) |
为什么必须完全一致:QString/QWidget等 Qt 对象跨 DLL 边界安全传递的前提,是双方 Qt 大版本、小版本与工具链完全一致,否则会出现虚表不匹配、堆分配器不同、布局错位等崩溃。 历史教训(2.2.6 事故):商店版主程序曾误用 MSVC,而插件按 MinGW 编译,LoadLibrary直接返回 126(找不到libgcc_s_seh-1.dll)。自 2.2.7.0 起,商店 x64 版与官网 EXE 版统一为 MinGW 主路线,主程序在QPluginLoader之前做 PE 工具链预检。
二、工程搭建最小模板
cmake_minimum_required(VERSION 3.20)
project(DemoPlugin LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# 指向与主程序完全一致的 Qt 安装目录(请自行修改)
set(CMAKE_PREFIX_PATH "E:/Qt/6.11.1/mingw_64")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_add_plugin(DemoPlugin
SHARED
demo/DemoPlugin.h
demo/DemoPlugin.cpp
demo/DemoPlugin.json
)
target_include_directories(DemoPlugin PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(DemoPlugin PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)三、命令行构建
前置:MinGW 13.1(mingw1310_64)、Ninja、CMake 三者加入 PATH。
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release `
-DCMAKE_PREFIX_PATH="E:/Qt/6.11.1/mingw_64"
cmake --build build验证产物工具链:g++ -v 应为 13.1,目标 x86_64-w64-mingw32;DLL 导入表应能看到 libgcc_s_seh-1.dll / libstdc++-6.dll(由主程序统一部署,不要随插件分发)。
四、编译自检 Checklist
逐条对照,任何一条不满足加载都会失败:
| # | 检查项 | 不满足的报错码 |
|---|---|---|
| 1 | DLL 可被 QLibrary::isLibrary() 识别 | FileCorrupted |
| 2 | 导出 qt_plugin_instance / qt_plugin_query_metadata | NotAQtPlugin |
| 3 | 元数据 IID 为 com.7aa7.toolbox.I7AA7Plugin/2.1 | WrongInterface |
| 4 | qobject_cast<I7AA7Plugin*> 返回非空 | WrongInterface |
| 5 | pluginId / pluginName 非空 | MetaInfoInvalid |
| 6 | hostMinimumVersion <= 主程序版本号 | VersionTooHigh |
| 7 | x64 Release + Qt 动态 + MinGW 13.1(PE 预检 Machine=0x8664) | PlatformMismatch |
| 8 | pluginInit() 返回 true 且未抛异常 | InitRaisedException |
五、ABI 兼容性红线
I7AA7Plugin虚函数顺序严禁改动:新纯虚函数只能追加到末尾;PluginMetaInfo字段顺序严禁改动:新增字段必须放进extra(QVariantMap);- 禁止在 SDK 头中定义静态全局对象 / 静态变量;
- 不要跨 DLL 边界
new/delete:返回的 QWidget 推荐以parent托管;插件自建裸对象须在pluginUninstall中自行delete; - 统一使用 MinGW 动态运行库:不要加
-static-libgcc -static-libstdc++。
六、调试与联调
- 取得可加载的签名插件后,在「系统 → 插件管理」确认显示「已启用」;
- 打开「功能分类 → 插件中心」,点击二级菜单查看 UI 挂载;
- 加载失败点「查看日志」定位报错码;
- 若崩溃,检查:是否误用私有头文件、是否在
getPluginMetaInfo中 new QWidget、返回的 QWidget 是否在pluginUninstall后仍被持有。