라이브러리: DLL 없는 파일 매핑 - 페이지 11

 
o_o:

레코드를 닫는 것이 아니라 파일을 닫고 삭제하는 것입니다.

그래서

더 이상 존재하지 않는 것을 열려고 하는 것입니다.

라이브러리 코드를 살펴보니 이 함수가 클래스 소멸자에서 호출되기 때문에 CMemMapFile 클래스의 Close() 함수가 직접 호출될 때뿐만 아니라 이 클래스의 객체에 대한 포인터가 삭제될 때도 파일이 삭제되는 것을 보았습니다. 조금 당황스럽습니다. 파일 쓰기와 읽기가 서로 다른 호출 컨텍스트(범위)에서 사용되는 경우 클래스 객체를 만드는 동적 방법을 사용할 수 없다는 것이 밝혀졌습니다. 예를 들어 터미널의 한 차트는 파일에 데이터를 쓰고, 두 번째 차트는 데이터를 읽고 이 파일을 삭제합니다. 파일이 강제로 삭제되지 않도록 객체 변수는 항상 전역 수준에서 유지되어야 한다는 것이 밝혀졌습니다. 또한 읽기 데이터의 크기를 지정하지 않고도 가능한지 여부도 명확하지 않습니다. 즉, 데이터를 쓸 때는 데이터의 크기를 알지만 다른 차트에서 읽을 때는 예를 들어 문자열 값의 경우처럼 데이터의 크기를 미리 알 수 없습니다. 아마도 제가 무언가를 잘못 이해했거나 라이브러리에서 조정해야 할 다른 것이 있을 수 있습니다.

제 실수입니다. 포인터를 사용하지 않고 삭제를 사용하지 않고 다시 확인했습니다. 이 경우 (함수에서) 범위를 벗어날 때 소멸자를 명시 적으로 호출하지 않고 클래스 객체의 지역 변수가 소멸됩니다.

수신자 측에서 수신된 데이터의 크기에 대한 질문이 여전히 남아 있습니다.

 
fxsaber:

라이브러리 작성자에게 감사드립니다!

저는 모든 데이터를 전송하는 함수를 만들었습니다. 아래 스크립트는 틱의 예에 대한 작업을 보여줍니다.


결과


슈퍼! 코드와 유사하게 라이브러리 사용을 단순화했습니다.

 
MT4의 호가 전송 예시

트레이딩, 자동매매 시스템 및 트레이딩 전략 테스트 포럼

메타트레이더 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:  
  // 데이터에 지정된 길이의 메모리를 할당합니다. 
  EXCHANGE_DATA( const int Amount, const bool ModeCreate = false, string FileName = "Local\\test" )
  {
// 파일 이름 += _기호;
    
    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));
  }  
};


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];
  }
}


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); // 확인을 반환했습니다.
}
//------------------------------------------------------------------ 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); // took bytebuffer
 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. 다른 전문가 조언자에서 100 바이트를 읽었습니다. 모든 것이 정상입니다.

4. 이제 같은 파일에 50바이트 또는 200바이트를 쓰는 방법은 무엇인가요?