Been waiting for an article like this for a long time. Thank you to the author.
Thank you. You are not the first to thank me. I will be glad to listen to all wishes and critical comments on the material of the article.
In the future I would like to develop the topic of programming in Delphi for MT5, adding new information to the site.
I think a useful article for many people. A couple of comments:
1. SysUtils and Classes units should have been left in the project. Despite the fact that their presence somewhat "bloats" the project, they have many small but important functions. For example, the presence of SysUtils automatically adds the processing of excepcions to the project. As you know, if an excepcion is not processed in the dll, it is passed to mt5, where it causes mql5 programme execution to stop.
2. You should not use all sorts of procedures within DllEntryPoint (aka DllMain). As Microsoft states in its documents, this is fraught with various unpleasant effects. Here is a small list of articles on this subject:
Best Practices for Creating DLLs by Microsoft - http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx.
DllMain and life before childbirth - http://transl-gunsmoker.blogspot.com/2009/01/dllmain.html
DllMain - a bedtime story - http://transl-gunsmoker.blogspot.com/2009/01/dllmain_04.html
A few reasons not to do anything scary in your DllMain - http://transl-gunsmoker.blogspot.com/2009/01/dllmain_05.html
More reasons why you shouldn't do anything scary in your DllMain: accidental locking -
http://transl-gunsmoker.blogspot.com/2009/01/dllmain_7983.htmlI already gave an excerpt of an unfinished article somewhere, I think on the quad forum. I will repeat it here.
begin...end
When creating a Delphi project intended for DLL compilation, the begin...end section appears in the .DPR project file. This section is always executed when the DLL is first projected into the process address space. In other words, it can be considered as a kind of initialisation section that all units have. In this place you can perform some actions that need to be performed at the very beginning and only once, for the current process. When loading the DLL into the address space of another process, this section will be executed there again. But since the address spaces of the processes are separated from each other, initialisation in one process will not affect the other process in any way.
This section has some limitations that you should be aware of and take into account. These limitations are related to the subtleties of the Windows DLL loading mechanism. We will talk about them in more detail later.
initialisation/finalisation
Each unit Delphi has special sections, the so-called initialisation and finalization sections. As soon as any unit is connected to the project these sections are connected to the special load and unload mechanism of the main module. And these sections are executed before the main begin...end section starts its work and after the work is finished. This is very convenient, because it eliminates the need to write initialisation and finalisation in the program itself. At the same time, connection and disconnection is carried out automatically, you only need to connect or disconnect unit to the project. And this happens not only in traditional EXE files, but also in DLLs. The order of initialisation of DLL, when "loading" it into memory is as follows, first all initialisation sections of unit are executed, in the order they are marked in the uses of the project, then the begin...end section is executed. Finalisation is done in the reverse order, except that there is no specially designed termination function in the DLL project file. This is, in general, another reason why it is recommended to separate DLL projects into a project file and a uses unit.
DllMain
This is the so-called DLL entry point. The point is that Windows occasionally needs to report any event that occurs within the process to the DLL itself. In order to do this and there is an entry point. That is, a specially predefined function that each DLL has and that can handle messages. And although we still have not seen this function in a DLL written in Delphi, nevertheless it has such a point. Just the mechanism of its functioning is veiled, but you can always get to it. The answer to the question - is it necessary at all? - is not as obvious as it seems.
First let's try to understand what it is that Windows is trying to tell DLL. There are a total of 4 messages with which the operating system comes to the DLL. The first one, the DLL_PROCESS_ATTACH notification, is sent whenever the system attaches a DLL to the address space of the calling process. In the case of MQL4it is an implicit loading. It does not matter that this DLL has already been loaded into the address space of another process, the message will still be sent. And it doesn't matter that in fact Windows loads a particular DLL into memory only once, all processes wishing to load this DLL into their address space receive only a reflection of this DLL. This is the same code, but the data that the DLL may have is unique to each process (although it is possible that common data exists). The second, the DLL_PROCESS_DETACH notification, tells the DLL to detach from the address space of the calling process. In fact, this message is received before Windows starts unloading the DLL. In fact, if the DLL is being used by other processes, no unloading occurs, Windows simply "forgets" that the DLL existed in the process's address space. Two more notifications, DLL_THREAD_ATTACH andDLL_THREAD_DETACH are received when the process that loaded the DLL spawns or destroys threads within the process. There are a few subtle issues related to the order in which thread notifications are received, but we will not consider them.
Now about how DLLs written in Delphi are arranged and what is usually hidden from programmers. After Windows has "projected the DLL into the address space of the calling process", or simply put, loaded the DLL into memory, at this point there is a call to the function located at the entry point and passing the notification DLL_PROCESS_ATTACH to this function. In a DLL written in Delphi, this entry point contains special code that does many different things, including starting the initialisation of units. It remembers that initialisation and the first run of the DLL has been done, and executes begin...end of the main project file. Thus, this initial loading code is executed only once, all other Windows calls to the entry point are made to another function, which handles subsequent notifications - in fact, it ignores them, except for the DLL_PROCESS_DETACH message, which finalises the unit. This is how the mechanism of loading a DLL written in Delphi looks like in general terms. In most cases, it is enough to write and use DLLs in MQL4.
If you still need a DllMain exactly the same as in C, it is not difficult to organise it. It is done as follows. When loading a DLL for the first time, among other things, the System module (it is always present in a program or DLL) automatically creates a global procedural variable DllProc, which is initialised with nil. This means that no additional processing of DllMain notifications, other than the one that exists, is required. As soon as the address of the function is assigned to this variable, all notifications for DLLs from Windows will go to this function. Which is what is required of the entry point. However, the DLL_PROCESS_DETACH notification will still be tracked by the DLL termination function, as before, to allow for finalisation.
procedureDllEntryPoint(Reason: DWORD);
begin
case Reason of
DLL_PROCESS_ATTACH : ;//'Connection process'
DLL_THREAD_ATTACH : ;//'Connecting the thread'
DLL_THREAD_DETACH : ;//'Disconnecting a thread'. stream'
DLL_PROCESS_DETACH : ;//'Disconnecting the process'
end;
end;
begin
if not Assigned(DllProc) then begin
DllProc :=@DllEntryPoint;
DllEntryPoint (DLL_PROCESS_ATTACH);
end;
end.
In case we are not interested in thread notifications, all this is unnecessary. It is only necessary to organise initialization/finalization sections in unit, as the process connection and disconnection events will be tracked automatically.
The perfidy and treachery of DllMain
Now, perhaps, it's time to touch upon an issue that is surprisingly little covered in programming literature. This topic concerns not only Delphi or C, but any programming language capable of creating DLLs. This is a property of the Windows DLL loader. From the translated serious and widespread literature on programming in the Windows environment, only one author managed to find a mention of it and that in the most vague terms. This author is J. Richter, and he is forgiven, because his wonderful book was published in 2001, when in general 32-bit Windows was not so widespread.
It is interesting that MS itself never hid the existence of the problem with DllMain and even posted a special document, something like - "The best way to use DllMain". In which he explained what can be done in DllMain and what is not recommended. And it was pointed out that unrecommended things lead to hard-to-see and inconsistent errors. Those who wish to read this document can look here. A more popular summary of several translations of several alarmist reports on the subject is outlined here.
The essence of the problem is very simple. The point is that DllMain, especially when loading a DLL, is a special place. A place where you should not do anything complicated and outstanding. For example, it is not recommended to CreateProcess, or LoadLibrary other DLLs. It is also not recommended to CreateThread or CoInitialise COM. And so on.
You can do the simplest things. Otherwise, nothing is guaranteed. Therefore, do not put anything unnecessary in DllMain, otherwise you will be surprised to see applications using your DLL crash. It is better to be safe and create special exported functions of initialisation and finalisation, which will be called by the main application at the right moments. This will at least help to avoid problems with DllMain.
ExitProc, ExitCode,MainInstance,HInstance....
The System module, which is always hooked into your DLL at compile time, has some useful global variables that you can use.
ExitCode, - a variable in which you can put a number other than 0 at loading, as a result DLL loading will stop.
ExitProc, - a procedural variable that can store the address of the function that will be executed on exit. This variable is a relic of the distant past, it does not function in DLLs and, moreover, Delphi developers do not recommend to use it in DLLs because of possible problems.
HInstance, - a variable in which after loading is stored descriptor of the DLL itself. It can be very useful.
MainInstance, - descriptor of the application that loaded the DLL into its address space.
IsMultiThread, - a variable that is automatically set to True, if the compilation of the DLL detects work with threads. Based on the value of this variable DLLmemory manager switches to multithreaded mode. In principle, it is possible to force the memory manager to switch to multithreaded mode even if threads are not explicitly used in the DLL. IsMultiThread:=True; Naturally, multithreaded mode is slower than single-threaded mode due to the fact that threads are synchronised with each other.
MainThreadID, - descriptor of the main thread of the application.
And so on. In general, the System module performs approximately the same functions as CRT in C. Including memory management functions. A list of all functions and variables that are present in the compiled DLL, not only exported ones, but all of them, can be obtained if you switch on the Linker option, Map file - Detailed, in the Project settings.
Memory management
The next, rather serious issue that often causes difficulties is memory management in DLLs. More precisely, memory management itself does not cause any problems, but as soon as the DLL tries to actively work with the memory allocated by the memory manager of the application itself, - this is where problems usually start.
The thing is that usually applications are compiled with MemoryManager built into it. The compiled DLL also contains its own MemoryManager. This is especially true for applications and DLLs created in different programming environments. As in our case, the terminal is in MSVC, DLL is in Delphi. It is clear that these are different managers by their structure, but at the same time they are physically different managers, each managing its own memory within the common address space of the process. In principle, they do not interfere with each other, do not take away each other's memory, they exist parallel to each other and usually do not know anything about the existence of competitors. This is possible because both managers access memory from the same source, the Windows memory manager.
Problems start when a DLL function and an application tries to manage memory sections distributed by a different memory manager. Therefore, there is a rule of thumb among programmers that says "memory should not cross the boundaries of a code module".
It is a good rule but it is not quite correct. It would be more correct to just use the same MemoryManager in the DLL that the application uses. Actually, I rather like the idea of connecting the MT4memory manager to the Delphimemory manager FastMM, but that's not a very feasible idea at all. Anyway, memory management should be one thing.
In Delphi it is possible to replace the default memory manager with any other memory manager that meets some requirements. Thus it is possible to make the DLL and the application have a single memory manager, and it will be the MT4 memory manager.

- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
New article Guide to writing a DLL for MQL5 in Delphi is published:
The article examines the mechanism of creating a DLL module, using the popular programming language of ObjectPascal, within a Delphi programming environment. The materials, provided in this article, are designed to primarily target beginner programmers, who are working with problems, which breach the boundaries of the embedded programming language of MQL5, by connecting the outside DLL modules.
Author: Андрей