
Como trocar dados: um DLL para o MQL5 em 10 minutos
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:
- 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
- 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
- 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
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Bom artigo.
Mas uma pergunta: Como posso chamar uma função MQ5 a partir da DLL?
Como posso chamar uma função MQ5 a partir da DLL? Além disso, criei uma thread via CreateThread e executei uma janela na DLL, ela funciona bem, mas quando descarrego o MQ5, a janela da dll pode ser fechada, mas o MT4 trava...
O que devo fazer a seguir?
Desde já, obrigado.
Hi
Eu tive esse erro
Resolvi o problema mudando para 64 bits
Hi,
Obrigado por seu excelente trabalho!
Como sou iniciante nessa área, ficarei muito feliz se você me disser se essa DLL me ajuda ou não.
O que eu quero fazer é exportar dados (on-line) do MT5 para meu aplicativo C++, fazer algumas análises sobre eles e, em seguida, enviar o resultado como comandos de compra/venda para o MT5 a partir do meu aplicativo. Você pode me orientar sobre como posso atingir esse objetivo?