English Русский 中文 Español Deutsch 日本語
Como escrever uma biblioteca DLL em MQL5 (Parte II) em 10 minutos: escrevendo no ambiente do Visual Studio 2017

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

MetaTrader 5Sistemas de negociação |
3 021 10
Andrei Novichkov
Andrei Novichkov

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 Ltd.
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)
Últimos Comentários | Ir para discussão (10)
Andrei Novichkov
Andrei Novichkov | 9 abr. 2019 em 10:58
Maxim Kuznetsov:

Não estou discutindo com você ;-)

então - algumas anotações de que me lembrei, talvez sejam úteis para alguém.

sem discutir com o artigo

que tipo de"programadores novatos" existem na junção de duas linguagens?

PS/ a propósito, você tem muita memória aí.

O que há com a memória, eu errei em algum lugar?

Programador iniciante é um conceito que está se esticando) Eu sou definitivamente um iniciante em Python) ou em java script. E em muitas outras coisas em que posso me considerar um iniciante. Aqui também, que tipo de situação existe, se uma pessoa nunca criou bibliotecas antes, mas está trabalhando com CAD há vinte anos ou escrevendo plugins para programas da Adobe? É claro que ele é um iniciante em um novo campo, mas experiente em seu antigo. De qualquer forma, está tudo bem, essa terminologia não é tão importante aqui.

Genaro Cancino
Genaro Cancino | 26 jul. 2023 em 18:13
Posso criar menus personalizados no Metatrader com uma dll c++?
Andrei Novichkov
Andrei Novichkov | 26 jul. 2023 em 19:11
Não)
Ivan Titov
Ivan Titov | 20 abr. 2024 em 06:47
Obrigado pelo artigo. Há uma sequência planejada para o VS 2022 com o acúmulo de alterações?
Andrei Novichkov
Andrei Novichkov | 20 abr. 2024 em 13:20
Por enquanto, não há planos
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.
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.
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.
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.