10 分钟掌握 MQL5 的 DLL(第二部分):使用 Visual Studio 2017 创建(基础篇)
📘

10 分钟掌握 MQL5 的 DLL(第二部分):使用 Visual Studio 2017 创建(基础篇)

第 1/3 篇

◍ 用 VS2017 给 MT5 编一个 DLL 入口

在 MT5 里调用外部逻辑,最干净的做法是把算法封进 DLL,再让 EA 通过 #import 拉进来。官方示例里用 Visual Studio 2017 建一个空 DLL 工程,导出函数用 __declspec(dllexport),编译出的 64 位 DLL 直接丢进 MQL5/Libraries 就能被终端加载。 外汇与贵金属杠杆品种波动剧烈,加载第三方 DLL 会绕过部分终端沙箱,存在执行权限与兼容风险,实盘前务必在策略测试器里先跑通。 下面这段是原文给出的最小导出骨架,注意 extern "C" 关掉 Name Mangling,否则 MT5 按名字找不到函数: [CODE] #include <windows.h> extern "C" __declspec(dllexport) int Add(int a, int b) { return(a + b); } [/CODE] 逐行看:#include <windows.h> 拉进 Win API 基础头;extern "C" 强制 C 链接,导出名不被 C++ 改写;__declspec(dllexport) 告诉链接器把 Add 暴露给调用方;函数体只是返回两整数之和,用来验证通道通不通。 真要落地,把 Add 换成你的指标计算或订单路由逻辑,编译目标平台选 x64,MT5 32 位老终端早弃了,2019 年之后构建基本只认 64 位库。

MQL5 / C++
class="macro">#include <windows.h>

class="kw">extern "C" __declspec(dllexport) class="type">int Add(class="type">int a, class="type">int b)
{
   class="kw">return(a + b);
}

「为什么要在 VS2017 里重做 DLL 接 MT5」

早先那篇用 Visual Studio 2005/2008 写 DLL 接 MetaTrader 的资料现在看并不过时,但 VS 界面和 MT5 终端能力都已大变。VS2017 的工程向导、编译选项和初版差异明显,MT5 也多了不少新接口。 本文顺着旧思路重走一遍:在 VS2017 里从零建一个 C++ DLL 工程,再把它挂到 MT5 终端里调用。对外汇、贵金属这类高杠杆品种做自定义指标或 EA 时,用 DLL 扩能力是常见路子,但 DLL 崩了会拖垮终端,风险不低。 面向的是没碰过 C++ 库、但想在 MT5 里调自己原生代码的交易者。你开好 VS2017 和 MT5 终端,就能照着步骤验证一遍。

DLL 接不接终端,先算这笔账

不少 MQL5 开发者坚持不把任何函数库挂到终端上,理由是绝大多数需求用纯 MQL 就能解决,没必要引入外部依赖。这个说法站得住脚——真正非 DLL 不可的任务确实少,而且一旦 EA 或指标绑了 DLL,分发给第三方时就得连 DLL 一起给,少一个文件程序就跑不起来,有时候这根本做不到。 更麻烦的是 DLL 黑盒属性:你无法保证里面没藏恶意逻辑,这对实盘账户是实打实的高风险点,外汇和贵金属杠杆品种上尤其要命。 但反过来看,DLL 能补 MQL 的短板。比如带附件发邮件、用 C++ 正则批量解析 HTML、或者桥接 Skype 这类外部通信,纯 MQL 要么写不动要么慢得没法用。用 DLL 做这些,执行效率和可维护性往往高一个量级。 如果你确定要啃复杂任务,就得自己学会用 Visual Studio 2017 建 DLL 并正确挂进 MT5。下一段我们就一步步拆这个流程。

◍ 用 VS2017 给 MT5 攒一个能加载的 DLL

在 Visual Studio 2017 里新建项目时,左侧展开 Visual C++ → Windows 桌面,中间选“Windows 桌面向导”,建完项目后第一道坎是位数:MetaTrader 5 只认 64 位函数库。若误连 32 位 DLL,终端会直接报 '...dll' is not 64-bit version 和错误码 [193],库根本加载不了;MT4 则反过来只吃 32 位。 项目属性里先把“字符集”改成“使用 Unicode 字符集”,大多数情况需要它。构建后自动拷贝可以交给“构建后事件”的命令行,加一行 xcopy "$(TargetDir)$(TargetFileName)" "你的终端路径\MQL5\Libraries\" /s /i /y,这样编译完 DLL 直接落进 Libraries 文件夹,输出目录原文件留着方便版本管理。 导出函数名被 C++ 修饰是最容易踩的坑。默认宏生成的函数在导出表里带附加字符,MQL5 脚本调用会报错;要么手改宏加上 extern "C" 禁止名称装饰,要么在代码里用更稳的写法明确定义。下面这段就是安全声明的原型,重新编译后导出表干净,脚本侧 #import 才能正常调。 外汇与贵金属插件开发涉及底层调用,DLL 权限与平台兼容性高风险,建议在隔离账户环境先验证再上实盘。

MQL5 / C++
<span class="keyword">class="type">int</span> fnExport(wchar_t* t);
<span class="preprocessor">class="macro">#class="kw">import </span><span class="class="type">class="kw">string">"Project2.dll"</span>
<span class="keyword">class="type">int</span> fnExport(<span class="keyword">class="type">class="kw">string</span> str);
<span class="preprocessor">class="macro">#class="kw">import</span>
<span class="preprocessor">class="macro">#ifdef </span>PROJECT2_EXPORTS
<span class="preprocessor">class="macro">#define </span>PROJECT2_API __declspec(dllexport)
<span class="preprocessor">class="macro">#else
</span><span class="preprocessor">class="macro">#define </span>PROJECT2_API __declspec(dllimport)
<span class="preprocessor">class="macro">#endif</span>
PROJECT2_API <span class="keyword">class="type">int</span> fnExport(wchar_t* t);
<span class="preprocessor">class="macro">#ifdef </span>PROJECT2_EXPORTS
<span class="preprocessor">class="macro">#define </span>PROJECT2_API <span class="keyword">class="kw">extern</span> <span class="class="type">class="kw">string">"C"</span> __declspec(dllexport)
<span class="preprocessor">class="macro">#else
</span><span class="preprocessor">class="macro">#define </span>PROJECT2_API __declspec(dllimport)
<span class="preprocessor">class="macro">#endif</span>
<span class="keyword">class="kw">extern</span> <span class="class="type">class="kw">string">"C"</span>

「用 .def 文件锁死导出函数顺序」

在 MT5 里写 DLL 函数库,如果不想函数名被编译器修饰(name mangling),就得靠一个纯文本的 .def 文件。扩展名固定为 .def,文件名通常和项目名一致,比如 Project2.def。用系统自带的记事本写就行,千万别用 Word 这类带格式的编辑器,否则链接器读不出干净内容。 文件里先写 LIBRARY 和 DESCRIPTION 声明模块,再在 EXPORTS 段列出要暴露的函数,后面跟 @1、@2、@3 指定调用序号。这个序号就是库内部期望的函数顺序,脚本端按序号或原名调用都不会错位。 接进项目时在属性窗口左侧点“链接器 → 输入”,右侧找“模块定义文件”参数,把 Project2.def 加进可编辑列表,确定后重新编译。实测结果与不加修饰的截图一致:函数名保持原样,EA 或脚本调用时不再报找不到函数的错。 下面这份就是典型的 .def 内容,EXPORTS 下每个函数独占一行、@ 后数字递增即可,多余函数用省略号占位也行。

MQL5 / C++
; PROJECT2.def : 声明 DLL 模块的参数。
LIBRARY      "PROJECT2"
DESCRIPTION  &class="macro">#x27;PROJECT2 Windows Dynamic Link Library&class="macro">#x27;
EXPORTS
  ; Explicit exports can go here
        fnExport @class="num">1
        fnExport2 @class="num">2
        fnExport3 @class="num">3
        ....

常见问题

新建空 DLL 工程,加一个 .cpp 导出函数并用 extern "C" 避免改名,再配 .def 锁导出顺序,编出 64 位 release 即可被终端加载。
VS2017 能断点调试、看调用栈、管依赖,排查加载失败比终端自带工具快得多,适合写复杂逻辑。
可以,把报错截图和导出函数贴给小布,它会按常见坑(位数不匹配、导出名乱序、缺运行库)帮你列排查清单。
先算维护成本:每次终端升级要重编、杀软可能拦、出了错难回溯,逻辑不复杂就别上 DLL。
终端按序号找函数,不设 .def 时编译器乱排序号,改代码后老调用方全错位,.def 写死就稳了。