
Getting Rid of Self-Made DLLs
Hala DLL'lerinizi yazıyor musunuz?
O zaman size gidelim!
Giriş
Her zaman MQL5 dil işlevinin görevleri yerine getirmek için yeterli olmadığı bir an gelir. Bu durumda, MQL5 programcısı ek araçlar kullanmak zorundadır. Örneğin, bir veritabanı ile çalışmak, iletişim soketlerini kullanmak veya işletim sistemi işlevlerini kullanmak mümkündür. MQL5 programcısı, kullandığı MQL5 programının olanaklarını genişletmek için çeşitli API'larla da ilgilenmek zorundadır. Ancak çeşitli nedenlerden dolayı programcı, aşağıdaki hususları bilmediği için gerekli işlevlere doğrudan MQL5'ten erişemez:
- Karmaşık bir veri türünün (örneğin yapı) API işlevine nasıl aktarılacağı;
- API işlevi tarafından döndürülen işaretçiyle nasıl çalışılacağı.
Bu nedenle, programcı farklı bir programlama dili kullanmaya ve gerekli işlevsellik ile çalışmak için bir ara DLL oluşturmaya mecbur kalır. MQL5'in çeşitli veri türlerini sunma ve API'a aktarma olanağı olmasına rağmen, ne yazık ki MQL5, kabul edilen işaretçiden veri ayıklama ile ilgili sorunu çözemez.
Bu makalede, tüm "i"leri işaretleyeceğiz ve karmaşık veri türlerini aktarmanın ve almanın ve getiri endeksleriyle çalışmanın basit mekanizmalarını göstereceğiz.
İçindekiler
- Endeksleri Alma
- Bellek alanlarını kopyalama
2. Yapıları API İşlevlerine Aktarma
- MQL5 kullanarak yapıları dönüştürme
- Soketler için yapı aktarma örneği
3. API İşlevi İşaretçileriyle Çalışma
- Bellek Eşleme Dosyası Örnekleri,
- MySQL örneği
4. API İşlevlerinden NULL ile Sonlandırılmış Dizeleri Okuma
1. Bellek, Her Şeydir
Bildiğiniz gibi, herhangi bir değişken (karmaşık veri türü değişkenler dahil), o değişkenin bellekte depolandığı kendi özel adresine sahiptir. Bu adres, bu değişkenin ilk baytının adresine eşit (int türünde) dört baytlık bir tamsayı değeridir.
Ve her şey iyi tanımlanmışsa, bu bellek alanı ile çalışmak mümkündür. C dil kitaplığı (msvcrt.dll) memcpy işlevini içerir. Amacı, MQL5 ve çeşitli API kitaplıklarını birbirine bağlayan ve bir programcı için büyük olanaklar sağlayan eksik öğedir.
Atalarımızın Bilgisine Dönelim
Memcpy işlevi, belirtilen sayıda baytı bir arabellekten diğerine kopyalar ve işaretçiyi bir alıcı arabelleğine döndürür.
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
Diğer bir deyişle, src adresinden başlayan cnt bayt boyutundaki bir bellek alanı, dst adresinden başlayan bellek alanına kopyalanır.
src adresinde bulunan veriler çeşitli türlerde olabilir. Bu, char bir baytlık değişken, double sekiz baytlık sayı, dizi, herhangi bir yapı ve herhangi bir bellek hacmi olabilir. Bu, adresleri ve boyutu biliyorsanız, verileri bir alandan diğerine serbestçe aktarabileceğiniz anlamına gelir.
Nasıl Çalışır?
Diyagram 1'de, bazı veri türlerinin karşılaştırmalı boyutları gösterilmiştir.
Verileri bir bellek alanından diğerine kopyalamak için Memcpy işlevi gereklidir.
Şekil 2'de, dört baytın kopyalanması gösterilmiştir.
Bu, MQL5'te aşağıdaki gibi görünecektir.
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)); }
Çeşitli veri türlerinin (aynı cnt boyutunda) dst ve src noktasındaki bellek alanları olarak kullanılabileceğine dikkat edilmelidir. Örneğin, src işaretçisi double değişkenine (cnt=8 bayt) ve dst, char[8] veya int[2] boyutuna eşdeğer olan diziye başvurabilir.
Bellek için, bir programcının şu anda onun hakkında ne fikri olduğu önemli değil. Bunu, bir char[8] dizisi veya sadece bir long değişkeni veya { int a1; int a2; } yapısı olup olmadığı önemli değil.
Bellek verileri, çeşitli türlerdeki veriler olarak kabul edilebilir. Örneğin, beş baytlık bir diziyi {int i; char c;} yapısına aktarmak veya bunun tam tersi mümkündür. Bu ilişki, doğrudan API işlevleriyle çalışma fırsatı sağlar.
memcpy uygulama sürümlerini belirli bir sıra ile inceleyelim.
Endeksleri Alma
Örnek 1'de memcpy işlevinin dst değişken adresini döndürdüğünü gösterdik.
Bu özellik, herhangi bir değişkenin adresini almak için kullanılabilir (diğer karmaşık türlerin dizileri dahil). Bunu yapmak için yalnızca aynı değişkeni kaynak ve alıcı parametreleri olarak belirtmemiz gerekir. cnt'de, gerçek kopyalama gerekli olmadığı için 0'ı aktarmak mümkündür.
Örneğin, double değişkeninin ve short dizisinin adresini alabiliriz:
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); }
Alınan adres daha sonra gerekli API işlevine veya bir yapı parametresi olarak ve ayrıca aynı memcpy işlevinin bir parametresi olarak aktarılabilir.
Dizileri Kopyalama
Bildiğiniz gibi bir dizi, ayrılmış bir bellek öbeğidir. Ayrılmış belleğin boyutu, öğelerin türüne ve miktarına bağlıdır. Örneğin, short dizi öğeleri türü ve öğelerin sayısı 10 ise, böyle bir dizi bellekte 20 bayt yer kaplar (çünkü short boyutu 2 bayttır).
Ancak bu 20 bayt, 20 char veya 5 int içeren diziler olarak da gösterilir. Her durumda, bellekte aynı 20 baytlık alanı işgal ederler.
Dizileri kopyalamak için aşağıdaki işlemleri gerçekleştirmeniz gerekir:
- dst belleği için gerekli miktarda öğeyi (elde edilen cnt bayttan az olmamak kaydıyla) ayırın;
- cnt'de, src'den kopyalanması gereken bayt sayısını belirtin.
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. Yapıları API İşlevlerine Aktarma
Diyelim ki doldurulmuş yapı işaretçisini API'ye aktarmanız gerekiyor. MQL5 dili, yapıları aktarmak için sınırlamalar koyar. Makalenin başında belleğin farklı şekilde sunulabileceğini belirtmiştim. Bu, gerekli yapının MQL5 tarafından desteklenen veri türüne kopyalanabileceği anlamına gelir. Genel olarak dizi, yapılara uygun bir türdür. Bu nedenle, bir yapıdan bir dizi almamız ve ardından bir diziyi API işlevine aktarmamız gerekecektir.
Yapıları kullanarak belleği kopyalama seçeneği belgeler bölümünde açıklanmıştır. Yapıları parametre olarak aktarmak imkansız olduğu ve yapıları kopyalamak burada tek yol olduğu için memcpy işlevini kullanamayız.
Şekil 3'te, char dizisi olarak sunulan, farklı türde 5 değişkenden oluşan yapının temsili ve eşdeğeri gösterilmiştir.
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; }
Bu kadar basit bir şekilde yapıyı byte dizisine kopyaladık.
Bu örneği daha pratik hale getirmek için soket oluşturma işlevini ele alalım.
int connect(SOCKET s, const struct sockaddr *name, int namelen);
Bu işlevde, yapı için işaretçiyi kabul ettiği için ikinci parametre sorunludur. Ama bununla ne yapacağımızı zaten biliyoruz. O halde başlayalım.
1. MQL5'te izin verilen yöntemle içe aktarma için connect işlevini yazalım:
int connect(int s, uchar &name[], int namelen);
2. Belgedeki gerekli yapıyı inceleyelim:
struct sockaddr_in { short sin_family; u_short sin_port; in_addr sin_addr; // additional 8 byte structure char sin_zero[8]; };
3. Benzer boyutta bir dizi ile bir yapı oluşturma:
struct ref_sockaddr_in { uchar c[2+2+8+8]; };
4. Gerekli sockaddr_in yapısını doldurduktan sonra bunu byte dizisine aktarıp connect parametresi olarak göndeririz.
Bu adımlara göre oluşturulan kod bölümü aşağıdadır.
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 }
Gördüğünüz gibi, DLL'nizi oluşturmanıza hiç gerek yok. Yapılar doğrudan API'a aktarılır.
3. API İşlevi İşaretçileriyle Çalışma
Çoğu durumda API işlevleri verilere bir işaretçi döndürür: Yapılar ve diziler. MQL5 verileri ayıklamak için uygun değildir; burada memcpy işlevi kullanılabilir.
Bellek Eşleme Dosyasından (MMF) bellek dizileriyle çalışma örneği
MMF ile çalışırken, ayrılmış bir bellek dizisine bir işaretçi döndüren işlev kullanılır.
int MapViewOfFile(int hFile, int DesiredAccess, int OffsetHigh, int OffsetLow, int NumOfBytesToMap);
Bu diziden Veri okuma, gerekli bayt miktarının memcpy işlevi tarafından basit bir şekilde kopyalanmasıyla yürütülür.
Verileri diziye yazma işlemi aynı memcpy kullanılarak gerçekleştirilir.
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); }
Gördüğünüz gibi, bellek dizisi için işaretçilerle çalışmak o kadar zor değil. Ve en önemlisi, bunun için ek DLL'nizi oluşturmanıza gerek yok.
MySQL için döndürülen yapılarla çalışma örneği
MySQL ile çalışırken önemli sorunlardan biri, MySQL'den veri almaktır. mysql_fetch_row işlevi, dizeler dizisini döndürür. Her dize, bir alanlar dizisidir. Böylece, bu işlev işaretçiyi işaretçiye döndürür. Görevimiz, tüm bu verileri döndürülen işaretçiden çıkarmaktır.
Alanların ikili olanlar da dahil olmak üzere çeşitli veri türleri olması nedeniyle görev biraz karmaşıktır. Bu, onları string dizisi olarak sunmanın imkansız olacağı anlamına gelir. mysql_num_rows, mysql_num_fields, mysql_fetch_lengths işlevleri, dizeler ve alan boyutları hakkında bilgi almak için kullanılır.
Şekil 4'te, sonucun bellekte sunulmasının yapısı gösterilmiştir.
Üç dizenin başlangıcının adresleri dizide toplanır. Ve dizinin başlangıcının adresi (örnekte = 94), mysql_fetch_row işlevinin döndüreceği şeydir.
Aşağıda, bir veritabanı isteğinden veri almak için kod örneği verilmiştir.
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. API İşlevlerinden NULL ile Sonlandırılmış Dizeleri Okuma
Bazı API işlevleri, işaretçiyi dizeye döndürür, ancak bize bu dizenin uzunluğunu göstermez. Bu durumda, sıfırla biten dizilerle ilgileniriz. Bu sıfır, dizenin sonunu belirlemeye yardımcı olur. Bu, boyutunun belirlenebileceği anlamına gelir.
C (msvcrt.dll) kitaplığı, NULL ile sonlandırılmış dizenin içeriğini uygun işaretçiden başka bir dizeye kopyalayan işleve zaten sahiptir. Dizenin boyutu işlev tarafından tanımlanır. API'lar genellikle Unicode yerine çok baytlı dizeler döndürdüğü için, alıcı olarak bir bayt dizisi kullanmak daha iyidir.
strcpy - NULL ile sonlandırılmış dizeleri kopyalar
char *strcpy(char *dst, const char *src); dst - the pointer to the destination string src - the pointer to the Null-terminated source string
Aslında, bu, memcpy işlevinin özel bir durumudur. Sistem, bir dizede bulunan sıfır üzerinde kopyalamayı durdurur. Bu işlev, bu tür işaretçilerle çalışırken her zaman kullanılacaktır.
Örneğin, MySQL'den API'da işaretçileri dizelere döndüren birkaç işlev vardır. Ve onlardan strcpy kullanarak veri almak önemsiz bir iştir.
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)); }
Sonuç
Dolayısıyla, bellekle çalışmanın üç temel mekanizmasının (yapıların kopyalanması, memcpy üzerinde işaretçilerin ve verilerinin alınması ve strcpy dizelerinin alınması) kullanımı çeşitli API işlevleriyle çalışırken hemen hemen tüm görevleri kapsar.
Uyarı. Alıcı arabelleği için yeterli miktarda veri ayrılmadıkça memcpy ve strcpy ile çalışmak güvenli olmayabilir. Bu nedenle, veri almak için ayrılan miktarların boyutuna dikkat edin.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/364





- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz