English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Eliminação de DLLs autogeradas

Eliminação de DLLs autogeradas

MetaTrader 5Exemplos | 10 março 2014, 16:06
976 0
---
---


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

1. Memória é tudo

  • 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.

Tamanhos de vários tipos de dados em MQL5


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.

Exemplo de cópia de 4 bytes com o uso de memcpy

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).

Apresentação da estrutura que consiste em 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á.

A estrutura de apresentação do resultado de pedido em memória

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.

Apresentação de uma cadeia de terminação NULL (nula) em memória

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? Quem é quem na MQL5.community?
O site MQL5.com o lembra muito bem disso! Quantos dos seus tópicos são épicos, quão popular são os seus artigos e quantas vezes seus programas na base do código são baixados - esta é apenas uma pequena parte do que é lembrado em MQL5.com. Suas realizações estão disponíveis no seu perfil, mas e o quadro geral? Neste artigo, vamos mostrar o quadro geral de todas as conquistas do membros da MQL5.community.
A transformação Box-Cox A transformação Box-Cox
O artigo é destinado a familiarizar seus leitores com a transformação Box-Cox. As questões relacionadas ao seu uso são abordadas e alguns exemplos são fornecidos para avaliar a eficiência da transformação com sequências aleatórias e cotas reais.
Fundamentos de estatística Fundamentos de estatística
Todo negociante trabalha usando cálculos estatísticos, mesmo se apoia a análise fundamental. Este artigo o leva através dos fundamentos da estatística, seus elementos básicos e mostra a importância da estatística na tomada de decisão.
Algumas dicas para clientes iniciantes Algumas dicas para clientes iniciantes
Um provérbio que é geralmente atribuído a diversas pessoas famosas diz: "Aquele que não comete erros nunca faz nada." A menos que você considere a própria inatividade um erro, é difícil argumentar contra esta afirmação. Mas você sempre pode analisar os erros anteriores (os seus e os dos outros) para minimizar o número dos seus erros futuros. Tentaremos analisar possíveis situações que surgem ao executar empregos no serviço de mesmo nome.