ライブラリ: DLLなしでのファイルマッピング - ページ 11

 
o_o:

レコードを閉じるのではなく、ファイルを閉じて削除 するのだ。

だから

そのため、あなたはもう存在しないものを開こうとしているのです。

ライブラリのコードを見てみると、ファイルが削除されるのは、CMemMapFileクラスのClose()関数が直接呼ばれたときだけでなく、このクラスのオブジェクトへのポインタが削除されたときでもあることがわかりました。私は少し困惑しています。ファイルの書き込みと読み込みが異なる呼び出しコンテキスト(スコープ)で使用されている場合、クラスオブジェクトを作成する動的メソッドは使用できないことがわかりました。例えば、ターミナルの1つのチャートがファイルにデータを書き込み、2番目のチャートがデータを読み込んでこのファイルを削除する。ファイルが強制的に削除されないように、オブジェクト変数は常にグローバル・レベルに保持されるべきであることが判明した。また、読み込んだデータのサイズを指定しなくても可能かどうかも不明である。つまり、データを書き込むときにはそのサイズを知っているが、別のチャートで読み込むときには、たとえば文字列の値のように、事前にデータのサイズを知らない場合がある。おそらく、私が何かを誤解しているか、ライブラリに微調整すべき点があるのだろう。

失礼しました。ポインターを使わずに、つまりdeleteを使わずに再確認してみました。この場合、(関数から)スコープを離れるとき、クラスオブジェクトのローカル変数は、明示的にデストラクタを呼び出すことなく破棄されます。

受信側で受信したデータのサイズに疑問が残る。

 
fxsaber:

ライブラリを提供してくれた作者に感謝する!

あらゆるデータを転送するための関数を作った。以下のスクリプトは、目盛りの例でその働きを示している。


結果


スーパー!作者さんのコードから類推して、私自身のためにライブラリの使い方を 簡略化しました。

 
MT4の価格移動の例

取引、自動取引システム、取引戦略のテストに関するフォーラム

メタトレーダー4のNamedPipes

fxsaber, 2017.11.30 14:18

取引所データ.mqh

#include <MemMapLib.mqh>
#include <TypeToBytes.mqh>

template <typename T>
class EXCHANGE_DATA
{
private:
  CMemMapFile* FileMemory;

public:  
  // 指定された長さのメモリをデータ用に確保する。 
  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;
  }

  // データをメモリに書き込む
  void DataSave( const T &Data[], const bool FromBegin = true  ) const
  {
    const int Size = ::ArraySize(Data) * sizeof(T);
    uchar Bytes[];
    
    _ArrayCopy(Bytes, _R(Size).Bytes);              // 数量を記録 
    _ArrayCopy(Bytes, _R(Data).Bytes, sizeof(int)); // データを記録
  
        if (FromBegin)
          this.FileMemory.Seek(0, SEEK_SET);
  
        this.FileMemory.Write(Bytes, ::ArraySize(Bytes)); // すべてをメモリにダンプ
    
    return;
  }
  
  // メモリからデータを読み込む
  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));  // メモリからデータ量を読み出す 
        this.FileMemory.Read(Bytes, _R(Bytes)[0]); // データそのものを取得
  
        _ArrayCopy(Data, Bytes);              // データを配列にダンプ
    
    return(::ArraySize(Data));
  }  
};


プライスギバー.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];
  }
}


PriceGiver.ex4と PriceTaker.ex4を 実行します。


実行結果

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
 

このライブラリでは、CMemMapFile::Openメソッドに小さなエラーがあります。 これはファイルハンドル(HANDLE64型)を返すべきですが、代わりに0またはエラーコードを 返します。

さらに、CMemMapApi::WriteとCMemMapApi::Readメソッドでは、なぜかデータが二重に再コピーされ(バイトがループされる!)、さらに、指定された部分のみが必要であるにもかかわらず、ファイル全体が上書き/読み込まれます。

一般的に、私はそれらを正常に見えるようにし、不必要なものはコメントアウトされている:

//------------------------------------------------------------------ Write
int CMemMapApi::Write(HANDLE64 hmem, const uchar &buf[], DWORD pos, int sz, DWORD &err) // 指定されたバイト数をメモリに書き込む
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // 開いていない場合
  DWORD size=GetSize(hmem, err);  if (pos+sz>size) { UnViewFile(view);  return(-2); }; // サイズが小さければ終了
  /*
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // バイトバッファを取得
 for(int i=0;i<sz; i++) src[pos+i+HEAD_MEM]=buf[i]; // メモリへの書き込み
 memcpyX(view, src, size); // コピーバック
 */    
  memcpyX(view+HEAD_MEM+pos, buf, sz);  
  UnViewFile(view); // ビューを閉じる
  return(0); // OKが返された。
}
//------------------------------------------------------------------ Read
int CMemMapApi::Read(HANDLE64 hmem, uchar &buf[], DWORD pos, int sz, DWORD &err) // 指定されたバイト数をメモリから読み込む
{
  if (hmem==NULL) return(-1);
  PBYTE64 view=ViewFile(hmem, err);  if (view==0 || err!=0) return(-1); // 開いていない場合
  DWORD size=GetSize(hmem, err); // サイズを取得
  /*
 uchar src[]; ArrayResize(src, size); memcpyX(src, view, size); // バイトバッファを取った
 ArrayResize(buf, sz);
 int i=0; for(i=0; i<sz && pos+i<size; i++) buf[i]=src[pos+i+HEAD_MEM]; // バイトを読み込む
 */    
  sz= fmin(sz, size-pos);
  ArrayResize(buf, sz);
  memcpyX(buf, view+HEAD_MEM+pos, sz);
  UnViewFile(view); // ビューを閉じる
  return sz; // コピーされたバイト数
}
最初の関数をコンパイルするためには、memcpyXと memcpy 関数で配列にconstを 設定する必要がある。
 

dwMaximumSizeHighに0が渡されるため、大きなサイズをコピーするとエラーが発生する。

        if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, 0, size + HEAD_MEM, path); // メモリ・オブジェクトを作成する

このように修正しました:

if (mode==modeCreate) 
           hmem=CreateFileMappingWX(INVALID_HANDLE_VALUE64, NULL, PAGE_READWRITE, size + HEAD_MEM, size + HEAD_MEM, path); // メモリ・オブジェクトを作成する


このサイズ(6MB)は問題なく転送される:

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:

このように修正した:

8バイトサイズの上位4バイトを適切に渡す。

 

さて、これでC#プログラムにすべてのレイトが揃い、LINQを使って必要なものをすべて分析できるようになった。

しかし、ターミナルとアプリケーション間のコマンドのメカニズムをどのように整理するかという問題がある。

ターミナルから:新しいロウソク、新しいレイト、ファイルの取得

アプリケーションから:計算が完了し、結果を取る(チャートに描画し、取引を開く)。


どなたかこのようなターミナルとコード間の相互作用の実装の経験をお持ちの方はいらっしゃいませんか?

 
pushdib:

さて、これでC#プログラムにすべてのレイトが揃い、LINQを使ってすべてを完璧に分析できるようになった。

しかし、ターミナルとアプリケーション間のコマンドのメカニズムをどのように整理するかという問題がある。

ターミナルから:新しいキャンドル、新しいレート、ファイルの取得

アプリケーションから:計算が完了し、結果を取る(チャートに描画し、取引を開く)。


このようなターミナルとコード間の相互作用の実装を経験したことがある人はいますか?

私はすべてpipで行いました。
 
これは本当に素晴らしい!-)).同時にバイナリファイルの 扱い方も学ばなければならなかったが、その価値はあった。
 

このような場合、どうすればよいでしょうか。

1.メモリ上に100バイトの新規ファイルを開いた。

2.100バイトを書き込んだ。

3.別のExpert Advisorで100バイトを読み込んだ。すべて正常です。

4.同じファイルに50バイトや200バイトを書き込むには?