Як відкрити світ C # з MQL5 шляхом експорту некерованого коду
Вступ
Довгий час я шукав просте рішення, яке дозволило б мені використовувати в MQL5 керовані (managed) DLL, написані на C #. Після читання безлічі статей, коли я вже був готовий реалізувати обгортку (wrapper) для керованої DLL на С ++, я натрапив на блискуче рішення, заощадити мені багато годин роботи.
Пропоноване рішення є простим прикладом експорту керованого C #-коду для некерованого (unmanaged) додатки. У даній статті я розгляну основи роботи керованих DLL, поясню причини, через які вони не можуть бути використані безпосередньо в MetaTrader 5, і запропоную знайдені мною рішення, які дозволять використовувати керований код з MetaTrader 5.
Я приведу приклад найпростішого використання шаблонів експорту некерованого коду (unmanaged exports) і розгляну моменти, які я виявив. Це може послужити основою для всіх, хто намагається використовувати в MetaTrader 5 бібліотеки DLL, написані на C #.
1. Керований і некерований код
Оскільки більшість читачів можуть бути не знайомі з відмінностями між керованим і некерованим кодом, коротко опишемо їх. Для реалізації торгівлі, індикаторів, радників і скриптів в MetaTrader 5 в основному використовується мова MQL5. Крім того, існує можливість використання (і динамічного завантаження) готових бібліотек, написаних на інших мовах. Ці бібліотеки також відомі як DLL або спільні бібліотеки .
Бібліотеки по суті є бінарними файлами, що містять скомпільований код, який може бути викликаний безліччю зовнішніх програм для проведення специфічних операцій. Наприклад, бібліотека нейромережі може експортувати функції тренування і тестування нейромережі, бібліотека для обчислення похідних може експортувати функції розрахунку різних похідних, матрична бібліотека може експортувати операції над матрицями.
Зростаючий інтерес до використання DLL в MetaTrader 5 обумовлений можливістю приховування деяких частин реалізації індикаторів або радників. Основна причина використання бібліотек - можливість повторного використання коду без необхідності його постійного переписування.
До появи платформи .NET всі існуючі DLL-бібліотеки компілювалися за допомогою Visual Basic, Delphi, VC ++, були реалізовані в вигляді COM , Win32, при цьому вони могли виконуватися безпосередньо операційною системою. Далі такий код ми будемо називати некерованим (unmanaged) або нативним (native) кодом.
Потім з'явилася платформа .NET з абсолютно іншим оточенням. Код контролюється (або управляється) загальномовного виконуючого середовищем (Common Language Runtime, CLR ). З вихідного коду, який може бути написаний на декількох різних мовах, CLR компілятори генерують код і метадані в проміжному мовою CIL (Common Intermediate Language).
CIL є машинно-незалежним мовою високого рівня, а метадані повністю описують типи об'єктів, оперованих CIL в форматі загальної специфікації типів (Common Type Specification, CTS ). Оскільки CLR відомо все про типах, він може надати нам кероване оточення виконання. Управління можна розглядати як збірку сміття - автоматичне керування пам'яттю, видалення об'єктів і забезпечення безпеки від загальних помилок native-мов, які можуть бути обумовлені виконанням чужорідного коду з привілеями адміністратора або простим перекриттям областей пам'яті.
Необхідно відзначити, що код на CIL ніколи не виконується безпосередньо, він завжди транслюється в native-код в режимі компіляції "на льоту" ( JIT , Just-In-Time) або попередньої компіляцією CIL в native-код:
Малюнок 1. Загальномовне керуюча середовище (CLR, Common Language Runtime)
2. Можливі варіанти реалізації доступу до керованого коду з MQL5
У наступних параграфах я розгляну методи, що дозволяють отримати доступ до керованого коду з некерованого коду.
Я вважаю, варто згадати всі відомі методи, можливо хтось вважатиме за краще використовувати інший спосіб, відмінний від того, який використовую я. Відомі методи: COM Interop, Reverse P / Invoke, C ++ IJW, C ++ / Cli class wrapper і Unmanaged Exports.
2.1. COM Interop (COM-взаємодія)
Об'єктна модель компонентів (Component Object Model, COM ) - це стандарт інтерфейсу, запропонований Microsoft на початку 90-х. Ключова ідея цієї технології полягає в можливості використання об'єктів, написаних на різних мовах програмування будь-яким іншим COM-об'єктом без знання його внутрішньої реалізації. Такі вимоги призводять до використання строго заданого інтерфейсу COM, повністю відокремленого від реалізації.
Фактично COM був замін технологією .NET, а Microsoft підштовхує до використання .NET замість COM. Для забезпечення зворотної сумісності зі старим кодом .NET може здійснювати взаємодію з COM в обох напрямках: .NET може викликати методи COM об'єкта, а COM об'єкт може використовувати керований код .NET.
Ця функціональність називається COM-взаємодія (COM Interoperability) або COM Interop. Програмний інтерфейс (API) COM-взаємодії знаходиться в просторі імен (namespace) System.Runtime.InteropServices.
Малюнок 2. Модель COM-взаємодії (COM Interoperability)
Наведений нижче код COM-взаємодії викликає єдину функцію raw_factorial.
Будь ласка, зверніть увагу на функції CoInitialize (), CoCreateInstance () і CoUninitialize () і функцію виклику інтерфейсу:
Для подальшого читання про COM-взаємодії рекомендую почитати документацію в статті Introduction to COM Interop і приклад використання How to call C ++ code from Managed, and vice versa (Interop) , Знайдений мною на блозі MSDN.
2.2. Reverse P / Invoke (передзвонити некерованого коду)
Виклик некерованого коду (Platform Invoke, P / Invoke) дозволяє .NET викликати будь-яку функцію некерованого мови відповідно до її сигнатурою. Це здійснюється виконанням нативной функції з .NET. Використання добре пояснено в статті Platform Invoke Tutorial .
В основі лежить використання атрибута DllImport для маркування імпортованої функції:
Зворотна дія може бути здійснено через керовану callback-функцію в некерований код.
Це називається зворотним викликом некерованого коду (Reverse P / Invoke) і досягається реалізацією публічного делегата (delegate) з керованою середовища і імпортом викликається функції, реалізованої в нативної DLL.
Приклад керованого коду:
Основною особливістю даного рішення є те, що воно вимагає, щоб виконання почав керований код.
Для подальшого вивчення почитайте Gotchas with Reverse Pinvoke (unmanaged to managed code callbacks) і PInvoke-Reverse PInvoke and stdcall - cdecl .
2.3. C ++ IJW
С ++ взаємодія (C ++ interop), також відомий як IJW (It Just Works) є спеціальною можливістю Managed Extensions for C ++ :
# Using <mscorlib.dll> using namespace System; using namespace System :: Runtime :: InteropServices; #include <stdio.h> int main () {String * pStr = S "Hello World!" ; char * pChars = (char *) Marshal :: StringToHGlobalAnsi (pStr) .ToPointer (); puts (pChars); Marshal :: FreeHGlobal (pChars); }Це рішення може бути корисно тим, хто хоче використовувати свій керований C ++ код в native-додатках. Для подальші вивчення подивіться статті Interoperability in Managed Extensions for C ++ і Using IJW in Managed C ++ .
2.4. C ++ / Cli wrapper class (Клас обгортки C ++ / Cli)
Реалізація класу за допомогою обгортки C ++ / Cli полягає у вкладенні (embedding) або "обгортці" (wrapping) керованого класу іншим класом, написаним в режимі C ++ / Cli.
Першим кроком до написання DLL-обгортки є написання класу на C ++, який обгортає методи вихідного керованого класу. Клас обгортки повинен містити хендл об'єкта .NET за допомогою шаблону gcroot <> і повинен делегувати всі виклики з оригінального класу. Клас обгортки є керованим, оскільки він компілюється в форматі IL (проміжного мови).
Наступним кроком є написання native-класу на С ++ з директивою "#pragma unmanaged", яка обгортає клас в IL-коді і делегує всі виклики за допомогою директиви __declspec (dllexport) . Ці кроки створять native-DLL, яка може бути використана будь-яким native-додатком.
Будь ласка, подивіться приклад реалізації. Першим кроком є реалізація коду на C #.
Цей приклад класу Calculator містить два public-методу:
Наступний крок - написання керованої обгортки (managed wrapper), яка буде делегувати всі методи класу Calculator:
#pragma once #pragma managed #include <vcclr.h> class ILBridge_CppCliWrapper_Calculator {private: gcroot <CppCliWrapper :: Calculator ^> __Impl; public: ILBridge_CppCliWrapper_Calculator () {__Impl = gcnew CppCliWrapper :: Calculator; } Int Add (int first, int second) {System :: Int32 __Param_first = first; System :: Int32 __Param_second = second; System :: Int32 __ReturnVal = __Impl-> Add (__ Param_first, __Param_second); return __ReturnVal; } Wchar_t * FormatAsString (float i) {System :: Single __Param_i = i; System :: String __ReturnVal = __Impl-> FormatAsString (__ Param_i); wchar_t * __MarshaledReturnVal = marshal_to <wchar_t *> (__ ReturnVal); return __MarshaledReturnVal; }};Зверніть увагу на те, що посилання на вихідний клас Calculator розміщуються за допомогою інструкції gcnew і зберігаються як шаблон gcroot <>. Всі загорнуті методи можуть мати ті ж імена і параметри, що й оригінальні методи, і повертати значення, що передують __Param і __ReturnVal відповідно.
Тепер потрібно реалізувати клас на C ++ (некерований код), який обгортає C ++ / Cli і експортує native-методи в DLL.
Файл заголовка повинен містити визначення класу з директивою __declspec (dllexport) і зберігати покажчик на клас обгортки.
#pragma once #pragma unmanaged #ifdef THISDLL_EXPORTS #define THISDLL_API __declspec (dllexport) #else #define THISDLL_API __declspec (dllimport) #endif class ILBridge_CppCliWrapper_Calculator; class THISDLL_API NativeExport_CppCliWrapper_Calculator {private: ILBridge_CppCliWrapper_Calculator * __Impl; public: NativeExport_CppCliWrapper_Calculator (); ~ NativeExport_CppCliWrapper_Calculator (); int Add (int first, int second); wchar_t * FormatAsString (float i); }; І його реалізація:
Покрокове керівництво для створення цього класу обгортки розглянуто в статті .NET to C ++ Bridge .
Повне керівництво для створення обгорток викладено в статті Mixing .NET and native code . З загальною інформацією про декларації хендлом в native-типах можна ознайомитися в статті How to: Declare Handles in Native Types .
2.5. Unmanaged exports (Експорт некерованого коду)
Ця техніка повністю описана в книзі Expert .NET 2.0 IL Assembler , Яку я рекомендую всім, хто хотів би почитати про деталі роботи компілятора .NET. Основна ідея полягає у відкритті керованих методів як експорт некерованого коду (unmanaged exports) в керованої DLL шляхом декомпіляцію (за допомогою ILDasm) скомпільованого модуля в IL-код, зміни таблиць VTable і VTableFixup модуля і повторна компіляція DLL за допомогою ILAsm.
Це завдання може здаватися дуже складною, але результатом буде створення DLL, яка може бути використана з будь-якого некерованого native-коду. Потрібно пам'ятати про те, що ця збірка все ще є керованою (managed), тому повинна бути встановлена платформа .NET. Покрокове керівництво викладено в статті Export Managed Code as Unmanaged .
Після декомпіляцію DLL за допомогою ILDasm ми отримаємо вихідний код на проміжному мовою IL. Подивіться на простий приклад коду на IL з експортом некерованого коду:
Рядки исходника на IL, що відповідають за реалізацію некерованого експорту:
..vtfixup [1] int32 fromunmanaged at VT_01 ..data VT_01 = int32 (0) і
Перша частина відповідає за додавання точки входу в функцію в таблиці VTableFixup і установку в VT_01 ВА функції. Друга частина вказує на те, який саме VTEntry буде використовуватися для цієї функції і псевдонім експортованої функції (export alias).
В процесі реалізації DLL у нас не було необхідності реалізовувати ніякої додатковий код за винятком звичайного керованого коду C # в DLL. Як зазначено в книзі Expert .NET 2.0 IL Assembler , Цей метод повністю відкриває світ керованого коду з усією його безпекою та бібліотеками класів для програм, написаних на native-коді.
Недоліком є те, що робота з проміжним IL-мовою підходить не для всіх. Я був упевнений в тому, що мені доведеться писати обгортку у вигляді клас на C ++, поки я не знайшов шаблон Unmanaged exports, створений Robert Giesecke http://sites.google.com/site/robertgiesecke/ , Який дозволяє використовувати експорт некерованого коду (unmanaged exports) без необхідності роботи з IL-кодом.
3. Шаблон Unmanaged exports на C #
У шаблонах проектів по створенню експорту некерованого коду від R.Giesecke використовується MSBuild task , Який автоматично додає створення згаданих вище модифікацій для VT, тому немає необхідності зміни коду на IL. Потрібно тільки завантажити проект шаблону (у вигляді zip-файлу) і скопіювати його вміст в папку ProjectTemplate середовища Visual Studio.
Після компіляції проекту отриману DLL можна без проблем використовувати в MetaTrader 5, в наступних розділах я приведу приклади.
4. Приклади
Завдання з'ясування того, як передавати змінні, масиви і структури між MetaTrader 5 і C # виявилася вельми непростою, тому я вважаю, що інформація, викладена тут, дозволить вам заощадити багато часу. Всі приклади були скомпільовані в Windows Vista з .NET 4.0 і Visual C # Express 2010. До статті додається приклад DLL і коду на MQL5, який викликає функції з DLL, написаної на C #.
4.1. Приклад 1. Додавання двох змінних типу integer, double або float в функції DLL і повернення результату в MetaTrader 5
using System; using System.Text; using RGiesecke.DllExport; using System.Runtime.InteropServices; namespace Testme {class Test {[DllExport ( "Add", CallingConvention = CallingConvention.StdCall)] public static int Add (int left, int right) {return left + right; } [DllExport ( "Sub", CallingConvention = CallingConvention.StdCall)] public static int Sub (int left, int right) {return left - right; } [DllExport ( "AddDouble", CallingConvention = CallingConvention.StdCall)] public static double AddDouble (double left, double right) {return left + right; } [DllExport ( "AddFloat", CallingConvention = CallingConvention.StdCall)] public static float AddFloat (float left, float right) {return left + right; }}}Як ви, можливо, помітили, кожної експортованої функції передує директива DllExport. Перший параметр описує псевдонім експортованої функції, а другий параметр задає спосіб виклику (calling convention), для MetaTrader 5 слід використовувати CallingConvention.StdCall.
Код на MQL5, який імпортує і використовує функції, експортовані з DLL, є простим і не відрізняється від коду, написаного в C ++. Спочатку потрібно оголосити імпортовані функції всередині блоку #import для вказівки списку функцій з DLL, які в подальшому будуть викликатися з коду на MQL5:
результат:
2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 664.50000 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 668.5 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 664 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 668 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 665.50000 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 667.5 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 ( EURUSD, M1) 665 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 667 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 666.50000 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 666.5 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 666 2011.01 .30 21: 28: 18 UnmanagedExportsDLLExample1 (EURUSD, M1) 6664.2. Приклад 2. Доступ до одновимірного масиву
[DllExport ( "Get1DInt", CallingConvention = CallingConvention.StdCall)] public static int Get1DInt ([MarshalAs (UnmanagedType.LPArray, SizeParamIndex = 1)] int [] tab, int i, int idx) {return tab [idx]; } [DllExport ( "Get1DFloat", CallingConvention = CallingConvention.StdCall)] public static float Get1DFloat ([MarshalAs (UnmanagedType.LPArray, SizeParamIndex = 1)] float [] tab, int i, int idx) {return tab [idx]; } [DllExport ( "Get1DDouble", CallingConvention = CallingConvention.StdCall)] public static double Get1DDouble ([MarshalAs (UnmanagedType.LPArray, SizeParamIndex = 1)] double [] tab, int i, int idx) {return tab [idx]; } Для передачі одновимірного масиву в директиві MarshalAs в якості першого параметра потрібно вказати UnmanagedType.LPArray, а в якості другого - SizeParamIndex. Параметр SizeParamIndex вказує на те, який параметр (починаючи з 0) є параметром, що містить розмір масиву.
У прикладах, описаних вище масив має розмір i, а в якості значення, що повертається функцією, використовується елемент масиву з індексом idx.
Приклад доступу до масиву на MQL5 наведено нижче:
результат:
4.3. Приклад 3. Заповнення одновимірного масиву і повернення результату в MetaTrader 5
[DllExport ( "SetFiboArray", CallingConvention = CallingConvention.StdCall)] public static int SetFiboArray ([MarshalAs (UnmanagedType.LPArray, SizeParamIndex = 1)] int [] tab, int len, [In, Out, MarshalAs (UnmanagedType.LPArray, SizeParamIndex = 1)] int [] res) {res [0] = 0; res [1] = 1; if (len <3) return - 1; for (int i = 2; i <len; i ++) res [i] = res [i- 1] + res [i- 2]; return 0; }У цьому прикладі використовуються два масиви для ілюстрації позначення вхідних параметрів. Якщо потрібно повернення елементів масиву (переданих за посиланням) в MetaTrader 5, досить вказати атрибути [In, Out,] перед атрибутом MarshalAs.
#property copyright "Copyright 2011, Investeo.pl" #property link "http: /Investeo.pl" #property version "1.00" #import "Testme.dll" int SetFiboArray (int & t [], int i, int & o []); #import void OnStart () {int fibo [10]; static int o [10]; for (int i = 0; i <4; i ++) {fibo [i] = i; o [i] = i; } SetFiboArray (fibo, 6, o); for (int i = 0; i <6; i ++) Print (IntegerToString (fibo [i]) + ":" + IntegerToString (o [i])); }результат:
2011.01 .30 22: 01: 39 UnmanagedExportsDLLExample3 (EURUSD, M1) 0: 5 2011.01 .30 22: 01: 39 UnmanagedExportsDLLExample3 (EURUSD, M1) 0: 3 2011.01 .30 22: 01: 39 UnmanagedExportsDLLExample3 (EURUSD, M1) 3: 2 2011.01 .30 22: 01: 39 UnmanagedExportsDLLExample3 (EURUSD, M1) 2: 1 2011.01 .30 22: 01: 39 UnmanagedExportsDLLExample3 (EURUSD, M1) 1: 1 2011.01 .30 22: 01: 39 UnmanagedExportsDLLExample3 (EURUSD, M1) 0 : 04.4. Приклад 4. Доступ до двовимірного масиву
public static int idx (int a, int b) {int cols = 2; return a * cols + b; } [DllExport ( "Set2DArray", CallingConvention = CallingConvention.StdCall)] public static int Set2DArray ([In, Out, MarshalAs (UnmanagedType.LPArray, SizeParamIndex = 1)] int [] tab, int len) {tab [idx (0 , 0)] = 0; tab [idx (0, 1)] = 1; tab [idx (1, 0)] = 2; tab [idx (1, 1)] = 3; tab [idx (2, 0)] = 4; tab [idx (2, 1)] = 5; return 0; } Маршаллінг двовимірного масиву не є настільки простим, але я використовував трюк: передавав двовимірний масив як одновимірний і здійснював доступ до елементів масиву за допомогою додаткової функції idx.
результат:
2011.01 .30 22: 13: 01 UnmanagedExportsDLLExample4 (EURUSD, M1) t2 [2] [1] = 5 2011.01 .30 22: 13: 01 UnmanagedExportsDLLExample4 (EURUSD, M1) t2 [2] [0] = 4 2011.01 .30 22 : 13: 01 UnmanagedExportsDLLExample4 (EURUSD, M1) t2 [1] [1] = 3 2011.01 .30 22: 13: 01 UnmanagedExportsDLLExample4 (EURUSD, M1) t2 [1] [0] = 2 2011.01 .30 22: 13: 01 UnmanagedExportsDLLExample4 (EURUSD, M1) t2 [0] [1] = 1 2011.01 .30 22: 13: 01 UnmanagedExportsDLLExample4 (EURUSD, M1) t2 [0] [0] = 04.5. Приклад 5. Заміна вмісту рядка
[DllExport ( "ReplaceString", CallingConvention = CallingConvention.StdCall)] public static int ReplaceString ([In, Out, MarshalAs (UnmanagedType.LPWStr)] StringBuilder str, [MarshalAs (UnmanagedType.LPWStr)] string a, [MarshalAs (UnmanagedType. LPWStr)] string b) {str.Replace (a, b); if (str.ToString (). Contains (a)) return 1; else return 0; }Незважаючи на те, що цей приклад короткий, його реалізація зайняла досить багато часу, оскільки спроби використання атрибутів [In, Out] або ключових слів ref і out не увінчалися успіхом.
Рішення полягає в використанні StringBuilder замість змінної string.
#property copyright "Copyright 2011, Investeo.pl" #property link "http: /Investeo.pl" #property version "1.00" #import "Testme.dll" int ReplaceString (string & str, string a, string b); #import void OnStart () {string str = "A quick brown fox jumps over the lazy dog"; string stra = "fox"; string strb = "cat"; Print (str); Print (ReplaceString (str, stra, strb)); Print (str); }результат:
2011.01 .30 22: 18: 36 UnmanagedExportsDLLExample5 (EURUSD, M1) A quick brown cat jumps over the lazy dog 2011.01 .30 22: 18: 36 UnmanagedExportsDLLExample5 (EURUSD, M1) 0 2011.01 .30 22: 18: 36 UnmanagedExportsDLLExample5 (EURUSD, M1) A quick brown fox jumps over the lazy dog4.6. Приклад 6. Передача і зміна структури типу MqlTick
private static List <MqlTick> list; [StructLayout (LayoutKind.Sequential, Pack = 1)] public struct MqlTick {public Int64 Time; public Double Bid; public Double Ask; public Double Last; public UInt64 Volume; } [DllExport ( "AddTick", CallingConvention = CallingConvention.StdCall)] public static int AddTick (ref MqlTick tick, ref double bidsum) {bidsum = 0,0; if (list == null) list = new List <MqlTick> (); tick.Volume = 666; list.Add (tick); foreach (MqlTick t in list) bidsum + = t.Ask; return list.Count; }Передача за посиланням структури типу MqlTick , Маркується за допомогою ключового слова ref. Опису структури MqlTick передує атрибут [StructLayout (LayoutKind.Sequential, Pack = 1)].
Параметр Pack вказує на вирівнювання даних в структурі, для деталей см. Опис StructLayoutAttribute.Pack Field .
#property copyright "Copyright 2011, Investeo.pl" #property link "http: /Investeo.pl" #property version "1.00" #import "Testme.dll" int AddTick (MqlTick & tick, double & bidsum); #import int OnInit () {return (0); } Int OnCalculate (const int rates_total, const int prev_calculated, const datetime & time [], const double & open [], const double & high [], const double & low [], const double & close [], const long & tick_volume [], const long & volume [], const int & spread []) {MqlTick newTick; double bidsum; SymbolInfoTick (Symbol (), newTick); Print ( "before =" + IntegerToString (newTick.volume)); Print (AddTick (newTick, bidsum)); Print ( "after =" + IntegerToString (newTick.volume) + ":" + DoubleToString (bidsum)); return (rates_total); }результат:
2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) after = 666: 8.167199999999999 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) 6 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) before = 0 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) after = 666: 6.806 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) 5 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) before = 0 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) after = 666: 5.4448 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) 4 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) before = 0 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) after = 666: 4.0836 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) 3 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1 ) before = 0 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) after = 666: 2.7224 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) 2 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) before = 0 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) after = 666: 1.3612 2011.01 .30 23: 59: 05 TickDLLSend (EURUSD, M1) 1 2011.01 .30 23: 59: 04 TickDLLSend (EURUSD, M1) before = 0Висновки
У даній статті я представив різні методи взаємодії між кодом, написаним на MQL5, і керованим кодом на C #.
Також я підготував кілька прикладів маршалинга структур MQL5 для C # і прикладів виклику експортованих функцій DLL в скриптах на MQL5. Я вважаю, наведені приклади можуть служити основою для подальших досліджень аспектів написання DLL в керованому коді.
Ця стаття також відкриває двері для використання в MetaTrader 5 безлічі бібліотек, вже реалізованих на C #. Для більш глибокого ознайомлення, будь ласка, прочитайте статті в розділі "Література".
Для тестування роботи додаються програм розмістіть файли в наступних каталогах:
MQL5 \ Libraries \ testme.dllMQL5 \ Scripts \ unmanagedexportsdllexample1.mq5
MQL5 \ Scripts \ unmanagedexportsdllexample2.mq5
MQL5 \ Scripts \ unmanagedexportsdllexample3.mq5
MQL5 \ Scripts \ unmanagedexportsdllexample4.mq5
MQL5 \ Scripts \ unmanagedexportsdllexample5.mq5
MQL5 \ Experts \ unmanagedexportsdllexample6.mq5
література
- P / Invoke і 64-бітна розробка
- Використання P / Invoke: ховаємо кнопку Пуск і панель завдань в Windows
- .NET в unmanaged оточенні: platform invoke або що таке LPTSTR
- Маршалинга даних при виклику некерованого коду
- маршалинга взаємодії
- Взаємодія з некерованим кодом
- Розділи практичних посібників, що описують взаємодію з некерованим кодом
- Exporting .NET DLLs with Visual Studio 2005 to be Consumed by Native Applications
- Interoperating with Unmadged Coded
- Introduction to COM Interop
- Component Object Model (COM)
- Exporting from a DLL Using __declspec (dllexport)
- How to: Declare Handles in Native Types
- How to call C ++ code from Managed, and vice versa (Interop)
- Reverse P / Invoke and exception
- How to call a managed DLL from native Visual C ++ code in Visual Studio.NET or in Visual Studio 2005
- Platform Invoke Tutorial
- PInvoke-Reverse PInvoke and __stdcall - __cdecl
- Gotchas with Reverse Pinvoke (unmanaged to managed code callbacks)
- Mixing .NET and native code
- Export Managed Code as Unmanaged
- Understanding Classic COM Interoperability With .NET Applications
- Managed Extensions for C ++ Programming
- Robert Giesecke's site
- MSBuild Tasks
- Common Language Runtime
Переклад з англійської проведений MetaQuotes Software Corp.
Оригінальна стаття: https://www.mql5.com/en/articles/249