Libraries: File Mapping without DLL - page 11

 
o_o:

it's not called closing the record, it's called closing and deleting the file.

which is why

you are trying to open something that doesn't exist anymore.

Looking through the library code I saw that the file is deleted not only when the Close() function of the CMemMapFile class is called directly, but also when the pointer to the object of this class is deleted, because this function is called in the class destructor. I am a bit perplexed. It turns out that the dynamic method of creating class objects cannot be used if writing and reading a file are used in different calling contexts (scopes). For example, one chart of the terminal writes data to a file, the second reads data and deletes this file. It turns out that the object variable should always be kept at the global level, so that the file is not forcibly deleted. It is also not clear whether it is possible to do without specifying the size of the read data. That is, we know the size of the data when writing it, but when reading it on another chart we may not know the size of the data in advance, as for example in the case of string values. Probably either I have misunderstood something or there is something else to tweak in the library.

My bad. I rechecked it without using the pointer and therefore without using delete. In this case, when leaving the scope (from a function), the local variable of the class object is destroyed without an explicit call to the destructor.

There is still a question about the size of received data on the receiver's side.

 
fxsaber:

Thanks to the author for the library!

I made functions for transferring any data. The script below shows their work on the example of ticks


Result


Super! By analogy with your code I simplified the use of the library for myself.

 
Example of price transfer for 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:  
  // Allocates memory of the specified length for the data 
  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;
  }

  // Writes data to memory
  void DataSave( const T &Data[], const bool FromBegin = true  ) const
  {
    const int Size = ::ArraySize(Data) * sizeof(T);
    uchar Bytes[];
    
    _ArrayCopy(Bytes, _R(Size).Bytes);              // Recorded the quantity 
    _ArrayCopy(Bytes, _R(Data).Bytes, sizeof(int)); // Recorded the data
  
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        this.FileMemory.Write(Bytes, ::ArraySize(Bytes)); // Dumped everything into memory
    
    return;
  }
  
  // Reads data from memory
  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));  // Read the amount of data from memory 
        this.FileMemory.Read(Bytes, _R(Bytes)[0]); // Got the data itself
  
        _ArrayCopy(Data, Bytes);              // Dumped the data into an array
    
    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
 

The library has a small error in the CMemMapFile::Open method. It should return the file handle (type HANDLE64), but instead it returns 0 or an error code.

In addition, the read and write methods CMemMapApi::Write and CMemMapApi::Read for some reason double recopying of data (and bytes are looped through!), and in addition the file is overwritten/read in its entirety, although only a specified piece is required.

In general, I've made them look normal, the unnecessary stuff is commented out :

//------------------------------------------------------------------ Write
int CMemMapApi::Write(HANDLE64 hmem, const uchar &buf[], DWORD pos, int sz, DWORD &err) // write the specified number of bytes to memory
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // if not open
  DWORD size=GetSize(hmem, err);  if (pos+sz>size) { UnViewFile(view);  return(-2); }; // if the size is smaller, exit
  /* 
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // took bytebuffer
 for(int i=0; i<sz; i++) src[pos+i+HEAD_MEM]=buf[i]; // write to memory
 memcpyX(view, src, size); // copy back
 */    
  memcpyX(view+HEAD_MEM+pos, buf, sz);  
  UnViewFile(view); // closed the view
  return(0); // returned OK.
}
//------------------------------------------------------------------ Read
int CMemMapApi::Read(HANDLE64 hmem, uchar &buf[], DWORD pos, int sz, DWORD &err) // read from memory the specified number of bytes
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // if not open
  DWORD size=GetSize(hmem, err); // got the size
  /* 
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // took bytebuffer
 ArrayResize(buf, sz);
 int i=0; for(i=0; i<sz && pos+i<size; i++) buf[i]=src[pos+i+HEAD_MEM]; // read bytes
 */    
  sz= fmin(sz, size-pos);
  ArrayResize(buf, sz);
  memcpyX(buf, view+HEAD_MEM+pos, sz);
  UnViewFile(view); // closed the view
  return sz; // number of bytes copied
}
To make the first function compile, you need to set const for arrays in memcpyX and memcpy functions, which the author didn't bother to set.
 

Error when copying large sizes, due to zero being passed in dwMaximumSizeHigh

        if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, 0, size + HEAD_MEM, path); // create a memory object

Fixed it like this:

if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, size + HEAD_MEM, size + HEAD_MEM, path); // create a memory object


This size - 6 mb, transfers without problems:

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:

Corrected it like this:

Properly pass the high 4 bytes of the 8-byte size there.

 

Ok, now we have all the raites in the C# program and we can analyse everything we need using LINQ.

But there is a question, how to organise the mechanism of commands between the terminal and the application.

From the terminal: new candle, new raites - take the file

From the application: calculation completed, take the result (draw on the chart, open a deal).


Does anyone have experience in such implementation of interaction between terminal and code?

 
pushdib:

Ok, now we have all the raites in a C# program and can use LINQ to analyse everything perfectly.

But there is a question, how to organise the mechanism of commands between the terminal and the application.

From the terminal: new candle, new rates - get the file

From the application: calculation completed, take the result (draw it on the chart, open a deal).


Does anyone have experience with such implementation of interaction between terminal and code?

I did it all through pip - works like clockwork.
 
This is just brilliant, thanks for the labours! -)). I had to learn how to work with binary files at the same time, but it was worth it.
 

Please advise what to do in this situation.

1. I opened a new file in memory with 100 bytes.

2. I wrote 100 bytes to it.

3. I read 100 bytes in another Expert Advisor. Everything is fine.

4. Now how to write 50 or 200 bytes to the same file?