Returning a char* string from a DLL in MT4 eats memory

 
Hi
I am in the process of developing a DLL for MT4 that I want to return a string that is generated inside the DLL. If I use a function in the DLL like char* ReturnResult(char* stringToReturn) and pass the same string in as I get in return there is naturally no memory leak as it is a pointer to the same char array. But if I use char* s = new char[stringsize] inside the DLL and tries to return s its a new pointer and I get a memory leak. If I run a backtest I can see that the memory usage is climbing especially with large strings. I have also tried to pass by reference but I get memory leaks or chrash MT4 or some other strange errors...I have had a look at several examples and tried the MqlStr example from Metaquotes but I have not found a solution yet. Ant suggestions? Maybe I could have explained this a little better maybe...Thanks for answers!
 

Does the string you pass in have a maximum size? If so stupid solution would be always pass in the maximum stringsize.

I guessing here but I think the new char*(stringsize) is overloading or creating a new instance of the string object
and on return leaves the old string instance in memory - ignore this just mental muttering to look for a solution
clue.

 
Hi Ickyrus

The code looks more like this:

- Taken from the ExpertSample
// Works OK
MT4_EXPFUNC char* __stdcall GetStringValue(char *spar)
{
printf("GetDoubleValue takes \"%s\"\n",spar);
return(spar);
}

// My code

// - Memory leak -
MT4_EXPFUNC char* __stdcall GetStringValue()
{
char *spar = new char[4];
strcpy(spar, "Test");

return(spar);
}

This is just an example of what I try to acheive. It works but eats memory if the string is big.

Thanks for your reply!
 
Since there is not such thing as a char* in mql4 what would you expect the main program to do with it?
Pass a max sized string by reference to the DLL and let the DLL populate it. Note a mql4 string is a structure.
 
WHRoeder:
Since there is not such thing as a char* in mql4 what would you expect the main program to do with it?
Pass a max sized string by reference to the DLL and let the DLL populate it. Note a mql4 string is a structure.


I would have expected that when used like this

#import "MyDLL.dll"
string test = GetStringValue();
#import

MT4 would clear the memory from it when finished. But it seems to me that it does not. I have also tried the MqlStr* str from the ExpertSample and passed it by reference but I could not get it to work either.
Do you have a short code example WHRoeder? Thanks!!!
 

More mental musing.

A char is a single byte a string is a sequence of char ended by a zero.

stringcpy will expect the array of char to be ended by a zero.

I am assuming that *char is a pointer where the char array - cant find reference to this yet and the mlq compiler
is a bastardised(hacked?) version of the C++ compiler (probably for the best too!)
therefore without reading up on what it actually does would char *spar = new char[4]; create a memory allocation of 4 byte referenced by the
*spar pointer. So you have now 'lost' the pointer to the origional string you were passing and hence the memory leak - (At least thats my theory! I can and often am very wrong)

this link may be of use
https://www.mql5.com/en/code/8701

 

Could you also post a link to the Exper sample you mention - I can't find it at the moment. thanks in advance

 
Hi Ickyrus
I think you are right about the lost pointer theory. I thought first the MT4 was clever enough to allocate/deallocate memory for the string when it got a char* returned from the function in the DLL. It certainly can get the value from char* into the string but it cannot deallocate the memory after it is done with it like it can when the string has been declared in mql.

The ExpertSample is in your experts\samples folder where you have installed MT4.

I havent yet tried to return the struct (MqlStr) from the ExpertSample. Maybe I should give it a try....
Thanks for your help!
 
ziggy:

I would have expected that when used like this [...] MT4 would clear the memory from it when finished.

It's odd that MQL4 allows and can interpret char * return values from DLL functions, but does not appear to do garbage collection on the memory. I'd expect it either to do both, or neither. Well-known and widely used libraries on this forum such as http51.dll are vulnerable to this memory leak (though the real-life impact is usually limited).

You basically have two options:

  • Use WHRoeder's suggestion of passing in a buffer. However, this can become very cumbersome in some scenarios.
  • Do your own explicit garbage collection. For example, get the DLL to log each memory allocation and the thread ID which requested it. At the end of your start() function, call a garbage collection function in the DLL which releases all the memory allocations logged against that thread. You might then need to be careful if allocating strings from the DLL to static variables within the EA.
 
Just to let you know. I got this to work without a memory leak by passing a MqlStr struct by reference. I could not get a return value to work. See the simplified example below for my solution:

C++:

struct MqlStr
{
int len;
char* string;
};

void FunctionWithoutMemoryLeak(MqlStr* fieldValue)
{
char *value = new char[4];
strcpy(value, "Test");

strcpy(fieldValue[0].string, value);
fieldValue[0].len = strlen(value);

delete [] value;
}

// MQL
#import "MyTest.dll"
void FunctionWithoutMemoryLeak(string& fieldValue[]);
#import

But beware that the string variable you pass in gets overwritten every time you call the same function even if you pass a new string variable. It uses the same pointer every time so you have to copy the value into a new variable before you call the function again.

Thanks for your help guys!

 

I could be wrong but to be safe I would make new char[5] to allow for the teminating 0 character of a string.
Unless you tell the compiler to use strict array range checking C of itself is not good at checking these things,
but this is ancient memory for for me.
-
Again not sure this is right but you could deallocate the memory of the old string and then
allocate memory for the new string. and then assign it might achieve the same result.

Reason: