receiving bad values by a 'wchar_t *' out of a DLL

 

Hello again,

the first time looking for string-Values in MT4 out of a c++ DLL takes me a day without success :-(  And I've been working with the example first ! All my calculations are working but strings....

The story:

c++      ----------------------------------------

I want to code with 'wstring' because it`s easier to concant with more input like int. So...

// on a global scope
   wchar_t *jobProtokoll = new wchar_t[10];     


// the property to get it:
  MT4_EXPFUNC wchar_t* __stdcall GetJobProtokoll()
  {
    return(jobProtokoll);
  }
   

// in one procedure:
   std::wstring name( L"nice to know !" );
   const wchar_t * s = name.c_str();
   jobProtokoll = const_cast< wchar_t* >( s );


I include this in mql4 and it is working, but the result are unidentified signs. Not my text.

         string t = GetJobProtokoll();


result in 't': ��������������������Ű
ოÝŸϫŸϫ��������������������������������������������Ý


And:

1) Writing 'wchar_t *' directly is working up to MT4. It's loosing if I walk through a wstring.

2) the result of the c++ code is correct - checked in a single *.exe-project.

3) only the handshake through the DLL makes trouble.


the example given with the mt4 only returns the parameter-string without changes.


Over all I'm looking for a simple snipped to work easy with strings in c++ and pass it to MT4.


Thanks for any help !

 
Abejorro:


Hello again,

the first time looking for string-Values in MT4 out of a c++ DLL takes me a day without success :-(  And I've been working with the example first ! All my calculations are working but strings....

The story:

c++      ----------------------------------------

I want to code with 'wstring' because it`s easier to concant with more input like int. So...


I include this in mql4 and it is working, but the result are unidentified signs. Not my text.


result in 't': ��������������������Ű
ოÝŸϫŸϫ��������������������������������������������Ý


And:

1) Writing 'wchar_t *' directly is working up to MT4. It's loosing if I walk through a wstring.

2) the result of the c++ code is correct - checked in a single *.exe-project.

3) only the handshake through the DLL makes trouble.


the example given with the mt4 only returns the parameter-string without changes.


Over all I'm looking for a simple snipped to work easy with strings in c++ and pass it to MT4.


Thanks for any help !



The easier way for avoiding string troubles in MT4 is allocating and passing a string buffer (or a char array) reference in a parameter, rather than returning a string value. In addition you would have full control of its lifecycle.
 
You can't return a points to mq4 because mq4 doesn't have pointers to data. You pass a buffer address to the DLL.
#import "<NAME>.dll"
   int CallDLL(ushort&[], int);
#import

ushort buffer[];  ArrayResize(buffer, maxLength); // Or ushort buffer[maxLength];
int    nChar = CallDLL(buffer, maxLength);
string result = ShortArrayToString(nChar, 0, nChar);
 
WHRoeder:
You can't return a points to mq4 because mq4 doesn't have pointers to data. You pass a buffer address to the DLL.

Yes you can, in the case of strings. The following works:

C:

extern "C" WCHAR * __stdcall Test()
{
        WCHAR * buffer = new WCHAR[6];
        wcscpy(buffer, L"Hello");
        return buffer;
}


MQL4:

#import "Test.dll"
   string Test();
#import

void OnStart()
{
   Print(Test());  // <!-- Prints "Hello"
}

As Ovo is saying/implying, the issue is probably that the memory allocated by c_str() is being freed on function-exit, and the pointer which is returned to MT4 is therefore no longer valid.

While the above code does work, it's not advisable because it almost certainly leaks memory. While MQL4 accepts WCHAR* return values from DLLs, I'm not aware of any documentation which says how/if such memory should be allocated so that MT4 then frees it.

You're right that passing a buffer into a DLL function is the sensible way to do things.

 
jjc:

I'm not aware of any documentation which says how/if such memory should be allocated so that MT4 then frees it.

It's possible (borderline-probable) that MT4 thinks that string return values from DLLs are BSTR values. If so, the following wouldn't leak memory:

extern "C" BSTR __stdcall Test()
{
        std::wstring mystring(L"Hello");
        return SysAllocString(mystring.c_str());
}

 But I can't be bothered to test whether MT4 thinks that a string return value is a BSTR. I'd still pass in a buffer into the DLL.

 

Wow, thank you very much for all your answers ! With that I've checked some more...

1) The sample of code which comes with the MT4 gives this option:

MT4_EXPFUNC wchar_t* __stdcall GetJobProtokoll()
  { return L"Hello world"; }

This is working. I agree with you, the issue in my design will be the c_str() with a result but a different lifecycle than the use of my variable ! And my testing in a further project was senseless because I didn't check this - only the values in calculation :-(

But then I was very surprised, that the use of a tricky BSTR will solve my questions - and the lifecycle is no more important !??! - thx jjc !

Yes, you're right. The same design with a global variable and a reply on demand after filling it in process is working with the correct answer if you are using a BSTR ! Simple as possible !

// at global:
BSTR jobProtokollB;

// the property to get it:
MT4_EXPFUNC BSTR  __stdcall GetJobProtokollB()
        { return(jobProtokollB); }

// writing the values:
        std::wstring thestring(L"Cycles made: ");

        // ver.1 (>= C++11)
        thestring += std::to_wstring( 2358 );

        // ver.2 (< C++11)
        // std::wostringstream wos;
        // wos << 2358;
        // const std::wstring value(wos.str()); 
        // thestring += value;

        thestring += L" - and ended successful";
        jobProtokollB = SysAllocString(thestring.c_str());

To me this seems to have some good points. I think it will be flexible and convenient. In a debugging-race the dll can talk something like my example above.

In a next step I will check your suggestions using an allocated string buffer. Thanks for that codes !


But, please let me ask one more:

   MqlRates rates[];
   ArrayCopyRates(rates);
   ArraySetAsSeries(rates, true);

If i use this 'ArraySetAsSeries()' with true/false it seems to have no influence if using 'rates' passed into a DLL. Is there a trick ?


Thanks again

 
Abejorro:

But then I was very surprised, that the use of a tricky BSTR will solve my questions - and the lifecycle is no more important !??! - thx jjc !

SysAllocString() works because it takes a copy of the string memory, and that new memory block is managed by the operating system and is not released until something - either your code or MT4 - calls SysFreeString(). But that's no different to my first example, where there is explicit memory allocation using "new WCHAR[]", instead of the implicit memory allocation and clean-up from c_str().

 
Abejorro:

If i use this 'ArraySetAsSeries()' with true/false it seems to have no influence if using 'rates' passed into a DLL. Is there a trick ?

ArraySetAsSeries() does not affect how the array data is held in the computer's memory. What it changes is how that data is addressed in MQL4 code: i.e. if you use arr[n] then the meaning of n changes depending on whether you have used ArraySetAsSeries().

Reason: