Bibliotecas: Mapeado de Ficheros sin la DLL - página 11

 
o_o:

no se llama cerrar el registro, se llama cerrar y borrar el archivo.

que es por lo que

estás intentando abrir algo que ya no existe.

Revisando el código de la librería he visto que el fichero se borra no sólo cuando se llama directamente a la función Close() de la clase CMemMapFile, sino también cuando se borra el puntero al objeto de esta clase, porque esta función se llama en el destructor de la clase. Estoy un poco perplejo. Resulta que el método dinámico de creación de objetos de clase no se puede utilizar si la escritura y la lectura de un archivo se utilizan en diferentes contextos de llamada (ámbitos). Por ejemplo, un gráfico del terminal escribe datos en un archivo, el segundo lee datos y borra este archivo. Resulta que la variable de objeto debe mantenerse siempre a nivel global, para que el fichero no se borre a la fuerza. Tampoco está claro si es posible prescindir de especificar el tamaño de los datos leídos. Es decir, conocemos el tamaño de los datos al escribirlos, pero al leerlos en otro gráfico podemos no conocer de antemano el tamaño de los datos, como por ejemplo en el caso de valores de cadena. Probablemente o he entendido algo mal o hay algo más que retocar en la librería.

Error mío. Lo he vuelto a comprobar sin usar el puntero y por tanto sin usar delete. En este caso, al salir del ámbito (desde una función), la variable local del objeto de clase se destruye sin una llamada explícita al destructor.

Queda una duda sobre el tamaño de los datos recibidos en el lado del receptor.

 
fxsaber:

¡Gracias al autor por la biblioteca!

Hice funciones para transferir cualquier dato. El script de abajo muestra su trabajo en el ejemplo de ticks


Resultado


¡Super! Por analogía con su código he simplificado el uso de la biblioteca para mí.

 
Ejemplo de transferencia de precios para MT4

Foro sobre trading, sistemas automatizados de trading y prueba de estrategias de trading

NamedPipes para 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:  
  // Asigna memoria de la longitud especificada para los datos 
  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;
  }

  // Escribe datos en la 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);              // Registrada la cantidad 
    _ArrayCopy(Bytes, _R(Data).Bytes, sizeof(int)); // Grabación de los datos
  
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        this.FileMemory.Write(Bytes, ::ArraySize(Bytes)); // Volcado de todo en la memoria
    
    return;
  }
  
  // Lee datos de la 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));  // Leer la cantidad de datos de la memoria 
        this.FileMemory.Read(Bytes, _R(Bytes)[0]); // Obtener los datos propiamente dichos
  
        _ArrayCopy(Data, Bytes);              // Volcado de los datos en 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];
  }
}


Ejecutar PriceGiver.ex4 y PriceTaker.ex4.


Resultado

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 librería tiene un pequeño error en el método CMemMapFile::Open. Debería devolver el manejador del fichero (tipo HANDLE64), pero en su lugar devuelve 0 o un código de error.

Además, los métodos de lectura y escritura CMemMapApi::Write y CMemMapApi::Read por alguna razón duplican la recopilación de datos (¡y los bytes se repiten en bucle!), y además el fichero se sobreescribe/lee en su totalidad, aunque sólo se necesite un trozo especificado.

En general, he hecho que se vean normales, las cosas innecesarias están comentadas :

//------------------------------------------------------------------ Write
int CMemMapApi::Write(HANDLE64 hmem, const uchar &buf[], DWORD pos, int sz, DWORD &err) // escribir el número especificado de bytes en la memoria
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // si no está abierto
  DWORD size=GetSize(hmem, err);  if (pos+sz>size) { UnViewFile(view);  return(-2); }; // si el tamaño es menor, salir
  /* 
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // tomó bytebuffer
 for(int i=0; i<sz; i++) src[pos+i+HEAD_MEM]=buf[i]; // escribir en memoria
 memcpyX(view, src, size); // copiar de vuelta
 */    
  memcpyX(view+HEAD_MEM+pos, buf, sz);  
  UnViewFile(view); // cerrar la vista
  return(0); // devuelto OK.
}
//------------------------------------------------------------------ Read
int CMemMapApi::Read(HANDLE64 hmem, uchar &buf[], DWORD pos, int sz, DWORD &err) // leer de la memoria el número especificado de bytes
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // si no está abierto
  DWORD size=GetSize(hmem, err); // tengo el tamaño
  /* 
 uchar src[]; ArrayResize(src, tamaño); memcpyX(src, vista, tamaño); // tomó bytebuffer
 ArrayResize(buf, sz);
 int i=0; for(i=0; i<sz && pos+i<size; i++) buf[i]=src[pos+i+HEAD_MEM]; // lee bytes
 */    
  sz= fmin(sz, size-pos);
  ArrayResize(buf, sz);
  memcpyX(buf, view+HEAD_MEM+pos, sz);
  UnViewFile(view); // cerrar la vista
  return sz; // número de bytes copiados
}
Para hacer que la primera función compile, necesitas establecer const para los arrays en las funciones memcpyX y memcpy, que el autor no se molestó en establecer.
 

Error al copiar tamaños grandes, debido a que se pasaba cero en dwMaximumSizeHigh

        if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, 0, size + HEAD_MEM, path); // crear un objeto de memoria

Arreglado así:

if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, size + HEAD_MEM, size + HEAD_MEM, path); // crear un objeto de memoria


Este tamaño - 6 mb, se transfiere sin problemas:

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:

Corregido así:

Pasar correctamente los 4 bytes altos del tamaño de 8 bytes allí.

 

Ok, ahora tenemos todos los raites en el programa C# y podemos analizar todo lo que necesitamos usando LINQ.

Pero hay una pregunta, cómo organizar el mecanismo de comandos entre el terminal y la aplicación.

Desde el terminal: nueva vela, nuevos raites - tomar el archivo

Desde la aplicación: cálculo completado, tomar el resultado (dibujar en el gráfico, abrir una operación).


¿Alguien tiene experiencia en este tipo de implementación de la interacción entre el terminal y el código?

 
pushdib:

Ok, ahora tenemos todos los raites en un programa C# y podemos usar LINQ para analizarlo todo perfectamente.

Pero hay una pregunta, cómo organizar el mecanismo de comandos entre el terminal y la aplicación.

Desde el terminal: nueva vela, nuevas tarifas - obtener el archivo

Desde la aplicación: cálculo finalizado, tomar el resultado (dibujarlo en el gráfico, abrir una operación).


¿Alguien tiene experiencia con este tipo de aplicación de la interacción entre el terminal y el código?

Lo hice todo a través de pip - funciona como un reloj.
 
Esto es simplemente brillante, ¡gracias por el trabajo! -)). Tuve que aprender a trabajar con archivos binarios al mismo tiempo, pero valió la pena.
 

Por favor, aconséjeme qué hacer en esta situación.

1. Abrí un nuevo archivo en memoria con 100 bytes.

2. Escribí 100 bytes en él.

3. Leí 100 bytes en otro Asesor Experto. Todo va bien.

4. Ahora, ¿cómo escribir 50 o 200 bytes en el mismo archivo?