//+------------------------------------------------------------------+ //| 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(); } } //+------------------------------------------------------------------+