问题
I have following function
__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;
}
This DLL returns wchar_t*
to MetaTrader4.
I tried many ways to return correct value and avoid memory leaks such as set return type const wchar_t*
, creating my own class with destructor with delete[]
in. But all this attempts was unsuccessful: I got '??ello'
instead of 'hello'
. Just first one or two symbols were incorrect. With creating thread
it works right. But, I want to know, may there be better solution?
回答1:
To create a string in your DLL and pass it to the caller, you must dynamically allocate some memory in the DLL to store the string's characters, and pass a pointer to that memory to the caller.
Moreover, the caller must be able to release that memory when the string is not needed anymore.
To make it work properly, you must use the same memory manager/allocator to both allocate and free the string's memory.
One option would be to use a common system-wide allocator like the COM allocator. In this way, you can allocate the memory in the DLL using CoTaskMemAlloc
, and the caller can free it using the matching CoTaskMemFree
.
Another option would be to return a BSTR
string, allocated with SysAllocString
in the DLL. And the caller would release that string invoking SysFreeString
.
Or, you could provide a custom function to free the string's memory in your DLL. For example, you could allocate the string's memory in your DLL using new[]
, and you could provide a MyDllFreeString
function that invokes delete[]
.
Note that, when you allocate memory for a C-style string, you must consider an additional slot for the string's NUL-terminator (so, you must allocate stringLength + 1
wchar_t
s).
回答2:
#ol' ASM hackers always used to start with
#assume nothing ; mql4_string != string
Bingo, the headbang is evident. Receiving side does not assume, since New-MQL4.56789
was introduced, it's representation of a block of bytes as a string
, but a struct
(!).
(cit.:) Internal representation of the string type is a structure of 12 bytes long:
#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)
(cit.:) ( MQL4-side doc: )
String Type
Thestring
type is used for storing text strings. A text string is a sequence of characters in the Unicode format with the final zero at the end of it.
回答3:
Accidentaly, I put my mind to BOOL APIENTRY DllMain
. So it solve my problem without creating threads.
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;
}
回答4:
Another way of doing that (a little bit simpler, but for some cases only):
//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();
来源:https://stackoverflow.com/questions/43320799/how-to-return-a-string-from-a-c-dll-into-a-metatrader-4-code-execution-ecosyst