![Eliminação de DLLs autogeradas](https://c.mql5.com/i/articles/overlay.png)
Eliminação de DLLs autogeradas
Você ainda escreve suas DLLs?
Então isso vai para você!
Introdução
Sempre chega um momento quando a linguagem funcional do MQL5 não é suficiente para realização de tarefas. Neste caso, um programador MQL5 tem que usar ferramentas adicionais. Por exemplo, é possível trabalhar com uma base de dados, utilizar soquetes de comunicação ou utilizar funções de sistema de operação. Um programador MQL5 também tem que lidar com várias APIs para expandir as possibilidades do programa MQL5 que usa. Mas por várias razões, o programador não pode acessar as funções necessárias diretamente do MQL5, pois ele não sabe o seguinte:
- Como transferir tipo de dados complexos (por exemplo, estrutura) à função API;
- Como trabalhar com o ponteiro que é retornado pela função API.
Portando, o programador é forçado a usar uma linguagem de programação diferente e criar uma DLL intermediária para trabalhar com a funcionalidade necessária. Embora o MQL5 tenha a possibilidade de apresentar vários tipos de dados e transferí-los à API, infelizmente o MQL5 não pode resolver a questão levando em consideração a extração de dados do ponteiro aceito.
Neste artigo vamos pontuar todos os "i"s e mostrar mecanismos simples de transferência e de recebimento de tipos de dados complexos e trabalhar com indicadores de retorno.
Conteúdo
- Obtenção de indicadores
- Cópia de áreas de memória
2. Transferência de estruturas a funções de API
- Transformação de estruturas utilizando MQL5
- Exemplo de transferência de estrutura para soquetes
3. Trabalho com ponteiros de funções de API
- Exemplos para arquivo de mapeamento de memória,
- Exemplo para MySQL
4. Leitura NULL(nula)-cadeia de caracteres da funções API
1. Memória é tudo
Como você sabe, qualquer variável (incluindo variáveis de tipos de dados complexos) tem seu endereço específico, a partir do qual a variável é armazenada na memória. Este endereço é um valor inteiro de quatro bytes (do tipo int) igual ao endereço do primeiro byte desta variável.
E se tudo está bem definido, é possível trabalhar com esta área de memória. Biblioteca de linguagem C (msvcrt.dll) contém função memcpy. Sua finalidade é o elemento que falta, que liga o MQL5 e várias bibliotecas API e oferece grandes possibilidades para um programador.
Vamos voltar ao conhecimento dos nossos antepassados, a função
Memcpy copia o número especificado de bytes de um buffer para outro e retorna o ponteiro a buffer receptor.
void *memcpy(void *dst, const void *src, int cnt); dst - pointer to the receiver buffer src - pointer to the source buffer cnt - number of bytes for copying
Em outras palavras, uma área de memória com um tamanho de bytes cnt começando a partir do endereço src é copiada à área de memória começando a partir do endereço dst.
Os dados localizados no endereço src podem ser de vários tipos. Isso pode ser variável de caractere de um byte, número duplo de bytes, matriz, qualquer estrutura e qualquer volume de memória. Isto significa que você pode transmitir livremente os dados de uma área para outra, se você souber endereços e um tamanho.
Como funciona
O diagrama 1 mostra os tamanhos comparativos de alguns tiposde dados.
A função memcpy é necessária para copiar os dados de uma área de memória à outra.
A figura 2 mostra a cópia de quatro bytes.
No MQL5, será como a seguir.
Example 1. Using memcpy #import "msvcrt.dll" int memcpy(int &dst, int &src, int cnt); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { int dst, src=4, cnt=sizeof(int); int adr=memcpy(dst, src, cnt); Print("dst value="+string(dst)+" Address dst="+string(adr)); }
Deve notar-se que vários tipos de dados (do mesmo tamanho de cnt) podem ser usados como áreas de memória dst e apontados para src. Por exemplo, o ponteiro src pode se referir à variável dupla (cnt=8 bytes) e dst pode se referir à matriz tendo o tamanho equivalente de char[8] ou int[2].
Não importa para a memória, que ideia o programador tem sobre isso no momento. Não importa, se é uma matriz de char[8] ou apenas uma variável longa ou estrutura { int a1; int a2; }.
Os dados da memória podem ser considerados como dados de vários tipos. Por exemplo, é possível transferir matriz de cinco bytes à estrutura {int i; char c;} ou vice-versa. Esta relação fornece uma oportunidade de trabalhar diretamente com as funções API.
Vamos examinar versões de aplicativos memcpy na ordem definida.
Obtenção de indicadores
No exemplo 1 mostramos que a função memcpy retorna endereço de variável dst.
Esta propriedade pode ser usada para obter um endereço de qualquer variável (incluindo as matrizes de outros tipos complexos). Para fazer isso, precisamos apenas especificar a mesma variável como uma fonte e parâmetros receptores. Em cnt é possível transferir 0, pois a cópia real não é necessária.
Por exemplo, podemos obter o endereço de uma variável dupla e matriz curta:
Example 2. Getting pointers to the variable #import "msvcrt.dll" int memcpy(short &dst[], short &src[], int cnt); int memcpy(double &dst, double &src, int cnt); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { short src[5]; //--- getting src array address (i.е., the address of the first element) int adr=memcpy(src, src, 0); double var; //--- getting var variable address adr=memcpy(var, var, 0); }
Endereço recebido, então pode ser transferido para à função API requerida ou como um parâmetro de estrutura e também como um parâmetro da mesma função memcpy.
Cópia de matrizes
Como se sabe, uma matriz é algum bloco de memória dedicada. O tamanho da memória dedicada depende do tipo de elementos e da sua quantidade. Por exemplo, se o tipo de elementos da matriz curta e o número dos elementos forem 10, tal matriz ocupa 20 bytes na memória (pois o tamanho curto é de 2 bytes).
Mas esses 20 bytes também são mostrados como matrizes compostas de 20 char ou 5 int. Em qualquer caso, elas ocupam os mesmos 20 bytes na memória.
Para copiar as matrizes, é necessário fazer o que segue:
- Distribua a quantidade necessária dos elementos (não inferior ao resultado de bytes cnt) para memória dst;
- Especifique o número de bytes em cnt que devem ser copiados de src.
Example 3. Copying the arrays #import "msvcrt.dll" int memcpy(double &dst[], double &src[], int cnt); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { double src[5]; //--- calculating the number of bytes!!! int cnt=sizeof(double)*ArraySize(src); double dst[]; ArrayResize(dst, 5); //--- the array has been copied from src to dst memcpy(dst, src, cnt); }
2. Transferência de estruturas a funções de API
Supondo que você precise transferir o ponteiro de estrutura cheio à API. A linguagem MQL5 estabelece limitações para transmitir as estruturas. No início do artigo, afirmei que a memória pode ser apresentada de forma diferente. Isso significa que a estrutura necessária pode ser copiada ao tipo de dados suportados pelo MQL5. Em geral, uma matriz é um tipo que é apropriada para as estruturas. Portanto, teremos que obter uma matriz de uma estrutura e então transferir uma matriz à função API.
A opção de copiar a memória utilizando as estruturas está descrita na seção documentação . Não podemos utilizar função memcpy, pois é impossível de transferir as estruturas como parâmetros e copiá-las é a única forma aqui.
A figura 3 mostra a representação da estrutura que consiste de 5 variáveis de diferentes tipos e seu equivalente apresentado como matriz char (caractere).
Example 4. Copying the structures by means of MQL5 struct str1 { double d; // 8 bytes long l; // 8 bytes int i[3]; // 3*4=12 bytes }; struct str2 { uchar c[8+8+12]; // str1 structure size }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { str1 src; src.d=-1; src.l=20; //--- filling the structure parameters ArrayInitialize(src.i, 0); str2 dst; //--- turning the structure into the byte array dst=src; }
Em tal maneira simples copiamos a estrutura na matriz de byte.
Vamos considerar a função de criação de soquete para tornar este exemplo mais prático.
int connect(SOCKET s, const struct sockaddr *name, int namelen);
Nesta função, o segundo parâmetro é problemático, pois ele aceita o ponteiro para a estrutura. Mas já sabemos o que fazer com isso. Então, vamos começar.
1. Vamos escrever função de conexão para importar pelo método permitido no MQL5:
int connect(int s, uchar &name[], int namelen);
2. Vamos observar a estrutura necessária na documentação:
struct sockaddr_in { short sin_family; u_short sin_port; in_addr sin_addr; // additional 8 byte structure char sin_zero[8]; };
3. Criação de uma estrutura com uma matriz de tamanho similar:
struct ref_sockaddr_in { uchar c[2+2+8+8]; };
4. Após o preenchimento da estrutura sockaddr_in necessária, a transferimos à matriz de byte e enviamos como parâmetro de conexão.
Abaixo está a seção de código feita de acordo com estes passos.
Example 5. Referring of the client socket to the server #import "Ws2_32.dll" ushort htons(ushort hostshort); ulong inet_addr(char &cp[]); int connect(int s, char &name[], int namelen); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- connecting the host after the socket initialization char ch[]; StringToCharArray("127.0.0.1", ch); //--- preparing the structure sockaddr_in addrin; addrin.sin_family=AF_INET; addrin.sin_addr=inet_addr(ch); addrin.sin_port=htons(1000); //--- copying the structure to the array ref_sockaddr_in ref=addrin; //--- connecting the host res=connect(asock, ref.c, sizeof(addrin)); //--- further work with the socket }
Como pode ser visto, você não precisa fazer a sua DLL de jeito nenhum. As estruturas são transferidas à API diretamente.
3. Trabalho com ponteiros de funções de API
Na maioria dos casos funções API retornam um ponteiro aos dados: estruturas e matrizes. O MQL5 não é apropriado para extrair os dados, a função memcpy pode ser utilizada aqui.
Exemplo de trabalhar com matrizes de memória a partir do Arquivo mapeado em memória (MMF)
Ao trabalhar com MMF, a função é usada, a qual retorna um ponteiro à uma matriz de memória dedicada.
int MapViewOfFile(int hFile, int DesiredAccess, int OffsetHigh, int OffsetLow, int NumOfBytesToMap);
Leitura de dados a partir desta matriz é executada simplesmente copiando a quantidade necessária de bytes pela função memcpy .
Escrever os dados na matriz é desempenhado pelo mesmo uso de memcpy.
Example 6. Recording and reading data from MMF memory #import "kernel32.dll" int OpenFileMappingW(int dwDesiredAccess, int bInheritHandle, string lpName); int MapViewOfFile(int hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap); int UnmapViewOfFile(int lpBaseAddress); int CloseHandle(int hObject); #import "msvcrt.dll" int memcpy(uchar &Destination[], int Source, int Length); int memcpy(int Destination, int &Source, int Length); int memcpy(int Destination, uchar &Source[], int Length); #import #define FILE_MAP_ALL_ACCESS 0x000F001F //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- opening the memory object int hmem=OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, "Local\\file"); //--- getting pointer to the memory int view=MapViewOfFile(hmem, FILE_MAP_ALL_ACCESS, 0, 0, 0); //--- reading the first 10 bytes from the memory uchar src[10]; memcpy(src, view, 10); int num=10; //--- recording the 4 byte int number to the memory beginning memcpy(view, num, 4); //--- unmapping the view UnmapViewOfFile(view); //--- closing the object CloseHandle(hmem); }
Como você pode ver, não é tão difícil de trabalhar com ponteiros para a matriz de memória. E mais importante, você não precisa criar sua DLL adicional para isso.
Exemplo de trabalho com estruturas retornadas para MySQL
Um dos problemas urgentes ao trabalhar com MySQL tem sido obter dados dele. A função mysql_fetch_row retorna a matriz de cadeias. Cada série é uma matriz de campos. Então, a função retorna o ponteiro ao ponteiro. Nossa tarefa é extrair todos estes dados a partir do ponteiro retornado.
A tarefa é um pouco complicada pelo fato de que os campos são de vários tipos de dados, incluindo os binários. Isso significa que será impossível apresentá-los como matriz de cadeia. As funções mysql_num_rows, mysql_num_fields, mysql_fetch_lengths são usadas para obter informações sobre os tamanhos de campo e cadeias.
A Figura 4 mostra a estrutura de apresentação do resultado em memória.
Os endereços do início de três cadeias estão reunidos na matriz. E o endereço do início da matriz (no exemplo = 94) é o que a função mysql_fetch_row retornará.
Abaixo está o exemplo do código para a obtenção de dados a partir de um pedido de banco de dados.
Example 7. Getting data from MySQL #import "libmysql.dll" int mysql_real_query(int mysql, uchar &query[], int length); int mysql_store_result(int mysql); int mysql_field_count(int mysql); uint mysql_num_rows(int result); int mysql_num_fields(int result); int mysql_fetch_lengths(int result); int mysql_fetch_row(int result); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ... preliminarily initialized mysql data base //--- request for getting all the strings from the table string query="SELECT * FROM table"; uchar aquery[]; StringToCharArray(query, aquery); //--- sending the request err=mysql_real_query(mysql, aquery, StringLen(query)); int result=mysql_store_result(mysql); //--- in case it contains the strings if (result>0) { ulong num_rows=mysql_num_rows(result); int num_fields=mysql_num_fields(result); //--- getting the first string pointer int r=0, row_ptr=mysql_fetch_row(result); while(row_ptr>0) { //--- getting the pointer to the current string columns lengths int len_ptr=mysql_fetch_lengths(result); int lens[]; ArrayResize(lens, num_fields); //--- getting the sizes of the string fields memcpy(lens, len_ptr, num_fields*sizeof(int)); //--- getting the data fields int field_ptr[]; ArrayResize(field_ptr, num_fields); ArrayInitialize(field_ptr, 0); //--- getting the pointers to the fields memcpy(field_ptr, row_ptr, num_fields*sizeof(int)); for (int f=0; f<num_fields; f++) { ArrayResize(byte, lens[f]); ArrayInitialize(byte, 0); //--- copy the field to the byte array if (field_ptr[f]>0 && lens[f]>0) memcpy(byte, field_ptr[f], lens[f]); } r++; //--- getting the pointer to the pointer to the next string row_ptr=mysql_fetch_row(result); } } }
4. Leitura NULL(nula)-cadeia de caracteres da funções API
Algumas funções API retornam o ponteiro à cadeia, mas não nos mostram o comprimento desta cadeia. Nesta situação, lidamos com cadeias que terminam em zero. Este zero, ajuda a determinar o fim da cadeia. Isto significa que o seu tamanho pode ser especificado.
A biblioteca C (msvcrt.dll) já possui uma função que copia o conteúdo da cadeia de terminação NULL do ponteiro apropriado à uma outra cadeia. O tamanho da cadeia é definido pela função. é melhor usar uma matriz de bytes como um receptor, pois APIs muitas vezes retornam cadeias multibyte em vez de unicode.
strcpy - cópias de cadeias de terminação NULL
char *strcpy(char *dst, const char *src); dst - the pointer to the destination string src - the pointer to the Null-terminated source string
Na verdade, é um caso especial de função memcpy. O sistema para a cópia ao encontrar zero em uma cadeia. Esta função sempre será usada ao trabalhar com tais ponteiros.
Por exemplo, há muitas funções em API a partir de MySQL que retornará os ponteiros às cadeias. E obter dados a partir destes utilizando strcpy é uma tarefa.
Example 8. Getting the strings from the pointers #import "libmysql.dll" int mysql_init(int mysql); int mysql_real_connect(int mysql, uchar &host[], uchar &user[], uchar &password[], uchar &DB[], uint port, uchar &socket[], int clientflag); int mysql_get_client_info(); int mysql_get_host_info(int mysql); int mysql_get_server_info(int mysql); int mysql_character_set_name(int mysql); int mysql_stat(int mysql); #import "msvcrt.dll" int strcpy(uchar &dst[], int src); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { uchar byte[]; ArrayResize(byte, 300); int ptr; string st; //--- pointer to the string ptr=mysql_get_client_info(); if (ptr>0) strcpy(byte, ptr); Print("client_info="+CharArrayToString(byte)); //--- initializing the base int mysql=mysql_init(mysql); //--- transferring the strings to the byte arrays uchar ahost[]; StringToCharArray("localhost", ahost); uchar auser[]; StringToCharArray("root", auser); uchar apwd[]; StringToCharArray("", apwd); uchar adb[]; StringToCharArray("some_db", adb); uchar asocket[]; StringToCharArray("", asocket); //--- connecting the base int rez=mysql_real_connect(mysql, ahost, auser, apwd, adb, port, asocket, 0); //--- determining the connection and the base status ptr=mysql_get_host_info(mysql); if (ptr>0) strcpy(byte, ptr); Print("mysql_host_info="+CharArrayToString(byte)); ptr=mysql_get_server_info(mysql); if (ptr>0) strcpy(byte, ptr); Print("mysql_server_info="+CharArrayToString(byte)); ptr=mysql_character_set_name(mysql); if (ptr>0) strcpy(byte, ptr); Print("mysql_character_set_name="+CharArrayToString(byte)); ptr=mysql_stat(mysql); if (ptr>0) strcpy(byte, ptr); Print("mysql_stat="+CharArrayToString(byte)); }
Conclusão
Assim, o uso de três mecanismos básicos de trabalho com a memória (copiar as estruturas, obter ponteiros e seus dados em memcpy e obter cadeias strcpy), abrangem praticamente todas as tarefas ao trabalhar com várias funções API.
Aviso. Pode não ser seguro trabalhar com memcpy e strcpy, a menos que uma quantidade suficiente de dados tenha sido distribuída para o buffer do receptor. Portanto, tenha cuidado com o tamanho das quantias distribuídas para recebimento de dados.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/364
![Quem é quem na MQL5.community?](https://c.mql5.com/2/0/whoiswho.png)
![A transformação Box-Cox](https://c.mql5.com/2/0/Cox-Box-transformation_MQL5.png)
![Fundamentos de estatística](https://c.mql5.com/2/0/statistic.png)
![Algumas dicas para clientes iniciantes](https://c.mql5.com/2/0/MQL5_Job_Service_Recommendations.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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