Calling a DLL from MQL
There are times when we want access to additional data structures and function libraries than the ones provided by MetaTrader’s MQL. Besides native open-source MQL options, we can create our own Dynamic Link Library (DLL) in C# or C++ and link it to our programs.
In this article, we research how to connect to DLLs created with these languages. We must keep in mind that the code generated by C# is managed code running on the .NET framework, while C++'s code is native. We are going to use Visual Studio 2022 for this exercise.
Creating a DLL with C#
To create a DLL with C#, we need the Unmanaged Exports library by Robert Giesecke or RGiesecke library for short. This library gives our functions the ability to interact with native (unmanaged) executables.
Start by installing Microsoft Build Tools 2015 and .NET 3.5 as recommended in this StackOverflow article. They are necessary to use the RGiesecke library. Restart Visual Studio and create a new C# Class Library (.NET Framework) project. Right-click on the project and select Properties. Make sure to do it on the project, not on the solution. Click on Build and select x86 for Platform target. MQL can only interact with 32-bit DLLs, hence the need to do this change.
On the top bar, look for Configuration Manager,
and click on it and add a new x86 Project context.
Add the RGiesecke library reference to the project by right-clicking on References and selecting Manage Nuget Packages. Look for unmanaged exports and click the Install button.
Add the following functions to the Class1.cs file.
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace MyDll
{
public class Class1
{
[DllExport("Add", CallingConvention = CallingConvention.StdCall)]
public static int Add(int a, int b)
{
return a + b;
}
[DllExport("AddDouble", CallingConvention = CallingConvention.StdCall)]
public static double AddDouble(double a, double b)
{
return a + b;
}
}
}
Original version from How To MT4 Call C# DLL Function
The DllExport
annotation is provided by the RGiesecke library. It defines the name of the exported function, or in other words, the name that an external program will use to call it. We are exporting two functions in this example.
Build the solution. This process, if successful, will generate a DLL file in the Debug folder of the solution. The full path to it is displayed by the build process. Copy the resulting DLL to MetaTrader’s Data folder > MQL4 > Libraries directory.
We will now create the MetaTrader code that will call the functions in our DLL.
On MetaTrader, open the MetaQuotes Language Editor. Create a new Indicator and add the following code to it.
#import "MyDll.dll"
int Add(int, int);
double AddDouble(double, double);
#import
int OnInit() {
Print("Add: " + IntegerToString(Add(1, 2)));
Print("Add Double: " + DoubleToString(AddDouble(1.5, 1.5)));
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { }
void OnTick() { }
The #import
code section tells MetaTrader the name of the DLL that we will use and the function declarations as defined in the DLL. And then, we simply call them, in this example, on the OnInit
function.
Compile the indicator and add it to a MetaTrader chart. During the configuration, make sure to select the Allow DLL imports box.
If everything goes ok, the result of these operations will be displayed in MetaTrader’s Experts tab.
Our last step is to install the corresponding .NET framework on the production computer and our Indicator and DLL files to the MetaTrader Data folder. Make sure the .NET version matches the one used for development.
See Exposing c# code to mql5 using unmanaged exports for additional examples, including passing strings as parameters and return values.
Creating a DLL with C++
It is simpler to create a C++ library because we do not need any external libraries.
Check that Visual Studio has the Desktop Development with C++ component installed by running the Visual Studio Installer application and selecting Modify.
Create a new C++ Dynamic-Link Library (DLL) project. Use the Configuration Manager to create a new 32-bit configuration. This is required by MQL4.
MQL4 uses 32-bit DLL’s, while MQL5 uses 64-bit ones. The compilation section of this example targets MQL4, but it should be easy to target x64 from here.
Add the 32-bit configuration:
Add a new header file called myfunctions.h with the following content:
#pragma once
#define EXPORT __declspec(dllexport)
extern "C" EXPORT int add(int a, int b);
extern "C" EXPORT double addDouble(double a, double b);
extern "C" EXPORT void greet(char* name, char* response, unsigned int size);
We have to prepend extern "C"
to function declarations to avoid name mangling when using C++.
Add a new source file called myfunctions.cpp with the following content:
#include "pch.h"
#include "myfunctions.h"
#include <string>
int add(int a, int b)
{
return a + b;
}
double addDouble(double a, double b)
{
return a + b;
}
/*
* Sample function to receive and return a string.
* `response` is an initialized char array provided by MQL.
*/
void greet(char* name, char* buffer, unsigned int size)
{
std::string str(name);
std::string result = "hello " + str;
strcpy_s(buffer, size, result.c_str());
}
We are creating an additional function for our C++ example. This function receives and returns a char array. Note that we convert the provided char array into a std::string
for internal manipulation and then turn it back into a char array to return it to MQL. The size
parameter contains the buffer size to avoid overflows.
Build the solution. This process will generate a DLL file in the directory displayed by the build phase. Copy the resulting file into the MetaTrader’s Data folder > MQL4 > Libraries directory.
On MetaTrader, open the MetaQuotes Language Editor. Create a new Indicator and add the following code to it.
#import "c-test.dll"
int add(int, int);
double addDouble(double, double);
void greet(char& [], char& [], unsigned int size);
#import
int OnInit()
{
//---
Print("Add: " + IntegerToString(add(1, 2)));
Print("Add Double: " + DoubleToString(addDouble(1.5, 1.5)));
char buffer1[1024]; // to store name
StringToCharArray("joe doe", buffer1);
char buffer2[1024]; // to store result
greet(buffer1, buffer2, ArraySize(buffer2));
string result = CharArrayToString(buffer2);
Print("Greet: " + result);
//---
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { }
void OnTick() { }
As we can see, passing simple values like ints and doubles is similar to our C# example. However, passing a string involves some additional steps:
char buffer1[1024];
reserves a buffer to store the input string, in this case a name.StringToCharArray("joe doe", buffer1);
converts an MQL string into a char array.char buffer2[1024];
reserves a buffer to store the resulting string.greet(buffer1, buffer2, ArraySize(buffer2));
calls the DLL function.string result = CharArrayToString(buffer2);
converts the char array stored in the resulting buffer to an MQL string.Print("Greet: " + result);
we use the string.
All char buffers will be automatically removed once we leave the function scope (unless we create them with new
).
Compile the indicator and add it to a MetaTrader chart. During the configuration, make sure to select the Allow DLL imports box.
If everything goes ok, the result of these operations will be displayed in MetaTrader’s Experts tab.
Unlike our C# example, we do not need to install any .NET library on the production computer when we are ready to deploy.
Troubleshooting
Long fields
I had some issues dealing with long fields in MQL.
MQL uses 64-bits for longs. The equivalent standard C++ type is long long
. Visual C++ offers the non-standard __int64
, while gcc allows you to import <stdint.h>
and use int64_t
as an alternative. Any of them will work fine with MQL.
Strangely, MQL5 was fine using an initial dll version with long
instead of long long
, but MQL4 crashed as soon as I tried to use the library. Once I updated my dll code to use long long
, everything worked fine on both platforms.
Be careful because this issue may remain hidden until you work with functions that use multiple parameters.
The following code shows how to use long long
in MQL and C++:
// .h
extern "C" __declspec(dllexport) void set_chart_id(long long chart_id);
// .cpp
void set_chart_id(long long chart_id) {
long long id = chart_id;
// do something else...
}
// .mql
#import "my-library.dll"
void set_chart_id(long chart_id);
#import
See how we use long
in MQL and long long
in C++.
Read more about the MQL4 and MQL5 data types.
Unicode Strings
I haven’t tested this, but seems like to use Unicode strings in MQL we need to use the wchar_t
type. See the following example:
// cpp
void WINAPI fnReplaceString(wchar_t * input, wchar_t * output) {
wchar_t replaceText[] = L"987654321";
memcpy(output, replaceText, wcslen(replaceText) * sizeof(wchar_t));
}
// mql
#import "MyDll.dll"
void fnReplaceString(string, ushort&[]);
#import
ushort outputBuffer[];
ArrayResize(outputBuffer, <whatever>);
fnReplaceString("Input string", outputBuffer);
string strOutput = ShortArrayToString(outputBuffer);
Things to note:
- We need to use
wchar_t
in our C++ code. - We need to use
ushort
in our MQL code. - We need to use
ShortArrayToString
to convert theushort
array into a string. - We are not converting the input string into a
wchar_t
array.
I’ll try to test this in the near future and update this section.
Additional resources
The following resources contain libraries that may be useful for C++ development:
- Awesome C/C++ - a great collection of libraries.
- JSON library - this library is included in the previous collection, but it looks so good we think it deserves its own bullet on our list.
- Notes on smart pointers - notes on
unique_ptr
andshared_ptr
. - Notes on unordered_map - C++'s hash tables.
Additional references
- Adam H. (2017, October 25). Building and using DLLs in C. Codementor. https://www.codementor.io/@a_hathon/building-and-using-dlls-in-c-d7rrd4caz
- Walkthrough: Create and use your own Dynamic Link Library (C++). (2021, October 12). Microsoft. https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-170
- Van, Thach. (2017, April 1). Passing strings between MQL and C++ DLL. VNDeveloper. https://www.vndeveloper.com/passing-strings-between-mql-and-c-dll/
Bonus: Creating a shared library for macOS
- Ngo, Tommy. (2017, July 8). C++ Creating Shared Library on macOS. YouTube. https://www.youtube.com/watch?v=PRUR_bN3r-E
This is a quick tutorial to create C++ Shared Library on macOS. Not a file we can use from MQL, but still somewhat related to what we do in this article.
PostgreSQL resources
An additional benefit of connecting a DLL is that we have access to any database. These are some valuable resources for Postgres:
- PostgreSQL C - a list of full examples for the most essential Postgres operations.
- C++ PostgreSQL Example - configure Postgres libraries in Visual Studio.
- PostgreSQL Upsert Using INSERT ON CONFLICT statement
- Connection Status Functions - relevant information for checking connection status and the functions we have to close or reset a connection.
- Using PostgreSQL in .NET
MySQL resources
- How to access the mysql database from MQL5 (MQL4) - this is a useful resource to avoid creating an external DLL and calling MySQL directly from MQL.