English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Como trocar dados: um DLL para o MQL5 em 10 minutos

Como trocar dados: um DLL para o MQL5 em 10 minutos

MetaTrader 5Exemplos | 30 dezembro 2013, 11:23
4 067 0
MetaQuotes
Renat Fatkhullin

Por falar nisso, não há muitos desenvolvedores que lembram exatamente como escrever uma simples biblioteca DLL e quais são os recursos dos diferentes sistemas de ligação.

Usando vários exemplos, vou tentar mostrar todo o processo da criação de um simples DLL em 10 minutos, bem como discutir alguns detalhes técnicos da nossa implementação de ligação. Vamos usar o Visual Studio 2005/2008; suas versões Express são gratuitas e podem ser baixadas no site da Microsoft.

1. Criação de um projeto de DLL em C++ no Visual Studio 2005/2008

Execute o Win32 Application Wizard usando o menu 'File -> New', selecione o tipo de projeto como 'Visual C++', escolha o template 'Win32 Console Application' e defina o nome do projeto (por exemplo, 'MQL5DLLSamples'). Escolha um diretório raiz para armazenar 'Location' de projeto, ao invés do padrão oferecido, desative a caixa de opção de 'Create directory for solution' e clique em' OK':

Fig. 1. Win32 Application Wizard, criação de projeto DLL

No próximo passo, pressione "Next" para ir para a página de configurações:

Fig. 2. Win32 Application Wizard, configurações do projeto

Na página final, selecione o tipo de aplicativo 'DLL', deixando outros campos vazios como estão e clique em 'Finish'. Não defina a opção 'Export symbols', se não quiser remover o código de demonstração adicionado automaticamente:

Fig. 3. Win32 Application Wizard, Configuração da aplicação

Como resultado você terá um projeto vazio:

Fig. 4. O projeto DLL vazio preparado pelo Assistente

Para simplificar o teste, é melhor especificar nas opções 'Output Directory' a saída de arquivos DLL diretamente para as '...\MQL5\Libraries' do terminal de cliente - mais adiante, economizará mais tempo:

Fig. 5. Diretório de saída DLL


2. Preparando para adicionar funções

Adicionar macro '_DLLAPI' no final do arquivo stdafx.h, para que você possa descrever funções exportadas de um modo conveniente fácil:

//+------------------------------------------------------------------+
//|                                                 MQL5 DLL Samples |
//|                   Copyright 2001-2010, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net |
//+------------------------------------------------------------------+
#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
#include 

//---
#define _DLLAPI extern "C" __declspec(dllexport)
//+------------------------------------------------------------------+

As funções importadas DLL exigidas no MQL5 devem ter a convenção de chamada stdcall e cdecl. Embora stdcall e cdecl difiram na forma de extração de parâmetros de um pilha, o ambiente runtime do MQL5 pode usar de forma segura ambas versões devido ao wrapper especial das chamadas do DLL.

O compilador C++ utiliza __cdecl de chamada por padrão, mas eu recomendei especificar explicitamente o modo __stdcall para funções exportadas.

Uma função de exportação escrita corretamente deve ter o seguinte formato:

_DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2)
  {
   return(0);
  }

Em um programa MQL5, a função deve ser definida e chamada como a seguir:

#import "MQL5DLLSamples.dll"
int  fnCalculateSpeed(int &res1,double &res2);
#import

//--- call
   speed=fnCalculateSpeed(res_int,res_double);

Após a compilação do projeto, este stdcall será exibido na tabela de exportação como _fnCalculateSpeed@8, onde o compilador adiciona um underline e número de bytes, transmitidos através do empilhamento. Tal decoração permite controlar melhor a segurança das chamadas de funções DLL devido ao fato de que o chamador sabe exatamente quantos (mas não do tipo!) de dados que devem ser colocados no empilhamento.

Se o tamanho final do bloco de parâmetros tiver um erro na descrição de importação da função DLL, a função não será chamada, e a nova mensagem aparecerá no jornal: 'Não foi possível encontrar'CrashTestParametersStdCall' em 'MQL5DLLSamples.dll'. Em tais casos, é necessário verificar cuidadosamente todos os parâmetros tanto no protótipo da função como na fonte DLL.

A busca pela descrição simplificada sem decoração é usada para compatibilidade no caso da tabela de exportação não conter o nome completo da função. Nomes como fnCalculateSpeed são criados se as funções forem definidas no formato__cdecl.
_DLLAPI int fnCalculateSpeed(int &res1,double &res2)
  {
   return(0);
  }


3. Métodos para passar parâmetros e trocar dados

Vamos considerar diversas variantes de parâmetros passados:

  1. Recebimento e passagem de variáveis simples
    O caso das variáveis simples é fácil - elas podem ser passadas pelo valor ou pela referência usando &.
    _DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2)
      {
       int    res_int=0;
       double res_double=0.0;
       int    start=GetTickCount();
    //--- simple math calculations
       for(int i=0;i<=10000000;i++)
         {
          res_int+=i*i;
          res_int++;
          res_double+=i*i;
          res_double++;
         }
    //--- set calculation results
       res1=res_int;
       res2=res_double;
    //--- return calculation time 
       return(GetTickCount()-start);
      }
        
    Chamada do MQL5:
    #import "MQL5DLLSamples.dll"
    int  fnCalculateSpeed(int &res1,double &res2);
    #import
    
    //--- calling the function for calculations
       int    speed=0;
       int    res_int=0;
       double res_double=0.0;
    
       speed=fnCalculateSpeed(res_int,res_double);
       Print("Time ",speed," msec, int: ",res_int," double: ",res_double);
    
    A saída é:
    MQL5DLL Test (GBPUSD,M1) 19:56:42 Time  16  msec, int:  -752584127  double:  17247836076609
  2. Recebimento e passagem de um banco de dados com preenchimento de elementos

    Ao contrário de outros programas MQL5, a passagem de banco de dados é realizada através da referência direta para o buffer de dados sem acesso a informações exclusivas sobre dimensões e tamanhos. é por isso que a dimensão e tamanho do banco de dados devem ser passados separadamente.

    _DLLAPI void __stdcall fnFillArray(int *arr,const int arr_size)
      {
    //--- check for the input parameters
       if(arr==NULL || arr_size<1) return;
    //--- fill array with values
       for(int i=0;i<arr_size;i++) arr[i]=i;
      }
        
    Chamada do MQL5:
    #import "MQL5DLLSamples.dll"
    void fnFillArray(int &arr[],int arr_size);
    #import
    
    //--- call for the array filling
       int    arr[];
       string result="Array: "; 
       ArrayResize(arr,10);
       
       fnFillArray(arr,ArraySize(arr));
       for(int i=0;i<ArraySize(arr);i++) result=result+IntegerToString(arr[i])+" ";
       Print(result);
    
    A saída é:
    MQL5DLL Test (GBPUSD,M1) 20:31:12 Array: 0 1 2 3 4 5 6 7 8 9 
  3. Passagem e modificação de cadeias
    As cadeias unicode são passadas usando referências diretas a seus endereços de buffer sem passar qualquer informação adicional.
    _DLLAPI void fnReplaceString(wchar_t *text,wchar_t *from,wchar_t *to)
      {
       wchar_t *cp;
    //--- parameters check
       if(text==NULL || from==NULL || to==NULL) return;
       if(wcslen(from)!=wcslen(to))             return;
    //--- search for substring
       if((cp=wcsstr(text,from))==NULL)         return;
    //--- replace it
       memcpy(cp,to,wcslen(to)*sizeof(wchar_t));
      }
    
    Chamada do MQL5:
    #import "MQL5DLLSamples.dll"
    void fnReplaceString(string text,string from,string to);
    #import
    
    //--- modify the string
       string text="A quick brown fox jumps over the lazy dog"; 
       
       fnReplaceString(text,"fox","cat");
       Print("Replace: ",text);
    O resultado é:
    MQL5DLL Test (GBPUSD,M1) 19:56:42 Replace:  Uma raposa pequena e marrom pula por cima de um cão preguiçoso
    O resultado é que a linha não tinha mudado! Este é um erro comum de iniciantes quando enviam cópias de objetos (uma cadeia é um objeto), ao invés de referir-se a eles. A cópia da string 'text' foi automaticamente criada que foi modificada no DLL, e depois foi removida automaticamente se afetar a original.

    Para remediar essa situação, é necessário passar uma string por referência. Para fazê-lo, basta modificar o bloco de importação, adicionando & ao parâmetro "texto":
    #import "MQL5DLLSamples.dll"
    void fnReplaceString(string &text,string from,string to);
    #import
    Após a compilação e início vamos obter o resultado correto:
    MQL5DLL Test (GBPUSD,M1) 19:58:31 Replace:  Um gato pequeno e marrom pula por cima de um cão preguiçoso

4. Captura de exceções em funções DLL

Para evitar que o terminal trave, cada chamada DLL é protegida automaticamente pelo Unhandled Exception Wrapping. Este mecanismo permite proteger da maioria dos erros padrão (erros de acesso à memória, divisão por zero, etc.).

Para ver como o mecanismo funciona, vamos criar o seguinte código:

_DLLAPI void __stdcall fnCrashTest(int *arr)
  {
//--- wait for receipt of a zero reference to call the exception
   *arr=0;
  }

e chamá-lo do terminal de cliente:

#import "MQL5DLLSamples.dll"
void fnCrashTest(int arr);
#import

//--- call for the crash (the execution environment will catch the exception and prevent the client terminal crush)
   fnCrashTest(NULL);
   Print("You won't see this text!");
//---

Como resultado, ele vai tentar escrever para o endereço zero e gerar uma exceção. O terminal do cliente vai capturá-lo, registrá-lo ao lançamento e continuar o seu trabalho:

MQL5DLL Test (GBPUSD,M1) 20:31:12 Access violation write to 0x00000000

5. Pacote de chamada DLL e perda de velocidade sobre chamadas

Como já descrito acima, todas as chamadas de funções DLL são envoltas em um pacote especial, a fim de garantir a segurança. Esta ligação mascara o código básico, substitui o empilhamento, suporta acordos stdcall/cdecl e monitora exceções dentro das funções chamadas.

Este volume de trabalhos não leva a um atraso significativo da função chamada.

6. A construção final

Vamos recolher todos os exemplos acima de funções DLL em arquivo 'MQL5DLLSamples.cpp' e exemplos MQL5 no script 'MQL5DLL Test.mq5. O projeto final para o Visual Studio 2008 e o script em MQL5 estão anexos ao artigo.

//+------------------------------------------------------------------+
//|                                                 MQL5 DLL Samples |
//|                   Copyright 2001-2010, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net |
//+------------------------------------------------------------------+
#include "stdafx.h"

//+------------------------------------------------------------------+
//| Passing and receving of simple variables                         |
//+------------------------------------------------------------------+
_DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2)
  {
   int    res_int=0;
   double res_double=0.0;
   int    start=GetTickCount();
//--- simple math calculations
   for(int i=0;i<=10000000;i++)
     {
      res_int+=i*i;
      res_int++;
      res_double+=i*i;
      res_double++;
     }
//--- set calculation results
   res1=res_int;
   res2=res_double;
//--- return calculation time
   return(GetTickCount()-start);
  }
//+------------------------------------------------------------------+
//| Filling the array with values                                    |
//+------------------------------------------------------------------+
_DLLAPI void __stdcall fnFillArray(int *arr,const int arr_size)
  {
//--- check input variables
   if(arr==NULL || arr_size<1) return;
//--- fill array with values
   for(int i=0;i<arr_size;i++) arr[i]=i;
  }
//+------------------------------------------------------------------+
//| The substring replacement of the text string                     |
//| the string is passed as direct reference to the string content   |
//+------------------------------------------------------------------+
_DLLAPI void fnReplaceString(wchar_t *text,wchar_t *from,wchar_t *to)
  {
   wchar_t *cp;
//--- parameters checking
   if(text==NULL || from==NULL || to==NULL) return;
   if(wcslen(from)!=wcslen(to))             return;
//--- search for substring
   if((cp=wcsstr(text,from))==NULL)         return;
//--- replace it 
   memcpy(cp,to,wcslen(to)*sizeof(wchar_t));
  }
//+------------------------------------------------------------------+
//| Call for the crush                                               |
//+------------------------------------------------------------------+
_DLLAPI void __stdcall fnCrashTest(int *arr)
  {
//--- wait for receipt of a zero reference to call the exception
   *arr=0;
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|                                                 MQL5DLL Test.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//---
#import "MQL5DLLSamples.dll"
int  fnCalculateSpeed(int &res1,double &res2);
void fnFillArray(int &arr[],int arr_size);
void fnReplaceString(string text,string from,string to);
void fnCrashTest(int arr);
#import

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- calling the function for calculations
   int    speed=0;
   int    res_int=0;
   double res_double=0.0;

   speed=fnCalculateSpeed(res_int,res_double);
   Print("Time ",speed," msec, int: ",res_int," double: ",res_double);
//--- call for the array filling
   int    arr[];
   string result="Array: "; 
   ArrayResize(arr,10);
   
   fnFillArray(arr,ArraySize(arr));
   for(int i=0;i<ArraySize(arr);i++) result=result+IntegerToString(arr[i])+" ";
   Print(result);
//--- modifying the string
   string text="A quick brown fox jumps over the lazy dog"; 
   
   fnReplaceString(text,"fox","cat");
   Print("Replace: ",text);
//--- and finally call a crash
//--- (the execution environment will catch the exception and prevent the client terminal crush)
   fnCrashTest(NULL);
   Print("You won't see this text!");
//---
  }
//+------------------------------------------------------------------+

Obrigado pelo seu interesse! Estou pronto para responder a quaisquer perguntas.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/18

Arquivos anexados |
mql5dll_test.mq5 (1.83 KB)
mql5dllsamples.zip (4.62 KB)
Troca de dados entre indicadores: é fácil! Troca de dados entre indicadores: é fácil!
Queremos criar um ambiente que fornecesse acesso aos dados de indicadores anexos ao gráfico e que teria as seguintes propriedades: ausência de cópia de dados; modificação mínima do código dos métodos disponíveis, se precisarmos usá-los; preferencialmente código MQL (claro, temos que usar o DLL, mas usaremos apenas algumas strings do código C++). O artigo descreve um método fácil para desenvolver um ambiente de programa para o terminal MetaTrader, que poderia fornecer meios para acessar os buffers dos indicadores de outros programas MQL.
O Histograma de preço (Perfil de mercado) e sua implementação no MQL5 O Histograma de preço (Perfil de mercado) e sua implementação no MQL5
O Perfil de mercado foi desenvolvido pelo pensador realmente brilhante Peter Steidlmayer. Ele sugeriu o uso da representação alternativa de informação sobre movimentos de mercados "horizontais" e "verticais" que levam a um conjunto de modelos completamente diferentes. Ele presumiu que existe um pulso subjacente do mercado ou de um padrão fundamental chamado de ciclo de equilíbrio e desequilíbrio. Neste artigo, considerarei o Histograma de preço - um modelo simplificado de Perfil de mercado e descreverei sua implementação no MQL5.
Desenhando emissões de indicador no MQL5 Desenhando emissões de indicador no MQL5
Neste artigo, consideraremos a emissão dos indicadores - uma nova abordagem para pesquisa de mercado. O cálculo da emissão é baseado na intersecção de diferentes indicadores: mais e mais pontos com diferentes cores e formas aparecem após cada tick. Eles formam vários clusters na forma de uma nebulosa, nuvens, pistas, linhas, arcos, etc. Estas formas podem ajudar a detectar as molas e forças invisíveis que afetam o movimento dos preços do mercado.
Expert Advisor baseado em "New Trading Dimensions" por Bill Williams Expert Advisor baseado em "New Trading Dimensions" por Bill Williams
Neste artigo, discutirei o desenvolvimento do Expert Advisor, baseado no livro "New Trading Dimensions: How to Profit from Chaos in Stocks, Bonds, and Commodities" por Bill Williams. A estratégia em si é bem conhecida e seu uso ainda é controverso entre os negociadores. O artigo considera os sinais de negociação do sistema, os aspectos específicos de sua implementação e os resultados de teste em dados do histórico.