
Datenaustausch: Erstellen einer DLL für MQL5 in 10 Minuten
Tatsächlich erinnern sich nicht viele Entwickler daran, wie eine simple DLL-Bibliothek geschrieben wird und was die Merkmale der unterschiedlichen Systemanbindungen sind.
Anhand mehrerer Beispiele werde ich versuchen, Ihnen den gesamten Prozess zur Erstellung einer simplen DLL in 10 Minuten zu zeigen, sowie einige technische Einzelheiten der Umsetzung unserer Anbindung zu besprechen. Wir nutzen Visual Studio 2005/2008. Die Express-Versionen sind kostenlos und können von der Microsoft-Webseite heruntergeladen werden.
1. Erstellen eines DLL-Projekts in C++ in Visual Studio 2005/2008
Führen Sie den Win32 Application Wizard mithilfe der Menüoption 'Datei -> Neu' aus, wählen Sie den Projekttyp 'Visual C++', die Vorlage 'Win32 Konsolenanwendung' und legen Sie den Projektnamen fest (zum Beispiel 'MQL5DLLSamples'). Wählen Sie unter 'Speicherort' ein Stammverzeichnis zum Speichern des Projekts anstelle des Standardverzeichnisses, deaktivieren Sie das Kontrollkästchen 'Verzeichnis für Lösung erstellen' und klicken Sie auf 'OK':
Abb. 1 Win32 Application Wizard, Erstellung eines DLL-Projekts
Klicken Sie im nächsten Schritt auf 'Weiter, um zu den Einstellungen zu gelangen:
Abb. 2 Win32 Application Wizard, Projekteinstellungen
Wählen Sie auf den letzten Seite den Anwendungstyp 'DLL', lassen Sie die anderen Felder leer und klicken Sie auf 'Fertigstellen'. Wählen Sie nicht die Option 'Symbole exportieren', wenn Sie den hinzugefügten Demonstrationscode nicht automatisch entfernen möchten:
Abb. 3 Win32 Application Wizard, Anwendungseinstellungen
Als Ergebnis erhalten Sie ein leeres Projekt:
Abb. 4 Durch den Wizard erzeugtes leeres DLL-Projekt
Um das Testen zu vereinfachten, sollte unter 'Ausgabeverzeichnis' die Ausgabe der DLL-Dateien direkt nach '...\MQL5\Libraries' des Client Terminals festgelegt werden. Dies wird Ihnen später viel Zeit ersparen:
Abb. 5 DLL-Ausgabeverzeichnis
2. Vorbereitung zum Hinzufügen von Optionen
Fügen Sie am Ende der Datei stdafx.h das Makro '_DLLAPI' hinzu, sodass Sie exportierte Funktionen schnell und einfach beschreiben können:
//+------------------------------------------------------------------+ //| 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) //+------------------------------------------------------------------+
Die Aufrufe der importierten DLL-Funktionen in MQL5 sollten der Verbindungskonvention für stdcall und cdecl entsprechen. Auch wenn stdcall und cdecl sich in ihrer Art des Extrahierens von Parametern aus einem Stack unterscheiden, kann die MQL5-Laufzeitumgebung dank des besonderen Wrappers von DLL-Aufrufen beide Versionen sicher nutzen.
Der C++-Compiler nutzt standardmäßig __cdecl-Aufrufe, ich empfehle allerdings ausdrücklich, dass sie für exportierte Funktionen den __stdcall-Modus nutzen.
Eine korrekt geschriebene Exportfunktion muss die folgende Form haben:
_DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2) { return(0); }
In einem MQL5-Programm muss die Funktion so definiert und aufgerufen werden:
#import "MQL5DLLSamples.dll" int fnCalculateSpeed(int &res1,double &res2); #import //--- call speed=fnCalculateSpeed(res_int,res_double);
Nach der Kompilierung des Projekts wird dieser stdcall in der Exporttabelle als _fnCalculateSpeed@8 angezeigt. Hier fügt der Compiler einen Unterstrich und die über das Stack übertragene Menge an Bytes hinzu. Diese Erweiterung ermöglicht eine bessere Kontrolle über die Sicherheit der Aufrufe von DLL-Funktionen, da der Caller genau weiß, welche Menge (aber nicht welche Art!) von Daten im Stack platziert werden soll.
Falls die finale Größe des Parameterblocks einen Fehler in der Importbeschreibung der DLL-Funktion hat, wird die Funktion nicht aufgerufen und im Logbuch erscheint eine neue Meldung: 'Cannot find 'fnCrashTestParametersStdCall' in 'MQL5DLLSamples.dll'. In solchen Fällen müssen alle Parameter im Prototyp der Funktion und in der DLL-Quelle sorgfältig überprüft werden.
Die Suche nach der vereinfachten Beschreibung ohne Erweiterung wird für Kompatibilitätszwecke genutzt, falls die Exporttabelle nicht den vollständigen Funktionsnamen enthält. Namen wie fnCalculateSpeed werden erstellt, wenn Funktionen im __cdecl-Format definiert werden.
_DLLAPI int fnCalculateSpeed(int &res1,double &res2) { return(0); }
3. Methoden zur Übergabe von Parametern und zum Datenaustausch
Betrachten wir mehrere Varianten von zu übergebenden Parametern:
- Annahme und Übergabe simpler Variablen
Simple Variablen sind einfach: Sie können per & nach Wert oder Verweis übergeben werden._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); }
Aufruf aus MQL5:#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);
Ergebnis:MQL5DLL Test (GBPUSD,M1) 19:56:42 Time 16 msec, int: -752584127 double: 17247836076609
- Empfangen und Übergeben von Arrays mit Befüllung von Elementen
Im Gegensatz zu anderen MQL5-Programmen geschieht die Übergabe von Arrays durch direkten Verweis auf den Datenpuffer ohne Zugriff auf die proprietären Informationen über Dimensionen und Größen. Deshalb müssen Dimension und Größe eines Arrays separat übergeben werden.
_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; }
Aufruf aus MQL5:#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);
Ergebnis:MQL5DLL Test (GBPUSD,M1) 20:31:12 Array: 0 1 2 3 4 5 6 7 8 9
- Übergeben und Modifizieren von Strings
Die Unicode-Strings werden durch direkte Verweise auf ihre Pufferadressen ohne Übergabe weiterer Informationen übergeben._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)); }
Aufruf aus MQL5:#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);
Das Ergebnis ist:MQL5DLL Test (GBPUSD,M1) 19:56:42 Replace: A quick brown fox jumps over the lazy dog
Die Zeile hat sich nicht verändert! Das ist ein klassischer Fehler, den Neueinsteiger machen, wenn sie Kopien von Objekten übertragen (ein String ist ein Objekt), anstatt auf sie zu verweisen. Für den String 'text' wurde automatisch eine Kopie erstellt, die in der DLL modifiziert wurde. Anschließend wurde diese Kopie automatisch ohne Auswirkungen auf das Original entfernt.
Um diese Situation zu vermeiden, müssen Strings per Verweis übergeben werden. Modifizieren Sie hierzu einfach den Importblock, indem Sie & zum Parameter "text" hinzufügen:#import "MQL5DLLSamples.dll" void fnReplaceString(string &text,string from,string to); #import
Nach der Kompilierung und dem Start erhalten wir das richtige Ergebnis:MQL5DLL Test (GBPUSD,M1) 19:58:31 Replace: A quick brown cat jumps over the lazy dog
4. Abfangen von Ausnahmen in DLL-Funktionen
Um Abstürze des Terminals zu vermeiden, ist jede DLL automatisch durch ein Wrapping von unbehandelten Ausnahmen geschützt. Dieser Mechanismus schützt die DLL vor einem Großteil der Standardfehler (Fehler beim Speicherzugriff, Teilung durch Null usw.).
Um zu sehen, wie dieser Mechanismus funktioniert, erstellen wir den folgenden Code:
_DLLAPI void __stdcall fnCrashTest(int *arr) { //--- wait for receipt of a zero reference to call the exception *arr=0; }
und rufen ihn aus dem Client Terminal auf:
#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!"); //---
Als Ergebnis wird versucht, in die Null-Adresse zu schreiben und eine Ausnahme zu generieren. Das Client Terminal fängt dies ab, trägt es in das Logbuch ein und setzt seine Arbeit fort:
MQL5DLL Test (GBPUSD,M1) 20:31:12 Access violation write to 0x00000000
5. DLL-Aufruf-Wrapper und Geschwindigkeitsverlust bei Aufrufen
Wie bereits weiter oben erwähnt, ist jeder Aufruf von DLL-Funktionen in einem speziellen Wrapper eingeschlossen, um die Sicherheit zu gewährleisten. Diese Verbindung maskiert den Basiscode, ersetzt das Stack, unterstützt stdcall-/cdecl-Vereinbarungen und überwacht Ausnahmen innerhalb der aufgerufenen Funktionen.
Diese Masse an Aufgaben führt zu keiner bedeutenden Verzögerung des Aufrufs von Funktionen.
6. Das finale Build
Sammeln wir alle oben aufgeführten Beispiele für DLL-Funktionen in der Datei 'MQL5DLLSamples.cpp' und MQL5-Beispiele im Script 'MQL5DLL Test.mq5'. Das finale Projekt für Visual Studio 2008 und das Script in MQL5 sind an den Beitrag angehängt.
//+------------------------------------------------------------------+ //| 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!"); //--- } //+------------------------------------------------------------------+
Vielen Dank für Ihr Interesse! Gerne beantworte ich all Ihre Fragen.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/18





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Is it possible to make a video of it how you build everything, because i think i make something wrong. I get always an error, and i am not sure, if i am doing everything right. Thanks in advance!
Wo hakt es denn ? ...du musst auf jeden Fall eine 64 bit DLL erstellen für den MT5.
Mit der aktuellen Version Visual Studio 15 gibt es keine Probleme.
Die Artikel sind leider oft nur "übersetzt" aus uralten Artikeln. ....Wer nutzt denn noch VC 2005/8 ? :-)