Veri Değişimi Nasıl Yapılır? 10 Dakikada MQL5 için bir DLL
Aslına bakılırsa, basit bir DLL kitaplığının nasıl yazılacağını ve farklı sistemleri bağlamanın özelliklerinin neler olduğunu tam olarak hatırlayan çok fazla geliştirici yoktur.
Birkaç örnek kullanarak, basit DLL oluşturma işleminin tamamını 10 dakikada göstermeye çalışacağım ve ayrıca bağlayıcı uygulamamızın bazı teknik ayrıntılarını ele alacağım. Visual Studio 2005/2008'i kullanacağız; Express sürümleri ücretsizdir ve Microsoft web sitesinden indirilebilir.
1. Visual Studio 2005/2008'de C++'da DLL projesi oluşturma
'Dosya -> Yeni' menüsünü kullanarak Win32 Uygulama Sihirbazını çalıştırın, proje türünü 'Visual C++' olarak seçin, 'Win32 Konsol Uygulaması' şablonunu seçin ve proje adını belirtin (örneğin, 'MQL5DLLSamples'). 'Konum' projesini saklamak için önerilen varsayılan dizin yerine bir kök dizin seçin, 'Çözüm için dizin oluştur' onay kutusunu devre dışı bırakın ve 'Tamam'a tıklayın:
Şek. 1. Win32 Uygulama Sihirbazı, DLL projesi oluşturma
Bir sonraki adımda, ayarlar sayfasına gitmek için 'İleri' düğmesine basın:
Şek. 2. Win32 Uygulama Sihirbazı, proje ayarları
Son sayfada, 'DLL' uygulama türünü seçin, diğer alanları olduğu gibi boş bırakın ve 'Bitir' düğmesine tıklayın. Otomatik olarak eklenen tanıtım kodunu kaldırmak istemiyorsanız 'Sembolleri dışa aktar' seçeneğini ayarlamayın:
Şek. 3. Win32 Uygulama Sihirbazı, Uygulama ayarları
Sonuç olarak boş bir projeniz olacak:
Şek. 4. Wizard tarafından hazırlanan boş DLL projesi
Testi basitleştirmek için, 'Çıktı Dizini' seçeneklerinde DLL dosyalarının çıktısını doğrudan istemci terminalinin '...\MQL5\Libraries' konumuna belirtmek daha iyidir; ayrıca, bu, size fazlasıyla zaman kazandıracaktır:
Şek. 5. DLL çıktı dizini
2. İşlev Eklemeye Hazırlanma
Dışa aktarılan işlevleri rahat ve kolay bir şekilde tanımlayabilmeniz için stdafx.h dosyasının sonuna '_DLLAPI' makrosunu ekleyin:
//+------------------------------------------------------------------+ //| MQL5 DLL Samples | //| Copyright 2001-2010, MetaQuotes Software Corp. | //| https://www.metaquotes.net | //+------------------------------------------------------------------+ #pragma once #include "targetver.h" #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include <windows.h> //--- #define _DLLAPI extern "C" __declspec(dllexport) //+------------------------------------------------------------------+
MQL5'teki DLL içe aktarılan işlev çağrıları stdcall ve cdecl çağırma kuralını içermelidir. stdcall ve cdecl, bir yığından parametre çıkarma yöntemlerinde farklılık gösterse de, MQL5 çalışma zamanı ortamı, DLL çağrılarının özel paketinden dolayı her iki sürümü de güvenle kullanabilir.
C++ derleyicisi varsayılan olarak __cdecl çağrısını kullanır, ancak dışa aktarılan işlevler için __stdcall modunun açıkça belirtilmesini öneririm.
Doğru yazılmış bir dışa aktarma işlevi aşağıdaki forma sahip olmalıdır:
_DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2) { return(0); }
Bir MQL5 programında işlev aşağıdaki gibi tanımlanmalı ve çağrılmalıdır:
#import "MQL5DLLSamples.dll" int fnCalculateSpeed(int &res1,double &res2); #import //--- call speed=fnCalculateSpeed(res_int,res_double);
Proje derlemesinden sonra, bu stdcall, dışa aktarma tablosunda _fnCalculateSpeed@8 olarak görüntülenecektir; burada derleyici yığın aracılığıyla iletilen bir alt çizgi ve bayt sayısı ekler. Böyle bir tasarım, çağıranın yığına yerleştirilmesi gereken tam olarak kaç veri olduğunu (ancak türünü değil!) bilmesi nedeniyle DLL işlev çağrılarının güvenliğinin daha iyi kontrol edilmesine olanak tanır.
Parametre bloğunun son boyutunda DLL işlevi içe aktarma açıklamasında bir hata varsa işlev çağrılmayacak ve günlükte yeni mesaj görünecektir: ''MQL5DLLSamples.dll' içinde 'fnCrashTestParametersStdCall' bulunamıyor. Bu gibi durumlarda, hem işlev prototipindeki hem de DLL kaynağındaki tüm parametrelerin dikkatlice kontrol edilmesi gerekir.
Dışa aktarma tablosunun tam işlev adını içermemesi durumunda uyumluluk için tasarımsız basitleştirilmiş açıklama araması kullanılır. fnCalculateSpeed gibi adlar, işlevler __cdecl biçiminde tanımlanırsa oluşturulur._DLLAPI int fnCalculateSpeed(int &res1,double &res2) { return(0); }
3. Parametre İletme ve Veri Değişimi Yöntemleri
İletilen parametrelerin birkaç çeşidini ele alalım:
- Basit değişkenleri alma ve iletme
Basit değişkenlerin durumu kolaydır; değere göre veya & öğesi kullanılarak referansa göre iletilebilirler._DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2) { int res_int=0; double res_double=0.0; int start=GetTickCount(); //--- simple math calculations for(int i=0;i<=10000000;i++) { res_int+=i*i; res_int++; res_double+=i*i; res_double++; } //--- set calculation results res1=res_int; res2=res_double; //--- return calculation time return(GetTickCount()-start); }
MQL5'ten çağrı:#import "MQL5DLLSamples.dll" int fnCalculateSpeed(int &res1,double &res2); #import //--- calling the function for calculations int speed=0; int res_int=0; double res_double=0.0; speed=fnCalculateSpeed(res_int,res_double); Print("Time ",speed," msec, int: ",res_int," double: ",res_double);
Çıktı:MQL5DLL Test (GBPUSD,M1) 19:56:42 Time 16 msec, int: -752584127 double: 17247836076609
- Öğeleri dolduran bir diziyi alma ve iletme
Diğer MQL5 programlarından farklı olarak, dizi geçişi, ebatlar ve boyutlar hakkında özel bilgilere erişim olmadan veri arabelleğine doğrudan referans yoluyla gerçekleştirilir. Bu nedenle dizi ebatı ve boyutu ayrı ayrı iletilmelidir.
_DLLAPI void __stdcall fnFillArray(int *arr,const int arr_size) { //--- check for the input parameters if(arr==NULL || arr_size<1) return; //--- fill array with values for(int i=0;i<arr_size;i++) arr[i]=i; }
MQL5'ten çağrı:#import "MQL5DLLSamples.dll" void fnFillArray(int &arr[],int arr_size); #import //--- call for the array filling int arr[]; string result="Array: "; ArrayResize(arr,10); fnFillArray(arr,ArraySize(arr)); for(int i=0;i<ArraySize(arr);i++) result=result+IntegerToString(arr[i])+" "; Print(result);
Çıktı:MQL5DLL Test (GBPUSD,M1) 20:31:12 Array: 0 1 2 3 4 5 6 7 8 9
- Dizeleri iletme ve değiştirme
Unicode dizeleri, herhangi bir ek bilgi aktarılmadan arabellek adreslerine doğrudan referanslar kullanılarak iletilir._DLLAPI void fnReplaceString(wchar_t *text,wchar_t *from,wchar_t *to) { wchar_t *cp; //--- parameters check if(text==NULL || from==NULL || to==NULL) return; if(wcslen(from)!=wcslen(to)) return; //--- search for substring if((cp=wcsstr(text,from))==NULL) return; //--- replace it memcpy(cp,to,wcslen(to)*sizeof(wchar_t)); }
MQL5'ten çağrı:#import "MQL5DLLSamples.dll" void fnReplaceString(string text,string from,string to); #import //--- modify the string string text="A quick brown fox jumps over the lazy dog"; fnReplaceString(text,"fox","cat"); Print("Replace: ",text);
Sonuç:MQL5DLL Test (GBPUSD,M1) 19:56:42 Replace: A quick brown fox jumps over the lazy dog
Çizginin değişmediği ortaya çıktı! Bu, yeni başlayanların, onlara başvurmak yerine nesnelerin kopyalarını (bir dize bir nesnedir) ilettikleri zaman yaptıkları yaygın bir hatadır. DLL'de değiştirilen 'metin' dizesinin kopyası otomatik olarak oluşturuldu ve ardından orijinali etkilemeden otomatik olarak kaldırıldı.
Bu durumu düzeltmek için bir dizeyi referans olarak iletmek gerekir. Bunu yapmak için, "metin" parametresine & öğesini ekleyerek içe aktarma bloğunu değiştirmeniz yeterlidir:#import "MQL5DLLSamples.dll" void fnReplaceString(string &text,string from,string to); #import
Derleme işleminden ve başladıktan sonra doğru sonucu elde edeceğiz:MQL5DLL Test (GBPUSD,M1) 19:58:31 Replace: A quick brown cat jumps over the lazy dog
4. DLL işlevlerinde istisnaları yakalama
Terminal çökmelerini önlemek için her DLL çağrısı, İşlenmeyen Özel Durum Paketi ile otomatik olarak korunur. Bu mekanizma, standart hataların çoğundan (bellek erişim hataları, sıfıra bölme vb.) korunmaya olanak tanır
Mekanizmanın nasıl çalıştığını görmek için aşağıdaki kodu oluşturalım:
_DLLAPI void __stdcall fnCrashTest(int *arr) { //--- wait for receipt of a zero reference to call the exception *arr=0; }
ve onu istemci terminalinden çağıralım:
#import "MQL5DLLSamples.dll" void fnCrashTest(int arr); #import //--- call for the crash (the execution environment will catch the exception and prevent the client terminal crush) fnCrashTest(NULL); Print("You won't see this text!"); //---
Sonuç olarak, sıfır adrese yazmaya ve bir istisna oluşturmaya çalışacaktır. İstemci terminali onu yakalayacak, günlüğe kaydedecek ve çalışmasına devam edecektir:
MQL5DLL Test (GBPUSD,M1) 20:31:12 Access violation write to 0x00000000
5. DLL çağrıları paketi ve çağrılarda hız kaybı
Yukarıda açıklandığı gibi, güvenliği sağlamak için her DLL işlevi çağrısı özel bir pakete sarılır. Bu bağlayıcı, temel kodu maskeler, yığının yerini alır, stdcall / cdecl anlaşmalarını destekler ve çağrılan işlevler içindeki istisnaları izler.
Bu iş hacmi, işlev çağırmada önemli bir gecikmeye yol açmaz.
6. Son oluşturma
Yukarıdaki tüm DLL işlevleri örneklerini 'MQL5DLLSamples.cpp' dosyasında ve MQL5 örneklerini 'MQL5DLL Test.mq5' script dosyasında toplayalım. Visual Studio 2008 için son proje ve MQL5'teki script dosyası makaleye eklenmiştir.
//+------------------------------------------------------------------+ //| MQL5 DLL Samples | //| Copyright 2001-2010, MetaQuotes Software Corp. | //| https://www.metaquotes.net | //+------------------------------------------------------------------+ #include "stdafx.h" //+------------------------------------------------------------------+ //| Passing and receving of simple variables | //+------------------------------------------------------------------+ _DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2) { int res_int=0; double res_double=0.0; int start=GetTickCount(); //--- simple math calculations for(int i=0;i<=10000000;i++) { res_int+=i*i; res_int++; res_double+=i*i; res_double++; } //--- set calculation results res1=res_int; res2=res_double; //--- return calculation time return(GetTickCount()-start); } //+------------------------------------------------------------------+ //| Filling the array with values | //+------------------------------------------------------------------+ _DLLAPI void __stdcall fnFillArray(int *arr,const int arr_size) { //--- check input variables if(arr==NULL || arr_size<1) return; //--- fill array with values for(int i=0;i<arr_size;i++) arr[i]=i; } //+------------------------------------------------------------------+ //| The substring replacement of the text string | //| the string is passed as direct reference to the string content | //+------------------------------------------------------------------+ _DLLAPI void fnReplaceString(wchar_t *text,wchar_t *from,wchar_t *to) { wchar_t *cp; //--- parameters checking if(text==NULL || from==NULL || to==NULL) return; if(wcslen(from)!=wcslen(to)) return; //--- search for substring if((cp=wcsstr(text,from))==NULL) return; //--- replace it memcpy(cp,to,wcslen(to)*sizeof(wchar_t)); } //+------------------------------------------------------------------+ //| Call for the crush | //+------------------------------------------------------------------+ _DLLAPI void __stdcall fnCrashTest(int *arr) { //--- wait for receipt of a zero reference to call the exception *arr=0; } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| MQL5DLL Test.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //--- #import "MQL5DLLSamples.dll" int fnCalculateSpeed(int &res1,double &res2); void fnFillArray(int &arr[],int arr_size); void fnReplaceString(string text,string from,string to); void fnCrashTest(int arr); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- calling the function for calculations int speed=0; int res_int=0; double res_double=0.0; speed=fnCalculateSpeed(res_int,res_double); Print("Time ",speed," msec, int: ",res_int," double: ",res_double); //--- call for the array filling int arr[]; string result="Array: "; ArrayResize(arr,10); fnFillArray(arr,ArraySize(arr)); for(int i=0;i<ArraySize(arr);i++) result=result+IntegerToString(arr[i])+" "; Print(result); //--- modifying the string string text="A quick brown fox jumps over the lazy dog"; fnReplaceString(text,"fox","cat"); Print("Replace: ",text); //--- and finally call a crash //--- (the execution environment will catch the exception and prevent the client terminal crush) fnCrashTest(NULL); Print("You won't see this text!"); //--- } //+------------------------------------------------------------------+
Gösterdiğiniz ilgi için teşekkürler! Tüm sorularınızı yanıtlamaya hazırım.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/18
- Ücretsiz ticaret 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