English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Getting Rid of Self-Made DLLs

Getting Rid of Self-Made DLLs

MetaTrader 5Örnekler | 22 Aralık 2021, 14:31
181 0
---
---


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

1. Bellek, Her Şeydir

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

MQL5'te çeşitli veri türlerinin boyutları


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.

Memcpy kullanarak 4 bayt kopyalama örneği

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.

Farklı türde 5 değişkenden oluşan yapıyı ve eşdeğerini char dizisi olarak sunma

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.

Bellekte istek sonucunu sunma yapısı

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.

Bellekte NULL ile sonlandırılmış dizeyi sunma

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

MQL5.community'de Kim Kimdir? MQL5.community'de Kim Kimdir?
MQL5.com web sitesi hepinizi oldukça iyi hatırlar! İleti dizilerinizden kaç tanesinin etkileyici olduğu, makalelerinizin ne kadar popüler olduğu ve Kod Tabanı'nda programlarınızın ne sıklıkla indirildiği... – Bu, MQL5.com'da hatırlananların yalnızca küçük bir kısmı. Başarılarınız profilinizde mevcut, peki ya genel görünüm? Bu makalede tüm MQL5.community üyelerinin başarılarının genel görünümünü göstereceğiz.
Box-Cox Dönüşümü Box-Cox Dönüşümü
Makale, okuyucularını Box-Cox dönüşümü hakkında bilgilendirmeyi amaçlamaktadır. Kullanımı ile ilgili konular ele alınmış ve rastgele diziler ve gerçek fiyat teklifleriyle dönüşüm verimliliğinin değerlendirilmesine olanak tanıyan bazı örnekler verilmiştir.
İstatistik Temelleri İstatistik Temelleri
Her yatırımcı, temel analizin destekçisi olsa dahi, belirli istatistiksel hesaplamaları kullanarak çalışır. Bu makale size istatistiğin temelleri ve temel unsurları konusunda rehberlik etmekte ve karar vermede istatistiğin önemini göstermektedir.
Yeni Müşteriler İçin Birkaç İpucu Yeni Müşteriler İçin Birkaç İpucu
Genellikle çeşitli ünlü kişilere atfedilen meşhur bir söz vardır: "Hata yapmıyorsan, hiçbir şey yapmıyorsun demektir." Aylaklığın kendisinin bir hata olduğunu düşünmedikçe, bu ifadeye itiraz etmesi zor. Ancak gelecekteki hatalarınızın sayısını en aza indirmek için her zaman geçmişteki hatalarınızı (kendinizin ve başkalarının) analiz edebilirsiniz. Aynı ad hizmetinde işler yürütülürken ortaya çıkabilecek olası durumları gözden geçirmeye çalışacağız.