issue to pass string to dll C

 

Hi,

To pass mql4 string to dll in C I see this articles:

https://docs.mql4.com/basis/types/stringconst

https://www.mql5.com/en/articles/1391

so in my dll I do this :

struct MqlString
{
        int size;       // 32 bit integer, contains the size of the buffer allocated for the string
        LPWSTR buffer_mt4;     // 32 bit address of the buffer that contains the string
        int reserved;   // 32 bit integer, reserved, do not use
};



bool fn_print_Gvar(char *functionName, char *message){
        FILE * pfilehandle;
        wchar_t buffer[80], filename[250] = L"";

        swprintf_s(filename, 250, L"%ls%mylog.log", Gpath);

        _wfopen_s(&pfilehandle,filename, L"a");
        
        if (pfilehandle != NULL){
                fprintf_s(pfilehandle, "(%s) %s\n", functionName, message);
                fclose(pfilehandle);
                return true;
        }
        else return false;

}


_DLLAPI int __stdcall fn_Test_stringMT4(MqlString *mystringmt4){

        char buffer[2000];
        wchar_t *Symbol = L"TEST_Symbol";

        sprintf_s(buffer, "Symbol = %ls, Size = %d, reserved = %d", Symbol, mystringmt4->size, mystringmt4->reserved);
        fn_print_Gvar("fn_Test_stringMT4", buffer);

        sprintf_s(buffer, "MQLSTring = %ls, Size = %d, reserved = %d", &mystringmt4->buffer_mt4, mystringmt4->size, mystringmt4->reserved);
        fn_print_Gvar("fn_Test_stringMT4", buffer);
        
        return 0;

}


and in mql4 :


#import "mydll.dll"
int fn_Test_stringMT4(string mystringmt4);
#import


 int start(){
    string teststr;
    
    teststr = "EURUSD";
    fn_Test_stringMT4(teststr);

}


When I print the string in mql4 I have the output: EURUSD

but when I print in C I have the ouput :

Symbol = TEST_Symbol, Size = 6619242, reserved = 7536741
MQLSTring = RUSD, Size = 6619242, reserved = 7536741

the first 2 characters of my mql4 string are not printed. I have RUSD instead of EURUSD

Where is the problem ?

 
florentjustin:

[...] Where is the problem ?

Strings are not passed to a DLL using MT4's internal MqlString structure. They are simply passed as a pointer (to a Unicode string). Therefore, the function definition should be:

 _DLLAPI int __stdcall fn_Test_stringMT4(LPWSTR mystringmt4)

From https://docs.mql4.com/runtime/imports:

When a string is passed, the address of the buffer of the copied string is passed; if a string is passed by reference, the address of the buffer of this string without copying it is passed to the function imported from DLL. 

(This means that strings passed to DLLs cannot contain null characters, and do not have an explicit size. If you need the size, then you simply use wcslen or lstrlenW in the usual way.)

 
jjc:

When a string is passed, the address of the buffer of the copied string is passed; if a string is passed by reference, the address of the buffer of this string without copying it is passed to the function imported from DLL. 

ok but is it sure that buffer means the buffer of the string in the struct in mql ? or buffer means the pointer on the structure.

because on https://www.mql5.com/en/articles/1391 they said in 2.4. Unicode strings and their use in a DLL: The internal structure of strings has changed in MQL4 (now it takes 12 bytes), and the MqlString structure should be used when passing strings to DLL.

and the link on MqlString is https://www.mql5.com/ru/forum/150672. It seems they use the mqlstring structure in their dll.

 
florentjustin:

[...] and the MqlString structure should be used when passing strings to DLL.

That bit of documentation is wrong. And it makes no sense: if MT4/MT5 passed strings as a MqlStr structure then it would be impossible to call Win32 functions which expect strings to be passed as char*/wchar*.

[EDIT...] 

MT4/MT5 don't in fact just pass a pointer to a string buffer. The four bytes below the string contain the int size of the number of characters in the string.

In other words, using the following MQL4 code...

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

void OnStart()
{
   string x = "ABCDEF";
   Test(x);
}

... you can then do the following in a DLL:

extern "C" void __stdcall Test(WCHAR * x)
{
        // Get the string length which is contained in an int in the four bytes
        // immediately preceding the string pointer referenced by x
        int lStringLength = *(((int*)x) -1);

        WCHAR tmp[30];
        _swprintf(tmp, L"String length: %i", lStringLength);

        MessageBoxW(NULL, x, tmp, 64);
}

But getting the string length this way is not what is documented at https://docs.mql4.com/runtime/imports, and it could potentially break in future. MT4/MT5 could simply start passing a WCHAR* without breaching the documentation.

For confirmation, MT4/5 is not passing a MqlStr structure. MqlStr is as follows:

int size;
WCHAR * buffer;
int reserved;

What MT4/5 are passing to a DLL is:

int size;
WCHAR buffer[];

... with the pointer which the DLL receives being the address of the buffer member, not the address of the size member. It's very like a Windows BSTR in that respect. 

 
ok thank you for your help. :-)
 

Hello,


Thank you for your informations, they helped me a lot.


I'm writing a dll  for mt4 version 4.00 build 1170.

When I use stdcall I encounter stack problem, with cedecl it works, this is not what I read in forums. Did it change, do you know sometingabout that ?

Janfi

 
Janfi-trading:

Hello,


Thank you for your informations, they helped me a lot.


I'm writing a dll  for mt4 version 4.00 build 1170.

When I use stdcall I encounter stack problem, with cedecl it works, this is not what I read in forums. Did it change, do you know sometingabout that ?

Janfi

Show your code if you need coding help.
 

Hello Alain,

I don't need help, my dll works perfectly, I'm just surprised to see that in the forums everyone said to use stdcall for calling dll functions, instead of cedecl, and it's wrong.

They were old articles, maybe MetaQuote changed in new versions. Also for passing strings, they were 1 byte per char and now they are 2 bytes per char.

Janfi

 
Janfi-trading:

Hello Alain,

I don't need help, my dll works perfectly, I'm just surprised to see that in the forums everyone said to use stdcall for calling dll functions, instead of cedecl, and it's wrong.

They were old articles, maybe MetaQuote changed in new versions. Also for passing strings, they were 1 byte per char and now they are 2 bytes per char.

Janfi

You said "cdecl" works where "stdcall" doesn't and asked for information about that.

The mql4 (and mql5) compiler is supposed to work with both. So my request for code to see why in your case it works with cdecl only.

From this article (by MetaQuotes CEO) :

The DLL imported functions calls in MQL5 should have the stdcall and cdecl calling convention. Though stdcall and cdecl differ in the ways of parameter extracting from a stack, the MQL5 runtime environment can safely use both versions because of the special wrapper of DLL calls.

The C++ compiler uses  __cdecl calling by default, but I recommend explicitly specifying the __stdcall mode for exported functions.

and

Forum on trading, automated trading systems and testing trading strategies

Chat with developers via Service Desk!

Renat Fatkhullin , 2015.03.19 13:57

And what are the problems with the import?

Everything works normally under x64 conventions (they are different with respect to 32 bits and are strictly standardized).

There are no problems at all. And even in 32 bits, there are no problems with different cdecl / stdcall calls. Due to our protective wrapper MQL4 / MQL5, we don’t care what the agreement of the imported function is.


The history of calling conventions, part 3
  • devblogs.microsoft.com
Okay, here we go: The 32-bit x86 calling conventions. (By the way, in case people didn’t get it: I’m only talking in the context of calling conventions you’re likely to encounter when doing Windows programming or which are used by Microsoft compilers.
 

The problem comes maybe because I wrote the dll with FreePascal, I'm a Linux user and I don't have many Windows tools.

 
Janfi-trading:
   

I don't need help, my dll works perfectly, I'm just surprised to see that in the forums everyone said to use stdcall for calling dll functions, instead of cedecl,

stdcall is a calling convention for VB6, VBA;

cdecl - for c++;

and 1 was used, not both;

but as so as now C# & VB are in .NET, & they have easy communication/understanding each other inside .NET environment, & most soft (perhaps something in MT) use .NET libs (even CLI C++ libs) - thus, this can explain the fact that boundary between cdecl & stdcall like not exist... imho... and you get lucky to use any

Reason: