Discusión sobre el artículo "Cómo escribir una biblioteca DLL para MQL5 en 10 minutos (Parte II): Escribiendo en el entorno de Visual Studio 2017"

 

Artículo publicado Cómo escribir una biblioteca DLL para MQL5 en 10 minutos (Parte II): Escribiendo en el entorno de Visual Studio 2017:

El artículo original «básico» de ningún modo perdió su actualidad, y todos los interesados en este asunto deben leerlo sí o sí. Pero ya pasó bastante tiempo desde aquel entonces, y ahora la versión Visual Studio 2017 es de la actualidad, disponiendo de una interfaz ligeramente modificada, mientras que la propia plataforma MetaTrader 5 tampoco estaba sin desarrollo. El presente artículo describe las etapas de la creación del proyecto DLL, sus configuraciones y el trabajo común con las herramientas del terminal MetaTrader 5.

Creando una biblioteca DLL simple

Este proceso ya fue realizado en el artículo inicial. Aquí vamos a repetirlo considerando las modificaciones acumuladas.

En el entorno de Visual Studio 2017, seleccionamos File -> New -> Project. En la parte izquierda de la ventana que aparece, abrimos la lista de Visual C++ y seleccionamos Windows Desktop, y en la parte media, seleccionamos la línea Windows Desktop Wizard. En la parte inferior hay varios campos de texto donde se puede cambiar el nombre (se recomienda definir su propio y razonable) y la ubicación del proyecto (es mejor dejarlo como sugerido). Todo está listo, pulsamos el botón "ОК" y vamos a la siguiente ventana:


Aquí, es necesario seleccionar Dynamic Link Library (.dll) en la lista desplegable y marcar la casilla "Export Symbols". En realidad, marcar esta casilla es opcional, pero es preferible para los desarrolladores principiantes. En este caso, un código de demostración será añadido a los archivos del proyecto (puede estudiar y eliminarlo después, o incluir comentarios). Hacemos clic en "ОК" para crear los archivos del proyecto que podemos editar después. No obstante, es pronto para hacerlo, vamos a aclararnos con las configuraciones del proyecto. Primero, hay que tener en la mente que MetaTrader 5 trabaja sólo con las bibliotecas de 64 bits. Si intentamos de conectar una de 32 bits, recibiremos los siguientes mensajes:

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

Por consiguiente, será imposible trabajar así.

Autor: Andrei Novichkov

 

Tal vez le sea útil a alguien, últimamente lo he estado haciendo de esta manera:

Es posible (e incluso más fácil) usar CodeLight IDE. En comparación con Studio, es rápido y menos "codicioso", acepta diferentes compiladores, incluyendo el de Microsoft.

A lo extraño, pero DLL utilizando gcc se construye más fácil. En la configuración del proyecto DLL especificar el tulchain necesario (gcc32 bits para MT4, gcc64 para MT5). Y eso es todo. Opcionalmente añada el comando "copiar DLL a la jerarquía MT" a PostBuild

No es necesario juguetear con *.def, def se genera automáticamente si es necesario. Por cierto, DllMain no es necesario en absoluto, puedes tirarlo :-) Para ser más precisos, a veces es necesario, pero muy rara vez y está más allá de las necesidades de las bibliotecas MT.

 

Otro punto - no tratado en el artículo, pero buscado.

Si C ++, entonces tal vez debería haber clases, y en ambos lados, tanto en Mql y C ++.

"arrastrar una clase C++ a Mql".

1. escribir (o tomar una clase ya hecha) :-)

obtener algo como esto :

#ifndef  MQLPLUG_H
#define  MQLPLUG_H 1
#include "mql45.h"
/** ejemplo de una clase que se "arrastra" a 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);
};
/* dado que los métodos C++ no pueden ser "arrastrados" a través de la DLL, sino sólo las funciones C
, se realizan funciones simples delegando la llamada al objeto
*/.
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. La implementación de "función-delegados" es trivial - el primer argumento es un puntero a un objeto, dentro de la función debemos comprobar su corrección, llamar a un método y atrapar excepciones.

/// MqlPlug.cpp
#include "MqlPlug.h"
/*** funciones envolventes de los métodos de la clase Plug
 todas (excepto _New) reciben un puntero al objeto
 como primer argumento. Los demás argumentos son los mismos que para el método
.si el puntero pasado es correcto (no nullptr), el método
 es llamado y al mismo tiempo todas las excepciones 
***/ son capturadas.
/// constructor
MQL_API(Plug *)
Plug_New() {
        try {
                return new Plug;
        } catch (...) {
        }
        return nullptr;
}
/// destructor
MQL_API(void)
Plug_Delete(Plug *plug) {
        try {
                delete plug;
        } catch (...) {
        }
}
// otros métodos
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. y finalmente Mql ! En las directivas de importación describes funciones-delegados, y escribes una clase que tiene un único campo obj - un manejador (puntero a) el objeto, y métodos que llaman a los delegados.

#ifdef __MQL4__
// para 4k el descriptor (puntero) es de 32 bits)
#define  HANDLE int
#else
// para un 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/ Una vez se hizo una solicitud de este tipo y yo honestamente trató de explicarlo. Pero o no era muy buen profesor o mi vis-a-vis en programación no era muy buena :-)) Pero los ejemplos populares quedaron - así que los comparto

 

Buenas tardes. Intentaré contestar a todo enseguida.

Maxim Kuznetsov:
...

No es necesario juguetear con *.def, def se genera automáticamente si es necesario. Por cierto, DllMain es innecesario en absoluto, se puede tirar a la basura :-) Para ser más precisos, a veces es necesario, pero muy raramente y está más allá de las necesidades de las librerías MT.

El artículo no trata de cómo generar un archivo de definición, si se puede o no hacer, qué herramienta utilizar para ello. El artículo habla de para qué se necesita, para qué se puede usar en VS y qué saldrá de él. Y cómo hacerlo, con qué y cuándo, no importa.

Sobre DllMain. Teóricamente tienes razón. Sí, se puede prescindir de esta función. Conozco herramientas en las que no se llama a DllMain aunque esté presente. Pero estoy completamente en desacuerdo con tu conclusión categórica de que está "más allá del límite". Estoy convencido de que esa conclusión debe hacerla el propio desarrollador, ya que él es el responsable del resultado. Si lo necesita, puede llamar a algo en DllMain. Si no quiere, puede escribir una función exportable separada. Personalmente, no me siento lo suficientemente competente como para privarle de esta función adicional tan fácilmente.

Maxim Kuznetsov:

Otro punto - no cubierto en el artículo, pero en la demanda.

Si C ++, entonces tal vez debería haber clases en ambos lados, tanto en Mql y C ++.

"arrastrar una clase C++ a Mql".

No es absolutamente necesario que haya exportación de clases. )) Gracias por mencionar esta característica y gracias especiales por el ejemplo. Personalmente, no se me ocurrió hacer algo así y menos recomendar tal técnica a desarrolladores novatos. Mira la artificialidad de este código y su irracionalidad. Y qué necesidad puede hacer que un desarrollador maneje los punteros de una forma tan extraña. En otras palabras, si este código no se bloquea, es interesante como ejemplo teórico, pero desde el punto de vista práctico - difícilmente) Diré más, en mi opinión, si un desarrollador necesita tal exportación, lo más probable es que haya cometido un error con el diseño. Además, me gustaría llamar su atención sobre el hecho de que alrededor de la mitad del artículo está dedicado a las estructuras, y estas son "casi clases". Merece la pena detenerse aquí y no seguir los pasos de algunos compañeros del foro que meterían con gusto todo C++17 en MQL )).

 

No estoy discutiendo contigo ;-)

solo algunos apuntes que me han venido a la mente, tal vez a alguien le sean de utilidad.

sin polemizar con el artículo

¿qué tipo de"programadores novatos" hay en la confluencia de dos lenguajes?

PD/ por cierto, tienes mucha memoria :-)

 
Maxim Kuznetsov:

No estoy discutiendo contigo ;-)

así que - algunas notas que me acordé, tal vez sea útil para alguien.

Gracias, será útil.

pero por desgracia tan buenos ejemplos son muy a menudo en los temas del foro, es muy difícil encontrarlos, por desgracia, yo peco a mí mismo )))).

Si usted no es perezoso, usted debe poner tan excelentes ejemplos en tu blog, por lo menos hay alguna probabilidad de que usted los encontrará más rápido, pero tampoco es un hecho (((( - MQL tiene posibilidades increíbles, pero a veces es más rápido para google usted mismo y encontrar un ejemplo listo ... por cierto, este foro está muy bien indexado por google, casi todas las consultas sobre el tema de las redes neuronales se traducirá en artículos MQL ;)

 
Maxim Kuznetsov:

No estoy discutiendo contigo ;-)

así que - algunas notas que me acordé, tal vez sea útil para alguien.

sin discutir con el artículo

¿qué tipo de"programadores novatos" hay en la confluencia de dos lenguajes?

PD/ por cierto, tienes mucha memoria ahí.

¿Qué pasa con la memoria, me he equivocado en algún sitio?

Programador principiante es un concepto que se estira ) Definitivamente soy un principiante en Python ) O en java script. Y muchas otras cosas en las que puedo llamarme principiante. Aquí también, ¿qué tipo de situación hay, si una persona no ha hecho bibliotecas antes, pero ha estado haciendo CAD durante veinte años, o escribiendo plugins para programas de Adobe? Por supuesto, es un principiante en un campo nuevo, pero experimentado en el antiguo. De todos modos, está bien, esta terminología no es tan importante aquí.

 
¿Puedo crear menús personalizados dentro de Metatrader con una dll de c++?
 
No)
 
Gracias por el artículo. Hay una secuela prevista para VS 2022 con la acumulación de cambios?
 
Por ahora no hay planes