Reputation: 79
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?
Upvotes: 2
Views: 3013
Reputation: 3043
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).
Upvotes: 4
Reputation: 1
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.
string
is not a C-lang string
, but rather a struct
! Handle with care!#property strict
GetLastError()
to handle such collision(s)KERNEL32.DLL
API usage unlocked stealth security flaw / enabling a run-time hijacking hackUpvotes: 1