Use of Resources in MQL5

MetaQuotes | 16 March, 2011


The Importance of the Interface in Modern Programs

Long ago the main purpose of computer programs was doing heavy mathematical calculations and processing large amounts of data. But with the increased power of computers, priorities has changed. Now, between two programs with identical functionality, a user chooses the one that is easier to work with.

Nowadays it is not enough to write a program in accordance with the required calculation algorithm, but you must also provide a user friendly graphical interface. Even the technical analysis arose from the desire of traders to have a visual representation of the current state of the market: trend lines, support and resistance levels, various channels and technical indicators have been developed to show an objective picture of what is happening.

The new MQL5 language has given even more powerful tools for creating fully functional applications, which do not require anything other than the MetaTrader 5 Client Terminal. In this article we will show how to use Resources for creating an executable EX5 file with a user friendly interface, and this file will not require any routine operations like installation or start.


The Possibilities of MQL5

First and foremost, of course, the possibility of working with graphics is very important. Examples can be found in articles, here are some of them:

It is the use of graphic elements that makes a program more interesting and easy to control from a user's perspective. In addition to the classical tools for Technical Analysis, the MetaTrader 5 terminal provides a wide range of graphical objects, which can be used as bricks to construct your own graphical interface.


Use of Image Files for Creating Interface

For creating a special interface, most often images from graphic files are used. This allows achieving a unique recognizable design of various control elements. The MQL5 language offers two graphical objects that use graphics:

These two objects allow you to create a huge variety of controls and to compare them to the Events handlers "Mouse click" (CHARTEVENT_OBJECT_CLICK). To set the desired image for OBJ_BITMAP or OBJ_BITMAP_LABEL,, specify the desired BMP file in the OBJPROP_BMPFILE property. This can be done manually on the "Parameters" tab of the graphical object.


The second and the main way for an MQL5 programmer is to specify a file name for the OBJPROP_BMPFILE property using the ObjectSetString() function. For example:

   //--- Load an image for the "Pressed" button state
   bool set=ObjectSetString(0,object_name,OBJPROP_BMPFILE,0,bmp_file_name);

A standard algorithm of use of OBJ_BITMAP or OBJ_BITMAP_LABEL:

  1. Create an object using the ObjectCreate() function.
  2. Using the ObjectSetInteger() function, anchor the object to the desired chart corner, if necessary. The X and Y coordinates of the anchor point in pixels will be set relative to this corner.
  3. In ObjectSetInteger(), set the values of X and Y coordinates (OBJPROP_XDISTANCE and OBJPROP_YDISTANCE).
  4. Using ObjectSetString(), set to the graphical object the value of the OBJPROP_BMPFILE property (one for BITMAP or two for OBJ_BITMAP_LABEL).
  5. For the OBJ_BITMAP_LABEL object, using ObjectSetInteger() you can set the initial state of the button - pressed or depressed (OBJPROP_STATE is true or false).

After creating and configuring the object, during MQL5-program running, you can change dynamically not only the position and condition of the graphical object, but also change the value of the OBJ_BITMAP_LABEL property to display images. Thus, the interface can be very flexible and reconfigurable.


Playing Sounds

An additional demanded convenience in programs is the ability to ask a user about an action in the event of a certain situation. To implement this reverse interaction, often playback of different sounds is used, depending on the event. This will relieve the trader of a continuous observation of price charts, by attracting his attention only in necessary cases. For playing audio files, the PlaySound() function is used in MQL5.

PlaySound() is very easy to use, and requires only specification of a path to a sound file:

//--- The path to a sound file  
string wav_file_name="Ok.wav";
...
//--- Play a sound from the file terminal_directory\Sounds\Ok.wav
bool played=PlaySound(wav_file_name);
if(!played)
  //--- Failed to play the sound, notify of this
  {
   PrintFormat("Failed to play file %s. Error code=%d", wav_file_name, GetLastError());
  }


Where to Locate the Sound and Image Files

Functions ObjectSetString() and PlaySound() require the specification of a path to a file. For security reasons, all the files that are used in MQL5 programs are located within the file sandbox. This means that files can only be stored in certain directories, work with files from other directories is not allowed. First, you need to learn what directories are available for file operations and functions, and how they are named.

There are three different directories:

To determine the location of these directories, you may use the script WhereMyFolders.mq5:

//+------------------------------------------------------------------+
//|                                               WhereMyFolders.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- The folder from which the terminal is started - terminal_directory
   string terminal_path=TerminalInfoString(TERMINAL_PATH);
//--- The folder that stores the terminal data - terminal_data_directory
   string terminal_data_path=TerminalInfoString(TERMINAL_DATA_PATH);
//--- The shared folder of all client terminals - common_terminal_folder
   string common_data_path=TerminalInfoString(TERMINAL_COMMONDATA_PATH);   
   //--- Show all the paths 
   Print("TERMINAL_PATH(terminal_directory) = ",TerminalInfoString(TERMINAL_PATH));
   Print("TERMINAL_DATA_PATH(terminal_data_directory) = ",TerminalInfoString(TERMINAL_DATA_PATH));
   Print("TERMINAL_COMMONDATA_PATH(comon_terminal_folder) = ",TerminalInfoString(TERMINAL_COMMONDATA_PATH));   
  }

Important: In some cases, the location of terminal_directory and terminal_data_directory may coincide, but it's better never to rely on this and therefore not to confuse these concepts.

Image and sound files are searched for by the terminal runtime system in the following order:

For the PlaySound() function, there is one addition: if the file has not been found using the above two methods, this sound file is searched for relative to terminal_directory\Sounds\.


Examples for Sound files:


Examples of image files:

Note that the double back slash "\\" is used as a separator when writing the path.

Important: When specifying the path, always use a double backslash as a separator, because a single backslash is a control character for the compiler when parsing the constant rows and character constants in the source code of a program.


New Possibilities - Resources

To use images and sounds in your MQL5 program, make sure that all the media files that it uses are located in the appropriate folders. This imposes a definite disadvantage when transferring a compiled EX5 file from one terminal to another. But it is possible to solve this problem at the stage of code writing. In such cases use resources.

In order to use a resource in a program, it should be declared using the compiler directive #resource

 #resource path_to_resource_file

Now, this resource can be used instead of a file path. The #resource command tells the compiler that the resource at the specified path path_to_resource_file should be included into the executable EX5 file. Thus all the necessary images and sounds can be located directly in an EX5 file. Now to start an MQL5 program in another terminal, you won't need to transmit all the files used in it.

Any EX5 file can contain resources, and any EX5 program can use resources from another EX5 program. That is, an Expert Advisor can use the resources that are located in an indicator or EX5 library. This is one more convenience from the use of resources.

Use of resources allows you to obtain all in one - the executable file itself and all resources that it uses are packaged in an EX5 file during the compilation of a source code.


Resource Search by a Compiler

A resource is specified using the directive #resource "<path to the resource file>"

 #resource "<path_to_resource_file>"
The length of the constant string ><path_to_resource_file> should not exceed 63 characters. The compiler searches for a resource at the specified path in the following sequence:1

Important: In the resource path, it is unacceptable to use substrings "..\\" and ":\\".

Examples of resource including from the Help topic Resources:

//--- Correct specification of a resource
#resource "\\Images\\euro.bmp" // euro.bmp is located in terminal_data_directory\MQL5\Images\
#resource "picture.bmp"        // picture.bmp is locate in the same directory with the source file
#resource "Resource\\map.bmp"  // The resource is located in the folder source_file_directory\Resource\map.bmp
 
//--- incorrect specification of resources
#resource ":picture_2.bmp"     // Use of ":" is not allowed
#resource "..\\picture_3.bmp"  // Use of ".." is not allowed
#resource "\\Files\\Images\\Folder_First\\My_panel\\Labels\\too_long_path.bmp" //More than 63 characters


Resource Names

After declaration of a resource using the #resource directive, it can be used in any part of a program. For the resource name, its path without a slash at the beginning of the string that defines the path to the resource will be used.

Examples:

//---Examples of specifying resources and their names in the comments
#resource "\\Images\\cat.bmp"           // Resource name - Images\cat.bmp
#resource "dog.bmp"                     // Resource name - dog.bmp
#resource "Resource\\map.bmp"           // Resource name - Resource\map.bmp
#resource "\\Files\\Pictures\\bird.bmp" // Resource name - Files\Pictures\bird.bmp
#resource "\\Files\\good.wav"           // Resource name - Files\good.wav"
#resource "\\Sounds\\thrill.wav"        // Resource name - Sounds\thrill.wav"

Resource names are not case sensitive - for the compiler, names dog.bmp and DOG.bmp will mean the same.


Using of Own and Third-Party Resources

To use a resource, you should specify its name. The resource name is its path without a backslash at the beginning of the line. When you use your own resource, special attribute "::" should be added before the resource name.

//--- Use of resources
ObjectSetString(0,bitmap_name,OBJPROP_BMPFILE,0,"::Images\\cat.bmp");
...
ObjectSetString(0,my_bitmap,OBJPROP_BMPFILE,0,"::dog.bmp");
...
set=ObjectSetString(0,bitmap_label,OBJPROP_BMPFILE,1,"::Files\\Pictures\\bird.bmp");
...
PlaySound("::Files\\good.wav");
...
PlaySound("::Sounds\\thrill.wav");

You can use not only your own resources (from your EX5 file), but also from any EX5 Libraries and modules. Thus, you can create a repository of resources and use them in many other mql5 programs.

To use resources from another EX5 file, the name of the resource must be specified in the form <EX5_file_name_path>::<resource_name>. Suppose the Draw_Triangles_Script.mq5 script contains a resource to an image in the file triangle.bmp:

 #resource "\\Files\\triangle.bmp"

Then its name, for using in the script itself, will look like "Files\triangle.bmp", and in order to use it, "::" should be added to the resource name - "::Files\triangle.bmp". In order to use the same resource from another program, e.g. from an Expert Advisor, we need to add to the resource name the path to the EX5 file relative to terminal_data_directory\MQL5\ and the name of the script's EX5 file - Draw_Triangles_Script.ex5. Suppose the script is located in the standard folder terminal_data_directory\MQL5\Scripts\, then the call should be written the following way:

//--- Use of a script resource in an Expert Advisor
ObjectSetString(0,my_bitmap_name,OBJPROP_BMPFILE,0,"\\Scripts\\Draw_Triangles_Script.ex5::Files\\triangle_1.bmp");

If the path to the executable file is not specified when calling the resource from another EX5, the executable file is searched in the same folder that contains the program that calls the resource. This means the following: If an Expert Advisor is located in terminal_data_directory\MQL5\Experts\ and a resource from file Draw_Triangles_Script.ex5 us requested in it without path specification, then the file will be searched in terminal_data_directory\MQL5\Experts\.

//--- Request for a resource from a script in an Expert Advisor without path specification
ObjectSetString(0,my_bitmap_name,OBJPROP_BMPFILE,0,"Draw_Triangles_Script.ex5::Files\\triangle_1.bmp");


Compression of Resources in EX5 Files - How It Works

Files in BMP and WAV are automatically compressed before they are included into an executable EX5 file. This means that the use of resources not only allows you to create full-featured MQL5 programs, but also reduces the overall size of the files required by the terminal when using images and sounds, as compared to the conventional way of writing MQL5 programs.

The resource file size cannot exceed 16 Mb.

Important: Additional advantage of using resources is automatic compression of WAV and BMP files when packing into an executable EX5 file. This reduces not only the quantity but also the size of files used by the program.

For example, consider a small program Animals_EA.mq5. A small block of code for use of resources is given below:

//+------------------------------------------------------------------+
//|                                                   Animals_EA.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- Declare image resources
#resource "\\Images\\cat.bmp"
#resource "\\Images\\dog.bmp"
#resource "\\Images\\cow.bmp"
#resource "\\Images\\bird.bmp"
//--- Declare sound resources
#resource "\\Files\\MySounds\\cat.wav"
#resource "\\Files\\MySounds\\dog.wav"
#resource "\\Files\\MySounds\\cow.wav"
#resource "\\Files\\MySounds\\bird.wav"
//--- Object names
string cat_dog="cat_dog";
string cow_bird="cow_bird";
string canvas="canvas";
string text="text";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create a substrate
   CreateCanvas(canvas,50,50,500,500);
//--- Create buttons
   CreateObjectBITMAP_LABEL(cat_dog,110,120,"::Images\\cat.bmp","::Images\\dog.bmp");
   CreateObjectBITMAP_LABEL(cow_bird,110,330,"::Images\\cow.bmp","::Images\\bird.bmp");
   CreateText(text,"Click on any graphical object",200,90,clrTan);
//--- Give a command for an immediate refresh to see the object
   ChartRedraw();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//|  Creating OBJ_BITMAP_LABEL with the specified images          |
//+------------------------------------------------------------------+
bool CreateObjectBITMAP_LABEL(string obj_name,int X,int Y,string res_name1,string res_name2)
  {
//--- If there is no such an object on the chart
   if(ObjectFind(0,obj_name)==-1)
     {
      //--- Create it
      bool res=ObjectCreate(0,obj_name,OBJ_BITMAP_LABEL,0,0,0);
      //--- Check the result
      if(!res)
        {
         PrintFormat("%s: Failed to create OBJ_BITMAP_LABEL with the name %s. Error code=%d",
                     __FUNCTION__,
                     GetLastError());
         return false;
        }
     }

//--- Set the coordinates
   ObjectSetInteger(0,obj_name,OBJPROP_XDISTANCE,X);
   ObjectSetInteger(0,obj_name,OBJPROP_YDISTANCE,Y);
//--- Disable display on the background
   ObjectSetInteger(0,obj_name,OBJPROP_BACK,false);
//--- Reset the error code
   ResetLastError();
//--- Set an image for the pressed condition
   bool res=ObjectSetString(0,obj_name,OBJPROP_BMPFILE,0,res_name1);
//--- Check the operation result
   if(!res)
     {
      PrintFormat("%s: Failed to upload an image from the resource %s. Error code=%d",
                  __FUNCTION__,
                  res_name1,
                  GetLastError());
      return false;
     }
//--- Set an image for the depressed state
   res=ObjectSetString(0,obj_name,OBJPROP_BMPFILE,1,res_name2);
//--- Check the operation result
   if(!res)
     {
      PrintFormat("%s: Failed to upload an image from the resource %s. Error code=%d",
                  __FUNCTION__,
                  res_name2,
                  GetLastError());
      return false;
     }
//--- Set the button pressed
   ObjectSetInteger(0,obj_name,OBJPROP_STATE,true);
   return true;
  }
//+------------------------------------------------------------------+

The program's task is to draw on a blue background (substrate), two graphic buttons that change their appearance by a mouse click. When you click on the substrate, it changes its color from blue to beige, and vice versa. At each change, a sound is played, the event of the mouse click is handled in the OnChartEvent() function. A chart immediately after the start of Adviser Animals_EA.mq5 is shown in the figure.



Have a look at the properties of the OBJ_BITMAP_LABEL object, for example, cat_dog. Changing the properties of Bitmap File (On) and Bitmap File (Off) is now impossible through a dialog box, these fields are unavailable and are dimmed.


Important: In graphical objects, images loaded from resources can be changed only programmatically. Manually change of these properties through the Properties window of the object becomes unavailable.

The total volume of images used by the Expert Advisor Animals_EA.mq5, is 430 kb.


But the size of the resulting executable file Animals_EA.ex5, which contains all these pictures, is 339 kb. Thus, instead of 9 files (one MQ5-file, four BMP files and four WAV files), we have now one EX5-file that contains all the necessary resources for the program.

Only images in 24 or 32-bit BMP can be used in resources. 32-bit BMP can contain alpha compositing - in this case they will be applied to a chart with transparency.

The file of the Expert Advisor Animals_EA.mq5, the images and sounds are attached to the article:

If you want to test this program in your terminal, simply download the attached compiled Expert Advisor Animals_EA.ex5, which contains all the necessary resources. In this case, you do not need to download and install the image and sound files.


Working with custom indicators included as resources

One or several custom indicators may be necessary for the operation of MQL5 applications. All of them can be included into the code of an executable MQL5 program. Inclusion of indicators as resources simplifies the distribution of applications.

Below is an example of including and using SampleIndicator.ex5 custom indicator located in terminal_data_folder\MQL5\Indicators\ directory:

//+------------------------------------------------------------------+
//|                                                     SampleEA.mq5 |
//|                        Copyright 2013, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#resource "\\Indicators\\SampleIndicator.ex5"
int handle_ind;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   handle_ind=iCustom(_Symbol,_Period,"::Indicators\\SampleIndicator.ex5");
   if(handle_ind==INVALID_HANDLE)
     {
      Print("Expert: iCustom call: Error code=",GetLastError());
      return(INIT_FAILED);
     }
//--- ...
   return(INIT_SUCCEEDED);
  }

The case when a custom indicator in OnInit() function creates one or more copies of itself requires special consideration. Please keep in mind that the resource should be specified in the following way: <path_EX5_file_name>::<resource_name>.

For example, if SampleIndicator.ex5 indicator is included to SampleEA.ex5 Expert Advisor as a resource, the path to itself specified when calling iCustom() in the custom indicator's initialization function looks the following way: "\\Experts\\SampleEA.ex5::Indicators\\SampleIndicator.ex5". When this path is set explicitly, SampleIndicator.ex5 custom indicator is rigidly connected to SampleEA.ex5 Expert Advisor losing ability to work independently.

The path to itself can be received using GetRelativeProgramPath() function. The example of its usage is provided below:

//+------------------------------------------------------------------+
//|                                              SampleIndicator.mq5 |
//|                        Copyright 2013, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_plots 0
int handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- the wrong way to provide a link to itself
//--- string path="\\Experts\\SampleEA.ex5::Indicators\\SampleIndicator.ex5";  
//--- the right way to receive a link to itself
  string path=GetRelativeProgramPath();
//--- indicator buffers mapping
   handle=iCustom(_Symbol,_Period,path,0,0);
   if(handle==INVALID_HANDLE)
     {
      Print("Indicator: iCustom call: Error code=",GetLastError());
      return(INIT_FAILED);
     }
   else Print("Indicator handle=",handle);
//---
   return(INIT_SUCCEEDED);
  }
///....
//+------------------------------------------------------------------+
//| GetRelativeProgramPath                                           |
//+------------------------------------------------------------------+
string GetRelativeProgramPath()
  {
   int pos2;
//--- get the absolute path to the application
   string path=MQLInfoString(MQL_PROGRAM_PATH);
//--- find the position of "\MQL5\" substring
   int    pos =StringFind(path,"\\MQL5\\");
//--- substring not found - error
   if(pos<0)
      return(NULL);
//--- skip "\MQL5" directory
   pos+=5;
//--- skip extra '\' symbols
   while(StringGetCharacter(path,pos+1)=='\\')
      pos++;
//--- if this is a resource, return the path relative to MQL5 directory
   if(StringFind(path,"::",pos)>=0)
      return(StringSubstr(path,pos));
//--- find a separator for the first MQL5 subdirectory (for example, MQL5\Indicators)
//--- if not found, return the path relative to MQL5 directory
   if((pos2=StringFind(path,"\\",pos+1))<0)
      return(StringSubstr(path,pos));
//--- return the path relative to the subdirectory (for example, MQL5\Indicators)
   return(StringSubstr(path,pos2+1));
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,        
                const double& price[])
  {
//--- return value of prev_calculated for next call
   return(rates_total);
  }


Saving the Memory of the Terminal

Each resource is loaded into the terminal memory only once. While under normal use, each access to the file causes an additional load of the file into the memory. For example, suppose we have 50 OBJ_BITMAP objects, each of them contains the same image sized 100 kb. Then, during normal use, these 50 objects would require the memory 50*100kb=5Mb.

If we declare a resource for the uploaded image, then this picture will be loaded into memory only once, regardless of the number of objects in which it is used.

Important: Resources are loaded into the memory only once and can save memory if used several times.


Conclusion

Use of resources facilitates the use and distribution of MQL5 programs. Creating comfortable, modern tools for trading requires the use of multimedia-based image and sound files. The concept of resource in MQL5 is simple and easy to understand, so try and check it out.

Images in the 32-bit BMP format can contain alpha compositing - in this case they will be applied to a chart with transparency.

The resources provide the following advantages: