Bibliothèque: Mappage de fichiers sans DLL - page 11

 
o_o:

il ne s'agit pas de fermer l'enregistrement, mais de fermer et d'effacer le fichier.

c'est pourquoi

vous essayez d'ouvrir quelque chose qui n'existe plus.

En examinant le code de la bibliothèque, j'ai vu que le fichier est supprimé non seulement lorsque la fonction Close() de la classe CMemMapFile est appelée directement, mais aussi lorsque le pointeur sur l'objet de cette classe est supprimé, parce que cette fonction est appelée dans le destructeur de la classe. Je suis un peu perplexe. Il s'avère que la méthode dynamique de création d'objets de classe ne peut pas être utilisée si l'écriture et la lecture d'un fichier sont utilisées dans des contextes d'appel différents (scopes). Par exemple, une carte du terminal écrit des données dans un fichier, la seconde lit des données et supprime ce fichier. Il s'avère que la variable objet doit toujours être conservée au niveau global, afin que le fichier ne soit pas supprimé de force. Il n'est pas non plus évident de savoir s'il est possible de ne pas spécifier la taille des données lues. En d'autres termes, nous connaissons la taille des données lorsque nous les écrivons, mais lorsque nous les lisons sur un autre graphique, il se peut que nous ne connaissions pas la taille des données à l'avance, comme c'est le cas, par exemple, pour les valeurs des chaînes de caractères. Il est probable que j'ai mal compris quelque chose ou qu'il y a quelque chose d'autre à régler dans la bibliothèque.

C'est une erreur. J'ai revérifié sans utiliser le pointeur et donc sans utiliser delete. Dans ce cas, en quittant le scope (depuis une fonction), la variable locale de l'objet de classe est détruite sans appel explicite au destructeur.

Il y a toujours un problème avec la taille des données reçues du côté du récepteur.

 
fxsaber:

Merci à l'auteur pour la bibliothèque !

J'ai créé des fonctions pour transférer n'importe quelle donnée. Le script ci-dessous montre leur travail sur l'exemple des ticks


Résultat


Super ! Par analogie avec votre code, je me suis simplifié l'utilisation de la bibliothèque.

 
Exemple de transfert de prix pour MT4

Forum sur le trading, les systèmes de trading automatisés et les tests de stratégies de trading

NamedPipes pour 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:  
  // Alloue une mémoire de la longueur spécifiée pour les données 
  EXCHANGE_DATA( const int Amount, const bool ModeCreate = false, string FileName = "Local\\test" )
  {
// Nom de fichier += _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;
  }

  // Écriture des données dans la mémoire
  void DataSave( const T &Data[], const bool FromBegin = true  ) const
  {
    const int Size = ::ArraySize(Data) * sizeof(T);
    uchar Bytes[];
    
    _ArrayCopy(Bytes, _R(Size).Bytes);              // Enregistrement de la quantité 
    _ArrayCopy(Bytes, _R(Data).Bytes, sizeof(int)); // Enregistrement des données
  
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        this.FileMemory.Write(Bytes, ::ArraySize(Bytes)); // Tout a été déversé dans la mémoire
    
    return;
  }
  
  // Lit les données dans la mémoire
  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));  // Lire la quantité de données dans la mémoire 
        this.FileMemory.Read(Bytes, _R(Bytes)[0]); // Obtenir les données elles-mêmes
  
        _ArrayCopy(Data, Bytes);              // Dépose les données dans un tableau
    
    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];
  }
}


Exécutez PriceGiver.ex4 et PriceTaker.ex4.


Résultat

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 bibliothèque a une petite erreur dans la méthode CMemMapFile::Open. Elle devrait retourner le handle du fichier (type HANDLE64), mais à la place elle retourne 0 ou un code d' erreur.

De plus, les méthodes de lecture et d'écriture CMemMapApi::Write et CMemMapApi::Read, pour une raison quelconque, recopient deux fois les données (et les octets sont bouclés !), et de plus, le fichier est écrasé/lu dans son intégralité, alors que seule une partie spécifiée est requise.

En général, j'ai fait en sorte qu'ils aient l'air normaux, les choses inutiles sont commentées :

//------------------------------------------------------------------ Write
int CMemMapApi::Write(HANDLE64 hmem, const uchar &buf[], DWORD pos, int sz, DWORD &err) // écrire le nombre d'octets spécifié dans la mémoire
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // s'il n'est pas ouvert
  DWORD size=GetSize(hmem, err);  if (pos+sz>size) { UnViewFile(view);  return(-2); }; // si la taille est plus petite, quitter
  /* 
 uchar src[] ; ArrayResize(src, size) ; memcpyX(src, view, size) ; // a pris le bytebuffer
 for(int i=0 ; i<sz ; i++) src[pos+i+HEAD_MEM]=buf[i] ; // écriture en mémoire
 memcpyX(view, src, size) ; // recopie
 */    
  memcpyX(view+HEAD_MEM+pos, buf, sz);  
  UnViewFile(view); // ferme la vue
  return(0); // a retourné OK.
}
//------------------------------------------------------------------ Read
int CMemMapApi::Read(HANDLE64 hmem, uchar &buf[], DWORD pos, int sz, DWORD &err) // lit dans la mémoire le nombre d'octets spécifié
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // s'il n'est pas ouvert
  DWORD size=GetSize(hmem, err); // a obtenu la taille
  /* 
 uchar src[] ; ArrayResize(src, size) ; memcpyX(src, view, size) ; // a pris un bytebuffer
 ArrayResize(buf, sz) ;
 int i=0 ; for(i=0 ; i<sz && pos+i<size ; i++) buf[i]=src[pos+i+HEAD_MEM] ; // lecture d'octets
 */    
  sz= fmin(sz, size-pos);
  ArrayResize(buf, sz);
  memcpyX(buf, view+HEAD_MEM+pos, sz);
  UnViewFile(view); // ferme la vue
  return sz; // nombre d'octets copiés
}
Pour que la première fonction compile, il faut mettre const pour les tableaux dans les fonctions memcpyX et memcpy, ce que l'auteur n'a pas pris la peine de faire.
 

Erreur lors de la copie de grandes tailles, due à la transmission d'un zéro dans dwMaximumSizeHigh

        if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, 0, size + HEAD_MEM, path); // créer un objet mémoire

Je l'ai corrigé comme suit :

if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, size + HEAD_MEM, size + HEAD_MEM, path); // créer un objet mémoire


Cette taille - 6 mb, se transfère sans problème :

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:

Je l'ai corrigé comme suit :

Passer correctement les 4 premiers octets de la taille de 8 octets.

 

Ok, maintenant nous avons tous les raites dans le programme C# et nous pouvons analyser tout ce dont nous avons besoin en utilisant LINQ.

Mais il y a une question, comment organiser le mécanisme des commandes entre le terminal et l'application.

Depuis le terminal : nouvelle bougie, nouvelles règles - prendre le fichier

Depuis l'application : calcul terminé, prendre le résultat (dessiner sur le graphique, ouvrir une transaction).


Quelqu'un a-t-il de l'expérience dans ce type de mise en œuvre de l'interaction entre le terminal et le code ?

 
pushdib:

Ok, maintenant nous avons tous les raites dans un programme C# et nous pouvons utiliser LINQ pour tout analyser parfaitement.

Mais il y a une question, comment organiser le mécanisme des commandes entre le terminal et l'application.

Depuis le terminal : new candle, new rates - obtenir le fichier

Depuis l'application : calcul terminé, prendre le résultat (le dessiner sur le graphique, ouvrir une transaction).


Est-ce que quelqu'un a de l'expérience avec une telle implémentation de l'interaction entre le terminal et le code ?

J'ai tout fait par pip - ça marche comme sur des roulettes.
 
C'est tout simplement génial, merci pour votre travail ! -)). J'ai dû apprendre à travailler avec des fichiers binaires en même temps, mais cela en valait la peine.
 

Merci de m'indiquer ce qu'il faut faire dans cette situation.

1. j'ai ouvert un nouveau fichier en mémoire avec 100 octets.

2. J'ai écrit 100 octets dans ce fichier.

3. J'ai lu 100 octets dans un autre Expert Advisor. Tout se passe bien.

4. Maintenant, comment écrire 50 ou 200 octets dans le même fichier ?