MT4 DLL接口与API跟单:从加载原理到部署验证
2026/9/16 9:05:02 网站建设 项目流程

简介:面向MT4平台二次开发者与跟单系统开发人员,压缩包主要用于解决MT4服务端与外部跟单系统对接时缺少现成DLL接口示例的难题。压缩包共2个文件,以动态链接库(DLL)为核心,另附同名XML注释文件,前者提供C#可直接调用的接口实现,后者可在C#开发环境中自动显示方法签名与参数说明,整体压缩后仅174KB,轻量易部署。目前已有331人学习下载,适合刚接触MT4接口开发、希望快速验证API调用或搭建跟单Demo的初中级开发者。通过该包,开发者能直接引用DLL开展通讯测试,并借助XML快速定位所需函数,省去自行封装底层接口的时间;将其用于MT4API跟单、账号状态读取、订单委托等场景,可显著降低项目前期调研与集成成本,也能作为学习C#调用MT4动态库的参考样例。

1. MT4 DLL 接口与 API 跟单:这包 zip 在解决什么问题

MT4 的外围系统,最后几乎都要落在 DLL 接口上。拿跟单来说,信号源终端要把 tick 和成交数据拿出来,目标终端要把单子送进去,而 MQL4 本身几乎没有跨进程通信能力,EA 之间也不存在直接的消息通道,这时候唯一稳定的路径就是 mt4apidll 或 mt4dll 接口这一层。像 mt4demo.zip 这类压缩包,里面通常是编译好的接口 DLL、一个 demo 环境的说明文件,以及 gravity1qr 这样的策略指标,解压后不需要重头编译,要做的只是搞清楚 DLL 是怎么被 MT4 识别和调用的。下面要讲的就是这套识别机制:32 位进程、__stdcall导出、宽字符参数,然后再落到跟单参数怎么设、zip 怎么安全部署。

2. mt4apidll 的接口原理:导出约定、调用约定与 32 位限制

2.1 MT4 是 32 位进程,64 位 DLL 一上来就加载失败

MT4 客户端即使在 64 位 Windows 上运行,进程本身也还是 32 位的。Windows 的LoadLibrary不会跨位数加载模块,所以拿到 zip 后第一件事应该是确认里面 DLL 的位数,而不是直接丢进MQL4/Libraries里试。在装有 Visual Studio 的机器上,可以用开发者命令行直接查 PE 头:

dumpbin /headers gravity1qr.dll | findstr machine

输出里出现x86就没问题,出现x64ARM64说明这个 DLL 与 MT4 进程位数不匹配,加载必然失败。没有 dumpbin 时有个更快的土办法:用记事本打开 DLL 看开头字符是不靠谱的,最直接的还是把文件拖进 7-Zip,它能直接显示 PE 头的 CPU 类型;当然最稳妥的做法是用corflags或者 Process Explorer 看模块信息。实际项目中,我见过不止一次因为拿错了 Release 配置的 DLL,在开发机上好好的,部署到客户机上就报Error loading DLL,换 32 位编译一遍就恢复了。

2.2 导出名不对是加载失败的第二个高频原因

MQL4 的#import在加载时是按导出函数名逐字匹配的,而 MSVC 编译__stdcall函数后,默认导出名会带参数总字节数的后缀,例如_GetQuote@12。MQL4 里声明为GetQuote,加载时按GetQuote去找,自然匹配不上。这跟函数实现本身对不对没关系,纯粹是命名层面的错位。

解决方案有两种。一是用 MinGW 编译时加-Wl,--kill-at,把@后缀去掉;二是用模块定义文件显式声明导出名:

LIBRARY gravity1qr EXPORTS GetQuote SendOrder InitLog

注意LIBRARY字段一般写成 DLL 的文件名不带扩展名,EXPORTS下面的名字必须与 MQL4 里#import的名字完全一致,区分大小写。用 MSVC 时,在工程设置里把模块定义文件加进链接器输入项即可,不需要额外代码。这里有个容易忽略的细节:如果 zip 里同时给了.def文件和.dll文件,优先看.def,它直接揭示了作者当初声明的导出名。

2.3 MQL4 的 string 到 C++ 是 wchar_t*,不是 char*

MQL4 内建的string类型内部是宽字符存储,传到 DLL 边界时,C++ 侧看到的参数类型是wchar_t*,不是char*。很多第一次写接口的人在这里踩坑:用char*接收品种代码,结果解析出来全是乱码,或者直接崩溃。

参数类型映射关系大致如下:

MQL4 类型C++ 侧对应类型说明
intint32 位有符号,没得商量
doubledoubleIEEE 754 双精度
stringwchar_t*指向宽字符串的指针
datetimelong longint推荐用 64 位避免 2038 问题
boolint用 0 / 1 判断,不声明为 C++ bool
数组参数类型*加长度MQL4 传数组时要注意数组指针语义

跟单场景里时间戳是高频参数。MT4 的datetime是自 1970 年 1 月 1 日的秒数,接口里用int接收在 2038 年会有溢出风险,所以新写的接口一般直接约定用long long传毫秒,或者传字符串格式如2025-04-01 10:00:00.123,由 DLL 内部自己解析。这套约定直接决定了导出函数怎么写,下一章给一个能编译、能跑通的最小实现。

3. 从 mt4dll 接口签名到 MQL4 调用:最小可复现例子

3.1 C++ 端导出三个函数

先定义一个纯 C 风格的导出接口,三个函数分别负责初始化日志、查询报价、发送订单。这个规模足够覆盖大多数 mt4demo 包的用法:以日志做验证、以报价做输入、以订单做输出。

// gravity1qr_bridge.cpp #include <windows.h> #include <wchar.h> #include <stdio.h> static FILE* g_log = NULL; extern "C" __declspec(dllexport) int __stdcall InitLog(const wchar_t* path) { if (g_log) fclose(g_log); g_log = _wfopen(path, L"a"); if (g_log) { fwprintf(g_log, L"[init] log opened\n"); fflush(g_log); } return g_log != NULL ? 1 : 0; } extern "C" __declspec(dllexport) int __stdcall GetQuote(const wchar_t* symbol, double* bid, double* ask, int* digits) { if (!g_log) return -1; fwprintf(g_log, L"[quote] %ls\n", symbol); fflush(g_log); // 真实实现中这里从共享内存或行情源读取数据 *bid = 1.23450; *ask = 1.23480; *digits = 4; return 0; }

这里有三处关键约定。extern "C"防止 C++ 名字改编,__stdcall匹配 MT4 的调用约定,指针参数对应 MQL4 里的引用参数。函数内部先写日志再返回数据,好处是后面部署时只要看日志有没有增长,就能判断调用链是否真的通了。

3.2 编译命令

如果机器上装了 MinGW-w64 的 32 位工具链,编译命令如下:

g++ -shared -o gravity1qr.dll gravity1qr_bridge.cpp -Wl,--kill-at -static

-shared生成 DLL,-Wl,--kill-at去掉__stdcall导出的@后缀,-static把 C 运行时静态链进去,避免目标机器缺vcruntime140.dlllibgcc_s_dw2-1.dll导致加载失败。编译结果会生成gravity1qr.dll,大小在几十 KB 到一两百 KB 之间,如果体积到了好几 MB,先怀疑是不是把调试符号和运行时全打进来了。

用 MSVC 的话,在工程属性里配置模块定义文件即可。这里额外提醒一点:/MT/MD的选择直接影响目标机器上是否需要装 VC++ 运行库,发给别人用的 DLL 一般选静态运行时更省事。

3.3 MQL4 导入声明与调用

MQL4 侧在 EA 或指标头部做导入声明:

#import "gravity1qr.dll" int InitLog(string path); int GetQuote(string symbol, double& bid, double& ask, int& digits); #import int OnInit() { if (InitLog("MQL4\\Files\\g1.log") != 1) { Print("InitLog failed: check sandbox path"); } double bid = 0, ask = 0; int digits = 0; int r = GetQuote("EURUSD", bid, ask, digits); Print("GetQuote return=", r, " bid=", bid, " ask=", ask, " digits=", digits); return INIT_SUCCEEDED; }

注意三个点。第一,路径MQL4\Files\g1.log是 MT4 沙箱里 DLL 可写的位置,写到别的目录会直接失败。第二,#import声明里的参数类型必须和 C++ 侧严格对应,字符串用string,双精度浮点用double,引用参数用&。第三,Print是调试手段,生产环境里不要每 tick 都打日志,否则日志文件会膨胀得非常快。

3.4 解压 zip 后先确认目录摆放

从 zip 里解压出来的文件,路径不一定和 MT4 目录结构一一对应。常见的 mt4demo 包有两种排布方式:一种已经按MQL4/ExpertsMQL4/IndicatorsMQL4/Libraries分好层,直接整体覆盖;另一种是根目录散放,就需要手工归类:

zip 内文件复制到 MT4 目录
gravity1qr.dllMQL4/Libraries
xxx.ex4xxx.mq4MQL4/IndicatorsMQL4/Experts
说明文档、demo 配置建议单独放MQL4/Files

复制之前先看有没有同名文件。MT4 加载 DLL 是按文件名匹配的,如果之前装过同名但不同版本的 DLL,覆盖后会出现行为异常,而且这个异常非常难查,因为编译时间对不上。稳妥的操作是先备份旧 DLL 再覆盖,把版本号或哈希写进部署记录。

4. mt4api 跟单的参数设定:时间戳、手数与重连

4.1 跟单的三种数据通道选型

跟单系统要解决的核心问题,是把信号源的tick、订单和成交数据搬到目标终端。接口 DLL 拿到数据之后,往哪儿送有几种做法,取舍点主要在延迟、实现复杂度和部署形态上:

通道端到端延迟实现复杂度适用场景
TCP 直连(内网)低,毫秒级同机房或局域网多终端
写文件 + 轮询中,几百毫秒到秒级demo、调试、小样本验证
共享内存 / WM_COPYDATA最低,亚毫秒级同机多终端、量级大

我一般会优先把 TCP 直连作为主链路,因为跟单消息本身是短文本,TCP 的粘包和重连机制成熟,而且调试时可以用telnet或者nc直接往端口里塞数据,验证成本低。文件轮询适合先把接口跑通、再上网络链路的过渡阶段,代码量最小,但别指望它做高频。

4.2 三个必调的同步参数

无论用哪条通道,有几个参数必须在部署前定下来,否则跟单会出现漏单、重复单、错价单:

参数建议初值作用与调整方向
time_tolerance_ms200信号端与目标端时钟偏差容限,超过视为无效信号,局域网内可以调到 50
min_lot0.01小于该手数的信号直接过滤,避免噪音小单干扰目标账户
max_slip10(点)信号发出到目标端执行之间的价格偏移容忍度,超了就放弃或重报

time_tolerance_ms是个容易出问题的参数。如果两台机器用 NTP 同步过,调 50 毫秒没问题;如果一台是 Windows 默认时间源,另一台是手工对时,建议至少留 200 毫秒,否则大量正常信号会被当成乱序数据丢掉。min_lot的坑在于它过滤的是信号端手数,不是目标端仓位比例,很多人在测试阶段把它设成 0,结果信号源里的 0.01 手测试单全部跟进去了。

4.3 跟单消息格式与幂等

消息格式建议用带固定分隔符的文本行,因为日志和抓包都可读,解析也简单:

2025-04-01 10:00:00.123|1|EURUSD|BUY|0.10|1.23450|ticket_10086

字段依次是时间戳、序号、品种、方向、手数、开仓价、原始单号。最后一位ticket_10086是去重键,目标端必须保留最近至少 1000 条单号,收到重复的单直接忽略。这个幂等设计比任何重传机制都重要,因为 TCP 重连后信号端重新发送未确认数据时,目标端不知道哪些已经执行过,去重键是唯一的判断依据。

4.4 断线补单与日志

断线重连是跟单跑崩的重灾区。做法上,信号端维护一个自增seq,每发一条消息序号加一;目标端记录最后成功处理的seq,重连后反向请求从断点开始补发。DLL 内部一般要维护一个循环缓冲区,存最近 N 条消息,seq_buffer就是个重要参数:

seq_buffer = 1000 # 缓冲区容纳 1000 条待补消息 request_interval_ms = 1000 # 重连后请求补单的轮询间隔

如果断线时间超过缓冲区容量所能覆盖的范围,就触发全量同步,相当于把当前持仓和未平订单全部重新对齐一遍。补单时候的日志要比正常跟单更详细,至少要输出补发 seq 范围目标端最后确认 seq本次补发条数,这样出了问题能在 5 分钟内定位是发送端丢数据还是接收端处理太慢。

5. 部署验证:gravity1qr 的 zip 校验、DLL 冲突与日志确认

5.1 解压前先校验 zip,目录先看再复制

解压之前先用哈希校验一遍,防止压缩包在传输过程中损坏。Windows 自带的命令就能做:

certutil -hashfile mt4demo.zip SHA256

把输出的哈希值和发布方给出的值比对,一致再解压。这一步不是仪式感,zip 包在 HTTP 下载中断续传后经常出现文件头正常但尾部数据损坏的情况,解压时 7-Zip 会报Could not find EOCD一类的错误,这时候不要去找什么 zip 修复工具,直接重下一次更可靠。解压后先列目录再动手复制,重点看里面有没有Libraries目录,以及 DLL 文件是放在根目录还是Libraries下,这和 MT4 的加载规则直接相关。

5.2 DLL 冲突:先看日志再下结论

MT4 加载 DLL 失败时弹出的报错窗口只有一句话,真正的细节在终端日志里。打开MQL4/Logs下当天的日志,搜索DLLimport相关行。常见的加载失败原因就几类:位数不对、导出名不匹配、依赖的运行库缺失。这里不要急着去下载什么 dll 修复工具,MT4 场景里 dll 修复工具基本帮不上忙,因为问题往往不是系统 DLL 损坏,而是你放进去的 DLL 自身不满足加载条件。

与你安装的其他指标 DLL 同名、同版本但来源不同的文件,是另一种容易忽略的 dll 冲突来源。如果一个指标突然开始报错,回想最近是不是装过新的 zip 包覆盖了目录,把旧 DLL 备份恢复回去对比一下行为,往往比反复改代码更快。

5.3 用日志函数确认接口真的被调到了

部署完之后的验证,我习惯用最笨的办法:启动终端,加载 EA 或指标,然后观察日志文件有没有增长。上一章的InitLog就是为了这一步准备的。确认路径为MQL4\Files\g1.log,如果这个文件按预期写入,说明 DLL 加载成功、导出函数匹配、调用约定正确,一整条链路都是通的。

用 Process Explorer 打开 MT4 进程,在 DLL 列表里搜gravity1qr.dll,能确认它确实被加载进了进程地址空间。结合日志文件的内容,就能判断是 MT4 根本没加载 DLL,还是加载了但函数调用没进去。确认日志文件按预期增长之后,再把min_lot和时间容差这两个参数从调试值调到生产值,跟单链路就算正式跑起来了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询