文章 "10 分钟掌握 MQL5 的 DLL(第二部分):使用 Visual Studio 2017 创建"

 

新文章 10 分钟掌握 MQL5 的 DLL(第二部分):使用 Visual Studio 2017 创建已发布:

初版文章依然具有其相关性,因此如果您对此主题感兴趣,请务必阅读第一篇文章。 从初版起已经过了很久时间,而当前的 Visual Studio 2017 具有全新的界面。 MetaTrader 5 平台也拥有了诸多新功能。 本文提供了开发 DLL 项目各个阶段的描述,以及如何设置 DLL 并与 MetaTrader 5 工具进行交互。

创建一个简单的 DLL

整个过程已在初版文章中有所描述。 如今我们再次研究软件的更新和变化。

运行 Visual Studio 2017,并导航到文件 -> 新建 -> 项目。 在新项目窗口的左侧,展开 Visual C++ 列表,然后从中选择 Windows 桌面。 在中间部分选择 Windows 桌面向导那一行。 使用底部的输入字段,您可以编辑项目名称(建议您设置有意义的名称),并设置项目位置(推荐保留建议值)。 单击“确定”,然后继续下一个窗口:


从下拉列表中选择动态链接库(.dll),然后选中“导出符号”。 勾选此项是可选的,但建议初学者这样做。 在这种情况下,演示代码将添加到项目文件中。 这段代码可以查看,之后删除或注释。 单击“确定”将创建项目文件,然后可以对其进行编辑。 不过,我们先要考虑项目设置。 首先,请记住,MetaTrader 5 仅可协同 64 位函数库操作。 如果您尝试连接 32 位 DLL,您将收到以下消息:

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

因此,您将无法使用此函数库。

作者:Andrei Novichkov

 

也许这对某人会有用,最近我一直是这么做的:

使用 CodeLight IDE 是可行的(甚至更简单)。与 Studio 相比,它不仅速度快,而且不那么 "贪婪",它接受不同的编译器,包括微软的编译器。

奇怪的是,使用 gcc 编译 DLL 更容易。在 DLL项目 的设置中指定必要的 tulchain(MT4 为 gcc32 位,MT5 为 gcc64)。仅此而已。可选择在 PostBuild 中添加 "将 DLL 复制到 MT 层次结构 "命令。

无需处理 *.def,如有必要,def 会自动生成。 顺便说一下,DllMain 完全没有必要,你可以把它扔掉 :-)更确切地说,有时需要它,但很少,而且它超出了 MT 库的需要。

 

还有一点--文章中没有涉及,但大家都想知道。

如果是 C++,那么也许应该有类,而且在 Mql 和 C++ 两种语言中都有。

"将 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;
}
// и так далее

最后是 Mql!在导入 指令中描述了函数委托,并编写了一个类,该类只有一个字段 obj - 对象的处理程序(指针),以及调用委托的方法。

#ifdef __MQL4__
// 4k 的描述符(指针)为 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 中调用。如果他不想这样做,可以编写一个单独的可导出函数。就我个人而言,我觉得自己没有足够的能力轻易剥夺他的这一附加功能。

马克西姆-库兹涅佐夫

还有一点--文章中没有涉及,但有需求。

如果是 C++,那么也许应该在 Mql 和 C++ 两种语言中都有类。

"将 C++ 类拖入 Mql"。

绝对没有必要导出类。))感谢您提到这一功能,并特别感谢您提供的示例。就我个人而言,我从未想过要做这样的事情,更不会向新手开发者推荐这样的技术。看看这段代码的人为性和不合理性。是什么原因让开发人员以如此奇怪的方式处理指针呢?换句话说,如果这段代码不会崩溃,那么作为一个理论示例它是有趣的,但从实践角度来看--几乎不可能)我想多说几句,在我看来,如果开发人员需要这样的导出,那么他很有可能在设计上犯了错误。 此外,我想提请大家注意的是,这篇文章大约有一半的篇幅都在讨论结构,而这些结构 "几乎都是类"。我们应该就此打住,不要步论坛上某些同行的后尘,他们很乐意将所有 C++17 都塞进 MQL 中))).

 

我不是在和你争论;-)

只是想到了一些注意事项,也许有人会觉得有用。

不与文章争论

在两种语言的交界处,会有什么样的"新手程序员"?

顺便说一句,你的记忆力真好:-)

 
Maxim Kuznetsov:

我没跟你争论;-)

所以--一些我记得的笔记,也许会对某人有用。

谢谢,会有用的。

但遗憾的是,这样的好例子通常都在论坛主题中,很难找到,唉,我自己也罪过 ))))。

如果您不懒惰,您应该把这些优秀的例子放到您的博客中,至少您有可能更快地找到它们,但这也不是事实 (((( - MQL 具有惊人的可能性,但有时自己谷歌一下,找到一个现成的例子会更快...顺便说一下,本论坛的谷歌索引非常好,几乎所有关于神经网络主题的查询都能找到 MQL 文章;)

 
Maxim Kuznetsov:

我没跟你争论;-)

所以--我想起了一些注意事项,也许对某些人有用。

不与文章争论

在两种语言的交界处,会有什么样的"新手程序员"?

顺便说一句,你的内存很大。

内存怎么了,我是不是哪里弄错了?

新手程序员是个很牵强的概念 ) 我绝对是 Python 的新手 ) 或者是 java 脚本的新手。还有很多其他东西,我都可以称自己是初学者。在这里,如果一个人以前没有做过程序库,但却做了二十年的 CAD 或为 Adobe 程序编写插件,会是什么情况呢?当然,他在新领域是初学者,但在老领域经验丰富。总之,没关系,这些术语在这里并不重要。

 
我能否使用 c++ dll 在 Metatrader 中创建自定义菜单?
 
无)
 
感谢您的文章。是否计划为 VS 2022 编写续集,以处理积压的更改?
 
暂无计划