Qt6开发通达信DLL插件:现代C++框架集成传统金融软件的实战指南
1. 项目概述为什么用Qt6给通达信写DLL如果你是一个在Windows上做C开发的尤其是跟金融数据、量化分析沾点边的大概率听说过或者折腾过通达信。通达信作为一个老牌的股票行情软件它的强大之处不仅在于看盘更在于它提供了一个相对开放的插件接口——允许用户通过编写DLL动态链接库来扩展其功能比如实现自定义指标、复杂选股公式甚至是接入外部数据源。这就像是给一个功能强大的机床开放了几个自定义的刀具接口让你能加工出标准件以外的特殊零件。传统的通达信DLL开发大家习惯用纯Win32 API或者MFC。这没什么问题但如果你想让你的插件有个稍微现代点的界面或者处理一些复杂的业务逻辑、网络请求纯Win32写起来就有点费劲了。这时候Qt6的优势就体现出来了。Qt是一个跨平台的C应用程序开发框架到了第六代它在性能、模块化和对现代C标准的支持上都有了长足进步。用Qt6来写这个DLL核心目标就一个用更高效、更现代的方式实现更强大、更稳定的通达信插件功能同时为可能的跨平台或界面需求留出空间。你可能会问一个DLL而已需要界面吗很多时候确实不需要DLL导出的函数被通达信调用默默计算返回数据就行。但有些场景下一个配置界面会非常有用。比如你的指标需要从网络API获取数据那么API密钥、服务器地址这些参数总不能硬编码在代码里吧再比如你的算法有一些可调节的参数用户可能想根据不同的市场情况微调。这时候如果DLL能弹出一个由Qt绘制的、美观易用的配置对话框用户体验会直接提升一个档次。这就是Qt的用武之地。所以这个教程要解决的就是如何把Qt6这个“现代武器”成功地集成到通达信这个“传统工坊”的插件体系里。这中间会涉及几个关键挑战如何让一个使用Qt复杂机制的动态库在通达信的进程里被安全、正确地加载和初始化如何设计DLL的导出接口以符合通达信的调用规范以及如何避免常见的陷阱比如运行时库冲突、内存管理不一致等问题。接下来我们就一步步拆解从环境搭建到代码实现再到最后的调试打包把这条路走通。2. 环境准备与工具链配置工欲善其事必先利其器。用Qt6开发通达信DLL环境配置是第一步也是最容易踩坑的一步。配置不对后面的一切都无从谈起。2.1 Qt6开发环境安装与选择首先你需要安装Qt6。我强烈建议通过Qt官方维护的安装工具Qt Online Installer来安装它比下载独立安装包更灵活可以方便地管理多个Qt版本和组件。下载与安装前往Qt官网下载在线安装程序。运行后在组件选择页面你需要重点关注两个部分Qt版本选择最新的长期支持版LTS比如 Qt 6.6 或 6.8。LTS版本更稳定社区支持更好。对于通达信DLL这种项目稳定性优先级高于追新。编译器套件这是重中之重通达信是运行在Windows上的32位应用程序。因此你必须为Qt选择对应的MSVC 32位编译器套件。通常它的名字类似于 “MSVC 2019 32-bit” 或 “MSVC 2022 32-bit”。即使你的开发机是64位系统也必须安装这个32位套件因为你的DLL需要被32位的通达信主程序加载。绝对不要选择MinGW套件或64位的MSVC套件否则编译出的DLL将无法被通达信使用。额外组件对于基础的DLL勾选Qt Core和Qt GUI通常就够了。如果你需要网络功能比如从网上获取数据再勾选Qt Network。其他如Qt Widgets用于传统桌面界面、Qt Quick用于QML界面根据你的UI需求选择。初次尝试建议从最小化开始。IDE选择你可以使用Qt Creator安装Qt时自带它和Qt框架集成得最好。也可以使用你熟悉的Visual Studio并安装Qt VS Tools插件。我个人更倾向于Qt Creator因为对于这类特定项目环境配置更简单直接。本教程后续示例将以Qt Creator为主。2.2 通达信接口研究与基础工程创建在写代码之前我们必须先搞清楚通达信期望我们的DLL长什么样。通达信官方有提供接口文档通常是一个.h头文件如TDX_DLL.h或者在其安装目录的T0002\dlls文件夹下有示例这是我们的“宪法”。如果没有也可以在网上搜索“通达信DLL接口规范”能找到很多历史版本核心接口大同小异。一个最精简的通达信DLL需要导出至少两个函数// 示例接口具体函数名和参数以你获得的官方文档为准 extern C __declspec(dllexport) int __stdcall GetPluginInfo(int nType, void* pInfo); extern C __declspec(dllexport) int __stdcall Calculate(int nType, float* pData, int nNum, char* pResult);GetPluginInfo: 通达信加载DLL时调用用于获取插件名称、版本、作者等信息。Calculate: 核心计算函数通达信将行情数据如开盘价、收盘价数组通过参数传入DLL计算后把结果写回指定缓冲区。关键点在于调用约定__stdcall和extern C。__stdcall是Windows API和通达信使用的标准调用约定。extern C是为了防止C编译器对函数名进行“名称修饰”Name Mangling确保导出的函数名是简单的、通达信能直接查找的格式如_Calculate16而不是一堆乱码。在Qt Creator中创建项目打开Qt Creator选择“新建项目”。在“Library”类别下选择“C Library”。不要选择“Qt Widgets Application”或“Qt Console Application”。在类型选择页面选择“共享库”Shared Library。这就是Windows下的DLL。在接下来的页面务必取消勾选“Qt”相关模块如QtCore, QtGui。因为我们先要创建一个最纯净的、符合通达信接口规范的C DLL项目框架。Qt的集成我们稍后手动添加这样可以最大程度控制初始化过程避免冲突。完成创建。你会得到一个包含.pro文件Qt的项目文件、.cpp和.h文件的基础工程。2.3 解决潜在的运行时库冲突这是用Qt或任何现代C框架开发传统Win32插件最常见、最头疼的问题没有之一。错误提示可能五花八门比如“找不到Qt6Core.dll”、“应用程序无法正常启动(0xc000007b)”或者更隐晦的崩溃。问题的根源在于运行时库Runtime Library的版本和链接方式必须一致。通达信主程序作为一个历史悠久的商业软件它极大概率是使用/MT或/MTd静态链接运行时库选项编译的。这意味着它把C/C标准库如msvcrt.lib的代码直接打包进了自己的exe里。你的Qt6 DLL默认情况下Qt的预编译库是使用/MD或/MDd动态链接运行时库选项编译的。这意味着你的DLL依赖于外部的MSVCP140.dll,VCRUNTIME140.dll等动态库。如果一个/MT编译的程序加载了一个/MD编译的DLL并且在两者之间传递了标准库对象比如std::string,std::vector的内存分配和释放就极有可能因为内存堆Heap不同而导致崩溃。解决方案编译选项统一在你的Qt项目文件.pro中强制指定使用静态运行时库。这是最一劳永逸的方法但会增大你的DLL体积。# 在 .pro 文件中添加 QMAKE_CXXFLAGS_RELEASE /MT QMAKE_CXXFLAGS_DEBUG /MTd QMAKE_LFLAGS_RELEASE /MT QMAKE_LFLAGS_DEBUG /MTd注意这要求你使用的Qt库本身也必须是用/MT选项编译的。而官方在线安装器提供的预编译包通常是/MD的。因此你需要自己从源码编译Qt并在配置时指定-static-runtime对于CMake或在configure脚本中添加对应选项。这是本方案最大的门槛。接口隔离推荐这是更安全、更通用的做法。原则是绝对不在DLL的导出函数接口中直接使用任何C标准库类型或Qt对象。所有数据交换通过最原始的C类型int,float,double,char*或简单的结构体struct其成员也全是POD类型进行。在DLL内部你可以随意使用std::vector和QString进行计算但在与通达信交互的边界上必须进行“序列化”和“反序列化”。传入通达信给你float*数组和长度你将其转换为std::vectorfloat进行处理。传出你将QString或std::string的结果通过strcpy_s等安全函数复制到通达信提供的char*缓冲区。内存管理谁分配谁释放。如果接口需要DLL返回一个字符串指针通常应由通达信分配好缓冲区并传入指针和缓冲区大小DLL只负责写入。避免DLL内部new/malloc内存然后让通达信去free这很容易因堆不同而崩溃。我们选择第二种方案因为它不依赖于特定的Qt编译方式兼容性最好。接下来的代码实现将严格遵守这一原则。3. 核心代码实现与接口设计环境配好了原则也清楚了现在开始写代码。我们将创建一个既能满足通达信基础调用又能安全使用Qt6功能的DLL。3.1 定义符合规范的导出接口首先在项目的主头文件例如tdx_qt_plugin.h中定义我们的接口。这里我们假设一个相对完整的接口集。// tdx_qt_plugin.h #ifndef TDX_QT_PLUGIN_H #define TDX_QT_PLUGIN_H // 为了确保C和C编译器都能理解用extern C包裹 #ifdef __cplusplus extern C { #endif // 明确使用__stdcall调用约定并导出函数 #define TDX_EXPORT __declspec(dllexport) __stdcall // 1. 获取插件信息 TDX_EXPORT int GetPluginInfo(int nInfoType, void* pInfo); // 2. 插件初始化可选但建议有。在这里初始化Qt应用对象 TDX_EXPORT int PluginInit(); // 3. 插件清理可选在这里清理Qt资源 TDX_EXPORT int PluginCleanup(); // 4. 核心计算函数 - 示例计算移动平均线 TDX_EXPORT int CalculateMA(int nDataLen, float* pfPrice, // 输入价格数组 int nPeriod, // 输入周期 float* pfOutMA, // 输出MA结果数组 char* szErrorMsg); // 输出错误信息缓冲区 // 5. 显示配置对话框如果需要 TDX_EXPORT int ShowConfigDialog(void* hParentWnd); // hParentWnd是通达信主窗口句柄 #ifdef __cplusplus } #endif #endif // TDX_QT_PLUGIN_H对应的源文件tdx_qt_plugin.cpp需要实现这些函数。注意所有函数实现必须放在extern C块内或者函数定义本身就包含extern C。3.2 安全地集成Qt应用实例这是关键一步。一个GUI Qt程序通常需要一个QApplication实例对于控制台程序是QCoreApplication。但在DLL中我们不能像普通应用那样在main函数里创建它。我们需要一个全局的、单例的应用对象并且要确保它在被任何Qt功能使用前就已初始化在DLL卸载前被正确销毁。// tdx_qt_plugin.cpp #include tdx_qt_plugin.h #include windows.h #include cstring // for strcpy_s #include vector #include algorithm // Qt头文件 #include QApplication #include QMessageBox #include QDialog // ... 其他需要的Qt头文件 // 全局Qt应用指针 static QApplication* g_pQtApp nullptr; static int g_nQtArgc 1; static char g_pQtArgv0[] TdxQtPlugin; static char* g_pQtArgv[] { g_pQtArgv0, nullptr }; // 一个简单的配置对话框类如果需要 class ConfigDialog : public QDialog { Q_OBJECT public: ConfigDialog(QWidget* parent nullptr) : QDialog(parent) { // ... 构建你的对话框UI可以使用Qt Designer生成的.ui文件也可以手动代码布局 // 例如添加一些输入框用于设置API密钥、计算参数等。 } // ... 其他成员函数 }; // 初始化Qt。考虑到可能被多次调用做成幂等的。 static bool EnsureQtAppInitialized() { if (!g_pQtApp) { // 注意我们创建的是QApplication即使暂时没有界面。 // 如果确定永远不需要任何GUI包括消息框可以用QCoreApplication更轻量。 g_pQtApp new QApplication(g_nQtArgc, g_pQtArgv); // 可以在这里进行一些全局的Qt设置比如设置编码、字体等。 // QTextCodec::setCodecForLocale(...); // Qt6中字符串编码处理方式有变化需注意 } return (g_pQtApp ! nullptr); } // 导出函数实现 extern C { int TDX_EXPORT PluginInit() { if (EnsureQtAppInitialized()) { return 0; // 成功 } return -1; // 失败 } int TDX_EXPORT PluginCleanup() { if (g_pQtApp) { delete g_pQtApp; g_pQtApp nullptr; } return 0; } int TDX_EXPORT GetPluginInfo(int nInfoType, void* pInfo) { if (!pInfo) return -1; switch (nInfoType) { case 0: { // 假设0代表获取插件名称 const char* szName Qt6高级指标插件 v1.0; strcpy_s((char*)pInfo, 256, szName); // 假设缓冲区足够大 break; } case 1: { // 假设1代表获取作者信息 const char* szAuthor YourName; strcpy_s((char*)pInfo, 256, szAuthor); break; } // ... 其他信息类型 default: return -2; // 不支持的信息类型 } return 0; } int TDX_EXPORT CalculateMA(int nDataLen, float* pfPrice, int nPeriod, float* pfOutMA, char* szErrorMsg) { // 1. 参数安全检查 if (nDataLen 0 || !pfPrice || !pfOutMA || nPeriod 0 || nPeriod nDataLen) { if (szErrorMsg) strcpy_s(szErrorMsg, 256, Invalid input parameters.); return -1; } // 2. 将原始C数组转换为更易用的std::vector内部使用安全 std::vectorfloat prices(pfPrice, pfPrice nDataLen); // 3. 核心计算逻辑简单移动平均 for (int i nPeriod - 1; i nDataLen; i) { float sum 0.0f; for (int j 0; j nPeriod; j) { sum prices[i - j]; } pfOutMA[i] sum / nPeriod; // 结果直接写回通达信提供的缓冲区 } // 前 nPeriod-1 个位置没有有效MA值可以设为0或一个特殊值 for (int i 0; i nPeriod - 1; i) { pfOutMA[i] 0.0f; } // 4. 可选使用Qt功能例如记录日志到文件需要QtCore // QFile logFile(plugin_log.txt); // if (logFile.open(QIODevice::Append)) { // QTextStream ts(logFile); // ts QDateTime::currentDateTime().toString() MA calculated, period nPeriod \n; // } return 0; // 成功 } int TDX_EXPORT ShowConfigDialog(void* hParentWnd) { // 确保Qt应用已初始化 if (!EnsureQtAppInitialized()) { return -1; } // 将通达信传入的窗口句柄转换为Qt可识别的WId WId parentWinId reinterpret_castWId(hParentWnd); // 注意QWidget::find(parentWinId) 可以找到对应的QWidget但通达信的窗口不是Qt控件。 // 更通用的做法是创建一个无父窗口的对话框或者使用QWidget::createWindowContainer等高级技术复杂。 // 简单起见这里创建一个顶级对话框。用户体验稍差但稳定。 ConfigDialog dlg; dlg.setWindowTitle(Qt插件配置); // 如果转换成功可以尝试设置模态到某个原生窗口但这需要平台相关代码。 // 对于简单需求直接exec()即可。 if (dlg.exec() QDialog::Accepted) { // 保存配置... return 0; } return 1; // 用户取消 } } // extern C 结束3.3 项目文件(.pro)的关键配置Qt Creator的项目文件.pro控制着编译和链接的所有细节。以下是一个经过精心配置的示例# 指定生成的目标是动态库DLL TEMPLATE lib CONFIG c17 shared dll # 关闭Qt的默认GUI模块链接我们需要手动控制 QT - core gui widgets # 但我们在代码中使用了Qt所以需要链接对应的库。 # 使用 - 再 是为了确保顺序清晰。或者直接写 QT core gui QT core QT gui # 如果用了网络、图表等其他模块在这里添加 # QT network # 定义预处理器宏用于在头文件中区分导入/导出 DEFINES TDX_QT_PLUGIN_LIBRARY # 输出目录和文件名配置 DESTDIR $$PWD/../bin # 将DLL输出到项目上一级的bin文件夹方便管理 TARGET TdxQtPlugin # 生成的DLL文件名不含后缀 # 如果需要生成导入库(.lib)和导出文件(.exp)保持默认即可CONFIG dll 会自动处理。 # 编译器标志 # 关闭一些安全警告视情况而定避免编译通达信老旧头文件时产生大量警告。 QMAKE_CXXFLAGS_WARN_ON - -Wall # 如果需要静态链接C运行时库方案一在这里添加/MT标志需配套使用静态编译的Qt # win32:CONFIG(release, debug|release): { # QMAKE_CXXFLAGS /MT # QMAKE_LFLAGS /MT # } else:win32:CONFIG(debug, debug|release): { # QMAKE_CXXFLAGS /MTd # QMAKE_LFLAGS /MTd # } # 源文件和头文件 SOURCES \ tdx_qt_plugin.cpp \ configdialog.cpp # 如果有其他cpp文件 HEADERS \ tdx_qt_plugin.h \ configdialog.h FORMS \ configdialog.ui # 如果有Qt Designer设计的.ui文件4. 编译、部署与调试实战代码写完了接下来就是把它变成通达信能用的东西。4.1 编译生成与依赖收集选择构建套件在Qt Creator左下角确保选择了正确的构建套件即你安装的MSVC 32-bit套件并且构建目标为“Release”。Debug版本会链接调试库不适合分发。构建项目点击构建按钮。如果一切顺利你会在DESTDIR指定的目录如../bin/下找到生成的TdxQtPlugin.dll可能还有TdxQtPlugin.lib和TdxQtPlugin.exp。收集运行时依赖这是部署的关键一步。你的DLL可能依赖于Qt6核心库Qt6Core.dll,Qt6Gui.dll等。它们位于你的Qt安装目录下的bin文件夹例如C:\Qt\6.6.0\msvc2019_32\bin。Visual C Redistributable即使你用了/MT如果Qt库是/MD编译的你仍然需要这些。通常是MSVCP140.dll,VCRUNTIME140.dll,VCRUNTIME140_1.dll。它们可以在系统目录找到但为了便携可以一并复制。平台插件如果你的DLL有GUI比如那个配置对话框还需要platforms/qwindows.dll这个插件。它位于Qt安装目录的plugins\platforms下。部署清单你需要将以下文件放在同一个文件夹比如MyTdxPlugin里TdxQtPlugin.dll你的主DLLQt6Core.dllQt6Gui.dll...其他你用到的Qt模块dllMSVCP140.dll,VCRUNTIME140.dll如果必要platforms/qwindows.dll整个platforms文件夹4.2 在通达信中安装与测试放置DLL在通达信的安装目录下通常有一个T0002\dlls文件夹对应你的登录账号。将上一步准备好的整个MyTdxPlugin文件夹或者至少把所有依赖的dll和你的主dll放在一起复制到这里。更规范的做法是把你的TdxQtPlugin.dll单独放在dlls目录下而将所有依赖的Qt库放在dlls下的一个子目录如Qt6中但这需要你修改Qt的库加载路径比较复杂。对于初次测试放在一起最简单。通达信内调用打开通达信软件。在K线图界面右键 - “专家系统” - “公式管理器”。在公式管理器中你可以新建或编辑一个技术指标公式。在公式编辑器中使用通达信特定的语法调用DLL函数。语法通常是这样的{插件名:函数名参数序列}例如假设你的DLL文件名为TdxQtPlugin.dll导出的计算MA的函数叫CalculateMA那么公式可能写成MA_VALUE:TDRAWICON(1, “TdxQtPlugin.dll”, “CalculateMA”, CLOSE, 20);注意具体的调用语法TDRAWICON,TDXDLL,CALC等函数和参数传递方式如何把收盘价数组CLOSE和周期20传递进去必须严格参照你所使用的通达信版本的DLL接口文档。不同版本、不同函数指标、交易系统、条件选股的调用方式可能有差异。网上常见的示例是使用“DLL1”等占位符然后在公式中指定DLL文件名和函数名。这一步是连接通达信和你的DLL的桥梁务必准确。验证保存公式在K线图上应用该指标。如果计算成功你应该能看到绘制出的均线。如果出错通达信通常会在状态栏或一个错误框中提示比如“DLL调用失败”。4.3 高级调试技巧与问题排查在非Qt环境中调试Qt DLL是件麻烦事。这里有几个实用的方法日志输出是生命线在DLL的关键位置如PluginInit、Calculate函数入口、错误分支写入日志文件。使用Qt的QFile和QTextStream或者更简单的C库fstream。把时间戳、函数名、关键变量值都记下来。当插件行为异常时日志文件是第一手资料。void LogMessage(const QString msg) { QFile file(C:\\TdxPluginLog.txt); // 指定一个绝对路径确保有写入权限 if (file.open(QIODevice::Append | QIODevice::Text)) { QTextStream out(file); out QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss.zzz) - msg \n; file.close(); } }使用Visual Studio附加调试编译你的DLL时使用Debug配置记得Debug版的DLL也需要对应的Qt调试库如Qt6Cored.dll。打开Visual Studio不要打开项目直接选择“调试” - “附加到进程”。在进程列表中找到tdxw.exe通达信主进程选中并附加。在VS中打开你的DLL源代码文件在你关心的位置如CalculateMA函数开始处设置断点。回到通达信触发调用你的DLL公式。如果一切顺利VS会中断在断点处此时你可以查看调用栈、变量值进行单步调试。这是解决复杂逻辑BUG的最强武器。依赖检查工具使用像Dependencies原Dependency Walker或Process Explorer这样的工具。在通达信运行并加载了你的插件后用Process Explorer查看tdxw.exe进程在“DLL”标签页里搜索你的TdxQtPlugin.dll查看它是否被加载以及它又加载了哪些其他DLL。这能帮你快速发现“找不到Qt6Core.dll”这类依赖缺失的问题。常见错误与解决“找不到指定模块”99%是依赖的DLL如Qt库、VC运行库不在搜索路径上。确保所有依赖库和你的主DLL在同一个目录。“DLL初始化例程失败”这通常发生在DLL的入口函数DllMain或全局/静态对象初始化时。Qt的全局对象如QApplication初始化失败可能导致此问题。检查EnsureQtAppInitialized()函数是否被成功调用资源分配是否成功。确保没有在DllMain中进行复杂的初始化如创建窗口这可能导致死锁。通达信调用后崩溃首先检查内存访问越界数组索引、缓冲区大小。其次严格检查接口的调用约定__stdcall和函数名导出是否正确。使用dumpbin /exports YourDll.dll命令查看导出的函数名是否与通达信公式中调用的完全一致包括名称修饰。最后回忆是否在接口边界传递了C对象如QString务必遵守“接口隔离”原则。5. 性能优化与进阶扩展一个能跑起来的插件只是开始要做得专业、好用还需要考虑更多。5.1 计算性能优化通达信的指标计算往往是实时、高频的。如果你的算法复杂性能至关重要。避免在计算函数中频繁分配内存不要在Calculate这样的核心函数里频繁new/delete或创建大的临时std::vector/QVector。理想情况下传入的输入/输出缓冲区应由通达信管理。如果内部需要中间存储可以考虑使用静态或线程局部的缓存。但要注意线程安全通达信调用DLL是否是多线程的需确认通常不是但为保险起见。// 使用静态缓存假设单线程调用 int CalculateMA(...) { static std::vectorfloat s_internalCache; s_internalCache.resize(nDataLen); // 重用内存 // ... 使用 s_internalCache 进行计算 }使用高效算法和数据结构对于移动平均这类计算可以使用滑动窗口或前缀和来避免重复计算将O(n*m)的复杂度降为O(n)。利用现代CPU特性如果计算量巨大可以考虑使用SIMD指令如SSE、AVX进行向量化计算。但这需要较高的编程技巧和对数据对齐的要求。5.2 功能进阶网络、多线程与界面网络数据获取在PluginInit中初始化QNetworkAccessManager。在计算函数中可以检查本地是否有缓存数据如果没有则异步发起网络请求。但是通达信的DLL接口函数通常是同步、要求立即返回的。所以网络请求更适合在后台线程进行定期更新一个共享的数据缓存而计算函数只从缓存中读取。这涉及到线程同步QMutex和与通达信主循环的集成可能需要定时器事件复杂度较高。多线程计算如果单个指标计算非常耗时可以考虑在DLL内部使用QThreadPool或std::async进行并行计算。但同样需要注意与通达信调用线程的同步以及资源竞争。更复杂的配置界面上面的示例使用了简单的模态对话框。你可以设计更复杂的非模态对话框甚至利用Qt的模型/视图框架来展示配置项。关键是要处理好与通达信主窗口的父子关系让对话框能正确地模态阻塞或跟随主窗口。可以尝试使用QWidget::createWindowContainer嵌入原生窗口或者使用SetParentWin32 API但这部分平台相关代码比较棘手。5.3 兼容性与维护建议版本管理在GetPluginInfo函数中返回清晰的版本号。当你的插件升级时考虑数据格式或接口的向后兼容性。错误处理每一个导出函数都必须有健壮的错误处理。不仅检查输入参数还要检查内部状态如Qt是否初始化成功。通过szErrorMsg等输出参数向通达信返回可读的错误信息方便用户排查。减少依赖只链接你真正用到的Qt模块。每个额外的Qt6XXX.dll都会增加部署复杂度和加载时间。定期检查生成的DLL的依赖项。文档与示例为你写的DLL配备一个简单的说明文档解释每个导出函数的功能、参数和返回值。最好再提供一个示例通达信公式文件.tni让用户能一键导入测试。走到这一步你已经拥有了一个用现代Qt6框架开发的、功能可扩展、具备潜在GUI配置能力的通达信插件。它比传统的Win32 DLL更易于维护和功能扩展虽然初始的集成过程需要克服一些障碍但一旦跑通后续的开发效率会高很多。记住稳定性和兼容性永远是这类嵌入式插件的首要考量任何花哨的功能都要为此让路。