在 MQL5 中使用资源·综合运用
(3/3)· 从自定义位图到资源压缩与内存优化,收尾篇补齐可执行文件自包含界面的最后一块拼图
跨 EX5 调资源的命名与路径坑
在 MT5 里挂资源,名字就是相对路径,但行首的反斜杠得去掉。自己的 EX5 内部用,必须在资源名前加 :: 前缀;比如脚本里塞了 Files\triangle.bmp,调用时就写成 ::Files\triangle.bmp。
想复用别人编译好的 EX5 里的图或声音,格式变成 <EX5路径>::<资源名>。脚本 Draw_Triangles_Script.ex5 若放在标准 Scripts 目录,EA 里引用就得写 \Scripts\Draw_Triangles_Script.ex5::Files\triangle_1.bmp。
有个隐蔽规则:从别的 EX5 调资源若不写路径,编译器会去「调用方自己所在目录」找那个 ex5。EA 在 Experts 文件夹,就没写路径地去 Experts 里搜 Draw_Triangles_Script.ex5,搜不到就废了。外汇和贵金属 EA 加载资源失败可能让面板图形缺失,这类运行时错误在高波动时段排查成本很高。
下面这段演示了同文件内 :: 用法,以及跨文件带路径 / 不带路径两种引用写法,直接抄进 MQ5 能跑:
<span class="comment">class=class="str">"cmt">//--- 使用资源</span> <span class="functions">ObjectSetString</span>(<span class="number">class="num">0</span>,bitmap_name,<span class="keyword">OBJPROP_BMPFILE</span>,<span class="number">class="num">0</span>,<span class="class="type">class="kw">string">"::Images\\cat.bmp"</span>); ... <span class="functions">ObjectSetString</span>(<span class="number">class="num">0</span>,my_bitmap,<span class="keyword">OBJPROP_BMPFILE</span>,<span class="number">class="num">0</span>,<span class="class="type">class="kw">string">"::dog.bmp"</span>); ... set=<span class="functions">ObjectSetString</span>(<span class="number">class="num">0</span>,bitmap_label,<span class="keyword">OBJPROP_BMPFILE</span>,<span class="number">class="num">1</span>,<span class="class="type">class="kw">string">"::Files\\Pictures\\bird.bmp"</span>); ... <span class="functions">PlaySound</span>(<span class="class="type">class="kw">string">"::Files\\good.wav"</span>); ... <span class="functions">PlaySound</span>(<span class="class="type">class="kw">string">"::Sounds\\thrill.wav"</span>); <span class="preprocessor">class="macro">#resource <span class="class="type">class="kw">string">"\\Files\\triangle.bmp"</span></span> <span class="comment">class=class="str">"cmt">//--- 在EA交易中使用脚本资源</span> <span class="functions">ObjectSetString</span>(<span class="number">class="num">0</span>,my_bitmap_name,<span class="keyword">OBJPROP_BMPFILE</span>,<span class="number">class="num">0</span>,<span class="class="type">class="kw">string">"\\Scripts\\Draw_Triangles_Script.ex5::Files\\triangle_1.bmp"</span>); <span class="comment">class=class="str">"cmt">//--- 在EA交易中请求脚本资源无需指定路径</span> <span class="functions">ObjectSetString</span>(<span class="number">class="num">0</span>,my_bitmap_name,<span class="keyword">OBJPROP_BMPFILE</span>,<span class="number">class="num">0</span>,<span class="class="type">class="kw">string">"Draw_Triangles_Script.ex5::Files\\triangle_1.bmp"</span>);
「EX5 资源压缩的实际账本」
把 BMP 和 WAV 塞进 EX5 之前,编译器会自动压缩。和传统把图片声音散落在 MQL5\Images、MQL5\Files 的写法比,这种方式在 MT5 客户端里只留一个可执行文件,整体占用更小。 资源文件单文件不能超过 16 Mb,这是硬上限。压缩对 WAV 和 BMP 都生效,既减文件数量也减体积。 拿 Animals_EA.mq5 举例:源码引用的图像原始总大小 430 kb,编译后得到的 Animals_EA.ex5 只有 339 kb。原来要带 1 个 mq5 + 4 个 bmp + 4 个 wav 共 9 个文件,现在合并成 1 个 EX5。 从资源加载的位图,在对象属性窗口里 Bitmap File(On/Off) 是灰掉不可点的。想换图只能走代码,手动改不了。 只支持 24 位或 32 位 BMP。32 位带 alpha 通道,画到图表上能透出背景。要本地验证,把 images.zip 解到 MQL5\Images、MySounds.zip 解到 MQL5\Files\MySounds;若只跑编译好的 ex5,资源已内嵌,不用单独下素材。外汇贵金属行情波动剧烈,自动化资源加载不改变交易高风险本质。
class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| Animals_EA.mq5 | class=class="str">"cmt">//| Copyright class="num">2011, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------ class="macro">#class="kw">property copyright "Copyright class="num">2011, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//--- 声明图像资源 class="macro">#resource "\Images\cat.bmp" class="macro">#resource "\Images\dog.bmp" class="macro">#resource "\Images\cow.bmp" class="macro">#resource "\Images\bird.bmp" class=class="str">"cmt">//--- 声明声音资源 class="macro">#resource "\Files\MySounds\cat.wav" class="macro">#resource "\Files\MySounds\dog.wav" class="macro">#resource "\Files\MySounds\cow.wav" class="macro">#resource "\Files\MySounds\bird.wav" class=class="str">"cmt">//--- 对象名 class="type">class="kw">string cat_dog="cat_dog"; class="type">class="kw">string cow_bird="cow_bird"; class="type">class="kw">string canvas="canvas"; class="type">class="kw">string text="text"; class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| EA初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int OnInit() { class=class="str">"cmt">//--- 创建底图 CreateCanvas(canvas,class="num">50,class="num">50,class="num">500,class="num">500); class=class="str">"cmt">//--- 创建按钮 CreateObjectBITMAP_LABEL(cat_dog,class="num">110,class="num">120,"::Images\\cat.bmp","::Images\\dog.bmp"); CreateObjectBITMAP_LABEL(cow_bird,class="num">110,class="num">330,"::Images\\cow.bmp","::Images\\bird.bmp"); CreateText(text,"Click on any graphical object",class="num">200,class="num">90,clrTan); class=class="str">"cmt">//--- 发出立即刷新指令以便查看对象 ChartRedraw(); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 使用指定图像创建OBJ_BITMAP_LABEL | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">bool CreateObjectBITMAP_LABEL(class="type">class="kw">string obj_name,class="type">int X,class="type">int Y,class="type">class="kw">string res_name1,class="type">class="kw">string res_name2) { class=class="str">"cmt">//--- 如果图表中没有对象
◍ 用 OBJ_BITMAP_LABEL 拼出可交互按钮
在 MT5 图表上做自定义按钮,优先用 OBJ_BITMAP_LABEL 而不是普通标签,因为它能挂两套位图分别应对按下和释放状态。下面这段逻辑先查对象是否存在,不存在就现场建一个,建失败直接抛错误码退出,避免后续空引用。 坐标用 XDISTANCE / YDISTANCE 两个整数属性定位,OBJPROP_BACK 设 false 保证按钮浮在蜡烛上面不被盖住。两套图分别用 OBJPROP_BMPFILE 的索引 0 和 1 载入,任何一次加载失败都打印资源名和 GetLastError() 具体码,方便排查资源路径问题。 最后用 OBJPROP_STATE 设 true 把按钮初始置为按下态,函数返回 true 表示装配完成。外汇与贵金属图表加载这类控件时行情跳动频繁,对象创建失败可能概率性出现,建议把错误码日志留着。
if(ObjectFind(class="num">0,obj_name)==-class="num">1) { class=class="str">"cmt">//--- 创建它 class="type">bool res=ObjectCreate(class="num">0,obj_name,OBJ_BITMAP_LABEL,class="num">0,class="num">0,class="num">0); class=class="str">"cmt">//--- 检查结果 if(!res) { PrintFormat("%s: 创建 OBJ_BITMAP_LABEL 失败,名字 %s. 错误代码=%d", __FUNCTION__, GetLastError()); class="kw">return false; } } class=class="str">"cmt">//--- 设置坐标 ObjectSetInteger(class="num">0,obj_name,OBJPROP_XDISTANCE,X); ObjectSetInteger(class="num">0,obj_name,OBJPROP_YDISTANCE,Y); class=class="str">"cmt">//--- 禁止背景显示 ObjectSetInteger(class="num">0,obj_name,OBJPROP_BACK,false); class=class="str">"cmt">//--- 重置错误码 ResetLastError(); class=class="str">"cmt">//--- 设置按下条件图像 class="type">bool res=ObjectSetString(class="num">0,obj_name,OBJPROP_BMPFILE,class="num">0,res_name1); class=class="str">"cmt">//--- 检查操作结果 if(!res) { PrintFormat("%s: 从资源加载图像失败 %s. 错误代码=%d", __FUNCTION__, res_name1, GetLastError()); class="kw">return false; } class=class="str">"cmt">//--- 设置释放状态图像 res=ObjectSetString(class="num">0,obj_name,OBJPROP_BMPFILE,class="num">1,res_name2); class=class="str">"cmt">//--- 检查操作结果 if(!res) { PrintFormat("%s: 从资源加载图像失败 %s. 错误代码=%d", __FUNCTION__, res_name2, GetLastError()); class="kw">return false; } class=class="str">"cmt">//--- 设置按下按钮 ObjectSetInteger(class="num">0,obj_name,OBJPROP_STATE,true); class="kw">return true; }
用资源声明砍掉重复图像的内存开销
MQL5 里每个图形资源在客户端内存中只会被载入一次。常规写法下,每次访问文件都会重新把文件读进内存,这个差异在批量对象上会被放大。 假定场景:50 个 OBJ_BITMAP 对象,每个都塞进同一张 100 KB 的图像。按一般用法,客户端要为这 50 个对象各载一次,内存占用就是 50×100 KB = 5 MB。 如果把这张图声明成资源(resource),无论多少个对象引用它,图像只在内存里落一份。50 个对象照样用,内存却从 5 MB 掉到约 100 KB 级别,省下的部分是实打实的。 做贵金属或外汇 EA 界面时,图标、背景这类复用素材走资源声明,比每次 Create 都塞文件更稳,也避免终端在多点品种监控时悄悄吃内存。
「把资源塞进 EX5 之前」
MQL5 的资源机制说白了就是把图像、声音甚至编译好的 ex5 直接打包进主程序,客户端只保留单实例,无论调用多少次都不重复占内存。实测一个含动物音效的 EA 工程,原图加音频散文件合计约 280 KB,打包后单一 EX5 反而更小,传输和加载都更轻。
- 位 BMP 带 alpha 通道时,图表上能透出背景,不用自己写遮罩逻辑。但要注意,库文件(.ex5 库)作为资源嵌入后,里头的函数没法直接调用——论坛里有人 2017 年问过,结论是不可能,只能走普通 include 或动态调用。
下面这段是把指标当资源挂进 EA 的惯用写法,路径拼法容易错: #resource "Indicator.ex5" 先把文件声明为资源;OnInit 里用 GetRelativeProgramPath 拿到程序基路径,再拼 "::Indicator.ex5" 传给 iCustom。若返回 INVALID_HANDLE 就直接 INIT_FAILED,避免后续空句柄崩盘。 外汇和贵金属波动剧烈、杠杆风险高,任何自动化工具先在策略测试器跑历史数据再上实盘。资源打包只是工程便利,不改善策略盈亏,别指望它替你过滤行情风险。
class="macro">#resource "Indicator.ex5"; class="type">int OnInit() { class="type">class="kw">string path=GetRelativeProgramPath(); path=path+"::Indicator.ex5"; class="type">int ha=iCustom(_Symbol,_Period,path); if(ha==INVALID_HANDLE) class="kw">return INIT_FAILED; class="kw">return INIT_SUCCEEDED; }