Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 64 additions & 5 deletions MT5Connector/MT5Connector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,27 @@
#include "Mt5Handler.h"
#include "MtService.h"

static void convertSystemString(wchar_t* dest, const std::string& src)
// Capacities (in wchar_t, including the null terminator) of the caller-allocated
// MQL string buffers. They must match the StringInit calls in the expert advisor:
// StringInit(_error, 1000, 0) and StringInit(payload, 5000, 0).
static const size_t ERROR_BUFFER_CAPACITY = 1000;
static const size_t PAYLOAD_BUFFER_CAPACITY = 5000;

static std::wstring convertToWString(const std::string& src)
{
using convert_typeX = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_typeX, wchar_t> converterX;
auto wstr = converterX.from_bytes(src);
memcpy(dest, wstr.c_str(), wcsnlen(wstr.c_str(), 1000) * sizeof(wchar_t));
return converterX.from_bytes(src);
}

static void convertSystemString(wchar_t* dest, const std::string& src, size_t capacity)
{
auto wstr = convertToWString(src);
size_t len = wstr.length();
if (len >= capacity)
len = capacity - 1;
memcpy(dest, wstr.c_str(), len * sizeof(wchar_t));
dest[len] = L'\0';
}

static std::string convertWString(const wchar_t* src)
Expand All @@ -34,7 +49,7 @@ template <typename T> T Execute(std::function<T()> func, wchar_t* err, T default
}
catch (std::exception& e)
{
convertSystemString(err, e.what());
convertSystemString(err, e.what(), ERROR_BUFFER_CAPACITY);
MtService::GetInstance().LogError(e.what());
}
return result;
Expand Down Expand Up @@ -84,7 +99,51 @@ _DLLAPI int _stdcall getCommandType(int expertHandle, int& res, wchar_t* err)
_DLLAPI int _stdcall getPayload(int expertHandle, wchar_t* res, wchar_t* err)
{
return Execute<int>([&expertHandle, res]() {
convertSystemString(res, MtService::GetInstance().GetCommandPayload(expertHandle));
convertSystemString(res, MtService::GetInstance().GetCommandPayload(expertHandle), PAYLOAD_BUFFER_CAPACITY);
return 1;
}, err, 0);
}

// Size-aware replacement for getPayload: copies at most (capacity - 1) characters
// into res (always null-terminated) and returns the full payload length in wchar_t
// (excluding the null terminator), or -1 on error. If the returned length is
// >= capacity, the caller should re-allocate its buffer and call again.
_DLLAPI int _stdcall getPayload2(int expertHandle, wchar_t* res, int capacity, wchar_t* err)
{
return Execute<int>([&expertHandle, res, &capacity]() {
auto wstr = convertToWString(MtService::GetInstance().GetCommandPayload(expertHandle));
int required = static_cast<int>(wstr.length());
if (capacity > 0)
{
int len = required < capacity ? required : capacity - 1;
memcpy(res, wstr.c_str(), static_cast<size_t>(len) * sizeof(wchar_t));
res[len] = L'\0';
}
return required;
}, err, -1);
}

// Combined replacement for getCommandType + getPayload2: pops the next command
// in a single call. type receives the command type (0 when the queue is empty)
// and at most (capacity - 1) characters of its payload are copied into payload
// (always null-terminated). Returns the full payload length in wchar_t
// (excluding the null terminator), or -1 on error. If the returned length is
// >= capacity, the caller should re-allocate its buffer and fetch the payload
// again via getPayload2, which reads the already-popped command without
// popping the queue again.
_DLLAPI int _stdcall getCommandInfo(int expertHandle, int& type, wchar_t* payload, int capacity, wchar_t* err)
{
return Execute<int>([&expertHandle, &type, payload, &capacity]() {
std::string payload_str;
MtService::GetInstance().GetCommandInfo(expertHandle, type, payload_str);
auto wstr = convertToWString(payload_str);
int required = static_cast<int>(wstr.length());
if (capacity > 0)
{
int len = required < capacity ? required : capacity - 1;
memcpy(payload, wstr.c_str(), static_cast<size_t>(len) * sizeof(wchar_t));
payload[len] = L'\0';
}
return required;
}, err, -1);
}
69 changes: 64 additions & 5 deletions MTConnector/MTConnector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,27 @@
#include <string>
#include <codecvt>

static void convertSystemString(wchar_t* dest, const std::string& src)
// Capacities (in wchar_t, including the null terminator) of the caller-allocated
// MQL string buffers. They must match the StringInit calls in the expert advisor:
// StringInit(_error, 1000, 0) and StringInit(payload, 5000, 0).
static const size_t ERROR_BUFFER_CAPACITY = 1000;
static const size_t PAYLOAD_BUFFER_CAPACITY = 5000;

static std::wstring convertToWString(const std::string& src)
{
using convert_typeX = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_typeX, wchar_t> converterX;
auto wstr = converterX.from_bytes(src);
memcpy(dest, wstr.c_str(), wcsnlen(wstr.c_str(), 1000) * sizeof(wchar_t));
return converterX.from_bytes(src);
}

static void convertSystemString(wchar_t* dest, const std::string& src, size_t capacity)
{
auto wstr = convertToWString(src);
size_t len = wstr.length();
if (len >= capacity)
len = capacity - 1;
memcpy(dest, wstr.c_str(), len * sizeof(wchar_t));
dest[len] = L'\0';
}

static std::string convertWString(const wchar_t* src)
Expand All @@ -34,7 +49,7 @@ template <typename T> T Execute(std::function<T()> func, wchar_t* err, T default
}
catch (std::exception& e)
{
convertSystemString(err, e.what());
convertSystemString(err, e.what(), ERROR_BUFFER_CAPACITY);
MtService::GetInstance().LogError(e.what());
}
return result;
Expand Down Expand Up @@ -84,7 +99,51 @@ _DLLAPI int _stdcall getCommandType(int expertHandle, int& res, wchar_t* err)
_DLLAPI int _stdcall getPayload(int expertHandle, wchar_t* res, wchar_t* err)
{
return Execute<int>([&expertHandle, res]() {
convertSystemString(res, MtService::GetInstance().GetCommandPayload(expertHandle));
convertSystemString(res, MtService::GetInstance().GetCommandPayload(expertHandle), PAYLOAD_BUFFER_CAPACITY);
return 1;
}, err, 0);
}

// Size-aware replacement for getPayload: copies at most (capacity - 1) characters
// into res (always null-terminated) and returns the full payload length in wchar_t
// (excluding the null terminator), or -1 on error. If the returned length is
// >= capacity, the caller should re-allocate its buffer and call again.
_DLLAPI int _stdcall getPayload2(int expertHandle, wchar_t* res, int capacity, wchar_t* err)
{
return Execute<int>([&expertHandle, res, &capacity]() {
auto wstr = convertToWString(MtService::GetInstance().GetCommandPayload(expertHandle));
int required = static_cast<int>(wstr.length());
if (capacity > 0)
{
int len = required < capacity ? required : capacity - 1;
memcpy(res, wstr.c_str(), static_cast<size_t>(len) * sizeof(wchar_t));
res[len] = L'\0';
}
return required;
}, err, -1);
}

// Combined replacement for getCommandType + getPayload2: pops the next command
// in a single call. type receives the command type (0 when the queue is empty)
// and at most (capacity - 1) characters of its payload are copied into payload
// (always null-terminated). Returns the full payload length in wchar_t
// (excluding the null terminator), or -1 on error. If the returned length is
// >= capacity, the caller should re-allocate its buffer and fetch the payload
// again via getPayload2, which reads the already-popped command without
// popping the queue again.
_DLLAPI int _stdcall getCommandInfo(int expertHandle, int& type, wchar_t* payload, int capacity, wchar_t* err)
{
return Execute<int>([&expertHandle, &type, payload, &capacity]() {
std::string payload_str;
MtService::GetInstance().GetCommandInfo(expertHandle, type, payload_str);
auto wstr = convertToWString(payload_str);
int required = static_cast<int>(wstr.length());
if (capacity > 0)
{
int len = required < capacity ? required : capacity - 1;
memcpy(payload, wstr.c_str(), static_cast<size_t>(len) * sizeof(wchar_t));
payload[len] = L'\0';
}
return required;
}, err, -1);
}
6 changes: 6 additions & 0 deletions MtService/MtExpert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,10 @@ int MtExpert::GetCommandType()
std::string MtExpert::GetCommandPayload()
{
return current_task_ ? current_task_->getCommand().getPayload() : "";
}

void MtExpert::GetCommandInfo(int& command_type, std::string& payload)
{
command_type = GetCommandType();
payload = (command_type != MT_COMMAND_TYPE_EMPTY) ? GetCommandPayload() : "";
}
1 change: 1 addition & 0 deletions MtService/MtExpert.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class MtExpert

int GetCommandType();
std::string GetCommandPayload();
void GetCommandInfo(int& command_type, std::string& payload);

boost::signals2::signal<void(const MtEvent& event)> OnEvent;
boost::signals2::signal<void(int)> OnDeinit;
Expand Down
25 changes: 25 additions & 0 deletions MtService/MtService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class MtServiceImpl

int GetCommandType(int handle);
std::string GetCommandPayload(int handle);
void GetCommandInfo(int handle, int& command_type, std::string& payload);

void LogError(const std::string& error);

Expand Down Expand Up @@ -152,6 +153,25 @@ std::string MtServiceImpl::GetCommandPayload(int handle)
return payload;
}

void MtServiceImpl::GetCommandInfo(int handle, int& command_type, std::string& payload)
{
// Single blocking round-trip to the io thread: pops the next command
// (like GetCommandType) and returns its payload in the same task.
std::packaged_task<std::pair<int, std::string>()> task([handle, this]() {
std::pair<int, std::string> info(0, "");
if (experts_.count(handle) > 0)
experts_[handle]->GetCommandInfo(info.first, info.second);
return info;
});
auto f = task.get_future();
boost::asio::post(context_, std::bind(std::move(task)));

auto info = f.get();
command_type = info.first;
payload = std::move(info.second);
log_.Trace("%s: handle = %d, command_type = %d, payload = %s", __FUNCTION__, handle, command_type, payload.c_str());
}

void MtServiceImpl::LogError(const std::string& error)
{
log_.Error("%s: %s", __FUNCTION__, error.c_str());
Expand Down Expand Up @@ -234,6 +254,11 @@ std::string MtService::GetCommandPayload(int handle)
return impl_->GetCommandPayload(handle);
}

void MtService::GetCommandInfo(int handle, int& command_type, std::string& payload)
{
impl_->GetCommandInfo(handle, command_type, payload);
}

void MtService::LogError(const std::string& error)
{
impl_->LogError(error);
Expand Down
1 change: 1 addition & 0 deletions MtService/MtService.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class MtService

int GetCommandType(int handle);
std::string GetCommandPayload(int handle);
void GetCommandInfo(int handle, int& command_type, std::string& payload);

void LogError(const std::string& error);

Expand Down
Binary file modified mq4/MtApi.ex4
Binary file not shown.
44 changes: 29 additions & 15 deletions mq4/MtApi.mq4
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

bool getCommandType(int expertHandle, int& res, string& err);
bool getPayload(int expertHandle, string& res, string& err);
int getPayload2(int expertHandle, string& res, int capacity, string& err);
int getCommandInfo(int expertHandle, int& type, string& payload, int capacity, string& err);
#import

//#define __DEBUG_LOG__
Expand All @@ -35,6 +37,7 @@ int ExpertHandle;

string _error;
string _response_error;
string _command_payload;
bool isCrashed = FALSE;

bool IsRemoteReadyForTesting = false;
Expand Down Expand Up @@ -531,17 +534,9 @@ public:

JSONObject* GetJsonPayload()
{
string payload;
StringInit(payload, 5000, 0);

if (!getPayload(ExpertHandle, payload, _error))
{
PrintFormat("%s [ERROR]: %s", __FUNCTION__, _error);
return NULL;
}

//--- the payload is fetched together with the command type in ExecuteCommand
JSONParser payload_parser;
JSONValue *payload_json = payload_parser.parse(payload);
JSONValue *payload_json = payload_parser.parse(_command_payload);

if (payload_json == NULL)
{
Expand Down Expand Up @@ -569,15 +564,34 @@ int ExecuteCommand()
{
int command_type = 0;

if (!getCommandType(ExpertHandle, command_type, _error))
{
Print("[ERROR] ExecuteCommand: Failed to get command type! ", _error);
return (0);
}
StringInit(_command_payload, 5000, 0);
int required = getCommandInfo(ExpertHandle, command_type, _command_payload, 5000, _error);

if (command_type == 0)
return 0;

if (required < 0)
{
//--- the command was already popped (command_type is set) but its payload
//--- could not be converted: dispatch with an empty payload so the handler
//--- still sends an error response instead of leaving the client waiting
Print("[ERROR] ExecuteCommand: Failed to get command info! ", _error);
_command_payload = "";
}

if (required >= 5000)
{
//--- payload larger than the default buffer: re-allocate and fetch the
//--- already popped command again via getPayload2 (non-destructive read)
StringInit(_command_payload, required + 1, 0);
required = getPayload2(ExpertHandle, _command_payload, required + 1, _error);
if (required < 0)
{
Print("[ERROR] ExecuteCommand: Failed to get command payload! ", _error);
_command_payload = "";
}
}

#ifdef __DEBUG_LOG__
Print("ExecuteCommand: commnad type = ", command_type);
#endif
Expand Down
Binary file modified mq5/MtApi5.ex5
Binary file not shown.
Loading