2

I am writing a script in MQL4 language for MetaTrader Terminal 4 which is using C# DLL library for further data processing.

What I'm trying to do is to pass an array of following MQL4-structures:

struct marketUpdate
{    
    double openPrice;
    double closePrice;
    double highPrice;
    double lowPrice;
    int volume;
    double pipValuePerLot;
    int intervalLength;
    string symbol;
    string time;
};

to this function in C# DLL:

[DllExport("HistoricMarketData", CallingConvention.StdCall)]
public static int HistoricMarketData(
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] MarketUpdate[] unmanagedArray,
    int length)
{
    // further processing
}

This is the function definition on MQL4 side:

int HistoricMarketData(
          marketUpdate &marketUpdates[],
          int length);

and this is the structure on C# side:

[StructLayout(LayoutKind.Explicit)]
public struct MarketUpdate
{
    [MarshalAs(UnmanagedType.R8)]
    [FieldOffset(0)]
    public double openPrice;

    [MarshalAs(UnmanagedType.R8)]
    [FieldOffset(8)]
    public double closePrice;

    [MarshalAs(UnmanagedType.R8)]
    [FieldOffset(16)]
    public double highPrice;

    [MarshalAs(UnmanagedType.R8)]
    [FieldOffset(24)]
    public double lowPrice;

    [MarshalAs(UnmanagedType.I4)]
    [FieldOffset(32)]
    public int volume;

    [MarshalAs(UnmanagedType.R8)]
    [FieldOffset(36)]
    public double pipValuePerLot;

    [MarshalAs(UnmanagedType.I4)]
    [FieldOffset(44)]
    public int intervalLength;

    [MarshalAs(UnmanagedType.LPWStr)]
    [FieldOffset(48)]
    public string symbol;

    [MarshalAs(UnmanagedType.LPWStr)]
    [FieldOffset(60)]
    public string time;
}

My problem is that it is not working when calling HistoricMarketData from MQL4. The script either crashes whole MetaTrader program or throws an exception and fails.

As you can see I'm using Robert Giesecke's Unmanaged Exports library to make it works and I correctly imported DLL to MQL4 as other functions inside are working without any issue.

After lot of trial and error I was able to find out that when I remove those 2 string attributes (symbol and time) from structure all works as expected and structure is correctly passed to C# function.

So my question is how to correctly pass string in structure from MQL4 to C#?

I already tried to change UnmanagedType parameter at MarshalAs annotation in structure to other string variants but it didn't help. It also didn't help to change string to char[] on both sides.

I'll appreciate any advices or help.

Thanks!

user3666197
  • 1
  • 6
  • 50
  • 92
Martin Vrábel
  • 830
  • 1
  • 13
  • 36

1 Answers1

2

Welcome to the Wild Worlds of MQL4
where string is not string,
but a struct

So,
this,
and not only this,
was causing a lot of headache for re-integrationg MQL4 with DLL-s

My own strategy is:

1) avoid MQL4-strings, if not of a core importance,
2) if needed, pass them via a DLL-proxy utility function, which handles needed conversions:

#import <dll>                                        // DLL-native CallSignatures: 
...
int          mql4s_send ( int socket, uchar &text[] );
...
#import
                                                     // DLL-proxy with  StringToCharArray() conversion:
int              s_send ( int socket, string text ) { uchar textChar[]; StringToCharArray( text, textChar );
    return ( mql4s_send (     socket,                       textChar ) );
  }

Anyway,
rather do read MQL4 Help, actually better re-read it again after each MetaTrader Build update ( yes, I am saying, that new help files contain ( at least ) some remarks on new MQL4-features, that have no other easy way to find and learn about -- it saves a lot of time. With code-base under maintenance, spanning more than a few hundreds man*years, I know the pain very well ).

The following can't be used for parameters in imported functions:
·pointers;
·links to objects that contain dynamic arrays and/or pointers.

Classes, string arrays or complex objects that contain strings and/or dynamic arrays of any types cannot be passed as a parameter to functions imported from DLL.

But Devil is hidden in details like this ( indirectly forbidden feature ):

...
Simple Structures
Structures that do not contain strings or objects of dynamic arrays are called simple structures; variables of such structures can be freely copied to each other, even if they are different structures. Variables of simple structures, as well as their array can be passed as parameters to functions imported from DLL.

user3666197
  • 1
  • 6
  • 50
  • 92
  • Ahh, thanks for clarification. I have read the documentation but I must have missed that part with simple structures and pointers. Could you please elaborate more how to use that proxy function? Should I pass string separately outside the structure (because passing strings as function arguments works well)? Thanks. – Martin Vrábel Aug 04 '16 at 08:47
  • Yessssss, the point is rather hidden in plain text reading, I used to headbang into this during previous Build updates, once stable DLL-interfaces have after many years working fine suddenly stopped working/crashed. **A good lesson from *New*-`MQL4.56789` creeping.** – user3666197 Aug 04 '16 at 09:03