Librerie: Mappatura dei file senza DLL - pagina 11

 
o_o:

non si chiama chiusura del record, ma chiusura e cancellazione del file.

ed è per questo che

per cui si sta cercando di aprire qualcosa che non esiste più.

Guardando il codice della libreria ho visto che il file viene cancellato non solo quando viene chiamata direttamente la funzione Close() della classe CMemMapFile, ma anche quando viene cancellato il puntatore all'oggetto di questa classe, perché questa funzione viene chiamata nel distruttore della classe. Sono un po' perplesso. Risulta che il metodo dinamico di creazione degli oggetti della classe non può essere utilizzato se la scrittura e la lettura di un file sono utilizzate in contesti di chiamata (scope) diversi. Ad esempio, un diagramma del terminale scrive dati in un file, il secondo legge i dati e cancella questo file. Si scopre che la variabile oggetto deve essere sempre mantenuta a livello globale, in modo che il file non venga cancellato forzatamente. Inoltre, non è chiaro se sia possibile fare a meno di specificare la dimensione dei dati letti. In altre parole, conosciamo la dimensione dei dati quando li scriviamo, ma quando li leggiamo su un'altra tabella potremmo non conoscere in anticipo la dimensione dei dati, come ad esempio nel caso dei valori delle stringhe. Probabilmente ho capito male qualcosa o c'è qualcos'altro da modificare nella libreria.

Errore mio. Ho ricontrollato senza usare il puntatore e quindi senza usare delete. In questo caso, quando si esce dall'ambito (da una funzione), la variabile locale dell'oggetto di classe viene distrutta senza una chiamata esplicita al distruttore.

C'è ancora un problema con la dimensione dei dati ricevuti dal lato del ricevitore.

 
fxsaber:

Grazie all'autore per la libreria!

Ho creato delle funzioni per trasferire qualsiasi dato. Lo script qui sotto mostra il loro lavoro sull'esempio delle zecche


Risultato


Super! Per analogia con il vostro codice ho semplificato l'uso della libreria.

 
Esempio di trasferimento del prezzo per MT4

Forum sul trading, sui sistemi di trading automatizzati e sul test delle strategie di trading

NamedPipes per 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:  
  // Alloca la memoria della lunghezza specificata per i dati 
  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;
  }

  // Scrive i dati nella memoria
  void DataSave( const T &Data[], const bool FromBegin = true  ) const
  {
    const int Size = ::ArraySize(Data) * sizeof(T);
    uchar Bytes[];
    
    _ArrayCopy(Bytes, _R(Size).Bytes);              // Registrazione della quantità 
    _ArrayCopy(Bytes, _R(Data).Bytes, sizeof(int)); // Registrazione dei dati
  
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        this.FileMemory.Write(Bytes, ::ArraySize(Bytes)); // Scarico di tutto in memoria
    
    return;
  }
  
  // Legge i dati dalla memoria
  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));  // Leggere la quantità di dati dalla memoria 
        this.FileMemory.Read(Bytes, _R(Bytes)[0]); // Ho i dati stessi
  
        _ArrayCopy(Data, Bytes);              // Scarico dei dati in un 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];
  }
}


Eseguire PriceGiver.ex4 e PriceTaker.ex4.


Risultato

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
 

La libreria presenta un piccolo errore nel metodo CMemMapFile::Open. Dovrebbe restituire l'handle del file (tipo HANDLE64), ma invece restituisce 0 o un codice di errore.

Inoltre, i metodi di lettura e scrittura CMemMapApi::Write e CMemMapApi::Read, per qualche motivo, ricopiano due volte i dati (e i byte vengono passati in loop!), e inoltre il file viene sovrascritto/letto nella sua interezza, anche se è richiesta solo una parte specifica.

In generale, li ho resi normali, le cose non necessarie sono commentate:

//------------------------------------------------------------------ Write
int CMemMapApi::Write(HANDLE64 hmem, const uchar &buf[], DWORD pos, int sz, DWORD &err) // scrivere il numero di byte specificato nella memoria
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // se non è aperto
  DWORD size=GetSize(hmem, err);  if (pos+sz>size) { UnViewFile(view);  return(-2); }; // se la dimensione è inferiore, uscire
  /* 
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // prende il bytebuffer
 for(int i=0; i<sz; i++) src[pos+i+HEAD_MEM]=buf[i]; // scrive in memoria
 memcpyX(view, src, size); // copia indietro
 */    
  memcpyX(view+HEAD_MEM+pos, buf, sz);  
  UnViewFile(view); // ha chiuso la vista
  return(0); // restituito OK.
}
//------------------------------------------------------------------ Read
int CMemMapApi::Read(HANDLE64 hmem, uchar &buf[], DWORD pos, int sz, DWORD &err) // leggere dalla memoria il numero di byte specificato
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // se non è aperto
  DWORD size=GetSize(hmem, err); // abbiamo la dimensione
  /* 
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // ha preso il bytebuffer
 ArrayResize(buf, sz);
 int i=0; for(i=0; i<sz && pos+i<size; i++) buf[i]=src[pos+i+HEAD_MEM]; // legge i byte
 */    
  sz= fmin(sz, size-pos);
  ArrayResize(buf, sz);
  memcpyX(buf, view+HEAD_MEM+pos, sz);
  UnViewFile(view); // ha chiuso la vista
  return sz; // numero di byte copiati
}
Per rendere la prima funzione compilabile, è necessario impostare const per gli array nelle funzioni memcpyX e memcpy, cosa che l'autore non si è preoccupato di impostare.
 

Errore durante la copia di grandi dimensioni, dovuto al passaggio di zero in dwMaximumSizeHigh

        if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, 0, size + HEAD_MEM, path); // creare un oggetto di memoria

Ho risolto il problema in questo modo:

if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, size + HEAD_MEM, size + HEAD_MEM, path); // creare un oggetto di memoria


Questa dimensione - 6 mb - viene trasferita senza problemi:

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:

Correggetelo in questo modo:

Passare correttamente i 4 byte superiori della dimensione di 8 byte.

 

Ok, ora abbiamo tutti i raites nel programma C# e possiamo analizzare tutto ciò che ci serve usando LINQ.

Ma c'è un problema: come organizzare il meccanismo dei comandi tra il terminale e l'applicazione.

Dal terminale: nuova candela, nuovo raites - prendere il file

Dall'applicazione: calcolo completato, prendere il risultato (disegnare sul grafico, aprire un'operazione).


Qualcuno ha esperienza di questa implementazione dell'interazione tra terminale e codice?

 
pushdib:

Ok, ora abbiamo tutti i raites in un programma C# e possiamo usare LINQ per analizzare tutto alla perfezione.

Ma c'è una domanda: come organizzare il meccanismo dei comandi tra il terminale e l'applicazione?

Dal terminale: nuova candela, nuove tariffe - ottenere il file

Dall'applicazione: calcolo completato, prendere il risultato (disegnarlo sul grafico, aprire una transazione).


Qualcuno ha esperienza di questa implementazione dell'interazione tra terminale e codice?

Ho fatto tutto tramite pip - funziona come un orologio.
 
Questo è semplicemente brillante, grazie per il lavoro! -)). Ho dovuto imparare a lavorare con i file binari, ma ne è valsa la pena.
 

Si prega di consigliare cosa fare in questa situazione.

1. Ho aperto un nuovo file in memoria con 100 byte.

2. Ho scritto 100 byte al suo interno.

3. Ho letto 100 byte in un altro Expert Advisor. Tutto va bene.

4. Ora come scrivere 50 o 200 byte nello stesso file?