Repository navigation
Expand file tree
/
Copy pathexports.cpp
More file actions
202 lines (171 loc) · 7.12 KB
/
Copy pathexports.cpp
File metadata and controls
202 lines (171 loc) · 7.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// The DLL's C ABI: UTF-16 <-> UTF-8 conversion and exception containment around the session.
// Nothing else belongs here.
#include "mqlconnector/mqlconnector.h"
#include "connector/session.h"
#include "core/utf.h"
#include "mqlconnector/version.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <climits>
#include <cstddef>
#include <exception>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
static_assert(sizeof(wchar_t) == 2, "MQL strings are UTF-16: the DLL is built for Windows only");
namespace {
using mqlc::connector::process_session;
std::string from_mql(const wchar_t* text) {
return text == nullptr ? std::string() : mqlc::utf::to_utf8(std::wstring_view(text));
}
/// Converts into the caller's per-thread buffer; the pointer lives until the next conversion
/// into that buffer on this thread.
const wchar_t* to_mql(std::wstring& buffer, std::string_view utf8) {
buffer.clear();
mqlc::utf::append_utf16(utf8, buffer);
return buffer.c_str();
}
void log_failure(const char* function, const char* what) noexcept {
try {
process_session().logger().log(mqlc::core::LogLevel::error,
std::string("[") + function + "]: Exception: " + what);
} catch (...) {
}
}
/// Runs `body`, turning any exception into a log line and `fallback`: nothing may cross the
/// C ABI into MT5.
template <class Result, class Body>
Result guarded(const char* function, Result fallback, Body&& body) noexcept {
try {
return body();
} catch (const std::exception& e) {
log_failure(function, e.what());
} catch (...) {
log_failure(function, "unknown exception");
}
return fallback;
}
/// Live EAs never call DestroyLib and MT5 may FreeLibrary the DLL while the reader thread still
/// runs its code. Pinning keeps the module mapped until the process exits.
void pin_module() noexcept {
static const bool pinned = [] {
static const char anchor = 0; // any address inside this module
HMODULE self = nullptr;
return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
reinterpret_cast<LPCWSTR>(&anchor), &self) != FALSE;
}();
(void)pinned;
}
const wchar_t kEmpty[] = L"";
/// The last binary reply that did not fit the caller's buffer, per thread, for DoFetchBinary.
thread_local std::string t_kept_frame;
thread_local bool t_has_kept = false;
std::string_view bytes_from_mql(const unsigned char* data, int32_t length) {
if (data == nullptr || length <= 0) return {};
return {reinterpret_cast<const char*>(data), static_cast<std::size_t>(length)};
}
/// Hands a received frame to MQL as a whole FBE message: its length when it fits `capacity`,
/// else `-length` with the frame kept for DoFetchBinary. 0 = no frame.
int32_t deliver_binary(std::optional<std::string> frame, unsigned char* out, int32_t capacity) {
if (!frame) return 0;
const std::size_t size = mqlc::connector::Session::framed_size(*frame);
if (size > static_cast<std::size_t>(INT32_MAX)) return 0;
if (out != nullptr && capacity >= 0 && size <= static_cast<std::size_t>(capacity)) {
mqlc::connector::Session::write_framed(*frame, out);
t_kept_frame.clear(); // an older kept frame was superseded by this reply
t_has_kept = false;
return static_cast<int32_t>(size);
}
t_kept_frame = std::move(*frame);
t_has_kept = true;
return -static_cast<int32_t>(size);
}
} // namespace
extern "C" {
int32_t MQLC_CALL InitLib(const wchar_t* json) noexcept {
return guarded("InitLib", int32_t{0}, [&] {
pin_module();
return static_cast<int32_t>(process_session().init(from_mql(json)));
});
}
void MQLC_CALL DestroyLib(int64_t chart_id) noexcept {
guarded("DestroyLib", 0, [&] {
process_session().destroy(chart_id);
return 0;
});
}
const wchar_t* MQLC_CALL DoSendMessage(const wchar_t* json) noexcept {
thread_local std::wstring result;
return guarded("DoSendMessage", kEmpty, [&] {
return to_mql(result, process_session().send_and_wait(from_mql(json)));
});
}
void MQLC_CALL DoPostMessage(const wchar_t* json) noexcept {
guarded("DoPostMessage", 0, [&] {
process_session().post(from_mql(json));
return 0;
});
}
const wchar_t* MQLC_CALL DoListenMessage(int64_t key) noexcept {
thread_local std::wstring result;
return guarded("DoListenMessage", kEmpty, [&] {
return to_mql(result, process_session().listen(key));
});
}
const wchar_t* MQLC_CALL GetLevels(const wchar_t* sym, int64_t chart_id) noexcept {
thread_local std::wstring result;
return guarded("GetLevels", kEmpty, [&] {
return to_mql(result, process_session().get_levels(from_mql(sym), chart_id));
});
}
const wchar_t* MQLC_CALL GetSignalCache(const wchar_t* key, const wchar_t* sym, int64_t chart_id) noexcept {
thread_local std::wstring result;
return guarded("GetSignalCache", kEmpty, [&] {
return to_mql(result, process_session().get_signal_cache(from_mql(key), from_mql(sym), chart_id));
});
}
int32_t MQLC_CALL DoSendBinary(const unsigned char* request, int32_t request_length, unsigned char* reply,
int32_t reply_capacity) noexcept {
return guarded("DoSendBinary", int32_t{0}, [&] {
return deliver_binary(process_session().send_binary(bytes_from_mql(request, request_length)), reply,
reply_capacity);
});
}
int32_t MQLC_CALL DoFetchBinary(unsigned char* reply, int32_t reply_capacity) noexcept {
return guarded("DoFetchBinary", int32_t{0}, [&] {
if (!t_has_kept) return int32_t{0};
const std::size_t size = mqlc::connector::Session::framed_size(t_kept_frame);
if (reply == nullptr || reply_capacity < 0 || size > static_cast<std::size_t>(reply_capacity))
return -static_cast<int32_t>(size);
mqlc::connector::Session::write_framed(t_kept_frame, reply);
t_kept_frame.clear();
t_has_kept = false;
return static_cast<int32_t>(size);
});
}
void MQLC_CALL DoPostBinary(const unsigned char* request, int32_t request_length) noexcept {
guarded("DoPostBinary", 0, [&] {
process_session().post_binary(bytes_from_mql(request, request_length));
return 0;
});
}
int32_t MQLC_CALL DoListenBinary(int64_t key, unsigned char* buffer, int32_t capacity) noexcept {
return guarded("DoListenBinary", int32_t{0}, [&] {
return deliver_binary(process_session().listen_binary(key), buffer, capacity);
});
}
const wchar_t* MQLC_CALL MQLC_GetVersion() noexcept {
// A literal: valid forever, touches no session, safe before InitLib.
return MQLC_VERSION_W;
}
} // extern "C"
// No DllMain: the CRT's default one is enough. Nothing runs at load or unload, so no thread is
// ever started or joined under the loader lock. DisableThreadLibraryCalls is deliberately not
// called: with the static CRT (/MT) the CRT itself needs DLL_THREAD_DETACH to free per-thread
// data, including the thread_local result buffers above.