English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
데이터 교환 방법: 10 분 안에 MQL5 용 DLL

데이터 교환 방법: 10 분 안에 MQL5 용 DLL

MetaTrader 5 | 5 7월 2021, 13:00
122 0
MetaQuotes
Renat Fatkhullin

사실, 간단한 DLL 라이브러리를 작성하는 방법과 서로 다른 시스템을 바인딩하는 기능을 정확히 기억하는 개발자는 많지 않습니다.

몇 가지 예제를 사용하여 간단한 DLL 생성의 전체 프로세스를 10 분 안에 보여주고 바인딩 구현에 대한 몇 가지 기술적인 세부 사항을 논의하려고 합니다. Visual Studio 2005/2008을 사용할 것입니다. Express 버전은 무료이며 Microsoft 웹 사이트에서 다운로드 할 수 있습니다.

1. Visual Studio 2005/2008에서 C++로 DLL 프로젝트 만들기

'파일 -> 새로 만들기'메뉴를 사용하여 Win32 응용 프로그램 마법사를 실행하고 프로젝트 유형을 'Visual C++'으로 선택하고 'Win32 콘솔 응용 프로그램'을 선택합니다. '템플릿을 선택하고 프로젝트 이름을 정의합니다 (예: ' MQL5DLLSamples'). 기본 제공되는 디렉토리 대신 '위치'프로젝트를 저장할 루트 디렉토리를 선택하고 '솔루션용 디렉토리 만들기'확인란을 비활성화하고 '확인을 클릭합니다':

그림 1. Win32 응용 프로그램 마법사, DLL 프로젝트 생성

다음 단계에서 '다음'을 눌러 설정 페이지로 이동합니다.

그림 2. Win32 응용 프로그램 마법사, 프로젝트 설정

마지막 페이지에서 'DLL'애플리케이션 유형을 선택하고 다른 필드는 그대로 두고 '마침'을 클릭합니다. 자동으로 추가된 데모 코드를 제거하지 않으려면 '기호 내보내기'옵션을 설정하지 마십시오.

그림 3. Win32 응용 프로그램 마법사, 응용 프로그램 설정

결과적으로 빈 프로젝트가 생성됩니다.

그림 4. 마법사가 준비한 빈 DLL 프로젝트

테스트를 단순화하려면 '출력 디렉토리'옵션에서 DLL 파일의 출력을 클라이언트 터미널의 '...\MQL5\Libraries'에 직접 지정하는 것이 좋습니다. 많은 시간을 절약 할 수 있습니다.

그림 5. DLL 출력 디렉토리


2. 기능 추가 준비

내 보낸 함수를 편리하고 쉽게 설명 할 수 있도록 stdafx.h 파일 끝에 ' _DLLAPI'매크로를 추가합니다

//+------------------------------------------------------------------+
//|                                                 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에서 DLL 가져오기 함수 호출에는 stdcall 및 cdecl 호출 규칙이 있어야 합니다. stdcall과 cdecl은 스택에서 매개 변수를 추출하는 방식이 다르지만 MQL5 런타임 환경은 DLL 호출의 특수 래퍼로 인해 두 버전을 모두 안전하게 사용할 수 있습니다.

C ++ 컴파일러는 기본적으로 __cdecl 호출을 사용하지만 내보낸 함수에 대해 __ stdcall 모드 를 명시적으로 지정하는 것이 좋습니다.

올바르게 작성된 내보내기 함수는 다음 형식을 가져야 합니다.

_DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2)
  {
   return(0);
  }

MQL5 프로그램에서 함수는 다음과 같이 정의되고 호출되어야 합니다.

#import "MQL5DLLSamples.dll"
int  fnCalculateSpeed(int &res1,double &res2);
#import

//--- call
   speed=fnCalculateSpeed(res_int,res_double);

프로젝트 컴파일 후이 stdcall은 내보내기 테이블에 _fnCalculateSpeed@8으로 표시됩니다. 여기서 컴파일러는 스택을 통해 전송되는 밑줄과 바이트 수를 추가합니다. 이러한 데코레이션은 호출자가 스택에 배치해야하는 데이터의 수 (유형은 아님)를 정확히 알고 있기 때문에 DLL 함수 호출의 보안을 더 잘 제어할 수 있습니다.

매개 변수 블록의 최종 크기에 DLL 함수 가져 오기 설명에 오류가 있는 경우 함수가 호출되지 않고 새 메시지가 저널에 나타납니다. 'Cannot find'fnCrashTestParametersStdCall 'in'MQL5DLLSamples.dll</ b0> '. 이러한 경우 함수 프로토 타입과 DLL 소스 모두에서 모든 매개 변수를 주의 깊게 확인해야 합니다.

내보내기 테이블에 전체 함수 이름이 포함되지 않은 경우 호환성을 위해 장식없이 단순화 된 설명 검색을 사용합니다. 함수가 __cdecl 형식으로 정의되면 fnCalculateSpeed​​와 같은 이름이 생성됩니다.
_DLLAPI int fnCalculateSpeed(int &res1,double &res2)
  {
   return(0);
  }


3. 매개 변수 전달 및 데이터 교환 방법

전달된 매개 변수의 여러 변형을 고려해 보겠습니다.

  1. 단순 변수 수신 및 전달
    단순 변수의 경우는 쉽습니다. 값이나 &를 사용하여 참조로 전달할 수 있습니다.
    _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에서 호출:
    #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);
    
    출력은 다음과 같습니다.
    MQL5DLL Test (GBPUSD,M1) 19:56:42 Time  16  msec, int:  -752584127  double:  17247836076609
  2. 요소가 채워진 배열 수신 및 전달

    다른 MQL5 프로그램과 달리 배열 전달은 차원 및 크기에 대한 독점 정보에 액세스하지 않고 데이터 버퍼에 대한 직접 참조를 통해 수행됩니다. 이것이 배열 차원과 크기를 별도로 전달해야 하는 이유입니다.

    _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에서 호출:
    #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);
    
    출력은 다음과 같습니다.
    MQL5DLL Test (GBPUSD,M1) 20:31:12 Array: 0 1 2 3 4 5 6 7 8 9 
  3. 문자열 전달 및 수정
    유니코드 문자열은 추가 정보를 전달하지 않고 버퍼 주소에 대한 직접 참조를 사용하여 전달됩니다.
    _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에서 호출:
    #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);
    결과는:
    MQL5DLL Test (GBPUSD,M1) 19:56:42 Replace:  A quick brown fox jumps over the lazy dog
    라인은 바뀌지 않은 것으로 밝혀졌습니다! 이것은 초보자가 객체를 참조하는 대신 객체의 복사본 (문자열은 객체)을 전송할 때 흔히 발생하는 실수입니다. DLL에서 수정된 문자열 'text'의 복사본이 자동으로 생성된 다음 원본에 영향을 주지 않고 자동으로 제거되었습니다.

    이 상황을 해결하려면 문자열을 참조로 전달해야 합니다. 이렇게하려면 "text" 매개 변수에 &를 추가하여 가져오기 블록을 수정하면 됩니다.
    #import "MQL5DLLSamples.dll"
    void fnReplaceString(string &text,string from,string to);
    #import
    컴파일하고 시작하면 올바른 결과를 얻을 수 있습니다.
    MQL5DLL Test (GBPUSD,M1) 19:58:31 Replace:  A quick brown cat jumps over the lazy dog

4. DLL 함수에서 예외 포착

터미널 충돌을 방지하기 위해 각 DLL 호출은 Unhandled Exception Wrapping에 의해 자동으로 보호됩니다. 이 메커니즘을 통해 대부분의 표준 오류 (메모리 액세스 오류, 0으로 나누기 등)로부터 보호 할 수 있습니다.

메커니즘이 어떻게 작동하는지 보기 위해 다음 코드를 만들어 보겠습니다.

_DLLAPI void __stdcall fnCrashTest(int *arr)
  {
//--- wait for receipt of a zero reference to call the exception
   *arr=0;
  }

클라이언트 터미널에서 호출합니다.

#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!");
//---

결과적으로 제로 주소에 쓰기를 시도하고 예외를 생성합니다. 클라이언트 터미널이 이를 포착하고 저널에 기록하고 작업을 계속합니다.

MQL5DLL Test (GBPUSD,M1) 20:31:12 Access violation write to 0x00000000

5. DLL 호출 래퍼 및 호출 속도 손실

위에서 이미 설명한 것처럼 DLL 함수의 모든 호출은 안전을 보장하기 위해 특수 래퍼로 래핑됩니다. 이 바인딩은 기본 코드를 마스킹하고 스택을 대체하며 stdcall / cdecl 계약을 지원하며 호출 된 함수 내에서 예외를 모니터링합니다.

이 작업량이 많아도 함수 호출이 크게 지연되지는 않습니다.

6. 최종 빌드

'MQL5DLLSamples.cpp' 파일에서 위의 모든 DLL 함수 예제를 수집하고 'MQL5DLL Test.mq5' 스크립트에서 MQL5 예제를 수집해보겠습니다. Visual Studio 2008의 최종 프로젝트와 MQL5의 스크립트가 기사에 첨부되어 있습니다.

//+------------------------------------------------------------------+
//|                                                 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!");
//---
  }
//+------------------------------------------------------------------+

관심을 가져주셔서 감사합니다! 질문에 답할 준비가 되어 있습니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/18

파일 첨부됨 |
mql5dll_test.mq5 (1.83 KB)
mql5dllsamples.zip (4.62 KB)
인디케이터 데이터 교환: 쉬워요! 인디케이터 데이터 교환: 쉬워요!
차트에 추가된 인디케이터 데이터에 액세스가 가능한 동시에, 데이터 복사가 불필요하고, 필요한 경우 최소한의 수정만을 거쳐 기존의 코드를 사용할 수 있으며, MQL 코드가 선호되는 환경을 제공하고 싶습니다. 물론 DLL을 사용하긴 하겠지만 C++ 문자열을 이용할 겁니다. 이 글은 다른 MQL 프로그램에서 MetaTrader 터미널로 인디케이터 버퍼를 가져올 수 있도록 하는 편리한 개발 환경 구축 방법을 설명하고 있습니다.
가격 히스토그램 (시장 프로필) 및 MQL5에서 구현 가격 히스토그램 (시장 프로필) 및 MQL5에서 구현
시장 프로필은 정말 뛰어난 사상가인 Peter Steidlmayer가 개발했습니다. 그는 완전히 다른 모델 세트로 이어지는 "수평" 및 "수직"시장 이동에 대한 정보의 대체 표현을 사용할 것을 제안했습니다. 그는 시장의 근본적인 맥박이나 균형과 불균형의 순환이라는 근본적인 패턴이 있다고 가정했습니다. 이 기사에서는 시장 프로필의 단순화된 모델인 가격 히스토그램을 고려하고 MQL5에서의 구현에 대해 설명합니다.
MQL5로 방출형 인디케이터 그리기 MQL5로 방출형 인디케이터 그리기
이 글에서는 새로운 시장 조사 접근법인 방출형 인디케이터에 대해 알아보겠습니다. 방출은 서로 다른 인디케이터의 교차점을 기반으로 계산됩니다. 각각의 틱 다음에 형형색색의 점이 나타나죠. 이 점들이 모여 성운, 구름, 궤도, 직선, 포물선 등의 형태를 갖는 클러스터를 형성합니다. 클러스터의 모양에 따라 시장 가격의 변화에 영향을 미치는, 눈에는 보이지 않는 원동력을 어느 정도 감지할 수 있죠.
초보자를 위한 간편 스타트 가이드 초보자를 위한 간편 스타트 가이드
여러분, 안녕하세요! 엑스퍼트 어드바이저 생성 방식이나 인디케이터 활용법을 쉽고 빠르게 이해할 수 있도록 돕고자 이번 글을 씁니다. 이 글은 초보자를 대상으로 하며 복잡하거나 난해한 예제는 포함하지 않습니다.