How to return a string from a C++ DLL into a MetaT

2019-03-31 20:39发布

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?

4条回答
劳资没心,怎么记你
2楼-- · 2019-03-31 21:18

#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


The string 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楼-- · 2019-03-31 21:29

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_ts).

查看更多
爷、活的狠高调
4楼-- · 2019-03-31 21:32

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;
}
查看更多
贼婆χ
5楼-- · 2019-03-31 21:36

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();
查看更多
登录 后发表回答