3

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?

user3666197
  • 1
  • 6
  • 50
  • 92

4 Answers4

2

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();
sibvic
  • 1,622
  • 2
  • 12
  • 19
  • you are life saver wow. Been slamming my head into integrating a C++ dll function into MQL5 that returns a string. I wanted to use a std::string but was having issues so tried using char* but was getting super confused. THANKS YOU ARE AWESOME :D – Drew Scatterday Sep 07 '22 at 20:48
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_ts).

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • You might want to know, the **receiver-side** of the DLL-block-of-data is a specific code-execution environment, in which some C-common features do not hold. While the **`MQL4`**-syntax was since it's initial days inspired by C-family of languages (+still creeping, under internal developments of the engine ), the `MQL4`-language compiler does have a lot of non-C specialties, so the problem is in expecting a `MQL4`-side **`struct`**, instead of a C-side **`string`**. You might want to update your post to reflect these facts so as to properly meet the asked `MQL4`-context of use. Long live C64! – user3666197 Apr 10 '17 at 17:26
1

#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.

user3666197
  • 1
  • 6
  • 50
  • 92
  • Unfortunately, using MqlString struct leads to access violation error in MetaTrader4. I read about this structure and already tried to use it as output of function but accidentaly found at one of forums, that using wchar_t* is better choice. – Vladislav Vazhenin Apr 11 '17 at 09:11
  • @VladislavVazhenin Yes, many issues ( just imagine re-design efforts for all the code-base .. ). Most of the times we created a new DLL-wrapper, which returns to MQL4-side a "*New*-`MQL4.56789` compatible" `char[]` and translate this using an MQL4 built-in functions ( to avoid any memory management ), whereas the DLL-proxy reformats the calling interface so as to meet the external DLL-function specifications. This approach was the least bleeding, once the whole re-design expenses and efforts had to be re-taken again. One never knows, when a next syntax-creep appears on the table, do we? – user3666197 Apr 11 '17 at 15:03
  • for me is strangely, that information about definition mql string in manual is same as you suggested. So users must experiment with casting types.... – Vladislav Vazhenin Apr 12 '17 at 10:09
  • Welcome to **The Club**! While **`#assume nothing`** methodology helps a bit to survive the speed of change(s) [ ... & still creeping ], it is burning a lot of fat, attention & both design and testing efforts. Quite a paranoid world, isn't it? But it helps in building a systematically robust, self-healing solution approaches. Relying on naive and optimistic belief that all initial assumptions will hold forever, is systematically punished on each arrival of such change. This is The Green Tree of Human Life :o) – user3666197 Apr 12 '17 at 11:32
  • n.b.: the above cited `#pragma`-wrapped `struct` internal definition was not "*suggested*", but was a citation from ***New-*`MQL4.56789`**, to make pure C++ guys start to believe in what I saying -- it was rather a warning, that without this internal know-how, one cannot solve the DLL-interface from just C-side. What I did suggest, was to systematically avoid any such dependence and to work with **`char[]`** and built-in `MQL4` conversion-functions, that ought ( remain to ) understand all `MQL4` code-execution internal "surprises" not seen from outside. Anyway- **Enjoy Wild Worlds of `MQL4`**. – user3666197 Apr 12 '17 at 11:41
0

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;
}