
Sbarazzarsi delle DLL auto-prodotte
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
- 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.
La funzione Memcpy è necessaria per copiare i dati da un'area di memoria all'altra.
La figura 2 mostra la copia di quattro byte.
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.
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à.
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.
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





- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso