简介这份资源面向具备C基础、希望在通达信中实现授权验证的量化开发者与证券软件爱好者核心是用DLL插件在通达信公式里调用接口生成与每台机器唯一绑定的标识码用于激活验证与许可证管理。压缩包共13个文件、约88KB以6个cpp源文件和4个头文件为主涵盖插件功能测试、自定义函数注册、MD5哈希与磁盘信息读取等模块另含Visual Studio项目配置及一份通达信DLL函数编程规范文档便于直接编译调试。已有2427人学习下载。通过源码可掌握通达信开发接口调用、DLL项目配置、一机一码生成算法与安全加密思路并理解插件在公式编辑器中的加载流程适合需要落地授权方案或研究插件架构的开发者参考。1. 通达信DLL编程把一机一码验证塞进公式里这事到底值不值得做如果你写过通达信公式大概率遇到过这种尴尬辛辛苦苦写了一套指标逻辑想分发给几个朋友用结果对方拿到公式文件直接复制粘贴你的劳动成果瞬间变成“公开源码”。公式语言本身是解释执行的没有编译保护也没有授权机制想靠它做商业分发基本等于裸奔。通达信DLL编程就是在这个场景下被逼出来的方案——把核心逻辑编译成动态链接库公式里只留一行调用别人拿到的只是一个函数入口看不到内部实现。而“一机一码”则是在DLL基础上再加一层绑定每台机器生成唯一标识DLL启动时校验不匹配就拒绝执行。这套组合适合谁独立指标开发者、小团队做策略分发、或者你只是想给自己写的公式加个简单的使用门槛。它不复杂但涉及C编译、DLL导出、公式调用、机器码生成几个环节任何一个环节掉链子都会让你卡半天。2. 环境搭建与DLL工程骨架从零建一个能被通达信认出来的动态库2.1 编译工具链的选择与工程配置通达信DLL本质是一个标准的Windows动态链接库导出特定签名的函数供公式引擎调用。编译器用Visual Studio的MSVC最稳版本选2015到2022都行关键是平台要匹配——通达信主程序是32位的所以DLL必须编译成Win32x86别顺手选了x64否则公式调用时直接报“找不到函数”或者通达信崩溃。新建项目时选“动态链接库(DLL)”空项目即可然后手动添加源文件。工程属性里有两处必须改第一C/C → 代码生成 → 运行库改成“多线程(/MT)”这样编译出来的DLL不依赖VC运行库换台机器也能跑第二链接器 → 常规 → 输出文件确认后缀是.dll。如果你打算导出C风格函数名避免C名称修饰导致公式找不到入口源文件里导出函数要用extern C包裹或者直接写.def文件。// TDXDLL.h - 通达信DLL导出函数声明 #pragma once // 通达信公式引擎会按这个签名调用 // 返回值是输出数据数组参数是输入数据数组 extern C __declspec(dllexport) void __stdcall Func( int nParam1, // 输入参数个数或自定义参数 int nParam2, // 数据长度 float* pfIn, // 输入数据指针 float* pfOut // 输出数据指针 );这段声明里__declspec(dllexport)告诉编译器把这个函数放进导出表__stdcall是Windows API标准调用约定通达信按这个约定找入口。函数名Func可以改成你自己的但公式里调用时要用同名。参数含义在不同通达信版本里略有差异常见做法是第一个参数传数据长度第二个传自定义参数输入输出用float数组指针。我一般会在函数开头加日志把参数值写到文件里方便确认通达信到底传了什么进来。2.2 通达信公式端的调用写法DLL编译好之后放到通达信安装目录的T0002\dlls文件夹下没有就新建。然后在公式管理器里新建一个公式类型选“技术指标”在代码区写调用语句。通达信调用DLL的语法是DLL名称.函数名(参数1,参数2,...)注意DLL名称不带.dll后缀。{ 通达信公式中调用DLL的写法 } DLLNAME: MyTdxDll.Func(10, 20);这里MyTdxDll是你编译出来的DLL文件名Func是导出函数名。参数个数和类型要和DLL里的签名对应传多了或传少了通达信可能不报错但结果不对。一个血泪经验公式里第一次调用DLL后如果修改了DLL重新编译必须重启通达信否则它加载的还是旧版本你会怀疑自己代码写错了。另外公式编辑器里点“测试”不一定能触发DLL调用最好直接加载到K线图上观察输出。3. 一机一码核心逻辑机器码生成、绑定校验与DLL内嵌实现3.1 机器码的生成策略一机一码的核心是“每台机器有一个唯一标识且这个标识不容易被伪造”。常见做法是取硬件信息组合后哈希硬盘序列号、主板序列号、CPU ID、网卡MAC地址任选两到三个组合再用MD5或SHA256算出一个固定长度的字符串。不要只取一个硬件信息因为虚拟机、硬件更换、驱动重装都可能导致单个信息变化组合后稳定性更好。// MachineCode.cpp - 生成机器码 #include windows.h #include intrin.h #include string #include sstream #include iomanip // 获取硬盘卷序列号 std::string GetVolumeSerial() { DWORD serial 0; GetVolumeInformationA(C:\\, NULL, 0, serial, NULL, NULL, NULL, 0); std::stringstream ss; ss std::hex std::setw(8) std::setfill(0) serial; return ss.str(); } // 获取CPU厂商型号字符串 std::string GetCpuInfo() { int cpuInfo[4] {0}; char vendor[13] {0}; __cpuid(cpuInfo, 0); memcpy(vendor, cpuInfo[1], 4); memcpy(vendor 4, cpuInfo[3], 4); memcpy(vendor 8, cpuInfo[2], 4); return std::string(vendor); } // 组合并生成机器码简化版实际可加MD5 std::string GenerateMachineCode() { std::string raw GetVolumeSerial() - GetCpuInfo(); // 这里用简单哈希示意生产环境建议用MD5/SHA256 unsigned int hash 0; for (char c : raw) { hash hash * 131 c; } std::stringstream ss; ss std::hex std::uppercase hash; return ss.str(); }这段代码里GetVolumeInformationA取C盘卷序列号__cpuid取CPU厂商信息两者拼接后做一个简单哈希。实际项目中我会用OpenSSL或Windows自带的CryptoAPI做MD5输出32位十六进制字符串看起来更像“机器码”。注意卷序列号在格式化硬盘后会变CPU信息在换主板后可能变所以如果你的用户群体硬件变动频繁建议把机器码做成“可重置”模式比如允许用户提交申请后你手动换绑。3.2 授权文件与校验逻辑机器码生成后需要和授权绑定。最简单的方案是用户把机器码发给你你用私钥对机器码签名生成一个授权文件比如license.dat用户把授权文件放到指定目录DLL启动时读取授权文件用公钥验签再比对当前机器码是否匹配。这样即使别人拿到授权文件换台机器也用不了。// LicenseCheck.cpp - 授权校验简化示意 #include fstream #include string bool VerifyLicense(const std::string machineCode) { std::ifstream file(C:\\TdxLicense\\license.dat); if (!file.is_open()) return false; std::string storedCode; std::getline(file, storedCode); file.close(); // 实际应用中是验签比对这里简化为直接比对 return storedCode machineCode; }校验逻辑放在DLL的初始化函数里或者每次公式调用时都检查一次。如果校验失败DLL可以返回空数据、返回错误码、或者输出一条提示信息到公式结果里。我一般会在DLL里加一个全局标志第一次调用时校验后续调用直接读缓存避免频繁读文件影响性能。授权文件路径不要写死在C盘根目录容易被清理建议放在通达信安装目录下的某个子文件夹或者用注册表存路径。3.3 把校验嵌入公式调用链路DLL导出函数被公式调用时第一件事就是校验授权。如果失败直接返回全0或者特定值公式那边看到的就是一条直线或者空值用户自然知道有问题。但这里有个坑通达信公式引擎对DLL返回值的处理是“有多少填多少”如果你返回的数组长度不对可能显示异常。所以校验失败时最好还是按正常长度填充只是值全部设为0或者NaN。extern C __declspec(dllexport) void __stdcall Func( int nParam1, int nParam2, float* pfIn, float* pfOut) { static bool checked false; static bool authorized false; if (!checked) { std::string mc GenerateMachineCode(); authorized VerifyLicense(mc); checked true; } if (!authorized) { for (int i 0; i nParam2; i) { pfOut[i] 0.0f; // 未授权输出全0 } return; } // 正常业务逻辑 for (int i 0; i nParam2; i) { pfOut[i] pfIn[i] * 2.0f; // 示例简单放大 } }这段代码把校验和业务逻辑串在一起checked和authorized用static变量缓存避免每次调用都重新生成机器码和读文件。注意nParam2是数据长度循环时不要越界。如果你有多个导出函数每个函数都要加这段校验或者抽成一个公共函数在入口处调用。4. 避坑与常见问题DLL加载失败、公式报错、机器码漂移的排查手册4.1 DLL加载失败通达信根本找不到你的库现象是公式里调用DLL后没有任何输出或者通达信直接卡死。原因通常是DLL位数不对64位DLL放到32位通达信里、DLL没放在T0002\dlls目录、或者导出函数名被C修饰了。解决用Dependency Walker或dumpbin /exports检查导出表确认函数名是Func而不是?FuncYGX...。如果是名称修饰问题在源文件里加extern C或者用.def文件显式导出。4.2 公式报“函数未定义”调用语法写错了通达信公式里调用DLL的语法比较死板DLL名和函数名之间用点号参数用括号括起来字符串要加引号。常见错误是写了MyTdxDll.dll.Func多写了.dll或者参数类型不匹配比如DLL要float你传了整数。解决先写一个最简单的DLL只返回固定值确认调用链路通了再往上加逻辑。4.3 机器码漂移用户换了个硬盘就失效现象是用户反馈“昨天还能用今天提示未授权”。原因是机器码取了硬盘序列号用户换了硬盘或者重装了系统。解决机器码生成时至少取两个硬件信息并且允许一定容错——比如两个信息中有一个匹配就放行。或者干脆做成在线验证用户换硬件后你后台重置绑定。离线方案没有完美解只能权衡安全性和用户体验。4.4 授权文件被复制一机一码形同虚设现象是用户把license.dat复制到另一台机器居然也能用。原因是校验逻辑只检查了文件存在没有比对机器码。解决授权文件里必须包含机器码的签名DLL启动时用公钥验签再比对当前机器码。不要用对称加密否则你把密钥硬编码在DLL里别人反编译就能伪造授权。4.5 性能问题每次调用都读文件导致公式卡顿现象是加载DLL的公式后通达信切换周期或刷新时明显变慢。原因是校验逻辑放在了每次调用的入口频繁读磁盘。解决用static变量缓存校验结果只在第一次调用时执行完整校验。如果DLL有多个导出函数把校验抽成独立函数用一个全局标志控制。5. 二次开发与改造思路从单机授权到可维护的分发体系5.1 把硬编码路径改成配置化上面示例里授权文件路径写死在C:\TdxLicense\license.dat实际分发时不同用户安装路径不一样。我一般会在DLL里读注册表或者环境变量获取通达信安装目录然后把授权文件放在T0002下的一个隐藏文件夹里。这样用户迁移通达信或者重装系统后只要目录结构一致授权文件不用动。// 从注册表获取通达信安装路径示意 std::string GetTdxPath() { HKEY hKey; char path[MAX_PATH] {0}; DWORD size MAX_PATH; RegOpenKeyExA(HKEY_CURRENT_USER, Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\通达信, 0, KEY_READ, hKey); RegQueryValueExA(hKey, InstallLocation, NULL, NULL, (LPBYTE)path, size); RegCloseKey(hKey); return std::string(path); }注册表键名根据实际安装情况可能不同常见做法是遍历Uninstall节点找包含“通达信”的项。拿到路径后拼上T0002\dlls\license.dat这样授权文件跟着通达信走用户换电脑只要重新申请授权就行。5.2 加一个简单的授权管理工具手动给每个用户生成授权文件太累我一般会写一个配套的小工具用户运行工具自动生成机器码并显示用户把机器码发给你你在后台用私钥签名生成授权文件回传。工具可以用C# WinForm或者Python tkinter写核心就是调用和DLL里一样的机器码生成逻辑保证两边算出来的码一致。这个工具不需要联网适合小规模分发。5.3 验证改造是否生效的检查清单改完代码后按这个顺序验证第一步用dumpbin /exports确认导出函数名和签名没变第二步把DLL放到T0002\dlls重启通达信加载公式看是否有输出第三步删除授权文件确认公式输出变成全0或异常第四步换一台机器复制DLL和授权文件确认校验失败第五步在目标机器上重新生成机器码并申请授权确认恢复正常。这五步走完基本能覆盖大部分翻车场景。5.4 一个容易忽略的细节DLL的线程安全通达信公式引擎可能在多线程环境下调用DLL如果你的校验逻辑里有全局变量且没有加锁可能出现竞态条件。我一般用std::atomic或者Windows的InterlockedExchange做标志位避免多线程下重复校验或者状态错乱。另外DLL里不要用std::cout输出调试信息通达信可能没有控制台输出会丢失用OutputDebugString或者写日志文件更靠谱。从那以后我每次编译完DLL都会先用dumpbin /exports看一眼导出表再丢到通达信里跑一遍最小公式确认链路通了才往上加业务逻辑。这个习惯帮我省了至少几十次“为什么没输出”的排查时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?