Обсуждение статьи "Как за 10 минут написать DLL библиотеку на MQL5 (Часть II): Пишем в среде Visual Studio 2017"

 

Опубликована статья Как за 10 минут написать DLL библиотеку на MQL5 (Часть II): Пишем в среде Visual Studio 2017:

Первоначальная "базовая" статья отнюдь не потеряла актуальности и всем интересующимся данной темой просто необходимо ее прочесть. Но с тех пор прошло достаточно много времени, сейчас актуальна Visual Studio 2017, в которой изменился, пусть ине значительно, интерфейс, да и сама платформа MetaTrader 5 развивалась и не стояла на месте. В статье рассмотрены этапы создания проекта dll, его настройки и совместной работы с инструментами терминала MetaTrader 5.

Создание простой DLL

Этот путь уже полностью проделан в первоначальной статье, здесь же мы повторим его, учитывая накопившиеся изменения.

Итак, в среде Visual Studio 2017 выбираем File -> New -> Project. В появившемся окне, в левой части, раскрываем список Visual C++ и в нем выбираем Windows Desktop, а в средней части  выделяем строку Windows Desktop Wizard. В нижней части имеются несколько полей ввода, где можно изменить имя (рекомендуется задать свое и осмысленное) и месторасположение проекта (лучше оставить так, как предлагается). Всё готово, нажимаем кнопку "ОК" и переходим в следующее окно:


Здесь в выпадающем списке нужно выбрать Dynamic Link Library (.dll) и отметить галкой пункт "Export Symbols". На самом деле отмечать галкой этот пункт необязательно, но желательно начинающим разработчикам. В этом случае в файлы проекта будет добавлен демонстрационный код, который можно просмотреть, а затем удалить, либо закомментировать. Нажимаем на кнопку "ОК" и создаются файлы проекта, которые мы можем затем редактировать. Однако делать это еще рано, пока разберемся с настройками проекта. Во первых, нужно помнить, что MetaTrader 5 работает только с 64-х разрядными библиотеками. Если попытаться присоединить 32-х разрядную, то мы получим следующие сообщения:

'E:\...\MQL5\Libraries\Project2.dll' is not 64-bit version
Cannot load 'E:\MetaTrader 5\MQL5\Libraries\Project2.dll' [193]

Соответственно, никакой работы сделать будет нельзя.

Автор: Andrei Novichkov

 

может кому будет полезно, последнее время делаю именно так:

можно (и даже проще) использовать CodeLight IDE. По сравнению со студией она быстрая и менее "жручая", цепляет разные компиляторы, и микрософтовский в том числе.

до странного, но DLL используя gcc строится проще. В настройках проекта DLL указать нужный тулчайн (gcc32 бит для MT4, gcc64 для MT5). И собственно всё. Опционально добавить команду "скопировать DLL в иерархию MT" в PostBuild

Никакие свистопляски с *.def ненужны, при необходимости def генерится автоматом.  Кстати, и DllMain вообще ненужен, его можно смело выкидывать :-) Точнее иногда бывает нужен, но крайне редко и это за гранью потребностей библиотек для MT.

 

Ещё момент - не освещённый в статье, но востребованный.

Если уж C++, то пожалуй должны быть классы и причём с обеих сторон, и в Mql и в С++.

"протаскивание класса C++ в Mql".

1. пишем (или берём готовый) класс :-)

получаем нечто такое :

#ifndef MQLPLUG_H
#define MQLPLUG_H 1
#include "mql45.h"
/** пример класса который "протаскивается" в MT
**/
class Plug {
public:
        Plug();
        ~Plug();

        mql_int OnInit();
        void OnDeinit(mql_int);

        mql_int Sum(mql_int,mql_int);
        mql_double Median(MqlRates *rate);
};
/* так как методы C++ нельзя  "протащить" через DLL, а только функции C
        то и делаются простые функции делегирующие вызов к объекту
*/
MQL_API(Plug *) Plug_New();
MQL_API(void) Plug_Delete(Plug *);
MQL_API(mql_int) Plug_OnInit(Plug *);
MQL_API(void) Plug_OnDeinit(Plug *,mql_int);
MQL_API(mql_int) Plug_Sum(Plug *,mql_int,mql_int);
MQL_API(mql_double) Plug_Median(Plug *,MqlRates *rate);
#endif

2. Реализация "функций-делегатов" тривиальна - первый агрумент это указатель на объект, внутри функции надо проверить его корректность, вызвать метод и перехватить исключения.

/// MqlPlug.cpp
#include "MqlPlug.h"
/*** функции-обёртки методов класса Plug
    все (кроме _New) первым аргументом получают указатель на объёкт
    прочие аргументы - такие же как у метода
    если переданные указатель корректен (не nullptr) то вызывается метод
    и заодно вылавливаются все исключения 
***/
/// конструктор
MQL_API(Plug *)
Plug_New() {
        try {
                return new Plug;
        } catch (...) {
        }
        return nullptr;
}
/// деструктор
MQL_API(void)
Plug_Delete(Plug *plug) {
        try {
                delete plug;
        } catch (...) {
        }
}
// прочие методы
MQL_API(mql_int)
Plug_Sum(Plug *plug,mql_int one,mql_int two) {
        try {
                if (plug) return plug->Sum(one,two);
        } catch (...) {
        }
        return 0;
}
// и так далее

3. И наконец-то Mql ! В директивах импорт описываются функции-делегаты, и пишется класс имеющий единственное поле obj - хандлер (указатль на) объект, и методы вызывающите делегалов

#ifdef __MQL4__
// для 4-ки дескриптор (указатель) 32 бита)
#define HANDLE int
#else
// для 5-ки - 64
#define HANDLE long
#endif

#import "Mql4Plug.dll"
HANDLE Plug_New(void);
void Plug_Delete(HANDLE);
int Plug_OnInit(HANDLE);
void Plug_OnDeinit(HANDLE,const int reason);
int Plug_Sum(HANDLE,int,int);
double Plug_Median(HANDLE,MqlRates &);
#import

class Plug {
public:
   HANDLE obj;
   Plug() {
      obj = Plug_New();
   }
   ~Plug() {
      Plug_Delete(obj);
   }
   int OnInit() {
      if (obj != NULL) {
         return Plug_OnInit(obj);
      }
      return INIT_FAILED;
   }
   void OnDeinit(const int reason) {
      if (obj != NULL) {
         Plug_OnDeinit(obj,reason);
      }
   }
   int Sum(int one,int two) {
      if (obj != NULL) {
         return Plug_Sum(obj,one,two);
      }
      return 0;
   }
   double Median(MqlRates &rates) {
      if (obj!=NULL) {
         return Plug_Median(obj,rates);
      }
      return EMPTY_VALUE;
   }
};

PS/ Как-то раз такое запрашивали и я честно пытался объяснить. Но то-ли педагог из меня так-себе, то-ли визави в программировании был не аллё :-) Но популярные примеры осталась - поэтому делюсь

 

Добрый вечер. Сразу постараюсь на все ответить.

Maxim Kuznetsov:
...

Никакие свистопляски с *.def ненужны, при необходимости def генерится автоматом.  Кстати, и DllMain вообще ненужен, его можно смело выкидывать :-) Точнее иногда бывает нужен, но крайне редко и это за гранью потребностей библиотек для MT.

В статье говорится совершенно не о том, как генерить файл определений, можно, или нет это сделать, какой инструмент для этого использовать. В статье говорится для чего он нужен, для чего может быть применен в VS и что из этого получится. А как его сделать, чем и когда, это не важно.

Насчет DllMain. Теоретически Вы правы. Да, без этой функции можно обойтись. Я знаю инструменты, где DllMain не будет вызываться, даже, если она есть. А вот с категоричностью Вашего вывода, что это "за гранью" я совершенно не согласен. Я убежден, что такой вывод должен сделать сам разработчик, как несущий ответственность за результат. Если ему нужно, он может что то вызвать в DllMain. Не захочет - напишет отдельную экспортируемую функцию. Лично я не чувствую себя достаточно компетентным, что бы вот так запросто взять и лишить его дополнительной возможности.

Maxim Kuznetsov:

Ещё момент - не освещённый в статье, но востребованный.

Если уж C++, то пожалуй должны быть классы и причём с обеих сторон, и в Mql и в С++.

"протаскивание класса C++ в Mql".

Вот совершенно не обязательно, что бы был экспорт классов. )) Спасибо, что Вы упомянули про эту возможность и отдельное спасибо за пример. Лично мне не приходило в голову делать что то подобное и тем более, рекомендовать такую методику начинающим разработчикам. Ну вот посмотрите сами на искусственность приведенного кода, на его не рациональность. И какая необходимость может заставить разработчика обращаться с указателями таким странным образом. Другими словами, если такой код не будет падать, то как теоретический пример он интересен, а с практической точки зрения - вряд ли) Больше скажу, по моему мнению, если у разработчика возникает необходимость в подобном экспорте, то, скорее всего, он очень сильно ошибся с дизайном Помимо этого, обращаю Ваше внимание на то, что где то половина статьи посвящена структурам, а это "почти классы". Стоит остановиться на этом и не идти по стопам некоторых наших коллег с форума, которые с удовольствием засунули бы в MQL весь С++17 ))

 

я с вами и не спорю ;-)

так - некоторые заметки которые вспомнились, глядишь кому и пригодится.

не споря со статьёй

какие такие "начинающие программисты" на стыке двух языков ?

PS/ кстати у вас там память упахана :-)

 
Maxim Kuznetsov:

я с вами и не спорю ;-)

так - некоторые заметки которые вспомнились, глядишь кому и пригодится.

Спасибо, пригодится

но к сожалению вот такие хорошие примеры очень часто лежат в топиках форума, найти очень сложно, увы и сам этим грешу ))))

Если не лень, то в блог бы себе такие отличные примеры складывали бы, хоть какая вероятность, что найдется быстрее, но тоже не факт ((((  -возможности у MQL потрясающие, но иногда быстрее самостоятельно прогуглить и найти готовый пример... да кстати, это форум очень хорошо индексируется гуглом, почти все запросы на тему нейросетей - будут результаты статей MQL ;)

 
Maxim Kuznetsov:

я с вами и не спорю ;-)

так - некоторые заметки которые вспомнились, глядишь кому и пригодится.

не споря со статьёй

какие такие "начинающие программисты" на стыке двух языков ?

PS/ кстати у вас там память упахана

Что там с памятью, я наколбасил где то ?

Начинающий программист это понятие растяжимое ) Вот я совершенно точно начинающий программист в Питоне ) Или в ява скрипте. И много в чем еще я могу назвать себя начинающим. Вот и здесь, ну мало ли какая ситуация, если человек не делал раньше библиотек, а занимался двадцать лет САПРом, или плагины писал к Адобовским прогам? Конечно, он начинающий в новой области, но опытный в своей старой. В прочем ладно, не так уж и важна здесь эта терминология.

 
Спасибо за статью. Планируется ли продолжение для VS 2022 с учетом накопившихся изменений?
 
Пока не планируется