Search code examples
dllmql4

How to use DLLs to exchange values between MQL4 programs?


First of all I need to say I don't know much about DLLs.

I am trying to send data from one program to another, using functions from kernel32.dll. My programs are coded in MQL4.

This is the code I use for the server part, which is supposed to save the data:

#define INVALID_HANDLE_VALUE    -1
#define BUF_SIZE                256
#define PAGE_READWRITE          0x0004
#define FILE_MAP_ALL_ACCESS     0xf001F

#import "kernel32.dll"
    int     CreateFileMappingA(int hFile, int lpAttributes, int flProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName);
    int     MapViewOfFile(int hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap);
    int     UnmapViewOfFile(int lpBaseAddress);
    int     RtlMoveMemory(int DestPtr, string s, int Length);   
    int     CloseHandle(int hwnd);  
    int     CreateMutexA(int attr, int owner, string mutexName);
    int     ReleaseMutex(int hnd);
    int     WaitForSingleObject(int hnd, int dwMilliseconds);       

bool started = False;   
int hMapFile = 0;
int pBuf=0;
int hMutex;

int OnInit()
  {

  if(!started) {
        started = true;
        string szName="Global\\Value1";
        int hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE,0,PAGE_READWRITE,0,BUF_SIZE,szName);
        if(hMapFile==0) {
            Alert("CreateFileMapping failed!");
            return;
        }       
        pBuf = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
        if(pBuf==0) {
            Alert("Map View failed!");
            return;
        }           
        hMutex = CreateMutexA(0,0,"PriceMapMutex");     
    }

  }

void OnTick()
{

    WaitForSingleObject(hMutex,1000);
    if(pBuf==0) return;
    string szMsg = DoubleToStr(Bid,Digits);
    Comment("Data: ",szMsg);
    RtlMoveMemory(pBuf, szMsg, StringLen(szMsg)+1);
    ReleaseMutex(hMutex);
    return(0);
}

int deinit()
{
    switch(UninitializeReason()) {
        case REASON_CHARTCLOSE:
        case REASON_REMOVE:
            UnmapViewOfFile(pBuf);
            CloseHandle(hMapFile);
            break;
    }
    return(0);  
}

This is what I use for my client part, which is supposed to pick up the data:

#define INVALID_HANDLE_VALUE    -1
#define BUF_SIZE                1024
#define FILE_MAP_READ           4
extern int      BufferSize = 1024;

#import "kernel32.dll"
    int     OpenFileMappingA(int dwDesiredAccess, bool bInheritHandle, string lpName);
    string  MapViewOfFile(int hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap);
    int     UnmapViewOfFile(string lpBaseAddress);
    int     CloseHandle(int hwnd);
    int     CreateMutexA(int attr, int owner, string mutexName);
    int     ReleaseMutex(int hnd);
    int     WaitForSingleObject(int hnd, int dwMilliseconds);   
   string szName;
   int hMapFile;
   string obj;
   string data;
   int hMutex;
   double dd;


int OnInit()
  {

  szName="Global\\Value1";
   hMapFile = OpenFileMappingA(FILE_MAP_READ,False,szName);
    if(hMapFile==0) {
        Alert("CreateFile Failed!");
        return;
    }
   obj="data";
    ObjectCreate(obj,OBJ_HLINE,0,0,0);
    ObjectSet(obj,OBJPROP_COLOR,Gold);
   hMutex = CreateMutexA(0,0,"PriceMapMutex");  

  }

void OnDeinit(const int reason)
  {
   CloseHandle(hMapFile);   
    Comment("");
    ObjectDelete(obj);
    return(0);  

  }

void start()
{
      getsignal();

      Comment("Data: ",DoubleToStr(dd,Digits));
      Sleep(50);

}
void getsignal() {

     WaitForSingleObject(hMutex,333);
     data = MapViewOfFile(hMapFile,FILE_MAP_READ,0,0,BUF_SIZE); 
      dd = StrToDouble(data);
      ReleaseMutex(hMutex);   
      UnmapViewOfFile(data);          
      ObjectMove(obj,0,Time[0],dd);

}

The code basically works. However I am facing 2 major problems with it.

Problem number 1:
I want to exchange multiple values ( value1, value2, value3, ... ). For some reason it seems to be irrelevant which name I use for szName="Global\\Value1". The server saves the value and the client picks it up no matter what names I use szName="Global\\Value1", szName="Global\\Value2" or szName="Global\\Value3". So for example in the server code I use szName="Global\\Value1" and in my client code I use szName="Global\\Value3" the client still picks up the value which the server writes to szName="Global\\Value1".

Problem number 2:
my client is only stable for about 5 hours. After that I get a message in the client programme saying

"There is a problem and the program needs to be closed...".

Then I close and restart my client and its again working for the next 5 hours.

Has anyone any idea?


Solution

  • FILE MEDIUM

    I agree that Kernel32 is not a good option if you need to do MT4-to-MT4 interfacing. The reason is that Kernel32 is Windows specific. The EA won't work on Mac. Also, messing around with Kernel32 DLL might cause memory leaks (eg, your 5hr live). Plus, it requires user to know how to enable DLL (you'd be surprise how many users have no idea how to enable it).

    Recommendation:

    If you only need SAME MT4 exchange (EAs between charts), then use the GlobalVariableGet(), GlobalVariableSet(), etc.

    If you need exchange between 2 different MT4s (on the same PC) --even if it is across different broker MT4, then use the FILE system: Files\FilePipe.mqh which allows you to write to the common MT4 folder:

    #include <Files\FilePipe.mqh>
    
    CFilePipe   voPipeOut;
    voPipeOut.Open("yourFileName.txt", FILE_WRITE|FILE_COMMON|FILE_BIN);
    voPipeOut.WriteString("WhatEverMessage, probably some CSV value here");
    voPipeOut.Close();
    

    and subsequently

    CFilePipe   voPipeFile;
    string      vsInString      = "";
    voPipeFile.Open("yourFileName.txt", FILE_SHARE_READ|FILE_COMMON|FILE_BIN);
    voPipeFile.Seek(0,SEEK_SET);
    voPipeFile.ReadString( vsInString );
    voPipeFile.Close();
    

    This way, your EA won't depend on DLLs and also works in a wide range of environments. It is very fast (under 2ms for a 1Mb pipe). It works even for cross-broker interfacing (exchanging info [feed?] between 2 different brokers).