English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Datenaustausch: Erstellen einer DLL für MQL5 in 10 Minuten

Datenaustausch: Erstellen einer DLL für MQL5 in 10 Minuten

MetaTrader 5Beispiele | 11 Februar 2016, 11:54
2 418 2
MetaQuotes
Renat Fatkhullin

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:

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

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

  3. Ü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

Beigefügte Dateien |
mql5dll_test.mq5 (1.83 KB)
mql5dllsamples.zip (4.62 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Stephanie Kissel
Stephanie Kissel | 1 Aug. 2016 in 19:50
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!
Christian
Christian | 25 Jan. 2017 in 21:10
Stephanie Kissel:
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 ? :-)

Sehr einfach: Der Datenaustausch zwischen Indikatoren Sehr einfach: Der Datenaustausch zwischen Indikatoren
Wir möchten eine Umgebung erschaffen, die den Zugriff auf Daten von Indikatoren ermöglicht, die an ein Diagramm angehängt sind, und deshalb die folgenden Eigenschaften aufweist: kein Kopieren von Daten; minimale Veränderung des Codes verfügbarer Methoden, wenn wir sie nutzen müssen; MQL-Code ist zu bevorzugen (natürlich müssen wir DLL nutzen, doch wir verwenden nur ein Dutzend Strings C++-Code). Dieser Beitrag beschreibt eine einfache Methode zur Entwicklung einer Programmumgebung für das MetaTrader-Terminal, die eine Zugriffsmöglichkeit auf Indikatorpuffer aus anderen MQL-Programmen bietet.
Das Preishistogramm (Marktprofil) und seine Umsetzung in MQL5 Das Preishistogramm (Marktprofil) und seine Umsetzung in MQL5
Das Marktprofil wurde von Peter Steidlmayer, einem wahrhaft brillanten Denker, entwickelt. Er schlug die alternative Darstellung von Informationen &uuml;ber &quot;horizontale&quot; und &quot;vertikale&quot; Marktbewegungen vor, die eine v&ouml;llig neue Reihe von Modellen erm&ouml;glicht. Er stellte die These auf, dass dem Markt ein Puls oder ein grundlegendes Muster namens Zyklus des Gleichgewichts und Ungleichgewichts zugrunde liegen muss. In diesem Beitrag werde ich auf das Preishistogramm eingehen, ein vereinfachtes Modell des Marktprofils, und beschreibe seine Umsetzung in MQL5.
Zeichnen von Indikatoremissionen in MQL5 Zeichnen von Indikatoremissionen in MQL5
In diesem Beitrag behandeln wir die Emission von Indikatoren, eine neuen Herangehensweise an die Marktforschung. Die Berechnung von Emissionen basiert auf den Schnittpunkten verschiedener Indikatoren: Nach jedem Tick erscheinen immer mehr Punkte mit unterschiedlichen Farben und Formen. Sie formen zahlreiche Cluster wie Nebel, Wolken, Bahnen, Linien, Bögen usw. Diese Formen helfen uns beim Finden der unsichtbaren Kräfte, die die Bewegung von Marktpreisen beeinflussen.
Geheimnisse des Client Terminals MetaTrader 4: Die Dateibibliothek in MetaEditor Geheimnisse des Client Terminals MetaTrader 4: Die Dateibibliothek in MetaEditor
Bei der Erstellung seines eigenen Programms hat einen wichtigen Sinn der Editor des Codes. Je mehr Funktionen der Editor bietet, desto einfacher und schneller wird das Programm erstellt. Viele Programme werden auf bestehenden Code basiert. Sie verwenden einen Indikator oder einen Skript, aber Sie sind nicht mit allem in ihm zufrieden? Laden Sie den Code für dieses Programm von unserer Website und ändern Sie ihn nach Ihrem Geschmack.