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

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий
MetaQuotes Software Corp.
Модератор
207468
MetaQuotes Software Corp.  

Опубликована статья Как за 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

Maxim Kuznetsov
19203
Maxim Kuznetsov  

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

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

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

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

Maxim Kuznetsov
19203
Maxim Kuznetsov  

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

Если уж 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/ Как-то раз такое запрашивали и я честно пытался объяснить. Но то-ли педагог из меня так-себе, то-ли визави в программировании был не аллё :-) Но популярные примеры осталась - поэтому делюсь

Andrei Novichkov
7923
Andrei Novichkov  

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

Maxim Kuznetsov:
...

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

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

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

Maxim Kuznetsov:

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

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

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

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

Maxim Kuznetsov
19203
Maxim Kuznetsov  

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

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

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

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

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

Igor Makanu
7494
Igor Makanu  
Maxim Kuznetsov:

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

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

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

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

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

Andrei Novichkov
7923
Andrei Novichkov  
Maxim Kuznetsov:

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

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

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

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

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

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

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

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий