2026-06-29 20:43:05 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Pw.mqh |
|
|
|
|
|
//| Copyright 2026, Niquel Mendoza. |
|
|
|
|
|
//| https://www.mql5.com/ |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Copyright 2026, Niquel Mendoza."
|
|
|
|
|
#property link "https://www.mql5.com/"
|
|
|
|
|
#property strict
|
|
|
|
|
|
|
|
|
|
#ifndef FULLMT5MCPBYLEO_SRC_COMPLEX_PW_MQH
|
|
|
|
|
#define FULLMT5MCPBYLEO_SRC_COMPLEX_PW_MQH
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Include |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "..\\Def\\Def.mqh"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
namespace TSN
|
|
|
|
|
{
|
|
|
|
|
class CMcpFuncRunCommand : public CMcpFunction
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
int m_mode;
|
|
|
|
|
CJsonBuilder m_builder;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CMcpFuncRunCommand(int mode)
|
|
|
|
|
: CMcpFunction(0, false, "run_command"), m_mode(mode) {}
|
|
|
|
|
~CMcpFuncRunCommand(void) {}
|
|
|
|
|
void Run(CJsonNode& param, string& res) override final;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CMcpFuncRunCommand::Run(CJsonNode& param, string& res) override final
|
|
|
|
|
{
|
|
|
|
|
//---
|
2026-06-30 10:46:22 -05:00
|
|
|
const string command = param["run"].ToStringNoEscape("");
|
2026-06-29 20:43:05 -05:00
|
|
|
const uint timeout = uint(param["timeout_segundos"].ToInt(1) * 1000);
|
|
|
|
|
//---
|
|
|
|
|
if(command == "")
|
|
|
|
|
{
|
|
|
|
|
res = "{\"ok\":false,\"result\":\"Empty command\"}";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(m_mode == 0)
|
|
|
|
|
{
|
|
|
|
|
res = "{\"ok\":false,\"result\":\"The user has disabled the use of this tool in the EA settings.\"}";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-30 10:46:22 -05:00
|
|
|
//--- Nombre único de archivo temporal, relativo a la carpeta Files (para usarlo con funciones nativas MQL5)
|
|
|
|
|
const string rel_file = "McpMt5ByLeo\\temp.txt";
|
|
|
|
|
|
|
|
|
|
//--- Asegurar que la subcarpeta exista, en el ámbito Common
|
|
|
|
|
FolderCreate("McpMt5ByLeo", FILE_COMMON);
|
|
|
|
|
|
|
|
|
|
//--- Ruta absoluta real en disco: Common + \Files\ + subcarpeta + archivo
|
|
|
|
|
const string abs_path = TERMINAL_MT5_COMMON_PATH + "\\Files\\" + rel_file;
|
|
|
|
|
|
|
|
|
|
//--- Borrar cualquier residuo previo antes de ejecutar
|
|
|
|
|
FileDelete(rel_file, FILE_COMMON);
|
|
|
|
|
|
|
|
|
|
//--- El propio shell redirige su salida (stdout + stderr) al archivo
|
|
|
|
|
string ps_args = "-NoProfile -NonInteractive";
|
2026-06-29 20:43:05 -05:00
|
|
|
if(m_mode == 1)
|
2026-06-30 10:46:22 -05:00
|
|
|
ps_args += " -ExecutionPolicy AllSigned";
|
|
|
|
|
else
|
|
|
|
|
if(m_mode == 2)
|
|
|
|
|
ps_args += " -ExecutionPolicy Restricted";
|
2026-06-29 20:43:05 -05:00
|
|
|
|
2026-06-30 10:46:22 -05:00
|
|
|
string ps_inner =
|
|
|
|
|
"$out = & { " + command + " } 2>&1 | Out-String; " +
|
|
|
|
|
"[System.IO.File]::WriteAllText('" + abs_path + "', $out, [System.Text.UTF8Encoding]::new($false))";
|
|
|
|
|
string full_command = "powershell " + ps_args + " -Command \"" + ps_inner + "\"";
|
2026-06-29 20:43:05 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
|
STARTUPINFOW si;
|
|
|
|
|
ZeroMemory(pi);
|
|
|
|
|
ZeroMemory(si);
|
|
|
|
|
si.cb = sizeof(si);
|
2026-06-30 10:46:22 -05:00
|
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
|
|
|
si.wShowWindow = SW_HIDE;
|
2026-06-29 20:43:05 -05:00
|
|
|
|
|
|
|
|
//---
|
2026-06-30 10:46:22 -05:00
|
|
|
const int created = CreateProcessW(NULL, full_command, NULL, NULL, 0, 0x08000000, NULL, NULL, si, pi);
|
2026-06-29 20:43:05 -05:00
|
|
|
|
|
|
|
|
if(created == 0)
|
|
|
|
|
{
|
|
|
|
|
res = StringFormat("{\"ok\":false,\"result\":\"Failed call CreateProcessW, last error = %u\"}", GetLastError());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-30 10:46:22 -05:00
|
|
|
//--- Esperar a que el proceso termine
|
2026-06-29 20:43:05 -05:00
|
|
|
const uint wait_result = WaitForSingleObject(pi.hProcess, timeout);
|
|
|
|
|
if(wait_result == WAIT_TIMEOUT_VAL)
|
|
|
|
|
TerminateProcess(pi.hProcess, 1);
|
|
|
|
|
|
|
|
|
|
uint exit_code = 0;
|
|
|
|
|
GetExitCodeProcess(pi.hProcess, exit_code);
|
|
|
|
|
|
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
|
|
2026-06-30 10:46:22 -05:00
|
|
|
//--- Leer el contenido del archivo con funciones nativas de MQL5 (ámbito Common)
|
|
|
|
|
uchar data[];
|
|
|
|
|
const long size = FileLoad(rel_file, data, FILE_COMMON);
|
|
|
|
|
|
|
|
|
|
//--- Borrar el archivo temporal para no acumular basura
|
|
|
|
|
FileDelete(rel_file, FILE_COMMON);
|
|
|
|
|
|
2026-06-29 20:43:05 -05:00
|
|
|
//---
|
2026-06-30 10:46:22 -05:00
|
|
|
m_builder.Clear();
|
2026-06-29 20:43:05 -05:00
|
|
|
m_builder.Obj();
|
|
|
|
|
m_builder.Key("ok").Val(bool(exit_code == 0));
|
|
|
|
|
m_builder.Key("result").Obj();
|
|
|
|
|
m_builder.Key("exit_code").Val(int(exit_code));
|
2026-06-30 10:46:22 -05:00
|
|
|
m_builder.Key("stdout").ValU(data);
|
2026-06-29 20:43:05 -05:00
|
|
|
m_builder.EndObj();
|
|
|
|
|
m_builder.EndObj();
|
|
|
|
|
res = m_builder.Build();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#endif // FULLMT5MCPBYLEO_SRC_COMPLEX_PW_MQH
|