Bibliotecas: Mapeamento de Arquivo sem DLL - página 11

 
o_o:

não se chama fechar o registro, mas sim fechar e excluir o arquivo.

e é por isso que

você está tentando abrir algo que não existe mais.

Analisando o código da biblioteca, vi que o arquivo é excluído não apenas quando a função Close() da classe CMemMapFile é chamada diretamente, mas também quando o ponteiro para o objeto dessa classe é excluído, porque essa função é chamada no destrutor da classe. Estou um pouco perplexo. Acontece que o método dinâmico de criação de objetos de classe não pode ser usado se a gravação e a leitura de um arquivo forem usadas em diferentes contextos de chamada (escopos). Por exemplo, um gráfico do terminal grava dados em um arquivo, o segundo lê dados e exclui esse arquivo. Acontece que a variável de objeto deve sempre ser mantida no nível global, para que o arquivo não seja excluído à força. Também não está claro se é possível fazer isso sem especificar o tamanho dos dados lidos. Ou seja, sabemos o tamanho dos dados ao escrevê-los, mas ao lê-los em outro gráfico, talvez não saibamos o tamanho dos dados com antecedência, como, por exemplo, no caso de valores de cadeia de caracteres. É provável que eu tenha entendido algo errado ou que haja algo mais a ser ajustado na biblioteca.

Erro meu. Verifiquei novamente sem usar o ponteiro e, portanto, sem usar delete. Nesse caso, ao sair do escopo (de uma função), a variável local do objeto de classe é destruída sem uma chamada explícita ao destrutor.

Ainda há uma dúvida sobre o tamanho dos dados recebidos no lado do receptor.

 
fxsaber:

Agradeço ao autor pela biblioteca!

Criei funções para transferir quaisquer dados. O script abaixo mostra o trabalho delas no exemplo dos ticks


Resultado


Excelente! Por analogia com seu código, simplifiquei o uso da biblioteca para mim.

 
Exemplo de transferência de preço para MT4

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

NamedPipes para o 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:  
  // Aloca a memória do comprimento especificado para os dados 
  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;
  }

  // Grava dados na memória
  void DataSave( const T &Data[], const bool FromBegin = true  ) const
  {
    const int Size = ::ArraySize(Data) * sizeof(T);
    uchar Bytes[];
    
    _ArrayCopy(Bytes, _R(Size).Bytes);              // Registrou a quantidade 
    _ArrayCopy(Bytes, _R(Data).Bytes, sizeof(int)); // Registrou os dados
  
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        this.FileMemory.Write(Bytes, ::ArraySize(Bytes)); // Despejou tudo na memória
    
    return;
  }
  
  // Lê dados da memória
  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));  // Ler a quantidade de dados da memória 
        this.FileMemory.Read(Bytes, _R(Bytes)[0]); // Obteve os dados propriamente ditos
  
        _ArrayCopy(Data, Bytes);              // Despejou os dados em uma matriz
    
    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];
  }
}


Execute PriceGiver.ex4 e 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
 

A biblioteca tem um pequeno erro no método CMemMapFile::Open. Ele deveria retornar o identificador de arquivo (tipo HANDLE64), mas, em vez disso, retorna 0 ou um código de erro.

Além disso, os métodos de leitura e gravação CMemMapApi::Write e CMemMapApi::Read, por algum motivo, duplicam a recopiagem de dados (e os bytes são repetidos!) e, além disso, o arquivo é sobrescrito/lido em sua totalidade, embora apenas uma parte específica seja necessária.

Em geral, eu os fiz parecer normais, as coisas desnecessárias estão comentadas:

//------------------------------------------------------------------ Write
int CMemMapApi::Write(HANDLE64 hmem, const uchar &buf[], DWORD pos, int sz, DWORD &err) // gravar o número especificado de bytes na memória
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // se não estiver aberto
  DWORD size=GetSize(hmem, err);  if (pos+sz>size) { UnViewFile(view);  return(-2); }; // se o tamanho for menor, sair
  /* 
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // pegou o bytebuffer
 for(int i=0; i<sz; i++) src[pos+i+HEAD_MEM]=buf[i]; // escreve na memória
 memcpyX(view, src, size); // copia de volta
 */    
  memcpyX(view+HEAD_MEM+pos, buf, sz);  
  UnViewFile(view); // fechou a visualização
  return(0); // retornou OK.
}
//------------------------------------------------------------------ Read
int CMemMapApi::Read(HANDLE64 hmem, uchar &buf[], DWORD pos, int sz, DWORD &err) // ler da memória o número especificado de bytes
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // se não estiver aberto
  DWORD size=GetSize(hmem, err); // obteve o tamanho
  /* 
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // pegou o bytebuffer
 ArrayResize(buf, sz);
 int i=0; for(i=0; i<sz && pos+i<size; i++) buf[i]=src[pos+i+HEAD_MEM]; // lê bytes
 */    
  sz= fmin(sz, size-pos);
  ArrayResize(buf, sz);
  memcpyX(buf, view+HEAD_MEM+pos, sz);
  UnViewFile(view); // fechou a visualização
  return sz; // número de bytes copiados
}
Para que a primeira função seja compilada, você precisa definir const para matrizes nas funções memcpyX e memcpy, o que o autor não se preocupou em definir.
 

Erro ao copiar tamanhos grandes, devido à passagem de zero em dwMaximumSizeHigh

        if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, 0, size + HEAD_MEM, path); // criar um objeto de memória

Corrigido desta forma:

if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, size + HEAD_MEM, size + HEAD_MEM, path); // criar um objeto de memória


Esse tamanho - 6 mb, é transferido sem 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:

Corrigido desta forma:

Passe corretamente os 4 bytes superiores do tamanho de 8 bytes para lá.

 

Ok, agora temos todos os raites no programa C# e podemos analisar tudo o que precisamos usando o LINQ.

Mas há uma questão: como organizar o mecanismo de comandos entre o terminal e o aplicativo.

Do terminal: nova vela, novos raites - pegue o arquivo

Do aplicativo: cálculo concluído, pegar o resultado (desenhar no gráfico, abrir uma transação).


Alguém tem experiência em tal implementação de interação entre o terminal e o código?

 
pushdib:

Ok, agora temos todos os requisitos em um programa C# e podemos usar o LINQ para analisar tudo perfeitamente.

Mas há uma questão: como organizar o mecanismo de comandos entre o terminal e o aplicativo.

No terminal: new candle, new rates - obter o arquivo

Do aplicativo: cálculo concluído, pegue o resultado (desenhe-o no gráfico, abra uma transação).


Alguém tem experiência com essa implementação de interação entre o terminal e o código?

Fiz tudo isso por meio do pip - funciona como um relógio.
 
Isso é simplesmente brilhante, obrigado pelo trabalho! -)). Tive que aprender a trabalhar com arquivos binários ao mesmo tempo, mas valeu a pena.
 

Por favor, informe o que fazer nessa situação.

1. abri um novo arquivo na memória com 100 bytes.

2. Escrevi 100 bytes nele.

3. Li 100 bytes em outro Expert Advisor. Tudo está funcionando bem.

4. Agora, como escrever 50 ou 200 bytes no mesmo arquivo?