280 lines
No EOL
10 KiB
MQL4
280 lines
No EOL
10 KiB
MQL4
//+------------------------------------------------------------------+
|
|
//| MagicNumberDuplicateChecker.mq4 |
|
|
//| Copyright (c) 2024, HANDY SYSTEMS |
|
|
//+------------------------------------------------------------------+
|
|
/*
|
|
The MIT License
|
|
|
|
Copyright (c) 2024, HANDY SYSTEMS
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#property copyright "Copyright (c) 2024, HANDY SYSTEMS"
|
|
#property version "1.0"
|
|
|
|
#property description "Magic Number Duplicate Checker"
|
|
|
|
#property strict
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Debug mode |
|
|
//+------------------------------------------------------------------+
|
|
#define DEBUG_MODE false
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Win32 API functions |
|
|
//+------------------------------------------------------------------+
|
|
#import "kernel32.dll"
|
|
int CreateFileW(string lpFileName, int dwDesiredAccess, int dwShareMode,
|
|
int lpSecurityAttributes, int dwCreationDisposition,
|
|
int dwFlagsAndAttributes, int hTemplateFile);
|
|
bool CloseHandle(int hObject);
|
|
int GetTempPathW(int nBufferLength, string& lpBuffer);
|
|
int GetCurrentProcessId();
|
|
#import
|
|
|
|
#import "ntdll.dll"
|
|
int RtlGetLastWin32Error();
|
|
#import
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Win32 API constants |
|
|
//+------------------------------------------------------------------+
|
|
#define GENERIC_WRITE 0x40000000
|
|
#define GENERIC_READ 0x80000000
|
|
#define FILE_SHARE_DELETE 0x00000004
|
|
#define CREATE_NEW 1
|
|
#define CREATE_ALWAYS 2
|
|
#define OPEN_EXISTING 3
|
|
#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
|
|
#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000
|
|
#define INVALID_HANDLE_VALUE -1
|
|
#define ERROR_FILE_EXISTS 80
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Message constants |
|
|
//+------------------------------------------------------------------+
|
|
#define MSG_DUPLICATE_WARNING "WARNING: Magic number %d is already in use.\n\n" + \
|
|
"There may already be an EA running with the\n" + \
|
|
"same magic number for the same currency pair.\n\n" + \
|
|
"Do you want to continue running it?"
|
|
#define MSG_SYSTEM_ERROR "System error occurred: %d"
|
|
#define MSG_TEMP_PATH_ERROR "Failed to get temporary path"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Input parameters |
|
|
//+------------------------------------------------------------------+
|
|
input int MagicNumber = 12345; // Magic Number: Unique identifier for EA
|
|
input bool CheckDuplicate = true; // Check Duplicate: Enable/disable duplicate checking
|
|
input int CheckInterval = 300; // Check Interval: Time between checks (seconds)
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Global variables |
|
|
//+------------------------------------------------------------------+
|
|
datetime lastCheckTime = 0; // Last check timestamp
|
|
int lockFileHandle = INVALID_HANDLE_VALUE; // Lock file handle
|
|
string tempPath = ""; // Temporary file path
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom logging function |
|
|
//+------------------------------------------------------------------+
|
|
void LogDebug(const string message)
|
|
{
|
|
if (DEBUG_MODE) Print("[DEBUG] ", message);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get Windows temporary path |
|
|
//+------------------------------------------------------------------+
|
|
bool GetTempPath()
|
|
{
|
|
string buffer = " "; // 50 chars buffer
|
|
int result = GetTempPathW(StringLen(buffer), buffer);
|
|
|
|
if (result > 0)
|
|
{
|
|
tempPath = StringSubstr(buffer, 0, result);
|
|
LogDebug("Temp path: " + tempPath);
|
|
return true;
|
|
}
|
|
|
|
Print(MSG_TEMP_PATH_ERROR);
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate lock file name |
|
|
//+------------------------------------------------------------------+
|
|
string GetLockFileName()
|
|
{
|
|
string fileName = StringFormat("MT4_Magic_%d_%s_%d.lock",
|
|
MagicNumber,
|
|
Symbol(),
|
|
GetCurrentProcessId()
|
|
);
|
|
|
|
LogDebug("Lock file name: " + fileName);
|
|
return tempPath + fileName;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Check for magic number duplication |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckDuplicateMagicNumber()
|
|
{
|
|
string fileName = GetLockFileName();
|
|
|
|
int fileHandle = CreateFileW(
|
|
fileName,
|
|
(int)GENERIC_READ,
|
|
0,
|
|
0,
|
|
OPEN_EXISTING,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (fileHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (fileHandle != lockFileHandle)
|
|
{
|
|
CloseHandle(fileHandle);
|
|
return true; // Duplicate found
|
|
}
|
|
CloseHandle(fileHandle);
|
|
}
|
|
|
|
return false; // No duplicate
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Register magic number |
|
|
//+------------------------------------------------------------------+
|
|
bool RegisterMagicNumber()
|
|
{
|
|
string fileName = GetLockFileName();
|
|
LogDebug("Attempting to register magic number: " + IntegerToString(MagicNumber));
|
|
|
|
lockFileHandle = CreateFileW(
|
|
fileName,
|
|
(int)GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
CREATE_NEW,
|
|
(int)(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
|
|
0
|
|
);
|
|
|
|
if (lockFileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
int error = RtlGetLastWin32Error();
|
|
|
|
if (error == ERROR_FILE_EXISTS)
|
|
{
|
|
// Show warning and ask user whether to continue
|
|
string message = StringFormat(MSG_DUPLICATE_WARNING, MagicNumber);
|
|
int response = MessageBox(
|
|
message,
|
|
"Magic Number Duplicate Warning",
|
|
MB_ICONWARNING | MB_OKCANCEL
|
|
);
|
|
|
|
if (response == IDCANCEL)
|
|
{
|
|
LogDebug("User canceled operation");
|
|
return false;
|
|
}
|
|
|
|
LogDebug("User chose to continue despite duplicate");
|
|
return true; // User accepted the risk
|
|
}
|
|
else
|
|
{
|
|
// Handle other system errors
|
|
string errorMsg = StringFormat(MSG_SYSTEM_ERROR, error);
|
|
MessageBox(
|
|
errorMsg,
|
|
"System Error",
|
|
MB_ICONERROR | MB_OK
|
|
);
|
|
Print(errorMsg);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogDebug("Successfully registered magic number");
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Get temporary path
|
|
if (!GetTempPath())
|
|
{
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Perform duplicate checking if enabled
|
|
if (CheckDuplicate)
|
|
{
|
|
if (!RegisterMagicNumber())
|
|
{
|
|
return INIT_FAILED;
|
|
}
|
|
}
|
|
|
|
lastCheckTime = TimeCurrent();
|
|
LogDebug("Initialization completed successfully");
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
if (lockFileHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
LogDebug("Closing lock file handle");
|
|
if (!CloseHandle(lockFileHandle))
|
|
{
|
|
int error = RtlGetLastWin32Error();
|
|
Print("Failed to close handle: ", error);
|
|
}
|
|
lockFileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
if (!CheckDuplicate) return;
|
|
|
|
// Perform periodic checks
|
|
if (TimeCurrent() - lastCheckTime >= CheckInterval)
|
|
{
|
|
if (CheckDuplicateMagicNumber())
|
|
{
|
|
Print("Warning: Duplicate magic number detected during runtime check");
|
|
Alert("Warning: Duplicate magic number detected\nMagic Number: ", MagicNumber);
|
|
}
|
|
|
|
lastCheckTime = TimeCurrent();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+ |