Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XX): Erstellen und Speichern von Programmressourcen

9 Dezember 2019, 10:32
Artyom Trishkin
0
385

Inhalt

Konzept

Bei der Entwicklung einer Anwendung benötigen wir oft Audio und Bilder. Die MQL-Sprache verfügt über mehrere Methoden zur Verwendung solcher Daten und alle erfordern das Laden von Dateien aus der "Sandox" des Terminals. Wenn das Endergebnis eine kompilierte Datei ist, müssen Sie nur die Datei als Ressource einbinden und sich von der Notwendigkeit befreien, zusätzliche Dateien für die Programmoperation zu übergeben. Diese Methode ist gut geeignet, um Programme für den mql5.com Market vorzubereiten, da dort nur ausführbare Dateien akzeptiert werden. Diese Methode ist jedoch nicht geeignet, um einen Quellcode auf mql5.com CodeBase zu veröffentlichen, da Audiodateien wie *.wav und grafische Dateien wie *.bmp dort nicht gespeichert werden können. SORT_BY_SYMBOL_SESSION_TURNOVER,                                // Сортировать по суммарному обороту в текущую сессию Ohne sie fehlen dem Quellcode aber einige wichtige Funktionen.
Die Lösung liegt auf der Hand: Die Daten aller benötigten Dateien sollten im Quellcode von *.mqh include-Dateien als Binär-Arrays gespeichert werden. Beim Start der Anwendung werden aus den vorhandenen Datensätzen alle notwendigen Dateien in den notwendigen Verzeichnissen erzeugt. Wenn Sie also einen solchen Code starten, generiert die Anwendung alle notwendigen Dateien, speichert sie im Ordner und funktioniert dann korrekt. All dies geschieht für Nutzer nahtlos, mit der einzigen Ausnahme, dass die Zeit des ersten Starts durch das Erzeugen und Schreiben fehlender Dateien leicht erhöht wird.

Wir werden zwei Klassen für die Arbeit mit Dateidaten erstellen:

  • Die Generator-Klasse der Datei aus vorbereiteten Daten,
  • Die Klasse für die Arbeit mit der Liste der erstellten Dateien — Kollektion von Objekten, die Dateien beschreiben.
Die Generator-Klasse der Datei verfügt über eine Reihe von statischen Methoden zum Erstellen von Dateien aus den entsprechenden Daten. Diese Methoden werden für jeden Teil der Bibliothek verfügbar sein.
Die Klasse für die Arbeit mit bereits erzeugten Dateien enthält die Liste aller erstellten Dateien und Methoden für den Zugriff auf diese Dateien über die in der Liste gespeicherten Daten. Tatsächlich wird die Liste einfache Beschreibungen der erstellten Dateien (Dateiname und Beschreibung) enthalten. Diese Daten ermöglichen es uns, jederzeit auf eine physische Datei zuzugreifen, um damit zu arbeiten.

Datei-Generator-Klasse

Beginnen wir mit der Datei Datas.mqh. Wir werden die notwendigen Nachrichten hinzufügen. Sie können während des Betriebs der angelegten Klassen dargestellt werden.

Zusätzliche Konstanten, die die Position neuer Nachrichten im Array der Bibliothekstextdaten zur Enumeration von Textnachrichtenindizes angeben:

//+------------------------------------------------------------------+
//| List of the library's text message indices                       |
//+------------------------------------------------------------------+
enum ENUM_MESSAGES_LIB
  {
   MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST,      // Beginning of the parameter list
   MSG_LIB_PARAMS_LIST_END,                           // End of the parameter list
   MSG_LIB_PROP_NOT_SUPPORTED,                        // Property not supported
   MSG_LIB_PROP_NOT_SUPPORTED_MQL4,                   // Property not supported in MQL4
   MSG_LIB_PROP_NOT_SUPPORTED_POSITION,               // Property not supported for position
   MSG_LIB_PROP_NOT_SUPPORTED_PENDING,                // Property not supported for pending order
   MSG_LIB_PROP_NOT_SUPPORTED_MARKET,                 // Property not supported for market order
   MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST,            // Property not supported for historical market order
   MSG_LIB_PROP_NOT_SET,                              // Value not set
   MSG_LIB_PROP_EMPTY,                                // Not set
   
   MSG_LIB_SYS_ERROR,                                 // Error
   MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER,                  // Error. No such symbol on server
   MSG_LIB_SYS_FAILED_PUT_SYMBOL,                     // Failed to place to market watch. Error: 
   MSG_LIB_SYS_NOT_GET_PRICE,                         // Failed to get current prices. Error: 
   MSG_LIB_SYS_NOT_GET_MARGIN_RATES,                  // Failed to get margin ratios. Error: 
   MSG_LIB_SYS_NOT_GET_DATAS,                         // Failed to get data
   
   MSG_LIB_SYS_FAILED_CREATE_STORAGE_FOLDER,          // Failed to create folder for storing files. Error: 
   MSG_LIB_SYS_FAILED_ADD_ACC_OBJ_TO_LIST,            // Error. Failed to add current account object to collection list
   MSG_LIB_SYS_FAILED_CREATE_CURR_ACC_OBJ,            // Error. Failed to create account object with current account data
   MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE,            // Could not open file for writing
   MSG_LIB_SYS_INPUT_ERROR_NO_SYMBOL,                 // Input error: no symbol
   MSG_LIB_SYS_FAILED_CREATE_SYM_OBJ,                 // Failed to create symbol object
   MSG_LIB_SYS_FAILED_ADD_SYM_OBJ,                    // Failed to add symbol
   
   MSG_LIB_SYS_NOT_GET_CURR_PRICES,                   // Failed to get current prices by event symbol
   MSG_LIB_SYS_EVENT_ALREADY_IN_LIST,                 // This event is already in the list
   MSG_LIB_SYS_FILE_RES_ALREADY_IN_LIST,              // This file already created and added to list:
   MSG_LIB_SYS_FAILED_CREATE_RES_LINK,                // Error. Failed to create object pointing to resource file
   MSG_LIB_SYS_ERROR_ALREADY_CREATED_COUNTER,         // Error. Counter with ID already created
   MSG_LIB_SYS_FAILED_CREATE_COUNTER,                 // Failed to create timer counter
   MSG_LIB_SYS_FAILED_CREATE_TEMP_LIST,               // Error creating temporary list
   MSG_LIB_SYS_ERROR_NOT_MARKET_LIST,                 // Error. This is not a market collection list
   MSG_LIB_SYS_ERROR_NOT_HISTORY_LIST,                // Error. This is not a history collection list
   MSG_LIB_SYS_FAILED_ADD_ORDER_TO_LIST,              // Could not add order to the list
   MSG_LIB_SYS_FAILED_ADD_DEAL_TO_LIST,               // Could not add deal to the list
   MSG_LIB_SYS_FAILED_ADD_CTRL_ORDER_TO_LIST,         // Failed to add control order
   MSG_LIB_SYS_FAILED_ADD_CTRL_POSITION_TO_LIST,      // Failed to add control position
   MSG_LIB_SYS_FAILED_ADD_MODIFIED_ORD_TO_LIST,       // Could not add modified order to the list of modified orders
    
   MSG_LIB_SYS_NO_TICKS_YET,                          // No ticks yet
   MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT,              // Could not create object structure
   MSG_LIB_SYS_FAILED_WRITE_UARRAY_TO_FILE,           // Could not write uchar array to file
   MSG_LIB_SYS_FAILED_LOAD_UARRAY_FROM_FILE,          // Could not load uchar array from file
   MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT_FROM_UARRAY,  // Could not create object structure from uchar array
   MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,      // Failed to save object structure to uchar array, error
   MSG_LIB_SYS_ERROR_INDEX,                           // Error. "index" value should be within 0 - 3
   MSG_LIB_SYS_ERROR_FAILED_CONV_TO_LOWERCASE,        // Failed to convert string to lowercase, error
   
   MSG_LIB_SYS_ERROR_EMPTY_STRING,                    // Error. Predefined symbols string empty, to be used
   MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY,        // Failed to prepare array of used symbols. Error 
   MSG_LIB_SYS_INVALID_ORDER_TYPE,                    // Invalid order type:
   


Die Texte in Englisch und Russisch, die den entsprechenden Indexkonstanten entsprechen, in das Array der Textnachrichten schreiben:

//+------------------------------------------------------------------+
string messages_library[][TOTAL_LANG]=
  {
   {"Начало списка параметров","Beginning of the event parameter list"},
   {"Конец списка параметров","End of the parameter list"},
   {"Свойство не поддерживается","Property not supported"},
   {"Свойство не поддерживается в MQL4","Property not supported in MQL4"},
   {"Свойство не поддерживается у позиции","Property not supported for position"},
   {"Свойство не поддерживается у отложенного ордера","Property not supported for pending order"},
   {"Свойство не поддерживается у маркет-ордера","Property not supported for market order"},
   {"Свойство не поддерживается у исторического маркет-ордера","Property not supported for historical market order"},
   {"Значение не задано","Value not set"},
   {"Отсутствует","Not set"},
   
   {"Ошибка ","Error "},
   {"Ошибка. Такого символа нет на сервере","Error. No such symbol on server"},
   {"Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in market watch. Error: "},
   {"Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "},
   {"Не удалось получить коэффициенты взимания маржи. Ошибка: ","Failed to get margin rates. Error: "},
   {"Не удалось получить данные ","Failed to get data of "},
   
   {"Не удалось создать папку хранения файлов. Ошибка: ","Could not create file storage folder. Error: "},
   {"Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию","Error. Failed to add current account object to collection list"},
   {"Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта","Error. Failed to create account object with current account data"},
   {"Не удалось открыть для записи файл ","Could not open file for writing: "},
   {"Ошибка входных данных: нет символа ","Input error: no "},
   {"Не удалось создать объект-символ ","Failed to create symbol object "},
   {"Не удалось добавить символ ","Failed to add "},
   
   {"Не удалось получить текущие цены по символу события ","Failed to get current prices by event symbol "},
   {"Такое событие уже есть в списке","This event already in the list"},
   {"Такой файл уже создан и добавлен в список: ","This file has already been created and added to list: "},   
   {"Ошибка. Не удалось создать объект-указатель на файл ресурса","Error. Failed to create resource file link object"},
   
   {"Ошибка. Уже создан счётчик с идентификатором ","Error. Already created counter with id "},
   {"Не удалось создать счётчик таймера ","Failed to create timer counter "},
   
   {"Ошибка создания временного списка","Error creating temporary list"},
   {"Ошибка. Список не является списком рыночной коллекции","Error. The list is not a list of market collection"},
   {"Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of history collection"},
   {"Не удалось добавить ордер в список","Could not add order to list"},
   {"Не удалось добавить сделку в список","Could not add deal to list"},
   {"Не удалось добавить контрольный ордер ","Failed to add control order "},
   {"Не удалось добавить контрольую позицию ","Failed to add control position "},
   {"Не удалось добавить модифицированный ордер в список изменённых ордеров","Could not add modified order to list of modified orders"},
   
   {"Ещё не было тиков","No ticks yet"},
   {"Не удалось создать структуру объекта","Could not create object structure"},
   {"Не удалось записать uchar-массив в файл","Could not write uchar array to file"},
   {"Не удалось загрузить uchar-массив из файла","Could not load uchar array from file"},
   {"Не удалось создать структуру объекта из uchar-массива","Could not create object structure from uchar array"},
   {"Не удалось сохранить структуру объекта в uchar-массив, ошибка ","Failed to save object structure to uchar array, error "},
   {"Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. \"index\" value should be between 0 - 3"},
   {"Не удалось преобразовать строку в нижний регистр, ошибка ","Failed to convert string to lowercase, error "},
   
   {"Ошибка. Строка предопределённых символов пустая, будет использоваться ","Error. String of predefined symbols is empty, symbol will be used: "},
   {"Не удалось подготовить массив используемых символов. Ошибка ","Failed to create array of used characters. Error "},
   {"Неправильный тип ордера: ","Invalid order type: "},



Bitte beachten Sie, dass die Reihenfolge der Texte im Array genau der Reihenfolge der Deklaration von Indexkonstanten in der Enumeration entsprechen sollte.

Damit die Datei der Generator-Klasse funktioniert, müssen wir eine Datenbank erstellen, aus der die Klasse Daten über Audio- und Bilddateien aufnehmen soll. Diese Daten sollten in Form von unsigned char Arrays vorliegen. Um sie zu erstellen, benötigen wir Audio- (*.wav) und Bitmap-Dateien (*.bmp), die in der Bibliothek gespeichert werden. Ich habe bereits einige Testdaten als Beispiel vorbereitet. Sound- und Bitmap-Dateien sind jeweils in einer eigenen Include-Datei zu speichern.

Erstellen Sie im Stammverzeichnis der Bibliothek \MQL5\Include\DoEasy\ die Include-Datei DataSND.mqh und fügen Sie die Namen der Audiodateien hinzu, die in den Arrays platziert werden sollen (Datenarraynamen können später hinzugefügt werden, da sie nur für eine schnelle Suche nach den Array-Deklarationsstellen mit den Daten einer bestimmten Datei benötigt werden, da die Arrays 16 MB nicht überschreiten dürfen):

//+------------------------------------------------------------------+
//|                                                      DataSND.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
//+------------------------------------------------------------------+
//| Audio                                                            |
//+------------------------------------------------------------------+
/*
   sound_array_coin_01           // Falling coin 1
   sound_array_coin_02           // Falling coins
   sound_array_coin_03           // Coins
   sound_array_coin_04           // Falling coin 2
   //---
   sound_array_click_01          // Button click 1
   sound_array_click_02          // Button click 2
   sound_array_click_03          // Button click 3
   //---
   sound_array_cash_machine_01   // Cash register 1
*/
//+------------------------------------------------------------------+


Um die Datei in den Programm-Quellcode einzufügen, klicken Sie auf Bearbeiten --> Einfügen --> Datei als Binär-Array":


Dadurch wird das Dateiauswahlfenster geöffnet, in dem Sie eine zuvor vorbereitete Datei finden sollten, um ihre Daten in das Array hochzuladen. Das Array wird automatisch basierend auf dem Namen der ausgewählten Datei generiert (das Beispiel ist nicht vollständig, da die Binärdaten zahlreich sind):

//+------------------------------------------------------------------+
//|                                                      DataSND.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
//+------------------------------------------------------------------+
//| Audio                                                            |
//+------------------------------------------------------------------+
/*
   sound_array_coin_01           // Falling coin 1
   sound_array_coin_02           // Falling coins
   sound_array_coin_03           // Coins
   sound_array_coin_04           // Falling coin 2
   //---
   sound_array_click_01          // Button click 1
   sound_array_click_02          // Button click 2
   sound_array_click_03          // Button click 3
   //---
   sound_array_cash_machine_01   // Cash register 1
*/
//+------------------------------------------------------------------+
//| Falling coin 01                                                  |
//+------------------------------------------------------------------+
unsigned char sound_array_coin_01[]=
  {
   0x52,0x49,0x46,0x46,0x1E,0x50,0x09,0x00,0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20,
   0x12,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x44,0xAC,0x00,0x00,0x20,0x62,0x05,0x00,
   0x08,0x00,0x20,0x00,0x00,0x00,0x64,0x61,0x74,0x61,0x28,0x35,0x09,0x00,0x11,0xBA,
   0x20,0xBB,0x11,0xBA,0x20,0xBB,0x74,0x81,0x62,0xBB,0x74,0x81,0x62,0xBB,0xE9,0x37,
   0x4D,0xBB,0xE9,0x37,0x4D,0xBB,0x7C,0x41,0x13,0xBB,0x7C,0x41,0x13,0xBB,0x8F,0xE4,
   0x84,0xBA,0x8F,0xE4,0x84,0xBA,0x51,0x6D,0x05,0x3A,0x51,0x6D,0x05,0x3A,0xE1,0xC7,
   0x85,0x3A,0xE1,0xC7,0x85,0x3A,0xF5,0xA9,0xA1,0x39,0xF5,0xA9,0xA1,0x39,0xD6,0xF7,
   0x25,0xBA,0xD6,0xF7,0x25,0xBA,0x88,0x38,0x5B,0xBA,0x88,0x38,0x5B,0xBA,0xC8,0x31,
   0x05,0x39,0xC8,0x31,0x05,0x39,0x62,0xA0,0xF8,0x39,0x62,0xA0,0xF8,0x39,0x62,0xE5,
   0x39,0xBA,0x62,0xE5,0x39,0xBA,0x8A,0xAA,0x8B,0xBA,0x8A,0xAA,0x8B,0xBA,0xDC,0xF9,
   0x0B,0xBA,0xDC,0xF9,0x0B,0xBA,0xEA,0x27,0x97,0xBA,0xEA,0x27,0x97,0xBA,0xA1,0x5E,
   0xDA,0xBA,0xA1,0x5E,0xDA,0xBA,0x96,0x5B,0x56,0xBA,0x96,0x5B,0x56,0xBA,0x13,0xB9,
   0xA6,0xB8,0x13,0xB9,0xA6,0xB8,0x3B,0xFD,0x39,0xB7,0x3B,0xFD,0x39,0xB7,0x05,0x39,
   0x37,0xB9,0x05,0x39,0x37,0xB9,0x7D,0xED,0x18,0xBA,0x7D,0xED,0x18,0xBA,0x73,0xDD,
   0x8A,0xBA,0x73,0xDD,0x8A,0xBA,0xD1,0x83,0xD4,0xBA,0xD1,0x83,0xD4,0xBA,0xFF,0x94,
   0x0C,0xBB,0xFF,0x94,0x0C,0xBB,0xAF,0x13,0xF6,0xBA,0xAF,0x13,0xF6,0xBA,0x5A,0x27,
   0x4A,0xBA,0x5A,0x27,0x4A,0xBA,0x68,0xA7,0xA1,0x39,0x68,0xA7,0xA1,0x39,0xBE,0x9B,
   0x32,0x3A,0xBE,0x9B,0x32,0x3A,0xFD,0x21,0x0C,0x3A,0xFD,0x21,0x0C,0x3A,0x36,0xFF,
   0x21,0x3A,0x36,0xFF,0x21,0x3A,0xB5,0xA2,0x36,0x3A,0xB5,0xA2,0x36,0x3A,0xBB,0x73,
   0xE2,0xB9,0xBB,0x73,0xE2,0xB9,0x16,0xDA,0x16,0xBB,0x16,0xDA,0x16,0xBB,0x41,0x70,
   0x23,0xBB,0x41,0x70,0x23,0xBB,0xA9,0xC6,0x34,0xBA,0xA9,0xC6,0x34,0xBA,0x78,0x88,
   0x35,0x37,0x78,0x88,0x35,0x37,0xFB,0x4C,0xA9,0xBA,0xFB,0x4C,0xA9,0xBA,0xDA,0xAA,


Da die Array-Daten die Datei vollständig duplizieren, sind die resultierenden Arrays recht groß. Aus diesem Grund habe ich die Namen der erstellten Arrays im Voraus eingegeben, um mit Strg+F schnell an den Anfang jedes Arrays in der Liste zu springen.

Nun bleibt nur noch, die erforderliche Anzahl von Audiodaten-Arrays in die Dateiliste aufzunehmen. Ich habe bereits mehrere Testsounds erstellt. Da die Datei groß ist, macht es keinen Sinn, sie in diesem Artikel anzuzeigen. Sie finden es in den unten angehängten Bibliotheksdateien.

Erstellen Sie die Datei mit Bitmap-Daten namens DataIMG.mqh auf genau die gleiche Weise. Die Datei erstellt bereits zwei Arrays, die eine zweifarbige LED-Glühbirne darstellen: Daten mit dem Bild der grünen LED in der einen und mit dem Bild der roten LED in der anderen:

//+------------------------------------------------------------------+
//|                                                      DataIMG.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
//+------------------------------------------------------------------+
//| Images                                                           |
//+------------------------------------------------------------------+
/*
   img_array_spot_green             // Green LED 16x16, 32 bit
   img_array_spot_red               // Red LED 16x16, 32 bit
*/
//+------------------------------------------------------------------+
//| Green LED 32 bit, alpha                                          |
//+------------------------------------------------------------------+
unsigned char img_array_spot_green[]=
  {
   0x42,0x4D,0x38,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
   0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00,
   0x00,0x00,0x02,0x04,0x00,0x00,0xC3,0x0E,0x00,0x00,0xC3,0x0E,0x00,0x00,0x00,0x00,
   0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,


...

   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00
  };
//+------------------------------------------------------------------+
//| Red LED 32 bit, alpha                                            |
//+------------------------------------------------------------------+
unsigned char img_array_spot_red[]=
  {
   0x42,0x4D,0x38,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
   0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00,
   0x00,0x00,0x02,0x04,0x00,0x00,0xC3,0x0E,0x00,0x00,0xC3,0x0E,0x00,0x00,0x00,0x00,
   0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
   0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,


Wie im Beispiel mit den Audiodaten stelle ich hier nicht die vollständige Auflistung der resultierenden Datei zur Verfügung.

Binden Sie die Dateien mit den Daten in die Datei Defines.mqh ein, so dass die binären Daten der Dateien in der Bibliothek verfügbar sind:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "DataSND.mqh"
#include "DataIMG.mqh"
#include "Datas.mqh"
#ifdef __MQL4__
#include "ToMQL4.mqh"
#endif 
//+------------------------------------------------------------------+


Im Makro-Substitutionsblock der Datei Defines.mqh fügen Sie die Makro-Substitution hinzu, indem Sie den Speicherort der Bibliotheksressourcendaten angeben:

//+------------------------------------------------------------------+
//| Macro-Substitution                                               |
//+------------------------------------------------------------------+
//--- Beschreibung der Funktion mit der Zeilennummer des Fehlers
#define DFUN_ERR_LINE                  (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ")
#define DFUN                           (__FUNCTION__+": ")        // "Function description"
#define COUNTRY_LANG                   ("Russian")                // Country language
#define END_TIME                       (D'31.12.3000 23:59:59')   // End date for account history data requests
#define TIMER_FREQUENCY                (16)                       // Minimal frequency of the library timer in milliseconds
//--- Parameters of the orders and deals collection timer
#define COLLECTION_ORD_PAUSE           (250)                      // Orders and deals collection timer pause in milliseconds
#define COLLECTION_ORD_COUNTER_STEP    (16)                       // Increment of the orders and deals collection timer counter
#define COLLECTION_ORD_COUNTER_ID      (1)                        // Orders and deals collection timer counter ID
//--- Parameters of the account collection timer
#define COLLECTION_ACC_PAUSE           (1000)                     // Account collection timer pause in milliseconds
#define COLLECTION_ACC_COUNTER_STEP    (16)                       // Account timer counter increment
#define COLLECTION_ACC_COUNTER_ID      (2)                        // Account timer counter ID
//--- Symbol collection timer 1 parameters
#define COLLECTION_SYM_PAUSE1          (100)                      // Pause of the symbol collection timer 1 in milliseconds (for scanning market watch symbols)
#define COLLECTION_SYM_COUNTER_STEP1   (16)                       // Increment of the symbol timer 1 counter
#define COLLECTION_SYM_COUNTER_ID1     (3)                        // Symbol timer 1 counter ID
//--- Symbol collection timer 2 parameters
#define COLLECTION_SYM_PAUSE2          (300)                      // Pause of the symbol collection timer 2 in milliseconds (for events of the market watch symbol list)
#define COLLECTION_SYM_COUNTER_STEP2   (16)                       // Increment of the symbol timer 2 counter
#define COLLECTION_SYM_COUNTER_ID2     (4)                        // Symbol timer 2 counter ID
//--- Collection list IDs
#define COLLECTION_HISTORY_ID          (0x7779)                   // Historical collection list ID
#define COLLECTION_MARKET_ID           (0x777A)                   // Market collection list ID
#define COLLECTION_EVENTS_ID           (0x777B)                   // Event collection list ID
#define COLLECTION_ACCOUNT_ID          (0x777C)                   // Account collection list ID
#define COLLECTION_SYMBOLS_ID          (0x777D)                   // Symbol collection list ID
//--- Data parameters for file operations
#define DIRECTORY                      ("DoEasy\\")               // Library directory for storing object folders
#define RESOURCE_DIR                   ("DoEasy\\Resource\\")     // Library directory for storing resource folders
//--- Symbol parameters
#define CLR_DEFAULT                    (0xFF000000)               // Default color
#define SYMBOLS_COMMON_TOTAL           (1000)                     // Total number of working symbols
//+------------------------------------------------------------------+


Innerhalb des Bibliotheksunterordners Resource\ werden die Verzeichnisse für Audio und Bilder zum Erstellen und Speichern von Audio- und Bilddateien automatisch generiert.

Wenn wir Dateien aus vorbereiteten Arrays erstellen, müssen wir deren Erweiterungen angeben. Um der Dateierzeugungsmethode mitzuteilen, welche Datei genau erstellt wird und in welchem Ordner sie abgelegt werden soll, benötigen wir die Aufzählung der Dateitypen, deren Daten in binäre Arrays .
geschrieben werden. Fügen Sie die benötigte Enumeration am Ende der Auflistung Defines.mqh hinzu:
.

//+------------------------------------------------------------------+
//| Data for working with program resource data                      |
//+------------------------------------------------------------------+
enum ENUM_FILE_TYPE
  {
   FILE_TYPE_WAV,                                           // wav file
   FILE_TYPE_BMP,                                           // bmp file
  };
//+------------------------------------------------------------------+


Da sich alle Bibliotheksressourcendateien in den Ordnern Sounds und Images in MQL5\Dateien\ des Terminals befinden, müssen wir die PlaySound()-Methode der Klasse CMessage korrigieren. Öffnen Sie \MQL5\Include\DoEasy\Services\Message.mqh, um den Dateipfad in der Methode PlaySound() anpassen:

//+------------------------------------------------------------------+
//| Play an audio file                                               |
//+------------------------------------------------------------------+
bool CMessage::PlaySound(const string file_name)
  {
   bool res=::PlaySound("\\Files\\"+file_name);
   CMessage::m_global_error=(res ? ERR_SUCCESS : ::GetLastError());
   return res;
  }
//+------------------------------------------------------------------+


Um die Datei abzuspielen, geben wir den Unterordner \Files\ an, da wir alle Daten in Bezug auf MQL5\ und diesen Ordner speichern sollen, während der verbleibende Dateipfad festgelegt und beim Erstellen des Dateibeschreibungsobjekts mit dem Parameter file_name an die Methode übergeben wird.

Derzeit reicht dies aus, um die notwendigen Klassen zu erstellen.

Erstellen Sie in \MQL5\Include\DoEasy\Services\ die neue Klasse CFileGen in der Datei FileGen.mqh:

//+------------------------------------------------------------------+
//|                                                      FileGen.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "..\\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| File generator class                                             |
//+------------------------------------------------------------------+
class CFileGen
  {
private:
   static string     m_folder_name;    // Name of a folder library resource files are stored in
   static string     m_subfolder;      // Name of a subfolder storing audio or bitmap files
   static string     m_name;           // File name
   static int        m_handle;         // File handle
//--- Set a (1) file, (2) subfolder name
   static void       SetName(const ENUM_FILE_TYPE file_type,const string file_name);
   static void       SetSubFolder(const ENUM_FILE_TYPE file_type);
//--- Return file extension by its type
   static string     Extension(const ENUM_FILE_TYPE file_type);
public:
//--- Return the (1) set name, (2) the flag of a file presence in the resource directory
   static string     Name(void)  { return CFileGen::m_name; }
   static bool       IsExist(const ENUM_FILE_TYPE file_type,const string file_name);
//--- Create a file out of the data array
   static bool       Create(const ENUM_FILE_TYPE file_type,const string file_name,const uchar &file_data_array[]);
  };
//+------------------------------------------------------------------+


Die Datei enthält automatisch die Bibliothek DELib.mqh der Servicefunktionen, da die für die Klassenoperation notwendigen Defines.mqh und Message.mqh bereits darin enthalten sind:

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property strict  // Notwendig für mql4
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "..\Defines.mqh"
#include "Message.mqh"
#include "TimerCounter.mqh"
//+------------------------------------------------------------------+


Alle Variablen der Klasse und ihre Methoden haben Überschriften im Code, und es hat keinen Sinn, ihren Zweck zu beschreiben — dort ist alles klar. Lassen Sie uns die Implementierung von Methoden betrachten.
Da die Klassenvariablen statisch sind, erfordern sie eine explizite Initialisierung:

//+------------------------------------------------------------------+
//| Initialization of static variables                               |
//+------------------------------------------------------------------+
string CFileGen::m_folder_name=RESOURCE_DIR;
string CFileGen::m_subfolder="\\";
string CFileGen::m_name=NULL;
int    CFileGen::m_handle=INVALID_HANDLE;
//+------------------------------------------------------------------+


Die Methode zum Erstellen einer Datei aus einem Datenfeld:

//+------------------------------------------------------------------+
//| Create a file out of a data array                                |
//+------------------------------------------------------------------+
bool CFileGen::Create(const ENUM_FILE_TYPE file_type,const string file_name,const uchar &file_data_array[])
  {
   //--- Set a file name consisting of the file path, its name and extension
   CFileGen::SetName(file_type,file_name);
   //--- If such a file already exists, return 'false'
   if(::FileIsExist(CFileGen::m_name))
      return false;
   //--- Open the file with the generated name for writing
   CFileGen::m_handle=::FileOpen(CFileGen::m_name,FILE_WRITE|FILE_BIN);
   //--- If failed to create the file, receive an error code, display the file opening error message and return 'false'
   if(CFileGen::m_handle==INVALID_HANDLE)
     {
      int err=::GetLastError();
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE),"\"",CFileGen::m_name,"\". ",CMessage::Text(MSG_LIB_SYS_ERROR),"\"",CMessage::Text(err),"\" ",CMessage::Retcode(err));
      return false;
     }
   //--- Write the contents of the file_data_array[] array, close the file and return 'true'

   ::FileWriteArray(CFileGen::m_handle,file_data_array);
   ::FileClose(CFileGen::m_handle);
   return true;
  }
//+------------------------------------------------------------------+


Die Methode verwendet die Standardfunktion FileWriteArray(), mit der Sie Arrays mit anderen Daten als Zeichenfolgen in eine Binärdatei schreiben können.
Die Methode erhält den Typ einer geschriebenen Datei (Audio oder Bild), einen Namen für die zukünftige Datei und das Array mit einem Satz von Binärdaten der zu erstellenden Datei.
Alle von der Methode ausgeführten Aktionen sind in der Liste aufgeführt — sie sind einfach und verständlich. Es besteht keine Notwendigkeit, sich mit ihnen zu beschäftigen.

Die Methode, die das Flag der Existenz einer Datei im Ressourcenverzeichnis zurückgibt:

//+------------------------------------------------------------------+
//| The flag of a file presence in the resource directory            |
//+------------------------------------------------------------------+
bool CFileGen::IsExist(const ENUM_FILE_TYPE file_type,const string file_name)
  {
   CFileGen::SetName(file_type,file_name);
   return ::FileIsExist(CFileGen::m_name);
  }
//+------------------------------------------------------------------+


Die Methode erhält den Typ einer geschriebenen Datei und den Namen einer Datei, deren Existenz überprüft werden soll. Dann wird der Dateiname, bestehend aus Dateipfad, -name und -erweiterung, gesetzt und das Ergebnis der Prüfung auf Vorhandensein von Dateien gleichen Namens mit der Funktion FileIsExist() zurückgegeben.

Die Methode, die einen Dateinamen festlegt, der aus dem Dateipfad, seinem Namen und seiner Erweiterung besteht:

//+------------------------------------------------------------------+
//| Set a file name                                                  |
//+------------------------------------------------------------------+
void CFileGen::SetName(const ENUM_FILE_TYPE file_type,const string file_name)
  {
   CFileGen::SetSubFolder(file_type);
   CFileGen::m_name=CFileGen::m_folder_name+CFileGen::m_subfolder+file_name+CFileGen::Extension(file_type);
  }
//+------------------------------------------------------------------+


Die Methode erhält den Typ der geschriebenen Datei und einen Dateinamen, aus dem die Methode den vollständigen Dateinamen zusammensetzt, einschließlich des Ordners zum Speichern von Bibliotheksdateien, des Unterordners für die Dateierweiterung, der mit der Methode SetSubFolder() erstellt wurde, des Dateinamens (file_name), der der Methode übergeben wurde und die Dateiendung, die durch die Methode Extension() nach dem Dateityp (Audio oder Bild) erzeugt wurde. Das erhaltene Ergebnis wird der Klassenvariable m_name zugewiesen.

Hier die Methode zum Einstellen des Unterordnernamens in Abhängigkeit vom Dateityp (Audio oder Bild):

//+------------------------------------------------------------------+
//| Set a subfolder name                                             |
//+------------------------------------------------------------------+
void CFileGen::SetSubFolder(const ENUM_FILE_TYPE file_type)
  {
   CFileGen::m_subfolder=(file_type==FILE_TYPE_BMP ? "Images\\" : file_type==FILE_TYPE_WAV ? "Sounds\\" : "");
  }
//+------------------------------------------------------------------+


Die Methode erhält einen Dateityp. Basierend auf diesem Typ wird der Klassenvariable m_subfolder die Namen der Unterordner Images\ oder Sounds\.

Die Methode gibt eine Dateierweiterung je nach Typ zurück:

//+------------------------------------------------------------------+
//| Return file extension by its type                                |
//+------------------------------------------------------------------+
string CFileGen::Extension(const ENUM_FILE_TYPE file_type)
  {
   string ext=::StringSubstr(::EnumToString(file_type),10);
   if(!::StringToLower(ext))
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_CONV_TO_LOWERCASE),CMessage::Retcode(::GetLastError()));
   return "."+ext;
  }
//+------------------------------------------------------------------+


Die Methode erhält einen Dateityp. Als Nächstes wird aus der Textform (EnumToString()) der Enumerationskonstanten ENUM_FILE_TYPE, die an die Methode übergeben wird, die Erweiterung mit der Funktion StringSubstr() (bei einer Audiodatei wird die Teilzeichenkette "WAV", bei einer Bitmap-Datei "BMP" extrahiert) aus der Zeichenkette "FILE_TYPE_WAV" abgerufen. Dann werden alle Symbole einer abgerufenen Teilzeichenkette mit der Dateierweiterung mit StringToLower() in Zeichenketten umgewandelt, das Zeichen "Punkt" (.) wird zur Zeichenkette (davor) hinzugefügt und das erzielte Ergebnis wird von der Methode zurückgegeben.

Damit ist die Erstellung der Datei der Generatorklasse abgeschlossen.

Jetzt sind wir in der Lage, Binärdaten beliebiger Audio- und Bitmap-Dateien im Quellcode zu speichern und aus diesen Daten beim Programmstart automatisch vollwertige Dateien zu erstellen. Die Dateien müssen sich in den entsprechenden Ordnern befinden, auf die die Bibliothek zugreifen wird, um die Dateien zu erhalten und sie für ihren Zweck zu verwenden.
Um auf sie bequem zuzugreifen, müssen wir die vorhandenen Dateien irgendwie beschreiben. Zu diesem Zweck erstellen wir die Klasse für die Kollektion von Programmressourcen. Dies wird keine Kollektion sein, die wir in der Bibliothek erstellen werden (Listen von Zeigern auf Kollektionsobjekte), sondern eine Kollektion von Dateibeschreibungsobjekten.
Für jede erstellte physische Datei werden wir ein Objekt erzeugen, das diese Datei beschreibt, wobei Name, Pfad und Beschreibung anzugeben sind. Wir werden diese Deskriptorobjekte verwenden, um auf physische Dateien zuzugreifen. Eine Dateibeschreibung kann aus einer beliebigen Textbeschreibung bestehen, die wir zu einem Deskriptorobjekt für jede einzelne Datei hinzufügen werden.
So kann beispielsweise das Geräusch eines Mausklicks als "Mausklick 01", "Klick 01" oder beliebig anders beschrieben werden. Um einen Deskriptor einer notwendigen Datei zu erhalten, geben wir einfach die Beschreibung in die Suchparameter ein. Die Suchmethode gibt den Datei-Deskriptor-Objektindex zurück, mit dem wir die Eigenschaften der physischen Datei ermitteln können.

Die Klasse der Kollektion von Programmressourcen

Erstellen Sie in dem Bibliotheksordner \MQL5\Include\DoEasy\Collections\ die neue Klasse CResourceCollection in der Datei ResourceCollection.mqh. Schreiben Sie in der neuen Klassenauflistung noch eine weitere Klasse — die Deskriptor-Objektklasse, deren Instanzen für jede neue Datei angelegt und der Deskriptor-Kollektion hinzugefügt werden sollen:

//+------------------------------------------------------------------+
//|                                           ResourceCollection.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\\Services\\FileGen.mqh"
//+------------------------------------------------------------------+
//| Descriptor object class for the library resource file            |
//+------------------------------------------------------------------+
class CResObj : public CObject
  {
private:
   string            m_file_name;      // Path + file name + extension
   string            m_description;    // File text description
public:
//--- Set (1) file name, (2) file description
   void              FileName(const string name)                  { this.m_file_name=name;      }
   void              Description(const string descr)              { this.m_description=descr;   }
//--- Return (1) file name, (2) file description
   string            FileName(void)                         const { return this.m_file_name;    }
   string            Description(void)                      const { return this.m_description;  }
//--- Compare CResObj objects by properties (to search for equal resource objects)
   bool              IsEqual(const CResObj* compared_obj)   const { return this.Compare(compared_obj,0)==0; }
//--- Compare CResObj objects by all properties (for sorting)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Konstructor
                     CResObj(void){;}
  };
//+------------------------------------------------------------------+
//| Compare CResObj objects                                          |
//+------------------------------------------------------------------+
int CResObj::Compare(const CObject *node,const int mode=0) const
  {
   const CResObj *obj_compared=node;
   if(mode==0)
      return(this.m_file_name>obj_compared.m_file_name ? 1 : this.m_file_name<obj_compared.m_file_name ? -1 : 0);
   else
      return(this.m_description>obj_compared.m_description ? 1 : this.m_description<obj_compared.m_description ? -1 : 0);
  }
//+------------------------------------------------------------------+


Nehmen Sie die notwendigen Dateien sofort in die Auflistung auf — die Klasse des dynamische Arrays von Zeigern auf CObject-Klasseninstanzen und ihre Nachkommen der Standardbibliothek und der Klasse, die CFileGen-Bibliotheksdateien erzeugt.

Damit das Deskriptorobjekt in der CArrayObjektliste gespeichert werden kann, müssen wir dieses Objekt zu einer abgeleiteten Klasse der Basisklasse CObject aus der Standardbibliothek
machen. Alle Variablen und Methoden der Klassenmitglieder sind in den Codekommentaren beschrieben. Es hat keinen Sinn, sich mit einer detaillierten Analyse ihres Zwecks zu befassen.
Bitte beachten Sie, dass die virtuelle Methode Compare() standardmäßig die Felder zweier Deskriptorobjekte anhand eines Dateinamens vergleicht. Wenn die Vergleichsfelder zweier Objekte gleich sind, gibt die Methode Null zurück. Wenn Objekte durch eine Dateibeschreibung verglichen werden sollen, sollte der Modus auf 1 gesetzt werden.
Die Methode IsEqual() , die das Flag der Objektgleichheit zurückgibt, vergleicht Objekte nur nach Dateinamen (da es nicht zwei Dateien mit ähnlichen Namen in einem Ordner geben kann). Standardmäßig gibt die Methode das Ergebnis der Operation der Methode Compare() mit 'mode' (0) zurück, was dem Vergleich der Dateinamen entspricht.

Lassen Sie uns die Sammelklasse der Deskriptorobjekte implementieren, die Programmressourcendateien beschreiben:

//+------------------------------------------------------------------+
//| Collection class of resource files descriptor objects            |
//+------------------------------------------------------------------+
class CResourceCollection
  {
private:
//--- List of pointers to descriptor objects
   CArrayObj         m_list_dscr_obj;
//--- Create a file descriptor object and add it to the list
   bool              CreateFileDescrObj(const string file_name,const string descript);
//--- Add a new object to the list of descriptor objects
   bool              AddToList(CResObj* element);
public:
//--- Create a file and add its description to the list
   bool              CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[]);
//--- Return the (1) list of pointers to descriptor objects, (2) index of the file descriptor object by description
   CArrayObj        *GetList(void)  { return &this.m_list_dscr_obj;  }
   int               GetIndexResObjByDescription(const string file_description);
//--- Konstructor
                     CResourceCollection(void);
  };
//+------------------------------------------------------------------+


Der Zweck jeder Methode ist auch hier in der Auflistung festgelegt, daher kommen wir zu den Klassenmethoden.

Schreiben Sie den Klassenkonstruktor außerhalb des Klassenkörpers:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CResourceCollection::CResourceCollection()
  {
   this.m_list_dscr_obj.Clear();
   this.m_list_dscr_obj.Sort();
  }
//+------------------------------------------------------------------+


Hier wird die Liste der Deskriptorobjekte gelöscht und das Flag der Sortierung nach Dateinamen wird für die Liste gesetzt (Standard ist 0).

Die Methode zum Erstellen einer Datei und zum Hinzufügen eines Deskriptorobjekts zur Kollektionsliste:

//+------------------------------------------------------------------+
//| Create a file and add its descriptor object to the list          |
//+------------------------------------------------------------------+
bool CResourceCollection::CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[])
  {
   if(!CFileGen::Create(file_type,file_name,file_data_array))
     {
      if(!::FileIsExist(CFileGen::Name()))
         return false;
     }
   return this.CreateFileDescrObj(file_type,CFileGen::Name(),descript);
  }
//+------------------------------------------------------------------+


Die Methode enthält den Typ einer erzeugten Datei (Audio oder Bild), Dateiname, Dateibeschreibung und eine Referenz zum binären Array von Dateidaten, aus dem die Datei bestehen soll.

Wenn es nicht gelungen ist, die Datei mit der Methode Create() der Klasse CFileGen zu erstellen, und die Datei tatsächlich fehlt (sie kann bereits existieren und kann deshalb nicht neu erstellt werden), wird false zurückgegeben.
Wenn die Datei noch nie dort war und gerade erstellt wurde, gibt das Operationsergebnis der Methode zurück, die ein Deskriptorobjekt erzeugt und es zur Kollektionsliste der Dateideskriptorobjekte aus der Methode hinzufügt.

Die Methode zum Erstellen eines Datei-Deskriptor-Objekts und Hinzufügen desselben zur Kollektionsliste:

//+------------------------------------------------------------------+
//| Create a file descriptor object and add it to the list           |
//+------------------------------------------------------------------+
bool CResourceCollection::CreateFileDescrObj(const string file_name,const string descript)
  {
   CResObj *res_dscr=new CResObj();
   if(res_dscr==NULL)
     {
      Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_RES_LINK));
      return false;
     }
   res_dscr.FileName(file_name);
   res_dscr.Description(descript);
   if(!this.AddToList(res_dscr))
     {
      delete res_dscr;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+


Die Methode erhält den Dateinamen und seine Beschreibung.
Erstellen Sie das neue Datei-Deskriptor-Objekt. Wenn es nicht gelungen ist, das Objekt zu erstellen, wird die entsprechende Nachricht ins Journal geschrieben und es wird false zurückgegeben.
Als Nächstes bestimmen Sie einen Dateinamen für das neu erstellte Deskriptorobjekt, sowie die Dateibeschreibung und fügen Sie es der Kollektionsliste hinzu. Wenn es nicht gelungen ist, das Objekt hinzuzufügen, sollte es zerstört werden, um ein Speicherleck zu vermeiden — entfernen Sie das Objekt und geben Sie false zurück.
Andernfalls wird wahr zurückgegeben — das Objekt wurde erfolgreich erstellt und der Kollektion hinzugefügt.

Die Methode, die ein neues Deskriptorobjekt zur Kollektionsliste hinzufügt:

//+------------------------------------------------------------------+
//| Add a new object to the list of file descriptor objects          |
//+------------------------------------------------------------------+
bool CResourceCollection::AddToList(CResObj *element)
  {
   this.m_list_dscr_obj.Sort();
   if(this.m_list_dscr_obj.Search(element)>WRONG_VALUE)
      return false;
   return this.m_list_dscr_obj.Add(element);
  }
//+------------------------------------------------------------------+


Die Methode empfängt ein Deskriptorobjekt und das Sortierflag nach einem Dateinamen wird für die Kollektionsliste gesetzt. Wenn das Objekt mit dem gleichen Namen bereits in der Liste vorhanden ist, geben Sie false zurück.
Andernfalls geben Sie das Operationsergebnis der Methode zum Hinzufügen eines Objekts zur Liste zurück.

Die Methode, die den Deskriptor-Objektindex in der Kollektionsliste durch die Dateibeschreibung zurückgibt:

//+----------------------------------------------------------------------+
//| Return the index of the file descriptor object by a file description |
//+----------------------------------------------------------------------+
int CResourceCollection::GetIndexResObjByDescription(const string file_description)
  {
   CResObj *obj=new CResObj();
   if(obj==NULL)
      return WRONG_VALUE;
   obj.Description(file_description);
   this.m_list_dscr_obj.Sort(1);
   int index=this.m_list_dscr_obj.Search(obj);
   delete obj;
   return index;
  }
//+------------------------------------------------------------------+


Die Methode erhält die Dateibeschreibung, die zum Suchen eines Deskriptorobjekts verwendet werden soll. Erstellen einer temporären Instanz eines Deskriptorobjekts und Setzen der an die Methode übergebenen Dateibeschreibung in ihrem Beschreibungsfeld.
Setzen Sie das Sortierflag durch die Dateibeschreibung auf die Kollektionsliste (1) und rufen Sie den Deskriptorobjektindex ab, dessen Beschreibungsfeld den notwendigen Text enthält.
Achten Sie darauf, das temporäre Objekt zu entfernen und den erhaltenen Index zurückzugeben (-1, falls es kein Objekt mit dieser Beschreibung in der Kollektionsliste gibt).

Die Klasse der Dateideskriptor-Objektsammlung ist bereit.

Nun müssen wir dem Basisobjekt CEngine der Bibliothek einige Methoden hinzufügen.

Öffnen Sie die Datei \MQL5\Include\DoEasy\Engine.mqh und binden Sie die Kollektionsliste der Deskriptor-Objekte ein:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
//+------------------------------------------------------------------+


Erstellen Sie das neue Kollektionsobjekt für Anwendungsressourcen (Kollektion von Datei-Deskriptoren):

//+------------------------------------------------------------------+
//| Bibliothek der Basisklasse                                       |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Kollektion der historischen Aufträge und Deals
   CMarketCollection    m_market;                        // Kollektion der Marktorder und Deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CResourceCollection  m_resource;                      // Resource list
   CArrayObj            m_list_counters;                 // Liste der Timerzähler
   int                  m_global_error;                  // Global error code
   bool                 m_first_start;                   // Flag des Erststarts
   bool                 m_is_hedge;                      // Flag des Hedging-Kontos
   bool                 m_is_tester;                     // Flag of working in the tester
   bool                 m_is_market_trade_event;         // Flag eines Handelsereignisses des Kontos
   bool                 m_is_history_trade_event;        // Flag eines historischen Handelsereignisses auf dem Konto
   bool                 m_is_account_event;              // Account change event flag
   bool                 m_is_symbol_event;               // Symbol change event flag
   ENUM_TRADE_EVENT     m_last_trade_event;              // Last account trading event
   int                  m_last_account_event;            // Last event in the account properties
   int                  m_last_symbol_event;             // Last event in the symbol properties
//--- Rückgabe des Zählerindex über die ID


und fügen Sie drei neue Methoden für das Arbeiten mit der Kollektion von Programmressourcen hinzu:

//--- Timer
   void                 OnTimer(void);
//--- Set the list of used symbols
   bool                 SetUsedSymbols(const string &array_symbols[])   { return this.m_symbols.SetUsedSymbols(array_symbols);}
   
//--- Create a resource file
   bool                 CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[])
                          {
                           return this.m_resource.CreateFile(file_type,file_name,descript,file_data_array);
                          }
//--- Return the list of links to resources
   CArrayObj           *GetListResource(void)                                 { return this.m_resource.GetList();                               }
   int                  GetIndexResObjByDescription(const string file_name)   { return this.m_resource.GetIndexResObjByDescription(file_name);  }

//--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value
   ushort               EventMSC(const long lparam)               const { return this.LongToUshortFromByte(lparam,0);         }
   ushort               EventReason(const long lparam)            const { return this.LongToUshortFromByte(lparam,1);         }
   ushort               EventSource(const long lparam)            const { return this.LongToUshortFromByte(lparam,2);         }
   
//--- Constructor/Destructor
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+


Die Methoden haben die gleichen Namen wie die Methoden in der Collection-Klasse der Deskriptorobjekte und rufen die gleichnamigen Methoden aus dieser Klasse auf.

Um die erstellten Klassen zu testen, erstellen Sie alle Dateien entsprechend den verfügbaren Daten in den Include-Dateien DataSND.mqh und DataIMG.mqh in binären Arrays. Zeigen Sie im Expertenjournal die Ergebnisse der Erstellung von Dateien aus Binärarrays und den Inhalt der resultierenden Kollektionsliste von Datei-Deskriptorobjekten an. Spielen wir auch einen der Sounds ab und zeigen wir das Bild an, das aus den beiden erstellten Bilddateien besteht, die rote und grüne LEDs in der unteren rechten Ecke des Bildschirms darstellen.

Testen des Zugriffs auf automatisch erstellte Dateien

Verwenden wir den EA TestDoEasyPart19.mq5 des vorherigen Artikels und speichern Sie ihn in \MQL5\Experts\TestDoEasy\ Part20\ unter dem Namen TestDoEasyPart20.mq5.

Da Dateien beim ersten Start des Programms erstellt werden sollten, sollten die Klassen zum Erstellen der Programmressourcen in OnInit() behandelt werden. Das erhaltene Ergebnis sollte dort ebenfalls getestet werden.

Fügen Sie den folgenden Codeblock ganz am Ende von OnInit() hinzu:

//--- Set CTrade trading class parameters
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Create and check the resource files
   Print("\n",TextByLanguage("--- Проверка успешности создания файлов ---","--- Verifying files were created ---"));
   string dscr=TextByLanguage("Проверка существования файла: ","Checking existence of file: ");
   
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Sound of falling coin 1"),sound_array_coin_01);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_01"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Sound fallen coins"),sound_array_coin_02);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_02"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Sound of coins"),sound_array_coin_03);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_03"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Sound of falling coin 2"),sound_array_coin_04);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_04"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Click on button sound 1"),sound_array_click_01);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_01"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Click on button sound 1"),sound_array_click_02);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_02"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Click on button sound 1"),sound_array_click_03);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_03"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Sound of cash machine"),sound_array_cash_machine_01);
   if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_cash_machine_01"))
      Print(dscr+CFileGen::Name(),": OK");

   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green);
   if(CFileGen::IsExist(FILE_TYPE_BMP,"img_array_spot_green"))
      Print(dscr+CFileGen::Name(),": OK");
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red);
   if(CFileGen::IsExist(FILE_TYPE_BMP,"img_array_spot_red"))
      Print(dscr+CFileGen::Name(),": OK");

//--- Check the file description list
   Print("\n",TextByLanguage("--- Проверка списка описания файлов ---","--- Checking file description list ---"));
   CArrayObj* list_res=engine.GetListResource();
   if(list_res!=NULL)
     {
      //--- Let's see the entire list of file descriptions
      for(int i=0;i<list_res.Total();i++)
        {
         CResObj *res=list_res.At(i);
         if(res==NULL)
            continue;
         //--- Display the paths to the files and the file description in the journal
         string type=(StringFind(res.FileName(),"\\Sounds\\")>0 ? TextByLanguage("Звук ","Sound ") : TextByLanguage("Изображение ","Image "));
         Print(type,string(i+1),": ",TextByLanguage("Имя файла :","File name: "),res.FileName()," (",res.Description(),")");
         
         //--- If the current description corresponds to the falling coin sound 1, play the appropriate sound
         if(res.Description()==TextByLanguage("Звук упавшей монетки 1","Sound of falling coin 1"))
           {
            CMessage::PlaySound(res.FileName());
           }
        }
      //--- Create the image of the red-green LED
      //--- Get the indices of red and green LEDs image descriptions
      int index_r=engine.GetIndexResObjByDescription(TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""));
      int index_g=engine.GetIndexResObjByDescription(TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""));
      if(index_g>WRONG_VALUE && index_r>WRONG_VALUE)
        {
         //--- Get two objects with files description from the list
         CResObj *res_g=list_res.At(index_g);
         CResObj *res_r=list_res.At(index_r);
         if(res_g==NULL || res_r==NULL)
           {
            Print(TextByLanguage("Не удалось получить данные с описанием файла изображения","Failed to get image file description data"));
            return(INIT_SUCCEEDED);
           }
         //--- Create a button based on image files
         long chart_ID=ChartID();
         string name=prefix+"RedGreenSpot";
         if(ObjectCreate(chart_ID,name,OBJ_BITMAP_LABEL,0,0,0))
           {
            ObjectSetString(chart_ID,name,OBJPROP_BMPFILE,0,"\\Files\\"+res_g.FileName());   // Изображение для нажатой кнопки
            ObjectSetString(chart_ID,name,OBJPROP_BMPFILE,1,"\\Files\\"+res_r.FileName());   // Released button image
            ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,CORNER_RIGHT_LOWER);
            ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER);
            ObjectSetInteger(chart_ID,name,OBJPROP_STATE,true);
            ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,16); 
            ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,16);
            ObjectSetInteger(chart_ID,name,OBJPROP_XOFFSET,0); 
            ObjectSetInteger(chart_ID,name,OBJPROP_YOFFSET,0);
            ObjectSetInteger(chart_ID,name,OBJPROP_BACK,false);
            ObjectSetInteger(chart_ID,name,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
            ChartRedraw(chart_ID);
           }
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Hier haben wir das Erstellen von Dateien aus Daten arrangiert, die sich in binären Arrays der Bibliotheks-Quellcodes befinden. Nachdem Sie jede Datei erstellt haben, überprüfen Sie ihre Existenz und zeigen Sie das Ergebnis im Journal an. Nachdem Sie alle Dateien und ihre Deskriptoren erstellt haben, überprüfen Sie die vollständige Liste aller Datei-Deskriptoren, die der Kollektion beim Erstellen der Dateien hinzugefügt wurden.

Die Auflistung beschreibt alle Aktionen. Sie können sie selbst analysieren.

Nach der Kompilierung des EAs zeigt es die Ergebnisse der Dateierstellung im Journal an, spielt den fallenden Münzton ab und zeigt das aus zwei Bildern bestehende LED-Bild in der rechten unteren Ecke des Bildschirms an. Sie können die Bilder wechseln, indem Sie auf die LED klicken. Tatsächlich ist dies eine Schaltfläche mit zwei Zuständen (ein/aus).



Wie wir sehen können, funktioniert alles so, wie es sein soll. Die Meldungen über eine erfolgreiche Dateierzeugung erscheinen im Journal, die LED ändert ihre Farbe, wenn wir auf sie klicken, und wenn wir den Terminalordner öffnen (Datei --> Dateiordner öffnen) und MQL5\Files\DoEasy\Resource\ eingeben, können wir die Unterordner Bilder und Sounds sehen, in denen sich alle neu erstellten Dateien befinden.

Was kommt als Nächstes?

Ab dem nächsten Artikel eröffnen wir den neuen Bibliotheksbereich — Handelsklassen und alles, was damit zusammenhängt.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie testen und herunterladen können.
Stellen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.

Zurück zum Inhalt

Frühere Artikel dieser Serie:

Teil 1. Konzept, Datenverwaltung.
Teil 2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung (Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4. Handelsereignisse. Konzept
Teil 5. Klassen und Kollektionen von Handelsereignissen. Senden von Ereignissen an das Programm
Teil 6. Ereignisse auf Netting-Konten
Teil 7. Ereignis der Aktivierung einer StopLimit-Order, Vorbereiten der Funktionsweise bei Änderungen von Orders und Positionen
Teil 8. Ereignisse von Änderungen von Orders und Positionen
Teil 9. Kompatibilität mit MQL4 - Datenvorbereitung
Teil 10. Kompatibilität mit MQL4 - Ereignisse der Positionseröffnung und Aktivierung von Pending-Orders
Teil 11. Kompatibilität mit MQL4 - Ereignisse des Schließens von Positionen
Teil 12. Objektklasse "Account" und die Kollektion von Konto-Objekten
Teil 13. Das Objekt der Kontoereignisse
Teil 14. Das Symbolobjekt
Teil 15. Die Kollektion der Symbolobjekte
Teil 16. Ereignisse der Kollektionssymbole
Teil 17. Interaktivität von Bibliotheksobjekten
Teil 18. Interaktivität des Kontos und aller anderen Bibliotheksobjekt
Teil 19. Klassenbibliothek für Nachrichten


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/7195

Beigefügte Dateien |
MQL5.zip (3556.3 KB)
MQL4.zip (3553.08 KB)
Erstellen eines Expert Advisors mit separaten Modulen Erstellen eines Expert Advisors mit separaten Modulen

Bei der Entwicklung von Indikatoren, Expert Advisors und Skripten müssen Entwickler oft verschiedene Codeteile erstellen, die nicht direkt mit der Handelsstrategie zusammenhängen. In diesem Artikel betrachten wir eine Möglichkeit, Expert Advisor zu erstellen, die zuvor erstellte Blöcke verwenden, wie z.B. Trailing, Filter und Ablauf-Code. Wir werden die Vorteile dieses Planungsansatzes erläutern.

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XIX): Klassenbibliothek für Nachrichten Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XIX): Klassenbibliothek für Nachrichten

In diesem Artikel werden wir die Klasse für die Darstellung von Textnachrichten besprechen. Derzeit haben wir eine ausreichende Anzahl verschiedener Textnachrichten. Es ist an der Zeit, die Methoden für die Speicherung, Anzeige und Übersetzung von russischen oder englischen Nachrichten in andere Sprachen neu zu organisieren. Außerdem wäre es gut, praktische Möglichkeiten einzuführen, um der Bibliothek neue Sprachen hinzuzufügen und schnell zwischen ihnen zu wechseln.

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXI): Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXI): Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt

In diesem Artikel werden wir mit der Entwicklung des neuen Bibliotheksbereichs beginnen - die Handelsklassen. Außerdem werden wir die Entwicklung eines einheitlichen Basisobjekts für den Handel auf den Plattformen MetaTrader 5 und MetaTrader 4 in Betracht ziehen. Wenn ein Auftrag an den Server gesendet wird, bedeutet ein solches Handelsobjekt, dass verifizierte und korrekte Parameter der Handelsanfrage an den Server übergeben werden.

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXI): Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXI): Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen

In diesem Artikel beginnen wir mit der Entwicklung der Bibliothek der Basisklasse des Handels und fügen die erste Überprüfung der Berechtigungen zur Durchführung von Handelsoperationen der ersten Version hinzu. Außerdem werden wir die Funktionen und Inhalte der Basishandelsklasse leicht erweitern.