用 Delphi 为 MQL5 编写 DLL 指南(基础篇)
📘

用 Delphi 为 MQL5 编写 DLL 指南(基础篇)

第 1/3 篇

◍ 用 Delphi 给 MT5 挂一个 DLL 入口

想在 MT5 里调用 Delphi 写的逻辑,核心是先让 MQL5 能认到 DLL 的导出函数。MQL5 侧用 #import 指令声明外部函数,编译时不会把代码打进 ex5,而是运行时去加载你指定的 dll 文件。 Delphi 这边要写出标准 Win32 DLL,导出函数需用 stdcall 调用约定,否则 MT5 调起来栈会错乱。下面是一段最小可跑的 Delphi 导出声明与 MQL5 引用对照。 实测在 MT5 build 920+ 环境,一个仅做两数相加的 Delphi DLL,从 MQL5 侧调用往返耗时约 0.3 微秒量级(i7-4770 环境),比纯 MQL5 循环略慢但能扛复杂计算外包。外汇与贵金属交易本身高风险,用 DLL 扩展逻辑不改变这一属性,加载第三方二进制还需防恶意代码。

MQL5 / C++
class="macro">#class="kw">import "MyDelphiLib.dll"
class="type">class="kw">double AddNumbers(class="type">class="kw">double a, class="type">class="kw">double b);
class="macro">#class="kw">import

class=class="str">"cmt">// MQL5 调用
class="type">void OnStart()
{
   class="type">class="kw">double res = AddNumbers(class="num">1.5, class="num">2.5);
   Print(res);
}

为 MQL5 写 DLL 该选哪版 Delphi

给 MT5 的 MQL5 做 DLL 扩展时,开发环境版本直接决定你跟终端的数据交换成本。MQL5 内部所有代码行都以 Unicode 格式存储,而 Delphi 2009 之前的版本,其 SysUtils 模块根本没有处理 Unicode 字符串的函数。 如果你还在用 Delphi 2007 或更早的版本,就只能按 ANSI 格式读写字符串,每次和 MetaTrader 5 交换数据都得手动做 Unicode 与 ANSI 的双向转换,既容易踩编码坑也拖慢开发。 实测最省事的做法是把环境升到 Delphi 2009 及以上,原生支持 Unicode,对接 MQL5 不再需要额外转码层。Embarcadero 官网提供 30 天试用版,足以跑通一套 DLL 验证流程。 外汇与贵金属品种波动剧烈、杠杆风险高,任何 DLL 接入都应先在模拟账户验证稳定性,再考虑实盘。

「在 Delphi 里把 DLL 项目接进 MT5 的 Libraries」

开 Delphi 后走菜单 File -> New -> Other ...-> DLL Wizard,能拉起一个空 DLL 工程。这一步只是骨架,真正要调的是工程属性,否则后面每次编译都得手动搬文件。 按 Shift+Ctrl+F11 或 Project -> Options 打开属性窗。在 DelphiCompiler 选项卡的 Output directory 里,直接填 MetaTrader5 的 ..\MQL5\Libraries 路径。设完之后编译产物落在这个目录,省掉从工程目录往客户端拷 DLL 的重复动作。 另一个坑在 Packages 选项卡:把 Build with runtime packages 的勾去掉。Windows 系统目录若没有对应 BPL 模块,带运行时包编译会让汇编过程直接挂起。 配置定好后把工程存到工作文件夹,工程名就是将来编译出的 DLL 文件名。外汇与贵金属 EA 调这类 DLL 属于高风险扩展,先在模拟环境验证再上实盘。

◍ DLL 里导出无参过程时的坑

写 MT5 要调的 DLL,第一步往往是塞一个不含参数的导出过程进去练手。用 Object Pascal 写这类模块时,弹个消息框可以用 Windows 的 API 函数 MessageBox,也能用 VCL 里的 ShowMessage 过程。后者得把 Dialogs 模块一起编进去,好处是标准 Windows 对话框随手就用,代价是 DLL 体积直接多出大约 500 KB——想控文件大小的就别碰 VCL 组件。 所有要导出的函数必须带 stdcall 或 cdecl 修饰符。Delphi 默认走 fastcall,参数塞 CPU 寄存器而不是堆栈,MQL5 端按堆栈约定去调就会传参错乱。下面这段例子把导出、事件句柄和 MQL5 调用全摆出来了,逐行拆完你能直接开 MT5 建个 dll_mql5 工程验证。 library 声明 DLL 工程名;uses 里 Windows 供 MessageBox,Dialogs 供 ShowMessage。MsgBox 过程显式标 stdcall,体内 {1} 走 API 弹框、{2} 走 VCL 弹框。exports 段 {A} 原样导出 MsgBox,{B} 把它改名成 MessageBox 输出,避免和 MQL5 自身同名函数撞车。DLLEntryPoint 在挂接/卸载时分配或释放 Buffer 内存,begin 末尾把句柄绑上并手动触发一次 ATTACH。MQL5 侧 #import 后,MsgBox() 直接调,重名的得写 dll_mql5::MessageBox() 指明来源。 外汇与贵金属杠杆高、波动烈,跑自建 DLL 前先在策略测试器脱机验一遍,别拿实盘账户试未签名的模块。

MQL5 / C++
library dll_mql5;
uses
  Windows, class=class="str">"cmt">// 必要工作,用于 MessageBox 函数
  Dialogs; class=class="str">"cmt">// 必要工作,用于 ShowMessage 进程 ,来自 Dialogs 模块
var   Buffer: PWideChar;
class=class="str">"cmt">//------------------------------------------------------+
procedure MsgBox(); stdcall; class=class="str">"cmt">// 
class=class="str">"cmt">//为避免错误, 使用 stdcall(或 cdecl) 来输出函数
class=class="str">"cmt">//------------------------------------------------------+
begin
    {class="num">1} MessageBox(class="num">0,&class="macro">#x27;你好世界!&class="macro">#x27;,&class="macro">#x27;终端&class="macro">#x27;, MB_OK);
    {class="num">2} ShowMessage(&class="macro">#x27;你好世界!&class="macro">#x27;); class=class="str">"cmt">// 替代 MessageBox 函数 
end;
class=class="str">"cmt">//----------------------------------------------------------+
exports
class=class="str">"cmt">//----------------------------------------------------------+
  {A} MsgBox,
  {B} MsgBox name &class="macro">#x27;MessageBox&class="macro">#x27;; class=class="str">"cmt">// 更名输出函数
class=class="str">"cmt">//----------------------------------------------------------+
procedure DLLEntryPoint(dwReason: DWord); class=class="str">"cmt">// 事件句柄
class=class="str">"cmt">//----------------------------------------------------------+
begin
    case dwReason of
      DLL_PROCESS_ATTACH: class=class="str">"cmt">// DLL 加挂处理;
          class=class="str">"cmt">// 分配内存
          Buffer:=AllocMem(BUFFER_SIZE);
      DLL_PROCESS_DETACH: class=class="str">"cmt">// DLL 卸载处理;
          class=class="str">"cmt">// 释放内存
          FreeMem(Buffer);
    end;
end;
class=class="str">"cmt">//----------------------------------------------------------+
begin
    DllProc := @DLLEntryPoint; class=class="str">"cmt">//分配事件句柄
   DLLEntryPoint(DLL_PROCESS_ATTACH);
end.
class=class="str">"cmt">//----------------------------------------------------------+
class="macro">#class="kw">import "dll_mql5.dll"
    class="type">void MsgBox(class="type">void);
    class="type">void MessageBox(class="type">void);
class="macro">#class="kw">import

class=class="str">"cmt">// 调用进程
   MsgBox();
class=class="str">"cmt">// 如果函数的名称与 MQL5 标准库函数的名称一致
class=class="str">"cmt">// 调用函数时使用 DLL 名称
   dll_mql5::MessageBox();

常见问题

在 Delphi 新建一个动态链接库项目,把导出的函数写成 stdcall 约定,编译出 .dll 后丢进终端的 Libraries 目录即可被调用。
优先用较老但稳定的 Delphi 版本(如 2007/2010),避免新版本运行时库依赖,减少在第三方终端加载时的报错。
小布可以读取你的品种页诊断信息,提示 DLL 加载失败或函数导出异常,你按提示改导出名和调用约定就行。
把编译好的 dll 放到终端数据目录的 MQL5/Libraries 下,EA 里用 #import 指向文件名并声明同名函数即可。
无参过程也要显式声明 stdcall 并在 exports 后加 name 指定导出名,否则终端按默认命名修饰找不到入口。