Обмен данными между dll на C# и Metatrader 4

Категория: Metatrader
Опубликовано 22.09.2012 00:20

Эта статья является логическим продолжением первой и также скопирована с форума.

Простые данные int, double передаются и возвращаются без проблем, тут вопросов быть не должно. Также можно передать ссылку на массив:

#import "Test.dll"
	void Test(int& arr[]);
#import
int arr[5];
void start()
{
	Test(arr);
}
[System.Reflection.Obfuscation(Feature = "DllExport")]
public static unsafe void Test(int* i)
{
	i[2] = 5;
}

Как видно из примера, в код C# нужно добавить unsafe, чтобы он начал работать с указателями.

Многоуровневые массивы тоже можно передавать, но в C# с ними работать, как с одноуровневыми.
Например в mql мы создали массив Arr[2, 2]. В дллке он будет выглядеть так:

Arr[0] - это Arr[0, 0]
Arr[1] - это Arr[0, 1]
Arr[2] - это Arr[1, 0]
Arr[3] - это Arr[1, 1]

Данные bool я передаю как int, потому что в mql4 bool по сути и есть int.

Со строками сложнее.
В mql4:

Строковая константа представляет собой последовательность символов кода ASCII. Внутреннее представление - структура размером 8 байт. Первый элемент структуры - длинное целое, содержит размер распределенного для строки буфера. Второй элемент структуры - 32-разрядный адрес буфера, содержащего строку.

В NET

Представляет текст как последовательность знаков Юникода.

Тем не менее передача по значению никаких проблем не должна вызвать.

#import "Test.dll"
	void Test(string);
#import
[System.Reflection.Obfuscation(Feature = "DllExport")]
public static void Test1(string str)
{
	System.Windows.Forms.MessageBox.Show(str);
}

Возвращать строку через функцию метаквоты не рекомендуют. Но можно передать строку и уже в C# ее изменить. Перед передачей строку надо разметить минимум на тот размер, который хотим использовать.

#import "Test.dll"
	void Test(string);
#import
string str="                                                                                          ";
void start()
{
	Test(str);
	Print(str);
}
[System.Reflection.Obfuscation(Feature = "DllExport")]
public static unsafe void Test(byte* c)
{
	string str = "String";
	Encoding enc = Encoding.GetEncoding(1251);
	byte[] bt = enc.GetBytes(str);
	for (int i = 0; i < bt.Length; i++)
		c[i] = bt[i];
	c[bt.Length] = (byte)'x0';
}

 То есть получаем строку, как указатель на массив byte. И потом заполняем его нужными значениями, предварительно эти значения преобразовав к кодировке ANSI. В конце ставим знак завершения, чтобы метатрейдер увидел окончание строки.

 

 Также можно работать с массивами string. Элемент [1] начинается с адреса (размер элемента [0] + 1).

Передача таймсерий

Используем функцию ArrayCopyRates. К сожалению работать в C# с полученным при копировании массивом просто так не получится. Потому что это структура, в которой 5 элементов имеют размер 8 байт, а время бара имеет размер 4 байта. Экономия в 4 байта несколько усложнила задачу.

MQL4

#import "Test.dll"
	void Test(double& Arr[],int);
#import
double Arr[,6];
 
void start()
{
	int i=ArrayCopyRates(Arr);
	Test(Arr,i);
}

То есть копируем таймсерию текущего таймфрейма и символа. Полученный массив отправляем в длл.

C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
 
namespace Test
{
	public class Class1
	{
 
		[System.Reflection.Obfuscation(Feature = "DllExport")]
		public static unsafe void Test(IntPtr Arr, int i)
		{
			RateInfo ri = new RateInfo(Arr, i);
 
			double Time = ri[0, 0];
			double Open = ri[0, 1];
			double Low = ri[0, 2];
			double High = ri[0, 3];
			double Close = ri[0, 4];
			double Volume = ri[0, 5];
			String[] str = { "Time=", Time.ToString(), "rn", "Open=", Open.ToString(), "rn", "Low=", Low.ToString(), "rn",
			"High=", High.ToString(), "rn", "Close=", Close.ToString(), "rn", "Volume=", Volume.ToString()};
			System.Windows.Forms.MessageBox.Show(String.Concat(str));
		}
	}
 
	//структура
	unsafe struct RateInfo
	{
		byte* EntPoint;
		int Rate;
 
		//конструктор
		public RateInfo(IntPtr addr, int i)
		{
			EntPoint = (byte*)addr.ToPointer();
			Rate = i;
		}
 
		//индексатор
		public double this[int x, int y]
		{
			get
			{
				if (x >= Rate)
					return 0;
				if (y == 0)
					return *(uint*)(EntPoint + 44 * (Rate - 1 - x));
				return *(double*)(EntPoint + 44 * (Rate - 1 - x) + 4 + (y - 1) * 8);
			}
		}
	}
}

Значит имеем структуру, в которой есть индексатор. Он и позволяет выводить данные. Компилируем скрипт и библиотеку, проверяем и видим, что все выводится нормально:

Методы преобразования данных

Метод конвертации структуры Color(.Net) к формату color MT4

public static int ColorToMT4Color(Color clr)
{
	return clr.R + clr.G * 256 + clr.B * 65536;
}

Метод конвертации структуры DateTime(.Net) к формату datetime MT4(Unix)

public static int DateTimeToMT4DateTime(DateTime dt)
{
	DateTime Point = new DateTime(1970, 1, 1, 0, 0, 0);
	TimeSpan ts = dt.Subtract(Point);
	if (ts.Ticks > 0)
		return (int)ts.TotalSeconds;
	return 0;
}

Метод конвертации времени формата MT4(Unix) в структуру DateTime(.Net)

public static DateTime MT4DateTimeToDateTime(int mt4time)
{
	DateTime Out = new DateTime(1970, 1, 1, 0, 0, 0);
	return Out.AddSeconds(mt4time);
}