Como escrever uma biblioteca DLL em MQL5 (Parte II) em 10 minutos: escrevendo no ambiente do Visual Studio 2017

13 maio 2019, 09:05
Andrei Novichkov
0
648

Introdução

Este é a continuação de um artigo escrito anteriormente sobre como criar uma DLL usando o Visual Studio 2005/2008. Esse texto básico inicial não perdeu sua importância e todos os interessados neste tópico simplesmente devem lê-lo. Mas já se passou muito tempo desde então, e agora o Visual Studio 2017 com uma nova interface está à frente, também a própria plataforma MetaTrader 5 vem se desenvolvendo e segue em frente. Obviamente, há uma necessidade de atualizar os conhecimentos, de analisar novos recursos e ajustar os antigos. É isso o que faremos agora, considerando desde a criação de um projeto DLL no Visual Studio 2017 até a conexão da DLL finalizada ao terminal e o trabalho com ela.

O artigo é destinado a desenvolvedores iniciantes que desejam dominar a criação e conexão de bibliotecas escritas em C++ com o terminal.

Qual o sentido disso tudo?

Existe uma opinião entre os desenvolvedores de que não é necessário conectar nenhuma biblioteca ao terminal, de que simplesmente não existem tarefas que requerem tal conexão, de que tudo pode ser feito usando ferramentas MQL. Até certo ponto, esta opinião é verdadeira. De fato, existem poucas tarefas que requerem conexão de bibliotecas. E, sim, muitas dessas tarefas podem ser resolvidas usando ferramentas MQL, e muitas vezes vemos exemplos disso. Além disso, ao aplicar uma biblioteca, é necessário ter em mente que o EA ou indicador que usa essa biblioteca estará operacional somente se ela estiver disponível. Se o desenvolvedor quiser transferir essa ferramenta para um terceiro, será preciso transferir dois arquivos: a própria ferramenta e a biblioteca usada pela ferramenta. Isso pode ser muito incômodo e às vezes até impossível. Além disso, as bibliotecas podem ser inseguras e conter códigos nocivos.

Apesar do acima mencionado, podem-se observar benefícios ao usar bibliotecas - eles definitivamente superam as desvantagens. Por exemplo:

  • Somente com bibliotecas de terceiros é possível resolver as tarefas que a MQL não pode resolver, por exemplo, executar o envio de emails e anexar um arquivo a cada mensagem, escrever no Skype e assim por diante.
  • Executar tarefas que podem ser executadas usando ferramentas MQL, mas, com mais rapidez e eficiência, por exemplo, analisar páginas HTML, trabalhar com expressões regulares.

É claro que, se um desenvolvedor quiser aprender como lidar com tarefas tão complexas, ele deve dominar a criação, a conexão e o trabalho com bibliotecas no nível exigido.

Agora, tendo considerado todos os prós e contras de usar bibliotecas em nossos projetos, começaremos a realizar o processo de criação de uma DLL no Visual Studio 2017 passo a passo.

Criando uma DLL simples

Esta travessia já foi totalmente concluída no artigo inicial, aqui vamos repeti-la, considerando as mudanças acumuladas.

No ambiente do Visual Studio 2017, selecionamos File -> New -> Project. Na janela que aparece, à esquerda, abrimos a lista do Visual C++ e selecionamos Windows Desktop e, na parte intermediária, selecionamos a linha Windows Desktop Wizard. Na parte inferior existem vários campos de entrada onde você pode alterar o nome (é recomendável definir seu próprio) e a localização do projeto (é melhor deixá-lo como sugerido). Tudo está pronto, clicamos em "OK" e vamos para a próxima janela:


Aqui é preciso selecionar Dynamic Link Library (.dll) na lista suspensa e marcar o item "Export Symbols". Na verdade, marcar este item é opcional, mas é de preferência para desenvolvedores iniciantes. Neste caso, um código de demonstração é adicionado aos arquivos do projeto, eles podem ser visualizados e depois excluídos ou comentados. Clicamos no botão "OK" para criar os arquivos de projeto, que podemos editar a seguir. No entanto, é muito cedo para fazer isso antes de descobrirmos as configurações do projeto. Em primeiro lugar, é preciso lembrar que o MetaTrader 5 só funciona com bibliotecas de 64 bits. Se tentarmos anexar uma de 32 bits, obteremos as seguintes mensagens:

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

Por conseguinte, é impossível trabalhar assim.

O mesmo se aplica ao MetaTrader 4, mas o oposto, isto é, são necessárias bibliotecas de 32 bits e é impossível anexar as de 64 bits. Vale lembrar disso para não fazer trabalho extra.

Agora continuamos com as configurações do projeto em si. Selecionamos no menu "Project" o item "Name Properties...", onde "Name" é o nome do projeto escolhido pelo desenvolvedor na etapa de criação. Como resultado, obtemos uma janela com diferentes configurações. A primeira coisa a fazer é ativar o suporte a Unicode. Na parte esquerda da janela selecionamos o item "General", e na linha direita com o título na primeira coluna: "Character Set". Em seguida, uma lista suspensa fica disponível na segunda coluna, nela devemos selecionar "Use Unicode Character Set". Em alguns casos, pode-se fazer sem o suporte do Unicode, mas isso será discutido posteriormente.

A cópia da biblioteca já pronta para a pasta "Library" do terminal é outra modificação muito útil (mas não necessária) das propriedades do projeto. No artigo inicial para isso, era recomendado alterar o parâmetro "Output Directory", que está na mesma janela do elemento "General" do projeto. Neste Visual Studio 2017, isso não é necessário. Este parâmetro deve ser deixado inalterado, mas preste atenção no elemento drop-down "Build Events" na janela esquerda e selecione o subelemento "Post Build Events". Na primeira coluna da janela da direita, aparece o parâmetro "Command Line", que ao ser selecionado dá acesso à lista suspensa na segunda coluna, que pode ser editada. A lista deve conter uma lista de ações que o Visual Studio 2017 executa após a criação da biblioteca. A esta lista adicionamos esta linha:

xcopy "$(TargetDir)$(TargetFileName)" "E:\...\MQL5\Libraries\" /s /i /y

Aqui, em vez do ponto, deve estar o caminho completo para a pasta do terminal correspondente. Agora, se a construção da biblioteca for concluída com sucesso, ela é copiada para o local especificado. Nesse caso, todos os arquivos no "Output Directory" permanecem no lugar, o que pode ser importante se o desenvolvedor trabalhar com sistemas de controle de versão, por exemplo:

A é etapa de configuração do projeto é último o estágio, ela é muito importante. Imagine que a biblioteca já esteja construída e nela haja uma função que o terminal possa usar. Assuma que esta função seja como esse protótipo:

int fnExport(wchar_t* t);
No script do terminal, essa função pode ser chamada da seguinte maneira:
#import "Project2.dll"
int fnExport(string str);
#import

No entanto, ao tentar fazer isso, será recebida a seguinte mensagem de erro:

O que fazer nesta situação? Observe que o Visual Studio 2017 gera uma macro ao gerar o código da biblioteca:

#ifdef PROJECT2_EXPORTS
#define PROJECT2_API __declspec(dllexport)
#else
#define PROJECT2_API __declspec(dllimport)
#endif

Todo o protótipo da nossa função é assim:

PROJECT2_API int fnExport(wchar_t* t);

Depois de compilar a biblioteca, vejamos como fica a tabela de exportação:


Para visualizar, basta selecionar o arquivo com a biblioteca na janela "Total Commander" e pressionar F3. Observe como fica o nome da função exportada. Agora editamos a macro que demos acima (é assim que foi feito no artigo inicial):

#ifdef PROJECT2_EXPORTS
#define PROJECT2_API extern "C" __declspec(dllexport)
#else
#define PROJECT2_API __declspec(dllimport)
#endif

Inserção

extern "C"

Ao inserir isso, é indicado o uso de uma geração de assinatura de função simples (no estilo da linguagem C) ao receber arquivos-objeto. Em particular, isso proíbe o compilador C++ de "decorar" o nome da função com símbolos adicionais ao exportar para uma DLL. Repetimos a compilação e vemos novamente como fica a tabela de exportação:

As alterações na tabela de exportação são óbvias e o erro ao chamar a função do script desapareceu. No entanto, do meu ponto de vista, este método tem uma desvantagem, isto é, é preciso editar o script criado pelo compilador. Existe uma maneira mais segura de obter os mesmos resultados, embora um pouco mais demorada:

Arquivo de definição

Este é um arquivo de texto simples, geralmente com um nome que corresponde ao nome do projeto e tem a extensão def. Ou seja, este caso, é o arquivo Project2.def. Esse arquivo é criado num bloco de notas regular, de maneira nenhuma no Word e em editores semelhantes. O conteúdo do arquivo fica assim:

; PROJECT2.def : Declares the module parameters for the DLL.

LIBRARY      "PROJECT2"
DESCRIPTION  'PROJECT2 Windows Dynamic Link Library'

EXPORTS
    ; Explicit exports can go here
        fnExport @1
        fnExport2 @2
        fnExport3 @3
        ....

Primeiro, o título e, em seguida, apenas uma lista de funções exportadas. Símbolos como @ 1, @ 2, etc. indicam a ordem de funções desejada na biblioteca. Este arquivo deve ser salvo na pasta do projeto.

Criamos este arquivo e ligamos ao projeto. Na janela de propriedades do projeto, na janela esquerda, selecionamos o elemento drop-down "Linker" e seu subelemento "Input", e no parâmetro à direita escolhemos "Module Definition File". Assim como nos casos anteriores, obtemos acesso à lista editável, aonde adicionamos o nome do arquivo: "Project2.def". Clicamos no botão "OK" e repetimos a compilação. Obtemos o mesmo resultado da última captura de tela. O nome não é decorado e não há erros ao chamar a função. Após lidar com as configurações do projeto, podemos começar a escrever o código da biblioteca em si.

Criando uma biblioteca e DllMain

O artigo inicial abordou totalmente as questões de intercâmbio de dados e chamadas para várias funções da DLL, por isso não vamos nos deter nisso novamente. No entanto, é preciso prestar atenção a determinados pontos e, para isso, criamos um código simples na biblioteca:

1. Adicionamos uma função à exportação (e não esqueçamos de editar o arquivo de definição):

PROJECT2_API int fnExport1(void) {
        return GetSomeParam();
}

2. Criamos e adicionamos o arquivo de cabeçalho Header1.h ao projeto e atribuímos outra função a ele:

const int GetSomeParam();
3. Modificamos o arquivo dllmain.cpp:
#include "stdafx.h"
#include "Header1.h"

int iParam;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
                iParam = 7;
                break;
    case DLL_THREAD_ATTACH:
                iParam += 1;
                break;
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

const int GetSomeParam() {
        return iParam;
}

O lógica deste código deve ser clara: à biblioteca é adicionada uma variável cujo resultado é calculado na função DllMain e pode ser acessada usando a função fnExport1. Chamamos a função no script:

#import "Project2.dll"
int fnExport1(void);
#import
...
void OnStart() {
Print("fnExport1: ",fnExport1() );

Recebemos a seguinte entrada:

fnExport1: 7

Isso sugere que essa parte do código no DllMain não é executada:

    case DLL_THREAD_ATTACH:
                iParam += 1;
                break;

Quão importante é isso? Do meu ponto de vista, é extremamente importante, porque se o desenvolvedor colocar uma parte do código de inicialização da biblioteca nessa ramificação na esperança de que ela seja executada quando a biblioteca estiver anexada à thread, seus cálculos não serão justificados. Além disso, não será registrado nenhum erro, o que, sem dúvida, complicará sua busca.

Strings

Como trabalhar com strings é indicado no artigo inicial. Este trabalho não é difícil, mas há um ponto que precisa ser esclarecido.

Criamos uma função simples na biblioteca (e editamos o arquivo de definição):

PROJECT2_API void SamplesW(wchar_t* pChar) {
        size_t len = wcslen(pChar);
        wcscpy_s(pChar + len, 255, L" Hello from C++");
}
Chamamos essa função no script:
#import "Project2.dll"
void SamplesW(string& pChar);
#import

void OnStart() {

string t = "Hello from MQL5";
SamplesW(t);
Print("SamplesW(): ", t);

Esperávamos receber esta mensagem:

SamplesW(): Hello from MQL5 Hello from C++

Alteramos a chamada de função:

#import "Project2.dll"
void SamplesW(string& pChar);
#import

void OnStart() {

string t;
SamplesW(t);
Print("SamplesW(): ", t);

Agora recebemos uma mensagem de erro assustadora:

Access violation at 0x00007FF96B322B1F read to 0x0000000000000008

Inicializamos a string que passamos para a função de biblioteca e repetimos a execução do script:

string t="";

A mensagem de erro desapareceu, novamente, obtemos a saída esperada:

SamplesW():  Hello from C++

Do exposto, podemos concluir: as strings passadas para as funções exportadas pela biblioteca devem ser inicializadas!

Aqui chegamos à questão que prometemos abordar ao discutir o uso do Unicode. Se não se planeja passar strings para a DLL, como no último exemplo, podemos fazer isso sem suporte a Unicode. No entanto, é melhor incluir esse suporte de qualquer maneira, pois as assinaturas das funções exportadas podem mudar, novas podem aparecer e o desenvolvedor pode simplesmente esquecer que não há suporte a Unicode.

Transmissão e recepção de arrays de símbolos não tem nenhuma característica particular, ela foi discutida no artigo inicial, e aqui não vamos nos deter nela.

Estruturas

Definimos a estrutura mais simples na biblioteca e no script:

//Na dll:
typedef struct E_STRUCT {
        int val1;
        int val2;
}ESTRUCT, *PESTRUCT;

// No script MQL:
struct ESTRUCT {
   int val1;
   int val2;
};

Adicionamos uma função para trabalhar com essa estrutura na biblioteca:

PROJECT2_API void SamplesStruct(PESTRUCT s) {
        int t;
        t = s->val2;
        s->val2 = s->val1;
        s->val1 = t;
}

A partir do código, fica claro que a função simplesmente realiza a troca usual de seus próprios campos.

Chamamos a função do script:

#import "Project2.dll"
void SamplesStruct(ESTRUCT& s);
#import
....
ESTRUCT e;
e.val1 = 1;
e.val2 = 2;
SamplesStruct(e);
Print("SamplesStruct: val1: ",e.val1," val2: ",e.val2);

Executamos o script e obtemos um resultado previsível:

SamplesStruct: val1: 2 val2: 1

O objeto foi passado para a função chamada de acordo coma referência, a função processou o objeto e retornou-o ao código de chamada.

No entanto, nem sempre é possível restringir-se ao trabalho com estruturas tão simples. Complicamos a tarefa adicionando outro campo de um tipo diferente à estrutura:

typedef struct E_STRUCT1 {
        int val1;
        char cval;
        int val2;
}ESTRUCT1, *PESTRUCT1;

Também adicionamos uma função para trabalhar com ela:

PROJECT2_API void SamplesStruct1(PESTRUCT1 s) {
        int t;
        t = s->val2;
        s->val2 = s->val1;
        s->val1 = t;
        s->cval = 'A';
}

A função, como a anterior, troca seus campos do tipo int e atribui um valor ao campo do tipo char. Chamamos essa função num script (exatamente da mesma maneira que a função anterior). Nós recebemos inesperadamente uma entrada de log deste tipo:

SamplesStruct1: val1: -2144992512 cval: A val2: 33554435

Claramente, os campos do tipo int contêm lixo. Nós não recebemos lixo aleatório, dados incorretos. O que aconteceu? O problema é o alinhamento! "Alinhamento" é um conceito não tão simples, mas também não é dos mais complexos. Na documentação, existe a seção pack dedicada a estruturas e descreve com algum detalhe o que isso é. Quanto ao alinhamento no ambiente do Visual Studio C++, também existe bastante material dedicado ao alinhamento.

Em nosso exemplo, a origem do erro é que a biblioteca e o script possuem alinhamentos diferentes e, portanto, o script "apanha lixo". Existem duas maneiras de resolver o problema:

  1. Especificar o novo alinhamento no script. Para isso existe o atributo pack(n). Tentamos alinhar a estrutura com o campo de magnitude máxima, ou seja, com int:
    struct ESTRUCT1 pack(sizeof(int)){
            int val1;
            char cval;
            int val2;
    };
    
    Em seguida, repetimos a saída executando o script. Agora a entrada de log muda: SamplesStruct1: val1: 3 cval: A val2: 2 . O problema está resolvido.

  2. Especificar um novo alinhamento na biblioteca. Por padrão, as estruturas em MQL possuem o alinhamento pack(1), é preciso aplicar o mesmo na biblioteca da seguinte forma:
    #pragma pack(1)
    typedef struct E_STRUCT1 {
            int val1;
            char cval;
            int val2;
    }ESTRUCT1, *PESTRUCT1;
    #pragma pack()
    
    Construímos a biblioteca, executamos o script novamente e obtemos o resultado correto, o mesmo que ao usar o primeiro método.
Verificamos mais uma coisa. O que acontece se, além dos campos de dados, na estrutura houver métodos? Isso é bem possível. Por exemplo, o desenvolvedor pode adicionar um construtor (embora isso não seja um método), um destrutor, ou algo mais segundo seu propósito. Conferimos isso em tal estrutura na biblioteca:
#pragma pack(1)
typedef struct E_STRUCT2 {
        E_STRUCT2() {
                val2 = 15;
        }
        int val1;
        char cval;
        int val2;
}ESTRUCT2, *PESTRUCT2;
#pragma pack()
Essa estrutura é usada pela seguinte função:
PROJECT2_API void SamplesStruct2(PESTRUCT2 s) {
        int t;
        t = s->val2;
        s->val2 = s->val1;
        s->val1 = t;
        s->cval = 'B';
}
Fazemos as alterações apropriadas no script:
struct ESTRUCT2 pack(1){
        ESTRUCT2 () {
           val1 = -1;
           val2 = 10;
        }
        int val1;
        char cval;
        int f() { int val3 = val1 + val2; return (val3);}
        int val2;
};

#import "Project2.dll" 
void SamplesStruct2(ESTRUCT2& s); 
#import
...
ESTRUCT2 e2;
e2.val1 = 4;
e2.val2 = 5;
SamplesStruct2(e2);
t = CharToString(e2.cval);
Print("SamplesStruct2: val1: ",e2.val1," cval: ",t," val2: ",e2.val2);

Observe que o método f() foi adicionado à estrutura para que haja mais diferenças da estrutura na biblioteca. Executamos o script e obtemos esta entrada no log: SamplesStruct2:  val1: 5 cval: B val2: 4  Está tudo bem! A presença de um construtor e um método adicional em nossa estrutura não tem efeito sobre o resultado.

Última experiência Da estrutura removemos o construtor e o método no script, deixamos apenas os campos de dados e mantemos a estrutura na biblioteca inalterada. Executamos o script novamente e obtemos o mesmo resultado. Agora podemos concluir que a presença de métodos adicionais em estruturas não afeta o resultado de forma alguma.

O projeto desta biblioteca para o Visual Studio 2017 e o script para o MetaTrader 5 estão nos arquivos anexados ao artigo.

Sobre o que não deve ser feito

Ao trabalhar com bibliotecas DLL, existem limitações, que são descritas na documentação. Não vamos repetir aqui o que está escrito na documentação. Aqui temos apenas um exemplo:

struct BAD_STRUCT {
   string simple_str;
};

Essa estrutura não pode ser passada para a dll. Pois nós apenas encapsulamos a strings (uma linha!) com a ajuda de uma estrutura! Além disso, é impossível passar objetos mais complexos para a dll sem receber uma exceção.

Sobre o que fazer quando algo não deve ser feito

Simplesmente, existem muitos casos em que é necessário transferir para a dll um objeto, que é proibido transferir - estrutura com objetos dinâmicos, array de engrenagens, etc. O que fazer neste caso? Se o desenvolvedor não tiver acesso ao código da biblioteca, ele precisará recusar essa solução. A situação é completamente diferente, se esse acesso estiver disponível.

Nós não consideraremos a situação da mudança no design de dados, devemos tentar resolver o problema com os meios disponíveis e, ao fazê-lo, não obter uma exceção. Quer dizer, como o artigo não é destinado a usuários experientes, aqui vamos delinear apenas possíveis soluções para o problema, e deixaremos a escrita do código real para o exercício e auto-aperfeiçoamento dos leitores.

  1. A capacidade de usar a função StructToCharArray(), tornando possível escrever algo assim no script:
    struct Str 
      {
         ...
      };
    
    Str s;
    uchar ch[];
    StructToCharArray(s,ch);
    
    SomeExportFunc(ch);
    
    E também no arquivo cpp da biblioteca:
    #pragma pack(1)
    typedef struct D_a {
    ...
    }Da, *PDa;
    #pragma pack()
    
    void SomeExportFunc(char* pA)
      {
            PDa = (PDa)pA;
            ......
      }
    
    Ao deixar entre colchetes a segurança e a qualidade de tal código, notamos imediatamente a inutilidade do próprio método: StructToCharArray() só funciona com estruturas PODe essas estruturas podem ser transferidas para bibliotecas sem conversões adicionais. Repare que o uso dessa função na vida real não foi verificado por mim.

  2. Escrever um próprio empacotador/descompactador de estruturas num objeto que possa ser transferido para a biblioteca. Existe uma maneira, mas obviamente muito complicada e trabalhosa. No entanto, isso nos leva a uma solução completamente adequada:

  3. Todos os objetos que não podem ser transferidos diretamente para a biblioteca devem ser empacotados numa sequência JSON no script e descompactados em estruturas na biblioteca, e vice versa. As ferramentas necessárias para fazer isso existem. Analisadores para JSON estão disponíveis para C++, C# e MQL. É possível usar este recurso se você dedicar tempo ao empacotamento/descompactação. Mas as vantagens são óbvias. Pode-se trabalhar com estruturas (e não apenas com estruturas) de alta complexidade e, se necessário, pode-se, em vez de escrever um empacotador/descompactador do zero, modificar um já existente, o que é obviamente mais simples.

Assim, teremos em mente que é possível transferir/receber um objeto complexo para/de uma biblioteca do mesmo jeito.

Aplicação prática

Apliquemos os conhecimentos adquiridos na prática e criemos uma biblioteca útil. Por exemplo, uma que envia mensagens. Observe alguns pontos:

  • A biblioteca não pode ser usada para enviar spam.
  • A biblioteca não necessariamente envia mensagens só do endereço e do servidor especificado nas configurações do terminal. Na verdade, nas configurações do terminal, o uso de correio pode ser desabilitado, não afetando o trabalho da biblioteca.

Finalmente, a maior parte do código C++ não é minha, ela é baixada dos fóruns da Microsoft. Este é um exemplo muito antigo e testado, cujas variantes também estão no VBS.

Comecemos, criamos um projeto no Visual Studio 2017 e alteramos suas configurações conforme descrito no início do artigo. Criamos um arquivo de definição e o ligamos ao projeto. Nós teremos uma única função exportada:

SENDSOMEMAIL_API bool  SendSomeMail(LPCWSTR addr_from,
        LPCWSTR addr_to,
        LPCWSTR subject,
        LPCWSTR text_body,

        LPCWSTR smtp_server,
        LPCWSTR smtp_user,
        LPCWSTR smtp_password);

Brevemente expliquemos seus argumentos:

  • addr_from, addr_to — endereços de e-mail do remetente e do destinatário.
  • subject, text_body — assunto e mensagem em si.
  • smtp_server, smtp_user, smtp_password — endereço do servidor SMTP, nome de usuário neste servidor e senha.

Observemos o seguinte:

  • A descrição dos argumentos indica que, para enviar e-mail, é preciso ter uma conta no servidor de e-mail e saber seu endereço. Portanto, é impossível que o remetente seja anônimo.
  • No código da biblioteca, o número da porta é hard-coded protegida. Este é o número de porta padrão vinte e cinco (25).
  • A biblioteca recebe os dados necessários, se comunica com o servidor e envia mensagens para ele. Durante uma chamada, pode-se enviar e-mail para apenas um endereço. Se o desenvolvedor quiser repetir o envio, a chamada de função terá que ser repetida com um novo endereço.

O código C++ em si não é mostrado aqui. Ele (e todo o projeto) pode ser encontrado no projeto SendSomeMail.zip anexado. Deixe-me apenas dizer que o objeto usado CDO tem muitos recursos e pode (e deve) ser usado para o desenvolvimento e aprimoramento da biblioteca.

Além deste projeto, vamos escrever um script simples para chamar a função de biblioteca (ela está localizada no arquivo SendSomeMail.mq5 anexado):

#import "SendSomeMail.dll"
bool  SendSomeMail(string addr_from,string addr_to,string subject,string text_body,string smtp_server,string smtp_user,string smtp_password);
#import

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   bool b = SendSomeMail("XXX@XXX.XX", "XXXXXX@XXXXX.XX", "hello", "hello from me to you","smtp.XXX.XX", "XXXX@XXXX.XXX", "XXXXXXXXX");
   Print("Send mail: ", b);
   
  }

Em vez dos caracteres "X", o desenvolvedor tem que substituir seus valores, porque, pois não consigo converter as informações da sua conta. Isso conclui o desenvolvimento. Depois de substituir os dados por uns corretos e possivelmente fazer adições ao código, a biblioteca pode ser usada.

Fim do artigo

Ao usar as informações do artigo inicial e considerar as novas contidas neste, o desenvolvedor pode rapidamente dominar o básico e passar para projetos mais complexos e interessantes.

No final, gostaria de me debruçar sobre um fato interessante que pode ser de grande importância em certas situações, isto é, e se houver necessidade de proteger o código na dll? A solução padrão é usar um empacotador. Há muitos empacotadores diferentes, e muitos deles podem fornecer um bom nível de proteção. Acontece que descobri que eu tenho dois: Themida 2.4.6.0 e VMProtect Ultimate v. 3.0.9 . Aplicamos esses empacotadores e compactamos nosso primeiro e mais simples Project2.dll em duas variantes para cada empacotador. Depois disso, usando o script que temos, chamamos as funções exportadas no terminal. Tudo funciona! O terminal pode trabalhar com essas bibliotecas, o que, no entanto, não pode garantir o funcionamento normal das bibliotecas cobertas por outros empacotadores. O Project2.dll empacotado em duas versões está no arquivo anexado Project2_Pack.zip.

Isso é tudo. Sucessos e boa sorte no trabalho.

Programas utilizados no artigo:

 # Nome
Tipo
 Descrição
1 Project2.zip Arquivo
Projeto dll simples
2
Project2.mq5
Script
Script para trabalhar com dll
3 SendSomeMail.zip Arquivo Projeto dll para enviar e-mail
4 SendSomeMail.mq5 Script
Script para trabalhar com a biblioteca SendSomeMail.dll
5 Project2_Pack.zip Arquivo Project2.dll empacotada por Themida e VMProtect




Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/5798

Arquivos anexados |
Project2.mq5 (3.42 KB)
SendSomeMail.mq5 (1.15 KB)
SendSomeMail.zip (16.62 KB)
Project2_Pack.zip (4645.25 KB)
Project2.zip (18.65 KB)
Indicadores MTF como ferramenta de análise técnica Indicadores MTF como ferramenta de análise técnica

A maioria de nós concorda com a opinião de que o processo de análise da situação atual do mercado começa com uma revisão dos períodos gráficos maiores, o que acontece até passarmos para o gráfico em que fazemos trading. Esta análise é uma das condições para uma negociação bem-sucedida e uma abordagem profissional. O artigo discute indicadores multiperíodo, formas de criá-los com exemplos de código MQL5. Além disso, avalia as desvantagens e vantagens de cada versão e propõe uma nova abordagem de indicadores usando o modo MTF.

Usando os recursos computacionais do MATLAB 2018 no MetaTrader 5 Usando os recursos computacionais do MATLAB 2018 no MetaTrader 5

Depois da atualizar o pacote MATLAB em 2015, é necessário considerar a maneira moderna de criar bibliotecas DLL. Como o exemplo de um indicador preditivo, o artigo ilustra os recursos de vinculação do MetaTrader 5 e do MATLAB usando versões modernas de plataformas de 64 bits. Ao analisar toda a sequência de conexão do MATLAB, o desenvolvedor MQL5 criará rapidamente aplicativos com recursos computacionais avançados, evitando riscos.

Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte III). Coleção de ordens e posições de mercado, busca e ordenação Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte III). Coleção de ordens e posições de mercado, busca e ordenação

Na primeira parte, começamos a criar uma grande biblioteca multi-plataforma, simplificando o desenvolvimento de programas para as plataformas MetaTrader 5 e MetaTrader 4. Além disso, nós implementamos a coleção do histórico de ordens e negócios. Nosso próximo passo é criar uma classe para uma seleção conveniente e a ordenação de ordens, negócios e posições nas listas de coleção. Nós vamos implementar o objeto da biblioteca base chamada Engine e adicionar uma coleção de ordens e posições de mercado para a biblioteca.

Visualização do histórico de negociação multimoeda em relatórios em HTML e CSV Visualização do histórico de negociação multimoeda em relatórios em HTML e CSV

Como é sabido, desde seu lançamento, o MetaTrader 5 vem oferecendo testes multimoedas. Essa função é procurada pela maioria dos traders, mas, infelizmente, não é tão universal quanto gostaríamos. O artigo apresenta vários programas para traçar gráficos usando objetos gráficos baseados no histórico de negociação a partir de relatórios nos formatos HTML e CSV. A negociação de vários instrumentos pode ser analisada em paralelo em várias sub-janelas, ou numa só janela usando a comutação dinâmica realizada pelo usuário.