У меня есть следующая функция
__declspec(dllexport) wchar_t* __stdcall __getJson(wchar_t * listN){
setlocale(LC_ALL, "");
//function logic
wstring ant = utf8_to_wstring(result);
const WCHAR* constRes = ant.c_str();
WCHAR* tempObj=new WCHAR[ant.length()];
wcscpy(tempObj, constRes);
thread Thread([tempObj]{
Sleep(1000);
delete[] tempObj;
});
Thread.detach();
return tempObj;
}
Эта DLL возвращается wchar_t*
в MetaTrader4.
Я пробовал много способов вернуть правильное значение и избежать утечек памяти, таких как заданный тип возврата const wchar_t*
, создавая свой собственный класс с деструктором с delete[]
в. Но все эти попытки оказались безуспешными: я получил '??ello'
вместо 'hello'
, Просто первые один или два символа были неверны. С созданием thread
это работает правильно. Но я хочу знать, может ли быть лучшее решение?
Чтобы создать строку в вашей DLL и передать ее вызывающей стороне, вы должны динамически выделить часть памяти в DLL для хранения символов строки и передать указатель на эту память вызывающей стороне.
Более того, вызывающая сторона должна иметь возможность освободить эту память, когда строка больше не нужна.
Для правильной работы необходимо использовать так же диспетчер памяти / распределитель для выделения и освобождения памяти строки.
Одним из вариантов будет использование общего общесистемного распределителя, такого как COM-распределитель. Таким образом, вы можете выделить память в DLL с помощью CoTaskMemAlloc
и вызывающий абонент может освободить его, используя соответствующий CoTaskMemFree
,
Другим вариантом будет вернуть BSTR
строка, выделенная SysAllocString
в DLL. И вызывающая сторона выпустит эту строку, вызывая SysFreeString
,
Или вы можете предоставить пользовательскую функцию для освобождения памяти строки в вашей DLL. Например, вы можете выделить память строки в вашей DLL, используя new[]
и вы могли бы предоставить MyDllFreeString
функция, которая вызывает delete[]
,
Обратите внимание, что когда вы выделяете память для строки в стиле C, вы должны рассмотреть дополнительный слот для строки NUL-терминатор (Итак, вы должны выделить stringLength + 1
wchar_t
с).
#assume nothing ; mql4_string != string
Бинго, сильный удар очевиден. Принимающая сторона не предполагает, так как новый—MQL4.56789
было представлено, это представление блока байтов в виде string
, но struct
(!).
(Соч. 🙂 Внутреннее представление строкового типа представляет собой структуру длиной 12 байтов:
#pragma pack(push,1)
struct MqlString
{
int size; // 32-bit integer, contains size of the buffer, allocated for the string.
LPWSTR buffer; // 32-bit address of the buffer, containing the string.
int reserved; // 32-bit integer, reserved.
};
#pragma pack(pop,1)
(Соч. 🙂 (Документация на стороне MQL4:)
String Type
string
Тип используется для хранения текстовых строк. Текстовая строка — это последовательность символов в формате Unicode с последним нулем в конце.
Случайно, я сосредоточился на BOOL APIENTRY DllMain
, Так что это решит мою проблему без создания темы.
vector<wchar_t*> tempObjVector;
BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
while (tempObjVector.size() != 0)
{
delete[] tempObjVector.back();
tempObjVector.pop_back();
}
break;
}
return TRUE;
}
__declspec(dllexport) wchar_t* __stdcall __getJson(wchar_t * listN){
....
....
wchar_t* tempObj=new wchar_t[ant.length()+1];
tempObj[ant.length()] = 0;
wcscpy(tempObj, constRes);
tempObjVector.push_back(tempObj);
return tempObj;
}
Другой способ сделать это (немного проще, но только для некоторых случаев):
//C++
extern "C" __declspec(dllimport) const wchar_t *GetMessage();
const wchar_t *GetMessage()
{
static std::wstring last_message;
last_message = GetSomeMessage();
return last_message.c_str();
}
//MQL
#import "MyDll.dll"string GetMessage();
#import
string message = GetMessage();