2

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?

user3425561
  • 79
  • 1
  • 10
  • Could you kindly post a full MCVE ( a **M**inimum + **C**omplete + **V**erifiable + **Example** ) representing the problem? In other words, minimise the size of the **Complete + Verifiable** miniature, that still exhibits your indicated problem in runtime and post such code to be able to **reproduce** the behaviour. This is not MCVE for many reasons - variables lack their respective declarations ( within their lexical-scopes, re-run of the schema is impossible et al ). Thanks for re-considering the StackOverflow Rules & Best Practices, before post revision & kindly complete the MCVE to proceed – user3666197 May 27 '17 at 06:41
  • Hmmm... not sure what you mean. That's really all I have – user3425561 May 27 '17 at 08:01
  • How does your statements *(cit.)* **"That's really all I have" + "The code basically works."** matches the fact, the **elementary compilation produces 15 errors straight** and the posted code is principally not able to run & never will in this shape and form? Are you willing to post an MCVE, that is reproducible == compile + run? – user3666197 May 27 '17 at 08:46
  • ok sure, pasted the full code for server and client side above. both runable. but you will need two metatrader instances and you can only reproduce the issue during business hours. – user3425561 May 27 '17 at 09:46
  • **Negative** Sir, there is no need to wait for market. Similarly, there is no need to have more than one instance of the MetaTrader Terminal 4. – user3666197 May 28 '17 at 14:35

2 Answers2

4

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).

jlee88my
  • 2,935
  • 21
  • 28
  • Hi Joseph, thanks a lot for your advice. Can I implement this directly in my MQL4 code? – user3425561 Jun 01 '17 at 08:04
  • The codes above are just abstract, but it will work if you put them into the correct sections ( start() and onTick() ). The key is the FILE_COMMON which indicates that the file should be located in a common folder shared across all MT4s. Have fun! – jlee88my Jun 01 '17 at 10:44
  • I have tried something similar before but i was not able to make it work. I heared that i cant write a file outside the MT4 sandbox thats why I was trying to do this with DLLs. – user3425561 Jun 01 '17 at 14:15
  • where would the "yourFileName.txt" from your code be located? – user3425561 Jun 01 '17 at 14:16
  • 1
    FILE_COMMON is defined as: "The file path in the common folder of all client terminals \Terminal\Common\Files. Flag is used in FileOpen(), FileCopy(), FileMove() and in FileIsExist() functions." This is a common/shared location to all MT4 installations on your computer. So, all the EAs on all the MT4 installations will be able to access to the same file. https://docs.mql4.com/constants/io_constants/fileflags – jlee88my Jun 04 '17 at 16:17
  • @JosephLee I am quite new to writing mql code. I asked https://stackoverflow.com/questions/55930471/how-can-i-cancel-a-trade-when-another-is-open-and-keep-the-open-trade-for-a-give?noredirect=1&lq=1 but haven't been able to get far with a solution. I would appreciate if you could help me out, if possible. Someone recently put a bounty on my question too. – NotEveryDay May 13 '19 at 15:31
1

The best idea?

The best thing I can advise you is to stop trying to tweak KERNEL32.DLL published API to make it use with MetaTrader Terminal 4 code-execution ecosystem and to rather start designing a professional distributed system, independently of injecting objects into the O/S pagefile and hassling with semaphores and MUTEX-es.

Besides the best next step:

  • MQL4 code ought NEVER block. A MUTEX-signalling turned into a non-blocking state is a must
  • MQL4 code / API mapper ought respect the data-types and their actual memory sizes in MQL4
  • MQL4 code ought conform to the recent New-MQL4 rules ( sections are in "old"-MQL4 )
  • MQL4 declared string is not a C-lang string, but rather a struct! Handle with care!
  • MQL4 code violated in several places syntax rules, just test with #property strict
  • MQL4 code is "shooting itself in leg" when ignoring namespace boundaries / scopes of declaration
  • MQL4 code ignores potential error states, not inspecting any GetLastError() to handle such collision(s)
  • MQL4 code does not gracefully return resources ( forgets to clear 'em )
  • MQL4 code proposed exposes itself into an immense risk of KERNEL32.DLL API usage unlocked stealth security flaw / enabling a run-time hijacking hack
  • better use separation of concerns, using ZeroMQ or nanomsg messaging to "exchange values between ( not only ) MQL4 programs"
user3666197
  • 1
  • 6
  • 50
  • 92
  • Thanks for your adivce. How would I facilitate ZeroMQ or nanomsg messaging? I am not a professional coder :-( – user3425561 May 28 '17 at 15:49
  • Maybe, a hire of such a professional would both make a sense for your FX / trading business & bring tangible results faster than using some other approach. – user3666197 May 28 '17 at 16:41