Библиотеки: File Mapping без DLL - страница 11

 
o_o:

это называется не закрываем запись, а закрываем и удаляем файл .

поэтому

вы пытаетесь открывать то, чего уже нет.

Просмотрев код библиотеки я увидел что файл удаляется не только при непосредственном вызове функции Close() класса CMemMapFile, но и при удалении указателя на объект этого класса, так как в деструкторе класса вызывается данная функция. Я немного в недоумении. Получается что динамический метод создания объектов класса использован быть не может, если запись и чтение файла используются в разных контекстах вызова (областей видимости). К примеру один график терминала записывает данные в файл, второй читает данные и удаляет этот файл. Получается что переменную объекта нужно всегда держать на глобальном уровне, чтобы файл принудительно не удалялся. Так же не ясно можно ли обходиться без указания размера считываемых данных. То есть при записи размер данных нам известен, но при чтении на другом графике нам заранее размер данных может быть не известен, как например в случае со строчными величинами. Вероятно либо я что-то недопонял, либо есть что еще подкрутить в библиотеке.

Виноват. Перепроверил без использования указателя, следовательно без применения delete. В этом случае при выходе из области видимости (из функции) локальная переменная объекта класса уничтожается без явного вызова деструктора.

Остался вопрос с размером принятых данных на стороне получателя. 

 
fxsaber:

Спасибо автору за библиотеку!

Сварганил функции для передачи любых данных. Ниже скрипт показывает их работу на примере тиков


Результат


Супер! По аналогии с Вашим кодом упростил для себя использование библиотеки.

 
Пример передачи цен для MT4

Forum on trading, automated trading systems and testing trading strategies

NamedPipes for metatrader 4

fxsaber, 2017.11.30 14:18

Exchange_Data.mqh

#include <MemMapLib.mqh>
#include <TypeToBytes.mqh>

template <typename T>
class EXCHANGE_DATA
{
private:
  CMemMapFile* FileMemory;

public:  
  // Выделяет память заданной длины под данные 
  EXCHANGE_DATA( const int Amount, const bool ModeCreate = false, string FileName = "Local\\test" )
  {
//    FileName += _Symbol;
    
    this.FileMemory = new CMemMapFile;
      
    if (this.FileMemory.Open(FileName, sizeof(T) * Amount + sizeof(int) + HEAD_MEM, ModeCreate ? modeCreate : modeOpen) != 0)
    {
      Alert("FileMemory.Open - ERROR!");
      
      delete &this;
    }
  }
  
  ~EXCHANGE_DATA( void )
  {
    this.FileMemory.Close();
    
    delete this.FileMemory;
  }

  // Записывает данные в память
  void DataSave( const T &Data[], const bool FromBegin = true  ) const
  {
    const int Size = ::ArraySize(Data) * sizeof(T);
    uchar Bytes[];
    
    _ArrayCopy(Bytes, _R(Size).Bytes);              // Записали количество  
    _ArrayCopy(Bytes, _R(Data).Bytes, sizeof(int)); // Записали данные
  
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        this.FileMemory.Write(Bytes, ::ArraySize(Bytes)); // Сбросили все в память
    
    return;
  }
  
  // Считывает данные из памяти
  int DataLoad( T &Data[], const bool FromBegin = true ) const
  {
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        uchar Bytes[];
          
        this.FileMemory.Read(Bytes, sizeof(int));  // Прочли из памяти количество данных        
        this.FileMemory.Read(Bytes, _R(Bytes)[0]); // Получили сами данные
  
        _ArrayCopy(Data, Bytes);              // Сбросили данные в массив
    
    return(::ArraySize(Data));
  }  
};


PriceGiver.mq4

#property strict

#include "Exchange_Data.mqh"

#define AMOUNT 100

EXCHANGE_DATA<MqlTick> ExchangeTicks(AMOUNT, true);

const bool Init = EventSetMillisecondTimer(100);

void OnTimer( void )
{
  static MqlTick Ticks[1];
  
  if (SymbolInfoTick(_Symbol, Ticks[0]))
    ExchangeTicks.DataSave(Ticks);
}


PriceTaker.mq4

#property strict

#include "Exchange_Data.mqh"

#define AMOUNT 100

EXCHANGE_DATA<MqlTick> ExchangeTicks(AMOUNT);

const bool Init = EventSetMillisecondTimer(100);

#define TOSTRING(A) (#A + " = " + (string)(A) + " ")

void OnTimer( void )
{  
  static MqlTick PrevTick = {0};  
  MqlTick Ticks[];
  
  if ((ExchangeTicks.DataLoad(Ticks) > 0) &&
      ((Ticks[0].bid != PrevTick.bid) || (Ticks[0].ask != PrevTick.ask)))
  {
    Print(TOSTRING(Ticks[0].time) + TOSTRING(Ticks[0].bid) + TOSTRING(Ticks[0].ask));
    
    PrevTick = Ticks[0];
  }
}


Run PriceGiver.ex4 and PriceTaker.ex4.


Result

2017.11.30 15:13:55.101 Expert PriceGiver EURUSD,M1: removed
2017.11.30 15:13:55.091 PriceGiver EURUSD,M1: uninit reason 1
2017.11.30 15:13:51.006 Expert PriceTaker GBPAUD,M1: removed
2017.11.30 15:13:50.996 PriceTaker GBPAUD,M1: uninit reason 1
2017.11.30 15:13:49.168 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:41 Ticks[0].bid = 1.18483 Ticks[0].ask = 1.18487 
2017.11.30 15:13:48.838 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:41 Ticks[0].bid = 1.18484 Ticks[0].ask = 1.18489 
2017.11.30 15:13:48.186 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:40 Ticks[0].bid = 1.18483 Ticks[0].ask = 1.18487 
2017.11.30 15:13:47.751 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:40 Ticks[0].bid = 1.18484 Ticks[0].ask = 1.18488 
2017.11.30 15:13:42.178 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:34 Ticks[0].bid = 1.18485 Ticks[0].ask = 1.18489 
2017.11.30 15:13:41.633 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:34 Ticks[0].bid = 1.18484 Ticks[0].ask = 1.18488 
2017.11.30 15:13:37.588 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:30 Ticks[0].bid = 1.18483 Ticks[0].ask = 1.18487 
2017.11.30 15:13:36.175 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:28 Ticks[0].bid = 1.18481 Ticks[0].ask = 1.18485 
2017.11.30 15:13:30.717 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:23 Ticks[0].bid = 1.18482 Ticks[0].ask = 1.18486 
2017.11.30 15:13:29.514 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:22 Ticks[0].bid = 1.18483 Ticks[0].ask = 1.18487 
2017.11.30 15:13:27.324 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:19 Ticks[0].bid = 1.1848 Ticks[0].ask = 1.18484 
2017.11.30 15:13:26.994 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:19 Ticks[0].bid = 1.18482 Ticks[0].ask = 1.18486 
2017.11.30 15:13:26.012 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:18 Ticks[0].bid = 1.18481 Ticks[0].ask = 1.18485 
2017.11.30 15:13:25.584 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:18 Ticks[0].bid = 1.18482 Ticks[0].ask = 1.18486 
2017.11.30 15:13:25.254 PriceTaker GBPAUD,M1: Ticks[0].time = 2017.11.30 15:13:16 Ticks[0].bid = 1.18481 Ticks[0].ask = 1.18485 
2017.11.30 15:13:25.147 PriceTaker GBPAUD,M1: initialized
2017.11.30 15:13:24.049 Expert Sparring\PriceTaker GBPAUD,M1: loaded successfully
2017.11.30 15:13:21.157 PriceGiver EURUSD,M1: initialized
2017.11.30 15:13:19.617 Expert Sparring\PriceGiver EURUSD,M1: loaded successfully
 

В библиотеке небольшая ошибочка в методе CMemMapFile::Open.  Он должен возвращать хэндл файла (тип HANDLE64), но вместо этого возвращает 0 либо код ошибки.

Кроме того, крайне нерационально реализованы чтение и запись.  В методах CMemMapApi::Write и CMemMapApi::Read  зачем-то сделано двойное перекопирование данных (причём байты перебираются в цикле!), и вдобавок файл перезаписывается/перечитывается целиком, хотя требуется лишь заданный кусок.

В общем я привёл их в нормальный вид, лишнее закомментировано :

//------------------------------------------------------------------    Write
int CMemMapApi::Write(HANDLE64 hmem, const uchar &buf[], DWORD pos, int sz, DWORD &err) // запись в память указанное число байт
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // если не открыт
  DWORD size=GetSize(hmem, err);  if (pos+sz>size) { UnViewFile(view);  return(-2); }; // если размер меньше, то выходим
  /*    
  uchar src[];  ArrayResize(src, size);  memcpyX(src, view, size); // взяли байтбуфер
  for(int i=0; i<sz; i++) src[pos+i+HEAD_MEM]=buf[i]; // записали в память
  memcpyX(view, src, size); // скопировали обратно
  */    
  memcpyX(view+HEAD_MEM+pos, buf, sz);  
  UnViewFile(view); // закрыли просмотр
  return(0); // вернули ОК
}
//------------------------------------------------------------------    Read
int CMemMapApi::Read(HANDLE64 hmem, uchar &buf[], DWORD pos, int sz, DWORD &err) // чтение из памяти указанное число байт
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // если не открыт
  DWORD size=GetSize(hmem, err); // получили размер
  /*    
  uchar src[];  ArrayResize(src, size);  memcpyX(src, view, size); // взяли байтбуфер
  ArrayResize(buf, sz);
  int i=0; for(i=0; i<sz && pos+i<size; i++) buf[i]=src[pos+i+HEAD_MEM]; // читаем байты
  */    
  sz= fmin(sz, size-pos);
  ArrayResize(buf, sz);
  memcpyX(buf, view+HEAD_MEM+pos, sz);
  UnViewFile(view); // закрыли просмотр
  return sz; // число скопированных байт
}
Чтобы первая функция компилировалась, нужно расставить const у массивов в функциях memcpyX и memcpy , которые автор не удосужился проставить.
 

Ошибка при копировании больших объемов, из-за нуля, который передается в dwMaximumSizeHigh

        if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, 0, size + HEAD_MEM, path); // создаем объект памяти

Поправил так:

if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, size + HEAD_MEM, size + HEAD_MEM, path); // создаем объект памяти


Такой объем - 6 мб, передает без проблем:

void OnStart()
{
   int bars = Bars(_Symbol, _Period);
   MqlRates rates[]; 
   ArraySetAsSeries(rates, true); 
   CopyRates(NULL, 0, 0, bars, rates); 

   int size = ArraySize(rates) * sizeof(MqlRates);
   
   uchar data[];
   _ArrayCopy(data, rates);
   
   CMemMapFile *hmem = new CMemMapFile();
   hmem.Open("test", size, modeCreate);
   hmem.Write(data, size);
   hmem.Close(); 
   delete hmem;

   Print(size);
}


 
pushdib:

Поправил так:

правильно передавать туда старшие 4 байта 8-байтного размера.

 

Ок, в проге на C# теперь имеем все рэйты и можем с помощью LINQ отлично все что нужно анализировать.

Но есть такой вопрос, а как лучше организовать механизм команд между терминалом и приложением.

От терминала: новая свеча, новые рэйты - забери файл

От приложения: расчет завершен, забери  результат (нарисуй на графике, открой сделку)


У кого-нибудь есть опыт подобной реализации взаимодействия терминала и кода?

 
pushdib:

Ок, в проге на C# теперь имеем все рэйты и можем с помощью LINQ отлично все что нужно анализировать.

Но есть такой вопрос, а как лучше организовать механизм команд между терминалом и приложением.

От терминала: новая свеча, новые рэйты - забери файл

От приложения: расчет завершен, забери  результат (нарисуй на графике, открой сделку)


У кого-нибудь есть опыт подобной реализации взаимодействия терминала и кода?

Сделал все это через пайп - работает как часы.
 
Это просто гениально, спасибо за труды! -)). Пришлось заодно учить, как работать, с бинарными файлами, но это того стоило. 
 

Подскажите, что делать в такой ситуации.

1. Открыл новый файл в памяти на 100 байт.

2. Записал туда 100 байт.

3. Считал  в другом советнике 100 байт. Все отлично.

4. Как теперь записать в тот же файл 50 или 200 байт ?