English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Sbarazzarsi delle DLL auto-prodotte

Sbarazzarsi delle DLL auto-prodotte

MetaTrader 5Esempi | 11 gennaio 2022, 15:25
146 0
---
---


Scrivi ancora le tue DLL?
Allora siamo qui per te!


Introduzione

Arriva sempre un momento in cui la funzionalità del linguaggio MQL5 non è sufficiente per svolgere le attività. In tal caso, un programmatore MQL5 deve utilizzare strumenti aggiuntivi. Ad esempio, è possibile lavorare con un database, utilizzare socket di comunicazione o utilizzare funzioni del sistema operativo. Un programmatore MQL5 deve anche occuparsi di varie API per espandere le possibilità del programma MQL5 che utilizza. Ma, per diversi motivi, il programmatore non può accedere alle funzioni richieste direttamente da MQL5, in quanto non conosce le seguenti cose:

  • Come trasferire un tipo di dati complesso (ad esempio, struttura) alla funzione API;
  • Come lavorare con il puntatore restituito dalla funzione API.

Pertanto, il programmatore è costretto a utilizzare un linguaggio di programmazione diverso e a creare una DLL intermedia per lavorare con la funzionalità richiesta. Sebbene MQL5 abbia la possibilità di presentare vari tipi di dati e trasferirli su API, sfortunatamente MQL5 non può risolvere il problema relativo all'estrazione dei dati dal puntatore accettato.

In questo articolo punteremo tutte le "i" e mostreremo semplici meccanismi per trasferire e ricevere tipi di dati complessi e lavorare con indici di ritorno.


Contenuti

1. La memoria è tutto

  • Ottenere gli indici
  • Copiare aree di memoria

2. Trasferimento delle strutture alle funzioni API

  • Trasformare le strutture con MQL5
  • Esempio di trasferimento della struttura per prese

3. Utilizzo dei puntatori alle funzioni API

  • Esempi di file di mappatura della memoria,
  • esempio per MySQL

4. Lettura di stringhe con terminazione NULL dalle funzioni API



1. La memoria è tutto

Come forse saprai, qualsiasi variabile (comprese le variabili di tipi di dati complessi) ha il suo indirizzo specifico, dal quale tale variabile viene archiviata in memoria. Questo indirizzo è un valore intero di quattro byte (di tipo int) uguale all'indirizzo del primo byte di questa variabile.

E se tutto è ben definito, è possibile lavorare con quest'area di memoria. La libreria del linguaggio C (msvcrt.dll) contiene la funzione memcpy. Il suo scopo è l'elemento mancante, che lega MQL5 e varie librerie API e offre grandi possibilità per un programmatore.


Passiamo alla conoscenza dei nostri antenati

La funzione Memcpy copia il numero specificato di byte da un buffer a un altro e restituisce il puntatore a un buffer del ricevitore.

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

In altre parole, un'area di memoria con una dimensione di cnt byte a partire dall'indirizzo src viene copiata nell'area di memoria a partire dall'indirizzo dst.

I dati che si trovano all'indirizzo src possono essere di vario tipo. Questo può essere una variabile di byte char, doppio numero di otto byte, array, qualsiasi struttura e qualsiasi volume di memoria. Significa che puoi trasmettere liberamente i dati da un'area all'altra, se conosci indirizzi e dimensioni.


Come funziona

Il diagramma 1 mostra le dimensioni comparative di alcuni tipi di dati.

Dimensioni di vari tipi di dati in MQL5


La funzione Memcpy è necessaria per copiare i dati da un'area di memoria all'altra.
La figura 2 mostra la copia di quattro byte.

Esempio di copia di 4 byte con l'utilizzo di memcpy

In MQL5 apparirà come segue.

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));
}

Va notato che vari tipi di dati (della stessa dimensione cnt) possono essere utilizzati come aree di memoria dst e src. Ad esempio, il puntatore src può fare riferimento alla doppia variabile (cnt=8 byte) e dst può fare riferimento all'array avente la dimensione equivalente char[8] o int[2].

Non importa che idea di memoria ha un programmatore in questo momento. Non importa se si tratta di un array char[8] o solo di una variabile long o di una struttura { int a1; int a2; }.

I dati di memoria possono essere considerati come dati di vario tipo. Ad esempio, è possibile trasferire un array di cinque byte a {int i; char c;} struttura o viceversa. Questa relazione offre l'opportunità di lavorare direttamente con le funzioni API.

Esaminiamo le versioni dell'applicazione memcpy nell'ordine definito.


Ottenere gli indici

Nell'esempio 1 abbiamo mostrato che la funzione memcpy restituisce l'indirizzo della variabile dst.

Questa proprietà può essere utilizzata per ottenere un indirizzo di qualsiasi variabile (inclusi gli array di altri tipi complessi). Per fare ciò, dobbiamo solo specificare la stessa variabile come parametri sorgente e ricevitore. In cnt è possibile trasferire 0, in quanto non è necessaria la copia vera e propria.

Ad esempio, potremmo ottenere l'indirizzo della variabile doppia e dell'array short:

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); 
}

L'indirizzo ricevuto può quindi essere trasferito alla funzione API richiesta o come parametro di struttura e anche come parametro della stessa funzione memcpy.


Copiare gli array

Come sai, un array è un pezzo di memoria dedicato. La dimensione della memoria dedicata dipende dal tipo di elementi e dalla loro quantità. Ad esempio, se il tipo di elementi dell'array short e il numero degli elementi è 10, tale array occupa 20 byte in memoria (poiché la dimensione short è 2 byte).

Ma questi 20 byte sono anche mostrati come array composti da 20 char o 5 int. In ogni caso occupano gli stessi 20 byte di memoria.

Per copiare gli array, è necessario eseguire le seguenti operazioni:

  • Allocare la quantità richiesta di elementi (non inferiore ai byte cnt risultanti) per la memoria dst;
  • Specificare il numero di byte in cnt che devono essere copiati da 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. Trasferimento delle strutture alle funzioni API

Supponiamo di dover trasferire il puntatore della struttura riempita all'API. Il linguaggio MQL5 pone delle limitazioni per trasmettere le strutture. All'inizio dell'articolo ho dichiarato che la memoria può essere presentata in modo diverso. Ciò significa che la struttura richiesta può essere copiata nel tipo di dati supportato da MQL5. In generale, un array è un tipo adatto alle strutture. Pertanto, dovremo ottenere un array da una struttura e quindi trasferire un array alla funzione API.

L'opzione di copiare la memoria utilizzando le strutture è descritta nella sezione della documentazione. Non possiamo usare la funzione memcpy, poiché è impossibile trasferire le strutture come parametri e la copia delle strutture è l'unico modo qui.

La figura 3 mostra la rappresentazione della struttura composta da 5 variabili di diverso tipo e il suo equivalente presentato come array char.

Presentazione della struttura composta da 5 variabili di diverso tipo e il suo equivalente presentato come array char

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; 
}

In un modo così semplice abbiamo copiato la struttura nell'array di byte.

Consideriamo la funzione di creazione socket per rendere questo esempio più pratico.

int connect(SOCKET s, const struct sockaddr *name, int namelen);

In questa funzione il secondo parametro è problematico, in quanto accetta il puntatore per la struttura. Ma sappiamo già cosa farne. Quindi, cominciamo.

1. Scriviamo la funzione di connessione per l'importazione con il metodo consentito in MQL5:

int connect(int s, uchar &name[], int namelen);

2. Osserviamo la struttura richiesta nella documentazione:

struct sockaddr_in
{
  short   sin_family;
  u_short sin_port;
  in_addr sin_addr; // additional 8 byte structure
  char sin_zero[8];
};

3. Creazione di una struttura con un array di dimensioni simili:

struct ref_sockaddr_in
{
  uchar c[2+2+8+8];
};

4. Dopo aver compilato la struttura sockaddr_in richiesta, la trasferiamo nell'array di byte e la inviamo come parametro di connessione.

Di seguito è riportata la sezione del codice realizzata secondo questi passaggi.

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
}

Come puoi vedere, non hai affatto bisogno di creare la tua DLL. Le strutture vengono trasferite direttamente all'API.


3. Utilizzo dei puntatori alle funzioni API

Nella maggior parte dei casi le funzioni API restituiscono un puntatore ai dati: strutture e array. MQL5 non è adatto per estrarre i dati, qui è possibile utilizzare la funzione memcpy.

Esempio di utilizzo di array di memoria da Memory Mapping File (MMF)



Quando si lavora con MMF, viene utilizzata la funzione che restituisce un puntatore a un array di memoria dedicato.

int MapViewOfFile(int hFile, int DesiredAccess, int OffsetHigh, int OffsetLow, int NumOfBytesToMap);

La lettura dei dati da questo array viene eseguita mediante la semplice copia della quantità richiesta di byte tramite la funzione memcpy.
La scrittura dei dati nell'array viene eseguita con lo stesso uso di 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); 
}

Come puoi vedere, non è così difficile lavorare con i puntatori per l'array di memoria. E, soprattutto, non è necessario creare la DLL aggiuntiva per questo.




Esempio di utilizzo di strutture restituite per MySQL

Uno dei problemi urgenti quando si lavora con MySQL è ottenere dati da esso. La funzione mysql_fetch_row restituisce l'array di stringhe. Ogni stringa è un array di campi. Quindi, questa funzione restituisce il puntatore al puntatore. Il nostro compito è estrarre tutti questi dati dal puntatore restituito.

Il compito è un po' complicato dal fatto che i campi sono vari tipi di dati, inclusi quelli binari. Questo significa che sarà impossibile presentarli come array di stringhe. Le funzioni mysql_num_rows, mysql_num_fields, mysql_fetch_lengths vengono utilizzate per ottenere le informazioni sulle stringhe e le dimensioni dei campi.

La figura 4 mostra la struttura di presentazione del risultato in memoria.
Gli indirizzi dell'inizio di tre stringhe vengono raccolti nell'array. E l'indirizzo dell'inizio dell'array (nell'esempio = 94) è ciò che la funzione mysql_fetch_row restituirà.

La struttura della presentazione della richiesta risulta in memoria

Di seguito è riportato l'esempio del codice per ottenere dati da una richiesta di database.

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. Lettura di stringhe con terminazione NULL dalle funzioni API

Alcune funzioni API restituiscono il puntatore alla stringa, ma non ci mostrano la lunghezza di questa stringa. In questa situazione abbiamo a che fare con stringhe che terminano con zero. Questo zero aiuta a determinare la fine della stringa. Ciò significa che la sua dimensione può essere specificata.

Presentazione della stringa con terminazione NULL in memoria

La libreria C (msvcrt.dll) dispone già della funzione che copia il contenuto della stringa con terminazione NULL dal puntatore appropriato a un'altra stringa. La dimensione della stringa è definita dalla funzione. È meglio usare un array di byte come ricevitore, poiché le API spesso restituiscono stringhe multibyte invece di Unicode.

strcpy copia le stringhe con terminazione NULL

char *strcpy(char *dst, const char *src);
dst - the pointer to the destination string
src - the pointer to the Null-terminated source string

In effetti, è un caso speciale di funzione memcpy. Il sistema interrompe la copia sullo zero trovato in una stringa. Questa funzione sarà sempre utilizzata quando si lavora con tali puntatori.

Ad esempio, ci sono diverse funzioni nell'API di MySQL che restituiscono i puntatori alle stringhe. E ottenere dati da loro usando strcpy è un compito banale.

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));
}


Conclusione

Pertanto, l'uso di tre meccanismi di base per lavorare con la memoria (copiare le strutture, ottenere puntatori e i loro dati su memcpy e ottenere stringhe strcpy) copre praticamente tutte le attività quando si lavora con varie funzioni API.

Attenzione Potrebbe non essere sicuro lavorare con memcpy e strcpy, a meno che non sia stata allocata una quantità sufficiente di dati per il buffer del ricevitore. Pertanto, presta attenzione alla dimensione degli importi stanziati per la ricezione dei dati.


Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/364

Chi è chi nella MQL5.community? Chi è chi nella MQL5.community?
Il sito MQL5.com ricorda tutti voi abbastanza bene! Quanti dei tuoi thread sono epici, quanto sono popolari i tuoi articoli e quanto spesso vengono scaricati i tuoi programmi nella Code Base: questa è solo una piccola parte di ciò che viene ricordato su MQL5.com. I tuoi risultati sono disponibili nel tuo profilo, ma per quanto riguarda il quadro generale? In questo articolo mostreremo il quadro generale di tutti i risultati dei membri della MQL5.community.
La trasformazione di Box-Cox La trasformazione di Box-Cox
L'articolo ha lo scopo di far conoscere ai suoi lettori la trasformazione di Box-Cox. Vengono affrontate le problematiche relative al suo utilizzo e vengono forniti alcuni esempi che permettono di valutare l'efficienza della trasformazione con sequenze casuali e quotazioni reali.
Fondamenti di Statistica Fondamenti di Statistica
Ogni trader lavora utilizzando determinati calcoli statistici, anche se è un sostenitore dell'analisi fondamentale. Questo articolo ti guida attraverso i fondamenti della statistica, i suoi elementi di base e mostra l'importanza delle statistiche nel processo decisionale.
Alcuni suggerimenti per i clienti alle prime armi Alcuni suggerimenti per i clienti alle prime armi
Un saggio proverbio spesso attribuito a vari personaggi famosi dice: "Chi non sbaglia non farà mai nulla." A meno che tu non consideri l'ozio stesso un errore, questa affermazione è difficile da discutere. Ma puoi sempre analizzare gli errori passati (tuoi e degli altri) per ridurre al minimo il numero dei tuoi errori futuri. Cercheremo di esaminare le possibili situazioni che si verificano durante l'esecuzione di lavori nel servizio con lo stesso nome.