Como escrever uma biblioteca DLL em MQL5 (Parte II) em 10 minutos: escrevendo no ambiente do Visual Studio 2017
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:
- 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.
- 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.
#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.
- 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.
- 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:
- 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
- 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