<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6789056281440007248</id><updated>2026-08-17T05:06:47.803+01:00</updated><category term="Delphi"/><category term="EL 7.x"/><category term="technical"/><category term="Releases"/><category term="how-to"/><category term="EL 6.x"/><category term="best practice"/><category term="non-technical"/><category term="feature"/><category term="Stories"/><category term="Access Violation"/><category term="bug reports"/><category term="debugging"/><category term="how it works"/><category term="other tools"/><category term="additional features"/><category term="external"/><category term="memory leaks"/><category term="tools"/><category term="tips"/><category term="Win64"/><category term="compiling"/><category term="customizations"/><category term="Random thoughts"/><category term="Visual Studio"/><category term="Lazarus"/><category term="OSX"/><category term="installation"/><category term="Blog"/><category term="Viewer"/><category term="bug"/><category term="command line"/><category term="logging"/><category term="shareware"/><title type='text'>EurekaLog&#39;s blog</title><subtitle type='html'>Your place for information about EurekaLog and debugging.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.eurekalog.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>129</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-1009098599120333923</id><published>2026-04-11T21:38:00.014+01:00</published><updated>2026-06-30T08:55:59.150+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="customizations"/><category scheme="http://www.blogger.com/atom/ns#" term="debugging"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><title type='text'>Logging additional exception information</title><content type='html'>This article discusses logging of exception&#39;s additional properties into EurekaLog bug reports.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h3&gt;What this article is about&lt;/h3&gt;
EurekaLog exception tracer creates bug reports for each unhandled exception in your app. For example (a part of bug report is shown):&lt;br /&gt;
&lt;pre&gt;Exception:
-------------------------------------------------------------------------------------------------------------------------------
  2.1 Date          : Sat, 11 Apr 2026 15:51:09 +0300
  2.2 Address       : 012AB618
  2.3 Module Name   : Project1.exe
  2.4 Module Version: 1.0.0.0
  2.5 Type          : EAccessViolation
  2.6 Message       : Access violation at address 012AB618 in module &#39;Project1.exe&#39; (offset A9B618). Write of address 00000000.
  2.7 ID            : 73BA1063&lt;/pre&gt;
EurekaLog captures type (class) of the exception object and its message. However, some exception classes may have additional information. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  EOleException = class(EOleSysError)
  private
    FSource: string;
    FHelpFile: string;
  public
    constructor Create(const Message: string; ErrorCode: HRESULT;
      const Source, HelpFile: string; HelpContext: Integer);
    property HelpFile: string read FHelpFile write FHelpFile;
    property Source: string read FSource write FSource;
  end;&lt;/pre&gt;
The &lt;code&gt;EOleException&lt;/code&gt; class has additional &lt;code&gt;HelpFile&lt;/code&gt; and &lt;code&gt;Source&lt;/code&gt; properties, and its ancestor (the &lt;code&gt;EOleSysError&lt;/code&gt; class) also has an additional &lt;code&gt;ErrorCode&lt;/code&gt; property.&lt;br /&gt;
&lt;br /&gt;
Historically, EurekaLog only saves the exception class and its message to bug reports. This is because EurekaLog doesn&#39;t know about other exception classes, as other exception classes are defined in units other than &lt;code&gt;System.SysUtils&lt;/code&gt;. For example, the &lt;code&gt;EOleException&lt;/code&gt; class is defined in the &lt;code&gt;System.Win.ComObj&lt;/code&gt; unit. Referencing this unit directly (so EurekaLog could reference the &lt;code&gt;EOleException&lt;/code&gt; class) would mean that every application running EurekaLog would carry COM code.&lt;br /&gt; 
&lt;br /&gt;
The same is true for other classes: &lt;code&gt;EUpdateError&lt;/code&gt; (defined in the &lt;code&gt;Data.DB&lt;/code&gt; unit), &lt;code&gt;EZipFileNotFoundException&lt;/code&gt; (defined in the &lt;code&gt;System.Zip&lt;/code&gt; unit), &lt;code&gt;ESocketError&lt;/code&gt; (defined in the &lt;code&gt;System.Net.Socket&lt;/code&gt; unit), &lt;code&gt;EJSONException&lt;/code&gt; (defined in the &lt;code&gt;System.JSON&lt;/code&gt; unit), &lt;code&gt;DOMException&lt;/code&gt; (defined in the &lt;code&gt;Xml.xmldom&lt;/code&gt; unit), &lt;code&gt;EXMLException&lt;/code&gt; (defined in the &lt;code&gt;Xml.Internal.OmniXML&lt;/code&gt; unit), &lt;code&gt;ERemotableException&lt;/code&gt; (defined in the &lt;code&gt;Soap.InvokeRegistry&lt;/code&gt; unit), &lt;code&gt;ERequestError&lt;/code&gt; (defined in the &lt;code&gt;REST.Types&lt;/code&gt; unit), and so on - you get the idea: you definitely don&#39;t want to include all these units in your practically empty application if you&#39;re just adding EurekaLog to it.&lt;br /&gt;
&lt;br /&gt;
Previosly, if you wanted to have additional properties of exceptions listed in your bug reports - you have to log it manually. However, there is another way starting with EurekaLog 7.15.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Logging additional exception properties&lt;/h3&gt;
EurekaLog 7.15 has the &lt;code&gt;\Source\Extras\EExtraExceptionInfo.pas&lt;/code&gt; unit, which is added to each project with EurekaLog by default. Once the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit is added to your project - it will register a callback which will save additional properties of known exception classes into bug reports produced by EurekaLog:&lt;br /&gt;
&lt;pre&gt;Exception:
-----------------------------------------------------
  2.1 Date          : Sat, 11 Apr 2026 20:35:48 +0300
  2.2 Address       : 0137F5D9
  2.3 Module Name   : Project1.exe
  2.4 Module Version: 1.0.0.0
  2.5 Type          : EOleException
  2.6 Message       : Catastrophic failure.
  2.7 ID            : 2EBFEC43

Custom Information:
---------------------------------------
  9.1 EOleSysError.ErrorCode: $8000FFFF&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Customization&lt;/h3&gt;
There is a catch though. The &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit knows only about exceptions from the &lt;code&gt;System.SysUtils&lt;/code&gt; unit by default. If your project uses other units (such as &lt;code&gt;System.Win.ComObj&lt;/code&gt; for the &lt;code&gt;EOleSysError&lt;/code&gt; exception class from the example above) - these has to be enabled explicitly. The way to do this is to use conditional defines:&lt;br /&gt;
&lt;ul&gt;
  &lt;li&gt;Open your project in IDE;&lt;/li&gt;
  &lt;li&gt;Go to IDE&#39;s &quot;Project&quot; / &quot;View source&quot;. This will open your project (&lt;code&gt;.dpr&lt;/code&gt;) file in IDE&#39;s editor;&lt;/li&gt;
  &lt;li&gt;You should see the list of EurekaLog units being included into your project. Find the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit, place cursor on it;&lt;/li&gt;
  &lt;li&gt;Press &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Enter&lt;/code&gt;, or right click with the mouse and select the &quot;Open File At Cursor&quot; command.&lt;/li&gt;
&lt;/ul&gt;
This will open the &lt;code&gt;EExtraExceptionInfo.pas&lt;/code&gt; file in IDE&#39;s editor.&lt;br /&gt;
&lt;br /&gt;
The beginning of the &lt;code&gt;EExtraExceptionInfo.pas&lt;/code&gt; file has a detailed description of all exception classes which it supports:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;...
//
// Select/add option(s) for exception classes which are used in your app:
//

// [System.Win.ComObj] EOleSysError, EOleException
// {$DEFINE E_SYSTEM_WIN_COM_INFO}

// DEPRECATED [Vcl.OleAuto] EOleSysError, EOleException (Delphi 6+)
// {$DEFINE E_VCL_OLEAUTO_INFO}

// [Data.DB] EUpdateError
// {$DEFINE E_DATA_DB_INFO}

// [System.Zip] EZipFileNotFoundException
// {$DEFINE E_SYSTEM_ZIP_INFO}
...&lt;/pre&gt;
You can browse this list and find units which are being used in your project. For example, if your project references the &lt;code&gt;System.Win.ComObj&lt;/code&gt; unit - you would need the &lt;code&gt;E_SYSTEM_WIN_COM_INFO&lt;/code&gt; symbol. If your project references the &lt;code&gt;Data.DB&lt;/code&gt; unit - you would need the &lt;code&gt;E_DATA_DB_INFO&lt;/code&gt; symbol. And so on.&lt;br /&gt;
&lt;br /&gt;
Tip: if you don&#39;t know what units are being used in your project - use the &quot;Start&quot; / &quot;Programs&quot; / &quot;EurekaLog&quot; / &quot;Tools&quot; / &quot;PE Analyzer&quot; tool to look what is inside your compiled executable file.&lt;br /&gt;
&lt;br /&gt;
Once you have learned the names of all the symbols you need - go to project&#39;s options (use IDE&#39;s &quot;Project&quot; / &quot;Options&quot; menu item). Then open conditional defines settings. These can be located in various places - depending on your IDE and personality. For example, it is located on the &quot;Building&quot; / &quot;Delphi compiler&quot; tab for modern Delphi:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLrrAV1MZm-_R0lz4mPTvZxfzuYiV2uQfOM1YaRB4mCwezQkh91Sah2mnxp6rnm-pAoMXvNJZ2w0v8nnlPBvmF0u9GEDU53n5UslmcVyIIXUG1LvCcjd7GGgyUzGEeJISKdYUdrxXyLvhCZItMXflT0Ogh0ELDtPi07YMSeBFK8wYO_frOHfh__cJiBdc/s1112/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;495&quot; data-original-width=&quot;1112&quot; height=&quot;285&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLrrAV1MZm-_R0lz4mPTvZxfzuYiV2uQfOM1YaRB4mCwezQkh91Sah2mnxp6rnm-pAoMXvNJZ2w0v8nnlPBvmF0u9GEDU53n5UslmcVyIIXUG1LvCcjd7GGgyUzGEeJISKdYUdrxXyLvhCZItMXflT0Ogh0ELDtPi07YMSeBFK8wYO_frOHfh__cJiBdc/w640-h285/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Click on the image to enlarge&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
Add all symbol names which you learned on the previous step. We recommend to add symbol names either to the &quot;All configuration&quot; target or to the platform-specific target. Adding a symbol name will uncomment code in the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit, which will include the corresponding unit and extract additional info. Like so:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  {$IFDEF E_SYSTEM_WIN_COM_INFO}ComObj,{$ENDIF}
  
...

  {$IFDEF E_SYSTEM_WIN_COM_INFO}
  if E is ComObj.EOleSysError then
  begin
    Fields.Values[&#39;EOleSysError.ErrorCode&#39;]   := &#39;$&#39; + IntToHex(ComObj.EOleSysError(E).ErrorCode, 8);
    if E is ComObj.EOleException then
    begin
      Fields.Values[&#39;EOleException.Source&#39;]   := ComObj.EOleException(E).Source;
      Fields.Values[&#39;EOleException.HelpFile&#39;] := ComObj.EOleException(E).HelpFile;
    end;
  end;
  {$ENDIF}&lt;/pre&gt;
&lt;br /&gt;
That is why the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit is placed as source code (&lt;code&gt;.pas&lt;/code&gt;) file into the &lt;code&gt;\Source\Extras\&lt;/code&gt; folder: so it can be recompiled from the source code when conditional defines change - unlike regular EurekaLog units which come precompiled and never change.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;IMPORTANT NOTE:&lt;/b&gt; do not forget to make a &lt;b&gt;full build&lt;/b&gt; of your project (not just compile) after changing the conditional defines option.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Using EExtraExceptionInfo with your own callbacks&lt;/h3&gt;
The &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit adds exception information into the &quot;Custom information&quot; section of your bug reports. However, you may also &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_add_information_to_bug_report&quot; title=&quot;How to add custom information to a bug report?&quot;&gt;add your own custom information&lt;/a&gt;. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure AddMyData(const ACustom: Pointer;
  AExceptionInfo: TEurekaExceptionInfo; ALogBuilder: TBaseLogBuilder;
  ADataFields: TStrings; var ACallNextHandler: Boolean);
begin
  // Your own code here. Code below is just an arbitrary example
  ADataFields.Values[&#39;License&#39;] := GetUserLicense;
  ADataFields.Values[&#39;User&#39;]    := GetUserName;
  ADataFields.Values[&#39;Build&#39;]   := &#39;3.5&#39;;
end;

initialization
  RegisterEventCustomDataRequest(nil, AddMyData);
end.&lt;/pre&gt;
How will your own event handler play with EurekaLog&#39;s &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit?&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;RegisterEvent*&lt;/code&gt; functions add event handlers into lists. EurekaLog would call each registered handler from a list until there are no more handlers or some handler would return &lt;code&gt;ACallNextHandler&lt;/code&gt; = &lt;code&gt;False&lt;/code&gt;. So, if you register your own &lt;code&gt;OnCustomData&lt;/code&gt; event handler - it will be called together with the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit, producing the following output (see code examples above):&lt;br /&gt; 
&lt;pre&gt;Exception:
-----------------------------------------------------
  2.1 Date          : Sat, 11 Apr 2026 20:35:48 +0300
  2.2 Address       : 0137F5D9
  2.3 Module Name   : Project1.exe
  2.4 Module Version: 1.0.0.0
  2.5 Type          : EOleException
  2.6 Message       : Catastrophic failure.
  2.7 ID            : 2EBFEC43

Custom Information:
-------------------------------------------------
  9.1 EOleSysError.ErrorCode: $8000FFFF
  9.2 License               : kjlasdioj234879asd
  9.3 User                  : example@example.com
  9.4 Build                 : 3.5&lt;/pre&gt;
Here: the 9.1 line is produced by the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit, while 9.2-9.4 lines are produced by your code (event handler).&lt;br /&gt;
&lt;br /&gt;
The last argument for any &lt;code&gt;RegisterEvent*&lt;/code&gt; function (named &lt;code&gt;AFirst&lt;/code&gt;) defines if the function should add your handler to the beginning of the list (&lt;code&gt;AFirst&lt;/code&gt; = &lt;code&gt;True&lt;/code&gt;) or to the end (&lt;code&gt;AFirst&lt;/code&gt; = &lt;code&gt;False&lt;/code&gt;; default). For example, registering your handler like so:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;initialization
  RegisterEventCustomDataRequest(nil, AddMyData, True { - added });
end.&lt;/pre&gt;
will produce the following output:&lt;br /&gt;
&lt;pre&gt;Exception:
-----------------------------------------------------
  2.1 Date          : Sat, 11 Apr 2026 20:35:48 +0300
  2.2 Address       : 0137F5D9
  2.3 Module Name   : Project1.exe
  2.4 Module Version: 1.0.0.0
  2.5 Type          : EOleException
  2.6 Message       : Catastrophic failure.
  2.7 ID            : 2EBFEC43

Custom Information:
-------------------------------------------------
  9.1 License               : kJlaSdioj23t879aSd
  9.2 User                  : example@example.com
  9.3 Build                 : 3.5
  9.4 EOleSysError.ErrorCode: $8000FFFF&lt;/pre&gt;
As you can see, now it is your output listed first, followed by the output from the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit.&lt;br /&gt;
&lt;br /&gt;
And since your handler will be called first (before &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit) - you can also do something like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure AddMyData(const ACustom: Pointer;
  AExceptionInfo: TEurekaExceptionInfo; ALogBuilder: TBaseLogBuilder;
  ADataFields: TStrings; var ACallNextHandler: Boolean);
begin
  ADataFields.Values[&#39;License&#39;] := GetUserLicense;
  ADataFields.Values[&#39;User&#39;]    := GetUserName;
  ADataFields.Values[&#39;Build&#39;]   := &#39;3.5&#39;;
  
  // Do not call code from EExtraExceptionInfo for the EOleException exceptions 
  if AExceptionInfo.ExceptionClass = EOleException.ClassName then
    ACallNextHandler := False;
end;&lt;/pre&gt;
&lt;br /&gt;
Additionally, if you don&#39;t want to add additional exception properties to the custom information section - you can change this behaviour to adding additional exception properties to exception&#39;s message. You need to add the &lt;code&gt;E_ADD_CUSTOM_MESSAGE&lt;/code&gt; symbol to conditional defines. Doing so will produce the following output:&lt;br /&gt;
&lt;pre&gt;Exception:
-----------------------------------------------------
  2.1 Date          : Sat, 11 Apr 2026 20:35:48 +0300
  2.2 Address       : 0137F5D9
  2.3 Module Name   : Project1.exe
  2.4 Module Version: 1.0.0.0
  2.5 Type          : EOleException
  2.6 Message       : Catastrophic failure.
                      ErrorCode=$8000FFFF
  2.7 ID            : 2EBFEC43

Custom Information:
----------------------------------
  9.1 License: kJlaSdioj23t879aSd
  9.2 User   : example@example.com
  9.3 Build  : 3.5&lt;/pre&gt;
Here: the &quot;ErrorCode&quot; line is added to exception&#39;s message by the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit with the &lt;code&gt;E_ADD_CUSTOM_MESSAGE&lt;/code&gt; option, while &quot;Custom Information&quot; section is composed by your &lt;code&gt;AddMyData&lt;/code&gt; &lt;code&gt;OnCustomData&lt;/code&gt; event handler from the example above.&lt;br /&gt;
&lt;br /&gt;
You may also enable the &lt;code&gt;E_ADD_NO_CUSTOM_INFO&lt;/code&gt; option (by adding its symbol&#39;s name to conditional defines) to completely disable assist from the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Implemenation details&lt;/h3&gt;
The &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit is distributed as a source code (&lt;code&gt;.pas&lt;/code&gt;) file located in the &lt;code&gt;\Source\Extras\&lt;/code&gt; folder of your EurekaLog installation - as opposed to the usual EurekaLog units, which are distributed as precompiled (&lt;code&gt;.dcu&lt;/code&gt;/&lt;code&gt;.obj&lt;/code&gt;) files located in the &lt;code&gt;\Lib\&lt;/code&gt; folder of your EurekaLog installation. It is shipped as source code even in those editions of EurekaLog that are normally distributed without source code (Trial and Professional). That is because you can&#39;t use the same precompiled &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit in all projects, you need to actually change this unit (by commenting/uncommenting parts of its code - which is done by defining conditional symbols) adapting to each application.&lt;br /&gt;
&lt;br /&gt;
EurekaLog is installed into the Program Files folder by default - which is write-protected for normal users. So adding source code files to your project from such location can lead to compilation failures when the compiler won&#39;t be able to write &lt;code&gt;*.dcu&lt;/code&gt; and &lt;code&gt;*.hpp&lt;/code&gt; files into that location (it is true for older IDEs, which has a default compiled units output folder match the source code location). That is why the &lt;code&gt;\Source\Extras\&lt;/code&gt; folder is marked as writeable.&lt;br /&gt;
&lt;br /&gt;
P.S. The &lt;code&gt;\Source\Extras\&lt;/code&gt; folder was not writable in EurekaLog earlier than 7.16 (e.g. 7.15.*, etc.). There was an alternative workaround: files from the &lt;code&gt;\Source\Extras\&lt;/code&gt; folder were cloned (copied) into your project&#39;s folder. This copy behaviour still exists in EurekaLog 7.16+, but it is disabled by default. If you want to enable this copy behaviour back for whatever reason: open EurekaLog settings for your project (go to &quot;Project&quot; / &quot;EurekaLog options&quot; in IDE&#39;s menu), then switch to the &quot;Advanced&quot; / &quot;Custom/Manual&quot; tab. Find the &quot;ExtrasCopy&quot; line (if it is present) and change it to 1. If the line does not exist (which is the default) - add it to any location:&lt;br /&gt;
&lt;code&gt;ExtrasCopy=1&lt;/code&gt;&lt;br /&gt;
(default is 0 on EurekaLog 7.16, while it is 1 on earlier versions)&lt;br /&gt;
You can also open the &lt;code&gt;.eof&lt;/code&gt; file for your project in any text editor and edit property manually.&lt;br /&gt;
&lt;br /&gt;
And while we are on this topic - you can also add:&lt;br /&gt;
&lt;code&gt;ExtrasAdd=0&lt;/code&gt;&lt;br /&gt;
(default is 1)&lt;br /&gt;
to completely disable adding the &lt;code&gt;EExtraExceptionInfo&lt;/code&gt; unit to your project.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/1009098599120333923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/1009098599120333923'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2026/04/logging-additional-exception-information.html' title='Logging additional exception information'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLrrAV1MZm-_R0lz4mPTvZxfzuYiV2uQfOM1YaRB4mCwezQkh91Sah2mnxp6rnm-pAoMXvNJZ2w0v8nnlPBvmF0u9GEDU53n5UslmcVyIIXUG1LvCcjd7GGgyUzGEeJISKdYUdrxXyLvhCZItMXflT0Ogh0ELDtPi07YMSeBFK8wYO_frOHfh__cJiBdc/s72-w640-h285-c/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-5611227189325185522</id><published>2026-03-31T11:37:00.000+01:00</published><updated>2026-03-31T11:37:19.373+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="Releases"/><title type='text'>EurekaLog 7.15 is out</title><content type='html'>We are pleased to announce the availability of the new 7.15 version.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;EurekaLog Logo&quot;&gt;&lt;img alt=&quot;EurekaLog Logo&quot; src=&quot;https://www.eurekalog.com/images/logo.png&quot; height=&quot;68&quot; width=&quot;295&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
EurekaLog is a tool for Delphi and C++Builder that gives your application the power to catch every exception and memory/resource leak, generating a detailed log of the call stack (with unit, procedure and line number), optionally sending you a copy of each log entry via email or the Web via the most used Web Bug-Tracking tools (as Mantis, BugZilla, FogBugz, JIRA, YouTrack, Redmine, Exceptionless, GitLab, and GitHub).&lt;/blockquote&gt;
EurekaLog represents the most advanced exception and memory leaks logger technology available for Delphi/C++Builder developers.&lt;br /&gt;
&lt;br /&gt;
To learn more about EurekaLog, please visit our website at:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com&quot;&gt;https://www.eurekalog.com&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
To download a demo, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/downloads&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/downloads&quot;&gt;https://www.eurekalog.com/downloads&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you would like to purchase a new license, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/buy&quot;&gt;https://www.eurekalog.com/buy&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Changes in 7.15 build:&lt;/strong&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Added: Recompiled for RAD Studio 13 update 1&lt;/li&gt;
&lt;li&gt;Added: \Source\Extras\EExtraExceptionInfo.pas for extracting additional fields of exception objects&lt;/li&gt;
&lt;li&gt;Added: The new _LOCATION_ function (combination of unit, class, function, line number)&lt;/li&gt;
&lt;li&gt;Added: The CurrentAddress function (ECallStack unit) - to avoid inclusion of ELowLevel for the GetEIP function&lt;/li&gt;
&lt;li&gt;Added: Added the ResStringCleanupCache function (EResourceStrings unit)&lt;/li&gt;
&lt;li&gt;Added: Registers preview on the Assembler tab&lt;/li&gt;
&lt;li&gt;Added: Stack dump improvements: EBP is highlighted, stack dump size is increased to include all arguments&lt;/li&gt;
&lt;li&gt;Added: New localized languages&lt;/li&gt;
&lt;li&gt;Added: Support for JSON and XML for HTTP posting&lt;/li&gt;
&lt;li&gt;Added: Expanding environment variables for passwords&lt;/li&gt;
&lt;li&gt;Added: The new TThreadData.OnExit event - free to use for any purposes&lt;/li&gt;
&lt;li&gt;Added: Preparation for BugID as arbitral string - currently for internal use only&lt;/li&gt;
&lt;li&gt;Added: IDE&#39;s &quot;Tools&quot; / &quot;EurekaLog&quot; / &quot;Bug Reports&quot; / &quot;Delete old bug reports...&quot; menu item&lt;/li&gt;
&lt;li&gt;Added: EurekaLog news/updates/RSS notifications in IDE&lt;/li&gt;
&lt;li&gt;Fixed: Major rewrite of lightweight DLLs&lt;/li&gt;
&lt;li&gt;Fixed: Using deprecated API in Jira&lt;/li&gt;
&lt;li&gt;Fixed: Stripping headers from web-fields&lt;/li&gt;
&lt;li&gt;Fixed: Wine compatibility for DLLs with packages&lt;/li&gt;
&lt;li&gt;Fixed: Possible unexpected resetting of last error in DLLs&lt;/li&gt;
&lt;li&gt;Fixed: Rare deadlock when opening a locked bug report file&lt;/li&gt;
&lt;li&gt;Fixed: Very rare deadlock in multithreading apps&lt;/li&gt;
&lt;li&gt;Fixed: A minor performance optimization for call stack tracing&lt;/li&gt;
&lt;li&gt;Fixed: Improvements for __initialize_security_cookie/__security_cookie_check functions (functions now also check return addresses; an additional human-readable description can be passed to __security_cookie_check)&lt;/li&gt;
&lt;li&gt;Fixed: Very rare memory corruption bug on shutdown during leaks checks&lt;/li&gt;
&lt;li&gt;Fixed: Various improvements on startup and shutdown stage&lt;/li&gt;
&lt;li&gt;Fixed: Various improvements for handling stack overflows&lt;/li&gt;
&lt;li&gt;Fixed: Updated existed localized languages&lt;/li&gt;
&lt;li&gt;Fixed: Other minor improvements&lt;/li&gt;
&lt;li&gt;Changed: Updated the included VirtualMM&lt;/li&gt;
&lt;/ol&gt;
&lt;a href=&quot;https://www.eurekalog.com/showchangelog&quot; title=&quot;EurekaLog: Full Change Log&quot;&gt;See full change log here&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/5611227189325185522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/5611227189325185522'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2026/03/EurekaLog-7-15.html' title='EurekaLog 7.15 is out'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-494005516775945209</id><published>2025-12-19T21:26:00.001+00:00</published><updated>2025-12-19T21:28:36.339+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="non-technical"/><title type='text'>EurekaLog 25% Christmas Sale + 3 Months Extra of Maintenance Period</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://www.eurekalog.com/buy.php?coupon=XMAS2025&quot;&gt;&lt;img style=&#39;height: 100%; width: 100%; object-fit: contain&#39; alt=&quot;Sale Announcement&quot; border=&quot;0&quot; data-original-height=&quot;1237&quot; data-original-width=&quot;1910&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi73u0xzyFk_wgGzQIYu8eezUy7166cq5DprCCQ-cUP-3orGwTAhgMq-ipDZMh1MZ5woJWeJlWPSA9hiAgKLPPDPX54-B3HXsAkUmHk1GthG2-Mzd9eFz_c4PJp6NDxq6xIzjl5KZpkYbKPG1foXMi1vu-hAmz07Pgv5kPLJW9ZPkq3ANI1PLEwGZm0OHI/s16000/pexels-candis-hidalgo-23771-639110.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h3&gt;We&#39;re excited to announce a special EurekaLog Christmas sale!&lt;/h3&gt; 
During this magical holiday season, we want to thank our users for their trust and give them a special surprise. EurekaLog continues to help developers around the world — and we&#39;re celebrating these successes with you.&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
Use the &lt;font color=&quot;red&quot;&gt;&lt;b&gt;XMAS2025&lt;/b&gt;&lt;/font&gt; discount coupon when purchasing a new license or a prolongation to get a &lt;b&gt;25% discount&lt;/b&gt; and a bonus of &lt;b&gt;3 extra months&lt;/b&gt; of maintenance period. The offer is valid starting &lt;b&gt;December 23&lt;/b&gt; and ends at the end of &lt;b&gt;December 31&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
You can use the discount code here:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; title=&quot;Purchase EurekaLog license&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Please, do not forget to log in into your account, if you already own a EurekaLog license.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/494005516775945209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/494005516775945209'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/12/Christmas-sale.html' title='EurekaLog 25% Christmas Sale + 3 Months Extra of Maintenance Period'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi73u0xzyFk_wgGzQIYu8eezUy7166cq5DprCCQ-cUP-3orGwTAhgMq-ipDZMh1MZ5woJWeJlWPSA9hiAgKLPPDPX54-B3HXsAkUmHk1GthG2-Mzd9eFz_c4PJp6NDxq6xIzjl5KZpkYbKPG1foXMi1vu-hAmz07Pgv5kPLJW9ZPkq3ANI1PLEwGZm0OHI/s72-c/pexels-candis-hidalgo-23771-639110.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-4226723568832431830</id><published>2025-11-25T11:00:00.001+00:00</published><updated>2025-11-25T11:00:00.114+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="non-technical"/><title type='text'>Black Friday and Cyber Monday Sale</title><content type='html'>We are pleased to announce Black Friday and Cyber Monday &lt;b&gt;30% discount&lt;/b&gt; on any of our EurekaLog products using the coupon code found below.&lt;br /&gt;
&lt;br /&gt;
The sale starts on Black Friday (November, 28) and ends at the end of Cyber Monday (December, 1).&lt;br /&gt;
&lt;br /&gt;
Enter this code when paying for the item on our web site:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;BFCM2025&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
Existing customers with valid or expired licenses can log in and purchase upgrades, new licenses and extensions here:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/login.php&quot; title=&quot;Log in to customer control panel&quot;&gt;https://www.eurekalog.com/login.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Use the login credentials we sent you at purchase time.&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
New customers (without existing licenses) can use the discount code here:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; title=&quot;Purchase EurekaLog&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4226723568832431830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4226723568832431830'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/11/black-friday-and-cyber-monday-sale.html' title='Black Friday and Cyber Monday Sale'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-284583682441319141</id><published>2025-11-08T13:34:00.002+00:00</published><updated>2025-11-17T13:59:29.151+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best practice"/><category scheme="http://www.blogger.com/atom/ns#" term="bug reports"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><title type='text'>Managing local bug reports by bug reports file rotation</title><content type='html'>EurekaLog can &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/bug_report_page.php&quot; title=&quot;Bug Report options&quot;&gt;store bug reports locally&lt;/a&gt; - in a &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/file_formats.php&quot;&gt;text file&lt;/a&gt;. This file is used both as a log and as a database. Each exception will be appended to the local bug report file. Some users want to know how to set up bug report file rotation. &lt;b&gt;Log rotation&lt;/b&gt; is the automated process of managing log files to prevent them from growing too large, which can impact system performance and storage. It involves doing something with old log files (compressing, archiving, renaming, or deleting) and creating a new, empty one to continue recording new events.&lt;br /&gt;
&lt;br /&gt;
This article will discuss what options you have for EurekaLog&#39;s local bug reports file.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h1&gt;Why you may want a bug reports file rotation&lt;/h1&gt;
First of all - EurekaLog is a tool which is designed to &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/reporting.php&quot; title=&quot;Bugs reporting with EurekaLog&quot;&gt;collect bug reports from PCs of your users/clients&lt;/a&gt;. It means its primary purpose is to &lt;b&gt;send&lt;/b&gt; bug reports to you via some &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/configuring_sending_report.php&quot; title=&quot;Configuring sending in EurekaLog&quot;&gt;sending method&lt;/a&gt; - be it a simple e-mail or a dedicated bug tracker. Therefore, local bug reports file is completely useless to you (the developer), as you typically don&#39;t have any remote access to PCs of your users/clients. In other words, local bug reports file rotation is not a problem for like 90% of EurekaLog&#39;s users, because you will be receiving bug reports and not using the local bug reports file in the first place.&lt;br /&gt;
&lt;br /&gt;
Therefore, if you set up sending in EurekaLog, you will probably want to &lt;b&gt;disable storing local bug reports&lt;/b&gt; completely. Thus, avoiding the rotation problem altogether.&lt;br /&gt;
&lt;br /&gt;
However, there are cases when you do need to use local bug reports:&lt;br /&gt;
&lt;ol&gt;
  &lt;li&gt;Some of EurekaLog&#39;s users use it in a way &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/debugging.php&quot; title=&quot;Local debugging with EurekaLog&quot;&gt;it was not designed to perform&lt;/a&gt; (at least it was not EurekaLog&#39;s main purpose). Specifically, they use EurekaLog as a replacement for IDE&#39;s debugger - to perform a local debugging/troubleshooting on their development machines. Naturally, they will use the local bug reports file and not set up any sending;&lt;/li&gt;
  &lt;li&gt;Some of EurekaLog&#39;s users develop apps which are designed to be run in private corporate environments. Corporate environments may have strict rules on network connectivity, so setting up sending is not viable/possible. Naturally, they will also use the local bug reports file and not set up any sending. For example, an app may be configured to store bug reports on a shared network drive, which can be accessed by you (the app&#39;s developer).&lt;/li&gt;
&lt;/ol&gt;
Once local bug reports file is enabled/set up - there may be two completely different reasons why you may want to implement a bug report file rotation:&lt;br /&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;b&gt;Removing obsolete bug reports&lt;/b&gt;. For example, you probably are not interested in bug reports from 5-10 years ago, as these will likely be irrelevant. That is - assuming you release updates to your app regularly.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Managing size of bug report files&lt;/b&gt;. This matters most for apps which run 24/7 non-stop, such as services. If your service is configured to continue to run after crashing with exception - your local bug reports file may grow fast.&lt;br /&gt;
    This reason is probably irrelevant for non-service apps, because you will likely to wipe obsolete bug reports (as in #1 above) long before file size become a problem.&lt;/li&gt;
&lt;/ol&gt;
&lt;b&gt;Note:&lt;/b&gt;&lt;br /&gt; 
&lt;blockquote&gt;If you need to implement any custom way to deliver reports to yourself (be it &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_save_report_instead_sending.php&quot; title=&quot;How to save report instead of sending it?&quot;&gt;saving into a specific folder&lt;/a&gt;, &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_send_exception.php&quot; title=&quot;How to send/upload exception to a file/DB/folder?&quot;&gt;inserting into database&lt;/a&gt;, or something else) - it is better to &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_send_bug_report_via_your_own_code.php&quot; title=&quot;How to send bug report via your own code?&quot;&gt;implement this as &quot;sending&quot;&lt;/a&gt;, rather than &quot;saving&quot; (for example, saving/inserting bug report in the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/topic_type_eevents_televexceptionnotifyproc.php&quot; title=&quot;Procedural event handler for OnExceptionNotify event&quot;&gt;&lt;code&gt;OnExceptionNotify&lt;/code&gt; event handler&lt;/a&gt;). That way you will actually be sending reports, so you can disable the local bug reports file - thus having all sending rules applied and without worrying about local bug reports file rotation in the first place.&lt;/blockquote&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Bug reports are not log entries&lt;/h1&gt;
EurekaLog does not have any sort of automated process, which is typically scheduled to run at regular intervals (e.g., hourly, daily, weekly) or when a file reaches a certain size. That is because local bug reports file contains &lt;b&gt;bug reports&lt;/b&gt; - a valuable information. It is not a log - which contains auxilary (less important) info. That is why EurekaLog will never delete bug report files without your explicit command. It is up to you to decide which bug reports are important and which ones are no longer relevant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Managing size of bug report files&lt;/h1&gt;
This is the most simple and straightforward way: you don&#39;t care about importance of bug reports, you just want to avoid unlimited file size grow. Perhaps, you are using local bug reports file as a backup to sending, so you are not really worried about losing precious reports.&lt;br /&gt;
&lt;br /&gt;
If this is the case - there are the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/bug_report_page.php&quot; title=&quot;Bug Reports options&quot;&gt;&quot;Max. reports in one file&quot; and &quot;Do not save duplicate errors&quot; options&lt;/a&gt;. You can limit how much bug reports can a single local bug reports file hold by altering these options. While you can&#39;t specify a file size limit in bytes, but you do this by specifying file size limit in reports. That way you will use a single local bug reports file, and will limit its size to some reasonable value of your choice.&lt;br /&gt;
&lt;br /&gt;
There is also the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/bug_report_page.php&quot; title=&quot;Bug Reports options&quot;&gt;&quot;Delete file at startup&quot; option&lt;/a&gt;, which can be useful in some cases.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Removing obsolete bug reports&lt;/h1&gt;
If you don&#39;t care about limiting file size, but rather want to tidy up your reports - you may want to delete old reports which lost their importance.&lt;br /&gt;
&lt;br /&gt;
EurekaLog has the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/bug_report_page.php&quot; title=&quot;Bug Report options&quot;&gt;&quot;Delete file at version change&quot; option&lt;/a&gt;, which will create a fresh local bug reports file once you deploy a new version of your application. This option requires you to embed version information into your executable, as well as increase it for each build/release. This option works well if you release new builds/versions of your app more or less regularly, and your users/clients/customers upgrade your app. This option will be useless if you very rarely make new releases, or if your users/clients/customers prefer to stay at single version of your app.&lt;br /&gt;
&lt;br /&gt;
There is also IDE&#39;s &quot;Tools&quot; / &quot;EurekaLog&quot; / &quot;Bug reports&quot; / &quot;Delete old bug reports...&quot; menu item, which you can invoke manually as often as you like:&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWetyckCOKs9GDrnHAEMUjkSt1NJzOvfjDdtvWu4LlOTH5yUmePeSFnAkE3yGNW2WQ270Cy9tqzMCN_YajSEnXCyPZIPCrmpvx5J1Il7CB7FOIeLHX55AuAxAGZyGo6F9SE4t76YDV2yuA_UCzR-sNkD6Oeil8KLOYecuOw8OfLXmtTe0Rm5EQqlWZ_Pg/s837/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;432&quot; data-original-width=&quot;837&quot; height=&quot;330&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWetyckCOKs9GDrnHAEMUjkSt1NJzOvfjDdtvWu4LlOTH5yUmePeSFnAkE3yGNW2WQ270Cy9tqzMCN_YajSEnXCyPZIPCrmpvx5J1Il7CB7FOIeLHX55AuAxAGZyGo6F9SE4t76YDV2yuA_UCzR-sNkD6Oeil8KLOYecuOw8OfLXmtTe0Rm5EQqlWZ_Pg/w640-h330/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Bug reports cleanup from IDE&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
There is no other option which can delete local bug reports file due to it obsolesence. However, what you can do is to &lt;b&gt;have multiple local bug reports files&lt;/b&gt;. For example, if you are developing a service application, you may want to use a new local bug report file for each day, week or month. Other possible alternative is to have one file per each bug (so the single file will contain multiple reports of the same bug). You can do either of these by using either &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/variables.php&quot; title=&quot;Environment Variables&quot;&gt;variables&lt;/a&gt; or &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/index_events.php&quot; title=&quot;EurekaLog Events&quot;&gt;events&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Using variables to rotate the local bug report file&lt;/h3&gt;
The default output for local bug report file is&lt;br /&gt;
&lt;code&gt;%AppData%\Neos Eureka S.r.l\EurekaLog\Bug Reports\%_ThisModuleName%\&lt;/code&gt;&lt;br /&gt;
which will be expanded to something like&lt;br /&gt; 
&lt;code&gt;C:\Users\&lt;i&gt;your_user_name&lt;/i&gt;\AppData\Roaming\Neos Eureka S.r.l\EurekaLog\Bug Reports\Project1.exe\Project1.el&lt;/code&gt;&lt;br /&gt;
at run-time. Notice how it will be the same file name for each bug/exception.&lt;br /&gt;
&lt;br /&gt;
You can alter local bug report file name to something like&lt;br /&gt; 
&lt;code&gt;%AppData%\Neos Eureka S.r.l\EurekaLog\Bug Reports\%_ThisModuleName%\%_ThisModuleName%_%DateFmt%.el&lt;/code&gt;&lt;br /&gt;
which will be expanded to&lt;br /&gt;
&lt;code&gt;C:\Users\&lt;i&gt;your_user_name&lt;/i&gt;\AppData\Roaming\Neos Eureka S.r.l\EurekaLog\Bug Reports\Project1.exe\Project1.exe_&lt;i&gt;20251031&lt;/i&gt;.el&lt;/code&gt;&lt;br /&gt;
at run-time. The important part: the expansion of environment variables happens during exception&#39;s processing. In other words, the local bug report file name will be calculated for each exception, resulting in multiple local bug report files if your application will run for multiple days.&lt;br /&gt;
&lt;br /&gt;
Obviosly, if you want your bug report file to contain multiple bug reports at once - you need to increase the above mentioned &quot;Max. reports in one file&quot; limit, because the default value is 1 (a bug report file can store only one bug report).&lt;br /&gt;
&lt;br /&gt;
Notice how you can change both folder name only (when ending with trailing path delimiter) or file name (when ending with file extension), so you can have one file per day or one folder per day.&lt;br /&gt;
&lt;br /&gt;
You can also use something like&lt;br /&gt;
&lt;code&gt;%AppData%\Neos Eureka S.r.l\EurekaLog\Bug Reports\%_ThisModuleName%\%_ThisModuleName%_%_BugID%.el&lt;/code&gt;&lt;br /&gt;
which will be expanded to&lt;br /&gt; 
&lt;code&gt;C:\Users\&lt;i&gt;your_user_name&lt;/i&gt;\AppData\Roaming\Neos Eureka S.r.l\EurekaLog\Bug Reports\Project1.exe\Project1.exe_&lt;i&gt;00CAB105&lt;/i&gt;.el&lt;/code&gt;&lt;br /&gt;
at run-time. That way you will have a standalone local bug report file for each bug/exception.&lt;br /&gt;
&lt;br /&gt;
Or you can even combine the two together like so:&lt;br /&gt;
&lt;code&gt;%AppData%\Neos Eureka S.r.l\EurekaLog\Bug Reports\%_ThisModuleName%\%DateFmt%\%_ThisModuleName%_%_BugID%.el&lt;/code&gt;&lt;br /&gt;
resulting in file names like&lt;br /&gt;
&lt;code&gt;C:\Users\&lt;i&gt;your_user_name&lt;/i&gt;\AppData\Roaming\Neos Eureka S.r.l\EurekaLog\Bug Reports\Project1.exe\&lt;i&gt;20251031&lt;/i&gt;\Project1.exe_&lt;i&gt;00CAB105&lt;/i&gt;.el&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Using events to rotate local bug report file&lt;/h3&gt;
There may be cases when you want to have a logic, which can&#39;t be implemented with environment variables. For example, if you want a new local bug report file for each month, but there is no environment variable that defines &quot;a month of bug&#39;s occurence&quot;. In this case: you can use event handlers.&lt;br /&gt;
&lt;br /&gt;
There is the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/topic_type_eevents_televcustomfilenameproc.php&quot; title=&quot;Procedural event handler for OnCustomFileName event&quot;&gt;&lt;code&gt;OnCustomFileName&lt;/code&gt; event handler&lt;/a&gt;. You may assign your custom code to perform modification of the local bug report file name. You will be insterested in the &lt;code&gt;AFileType&lt;/code&gt; = &lt;code&gt;ftSavedBugReport&lt;/code&gt; case:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  DateUtils,      // for YearOf and MonthOf
  EEvents,        // for RegisterEventCustomFileName
  EException,     // for TEurekaExceptionInfo
  EConsts,        // for EEurekaLogReportExt
  ETypes,         // for TEurekaLogFileType
  ECompatibility; // for YearOf and MonthOf on IDEs which do not have the DateUtils unit

// Alter local bug reports file name in any way we want
procedure ChangeLocalBugReportFileName(const ACustom: Pointer; 
  AExceptionInfo: TEurekaExceptionInfo; 
  const AFileType: TEurekaLogFileType; 
  var AFileName: String; 
  var ACallNextHandler: Boolean);
begin
  // Is this a local bug reports file name?
  if AFileType = ftSavedBugReport then
    // If yes - then change it
    // This code is just an example
    // Replace it with your own code
    AFileName := ExtractFilePath(AFileName) + // folder
                 Format(&#39;%s_%d_%d%s&#39;,         // file
                   [ExtractFileName(AExceptionInfo.ExceptionModuleName),
                    YearOf(AExceptionInfo.Time),
                    MonthOf(AExceptionInfo.Time),
                    EEurekaLogReportExt]);
end;

initialization
  // Ask EurekaLog to use our code to customize local bug reports file name
  RegisterEventCustomFileName(nil, ChangeLocalBugReportFileName);
end.&lt;/pre&gt;
This code will produce file names like&lt;br /&gt;
&lt;code&gt;C:\Users\&lt;i&gt;your_user_name&lt;/i&gt;\AppData\Roaming\Neos Eureka S.r.l\EurekaLog\Bug Reports\Project1.exe\Project1.exe_&lt;i&gt;2025_10&lt;/i&gt;.el&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
Naturally, you can do literally anything with the local bug reports file name when you are using an event handler.&lt;br /&gt;
&lt;br /&gt;
Obviosly, if you want your bug report file to contain multiple bug reports at once - you need to increase the above mentioned &quot;Max. reports in one file&quot; limit, because the default value is 1 (a bug report file can store only one bug report).&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Performing the actual rotation&lt;/h3&gt;
While both methods discussed above will spawn a new local bug reports files from time to time (based on the conditions that you set) - they will not delete any old files. That is because EurekaLog does not know if you are interested in, say, bug reports from yesterday or not. It is up to you to decide which bug report files are qualified for deletion.&lt;br /&gt;
&lt;br /&gt;
If you are running a normal application (not a service) - then you can perform the check on startup. If you are running a service application - then you may be interesting in performing the check, say, once per 100 exceptions:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  EEvents,    // for RegisterEventExceptionNotify
  EException, // for TEurekaExceptionInfo
  ESysInfo;   // for ExpandEnvVars

// Remove old local bug reports files
procedure LocalBugReportsFilesCleanup; overload;
var
  Folder: String;
begin
  // Change the target folder for your specific location
  Folder := ExpandEnvVars(&#39;%APPDATA%\Neos Eureka S.r.l\EurekaLog\Bug Reports\&#39;);

  // Write here your code to cleanup old local bug reports files
  // ...
end;

var
  // Counter on how many unhandled exceptions were processed
  // In other words, how many bug reports were created
  GProcessedExceptionsCount: Integer;

// Will be called by EurekaLog for each unhandled exception
procedure LocalBugReportsFilesCleanup(const ACustom: Pointer;
  AExceptionInfo: TEurekaExceptionInfo;
  var AHandle: Boolean;
  var ACallNextHandler: Boolean); overload;
const
  // Customize this constant to your liking
  MaxExceptionsBetweenCleanups = 100;
begin
  // If there were enough exceptions...
  if GProcessedExceptionsCount &amp;gt; MaxExceptionsBetweenCleanups then
  begin
    GProcessedExceptionsCount := 0;
    // ...perform the cleanup
    LocalBugReportsFilesCleanup;
  end;
  Inc(GProcessedExceptionsCount);
end;

initialization
  // Perform the cleanup on startup:
  LocalBugReportsFilesCleanup;
  // Ask EurekaLog to perform the cleanup on exceptions:
  RegisterEventExceptionNotify(nil, LocalBugReportsFilesCleanup);
end.&lt;/pre&gt;
How to implement the cleanup is up to you and it highly depends on how you organize your reports. For example, if you spawn a new local bug report file each day, you may enumerate all files and delete ones from yesterday or ones older than 1 year. You can extract the date part from a file name (if it is present) to get a sense on when this file was created. Surely, you can also use file&#39;s creation or modification date - assuming you are sure file dates were not tampered with.&lt;br /&gt;
&lt;br /&gt;
If you spawn a new local bug report file for each bug/exception, you may collect/group these files into folders corresponding to dates, so you can wipe individual folders (older than X days) instead of files.&lt;br /&gt;
&lt;br /&gt;
Here is an example of how your cleanup code &lt;b&gt;might&lt;/b&gt; look like:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  EEvents,        // for RegisterEventExceptionNotify
  EException,     // for TEurekaExceptionInfo
  ESysInfo,       // for ExpandEnvVars
  EConsts,        // for EEurekaLogReportExt
  ETools,         // for BuildFileList and GetFileCreation
  ETypes;         // for amSuperSetOf

// Remove old local bug reports files
procedure LocalBugReportsFilesCleanup; overload;
var
  Folder: String;
  FileName: String;
  Files: TStrings;
  Today: TDateTime;
  FileCreated: TDateTime;
  X: Integer;
const
  MaxAgeInDays = 365;
begin
  // Change the target folder for your specific location
  Folder := ExpandEnvVars(&#39;%AppData%\Neos Eureka S.r.l\EurekaLog\Bug Reports\%_ThisModuleName%\&#39;);

  // Write here your code to cleanup old local bug reports files
  // The code below is just an example
  // Replace it with your own code

  Today := Now;
  Files := TStringList.Create;
  try
    // Enumerate all local bug report files in the mentioned folder
    AdvBuildFileList(Folder + &#39;*&#39; + EEurekaLogReportExt,
                     faAnyFile, Files, amSuperSetOf,
                     [flFullNames, flRecursive]);
    // Check each found bug report file
    for X := 0 to Files.Count - 1 do
    begin
      // Get file&#39;s properties
      FileName := Files[X];
      if not GetFileCreation(FileName, FileCreated) then
        FileCreated := Today;

      // If the file is too old - delete it
      if DaysBetween(Today, FileCreated) &amp;gt; MaxAgeInDays then
        DeleteFile(FileName);
    end;
  finally
    FreeAndNil(Files);
  end;
end;&lt;/pre&gt;
This code will delete all local bug reports files in the specified folder which are older than 365 days (1 year). If you wish to use a different logic to wipe files (say, wipe files from an older version of your app, wipe files for a fixed bug/exception, or anything else) - you would need to change or adjust this code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See also:
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/downloads_extras.php&quot; title=&quot;EurekaLog Extra Downloads&quot;&gt;Additional downloads for EurekaLog&lt;/a&gt; (contains a sample bug reports cleanup tool)&lt;/li&gt; 
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/configuring_bug_report.php&quot; title=&quot;Configuring bug report&quot;&gt;Managing bug reports in EurekaLog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/managing_bug_reports.php&quot; title=&quot;Managing bug reports in issue tracker&quot;&gt;Managing bug reports in a bug tracker&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_write_code_with_eurekalog.php&quot; title=&quot;How to write code with EurekaLog?&quot;&gt;How to write code with EurekaLog?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_register_event_handler.php&quot; title=&quot;How to register en event handler?&quot;&gt;How to register en event handler?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_register_event_handler_for_leaks.php&quot; title=&quot;How to register event handler for leaks?&quot;&gt;How to register event handler for leaks?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_add_exception_to_a_log.php&quot; title=&quot;How to add/save exception to a log?&quot;&gt;How to add/save exception to a log?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_send_exception.php&quot; title=&quot;How to send/upload exception to a file/DB/folder?&quot;&gt;How to send/upload exception to a file/DB/folder?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_save_report_instead_sending.php&quot; title=&quot;How to save report instead of sending it?&quot;&gt;How to save report instead of sending it?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_send_reports_automatically.php&quot; title=&quot;How to send reports automatically?&quot;&gt;How to send reports automatically?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_send_bug_report_via_your_own_code.php&quot; title=&quot;How to send bug report via my own code?&quot;&gt;How to send bug report via my own code?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/offline_storage.php&quot; title=&quot;Implementing offline storage for bug reports&quot;&gt;Implementing offline storage for bug reports&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/284583682441319141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/284583682441319141'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/11/bug-reports-file-rotation.html' title='Managing local bug reports by bug reports file rotation'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWetyckCOKs9GDrnHAEMUjkSt1NJzOvfjDdtvWu4LlOTH5yUmePeSFnAkE3yGNW2WQ270Cy9tqzMCN_YajSEnXCyPZIPCrmpvx5J1Il7CB7FOIeLHX55AuAxAGZyGo6F9SE4t76YDV2yuA_UCzR-sNkD6Oeil8KLOYecuOw8OfLXmtTe0Rm5EQqlWZ_Pg/s72-w640-h330-c/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-3714843665136326343</id><published>2025-10-29T10:16:00.009+00:00</published><updated>2025-12-12T12:27:02.962+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Access Violation"/><category scheme="http://www.blogger.com/atom/ns#" term="additional features"/><category scheme="http://www.blogger.com/atom/ns#" term="debugging"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="technical"/><title type='text'>Introducing VirtualMM debugging memory manager</title><content type='html'>We will talk about the VirtualMM debugging memory manager. What it is, when to use it, when not to use it, how to use it, where to download it, how to configure it, and so on.&lt;br /&gt;
&lt;br /&gt;
This article assumes that you are familiar (at least in general terms) with the concepts of address space, memory page, RESERVE, and COMMIT.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h1&gt;Contents&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h1&quot;&gt;Memory architecture in Delphi applications&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h2&quot;&gt;How do Delphi applications allocate memory?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h3&quot;&gt;How does System allocate memory?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h4&quot;&gt;What is a memory manager?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h5&quot;&gt;Why do Delphi applications use a memory manager?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h6&quot;&gt;What is the problem with a memory manager?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h7&quot;&gt;How do you find memory bugs in Delphi applications?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h8&quot;&gt;Write after delete&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h9&quot;&gt;Buffer overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h10&quot;&gt;Calling methods of a deleted object&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h11&quot;&gt;Calling a &lt;b&gt;static&lt;/b&gt; method of a deleted object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h12&quot;&gt;Calling a &lt;b&gt;virtual&lt;/b&gt; method of a deleted object&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h13&quot;&gt;Read after delete&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h14&quot;&gt;Memory reuse&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h15&quot;&gt;What is the VirtualMM debugging memory manager?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h16&quot;&gt;Where can I download VirtualMM?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#SafeMM&quot;&gt;Where can I download SafeMM?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h17&quot;&gt;How do I install VirtualMM?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h18&quot;&gt;How do I add (connect) VirtualMM to my project?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h19&quot;&gt;How do I configure VirtualMM?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h20&quot;&gt;What problems does VirtualMM solve?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h21&quot;&gt;Referencing already freed memory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h22&quot;&gt;Buffer overflow&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h23&quot;&gt;If &lt;code&gt;USE_SMALL_BLOCKS&lt;/code&gt; is &lt;b&gt;disabled&lt;/b&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h24&quot;&gt;If &lt;code&gt;USE_SMALL_BLOCKS&lt;/code&gt; is &lt;b&gt;enabled&lt;/b&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h25&quot;&gt;Calling a &lt;b&gt;static&lt;/b&gt; method of a deleted object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h26&quot;&gt;Calling a &lt;b&gt;dynamic&lt;/b&gt; method of a deleted object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h27&quot;&gt;Memory reuse&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h28&quot;&gt;When should I use VirtualMM?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h29&quot;&gt;When should I &lt;b&gt;NOT&lt;/b&gt; use VirtualMM?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h30&quot;&gt;Using VirtualMM with EurekaLog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h32&quot;&gt;What should I do if my application crashes with &quot;Out Of Memory&quot; when using VirtualMM?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h31&quot;&gt;Notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;a id=&quot;h1&quot;&gt;&lt;/a&gt;&lt;h1&gt;Memory architecture in Delphi applications&lt;/h1&gt;
To understand the purpose (essence) of the VirtualMM debugging memory manager, we first need to refresh our knowledge on the basics of memory management &lt;a href=&quot;#a1&quot;&gt;(*)&lt;/a&gt; in Delphi applications.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h2&quot;&gt;&lt;/a&gt;&lt;h3&gt;How do Delphi applications allocate memory?&lt;/h3&gt;
To recap: we have a CPU stack for local variables and a block (section) for global variables, as well as dynamic memory (the heap) for any variable-sized data in a Delphi application. Although Delphi has a variety of dynamic data (objects, arrays, strings, interfaces, etc.), they all call the &lt;code&gt;GetMem&lt;/code&gt; function in one way or another. For example, creating a string will call the &lt;code&gt;GetMem&lt;/code&gt; function, passing it the character size of the string plus the size of the string&#39;s service header. Creating an object will call the &lt;code&gt;GetMem&lt;/code&gt; function, passing it the object&#39;s &lt;code&gt;.InstanceSize&lt;/code&gt; property. And so on. In other words, any dynamic data in Delphi is a superset of the &lt;code&gt;GetMem&lt;/code&gt; function.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h3&quot;&gt;&lt;/a&gt;&lt;h3&gt;How does System allocate memory?&lt;/h3&gt;
Just as Delphi has a base memory allocation function (&lt;code&gt;GetMem&lt;/code&gt;) that handles all memory allocations, Windows also has its own base memory allocation function: the &lt;code&gt;VirtualAlloc&lt;/code&gt;. It actually has several variants, but for simplicity, we&#39;ll ignore them. For the purposes of this article, we&#39;ll refer to all functions in the &quot;&lt;code&gt;VirtualAlloc&lt;/code&gt; family&quot; simply as the &quot;&lt;code&gt;VirtualAlloc&lt;/code&gt; function&quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h4&quot;&gt;&lt;/a&gt;&lt;h3&gt;What is a memory manager?&lt;/h3&gt;
If you don&#39;t know anything about memory in Delphi and Windows applications, you might assume that the &lt;code&gt;GetMem&lt;/code&gt; function in Delphi simply calls the &lt;code&gt;VirtualAlloc&lt;/code&gt; function in Windows. In other words, they&#39;re the same function. But that&#39;s not true: in fact, the &lt;code&gt;GetMem&lt;/code&gt; function doesn&#39;t call the &lt;code&gt;VirtualAlloc&lt;/code&gt; function directly. Instead, the &lt;code&gt;GetMem&lt;/code&gt; function calls Delphi&#39;s &lt;b&gt;memory manager&lt;/b&gt;. Delphi’s memory manager then calls the &lt;code&gt;VirtualAlloc&lt;/code&gt; function.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h5&quot;&gt;&lt;/a&gt;&lt;h3&gt;Why do Delphi applications use a memory manager?&lt;/h3&gt;
Why do we need a memory manager at all? Why can&#39;t we use the operating system&#39;s memory management functions directly? That is, why can&#39;t the &lt;code&gt;GetMem&lt;/code&gt; function simply call the &lt;code&gt;VirtualAlloc&lt;/code&gt; function? The problem is that the &lt;code&gt;VirtualAlloc&lt;/code&gt; function allocates memory at a granularity of 64 KB &lt;a href=&quot;#a2&quot;&gt;(**)&lt;/a&gt;. This means that you can&#39;t allocate memory smaller than 64 KB. So, if you allocate 8 bytes for a &lt;code&gt;TObject&lt;/code&gt; using the &lt;code&gt;VirtualAlloc&lt;/code&gt; function, the &lt;code&gt;VirtualAlloc&lt;/code&gt; function will take a full 64 KB from your address space instead of 8 bytes. And these 64 KB cannot be used (to allocate another block of memory via the &lt;code&gt;VirtualAlloc&lt;/code&gt; function) until you release the created &lt;code&gt;TObject&lt;/code&gt; object and return the allocated memory.&lt;br /&gt;
&lt;br /&gt;
That is, if you create 100 objects of, say, 20 bytes each (very simple objects, you inherited them from &lt;code&gt;TObject&lt;/code&gt;), then instead of two kilobytes (20 bytes * 100 = 2 KB), you&#39;re now occupying almost 6.5 MB (64 * 100 = 6,400 KB) – several orders of magnitude more!&lt;br /&gt;
&lt;br /&gt;
This is precisely the problem the memory manager solves: it allocates one large chunk of memory using the &lt;code&gt;VirtualAlloc&lt;/code&gt; function (for example, 1 MB), and then places several smaller memory allocations into this block, which come from the &lt;code&gt;GetMem&lt;/code&gt; function. Thus the memory manager will be able to accommodate approximately 50,000 20-byte objects using a single 1 MB block.&lt;br /&gt;
&lt;br /&gt;
Further in the text, I will refer to &lt;i&gt;allocated/free&lt;/i&gt; memory (without quotation marks), meaning &lt;b&gt;actually&lt;/b&gt; allocated (free) memory that was allocated and freed through the &lt;code&gt;VirtualAlloc&lt;/code&gt;/&lt;code&gt;VirtualFree&lt;/code&gt; family of functions.&lt;br /&gt;
I will refer to &lt;i&gt;&quot;allocated&quot;/&quot;free&quot;&lt;/i&gt; memory (in quotation marks), meaning memory that was allocated (via the &lt;code&gt;VirtualAlloc&lt;/code&gt; function), but &lt;b&gt;logically marked&lt;/b&gt; as allocated or free through the &lt;code&gt;GetMem&lt;/code&gt;/&lt;code&gt;FreeMem&lt;/code&gt; functions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h6&quot;&gt;&lt;/a&gt;&lt;h1&gt;What is the problem with a memory manager?&lt;/h1&gt;
Let&#39;s say you allocated memory for an object using the &lt;code&gt;VirtualAlloc&lt;/code&gt; function, worked with the object, and then freed it (using the &lt;code&gt;VirtualFree&lt;/code&gt; function). If you now mistakenly try to do something with the (already freed) object, you&#39;ll get an Access Violation exception because you&#39;re accessing inaccessible memory (memory that wasn&#39;t allocated). For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  P: Pointer;
begin
  P := VirtualAlloc({...}); // allocate memory for P
  P^ := {...};              // do something (work) with P
  VirtualFree(P);           // finished working, free the memory

  // BUG: accessing memory that has already been freed
  P^ := {...};              // this line will ALWAYS throw an Access Violation exception
end;&lt;/pre&gt;
That&#39;s good. Memory bugs are immediately visible. We detect them right where they occur.&lt;br /&gt;
&lt;br /&gt;
Will anything change if we don&#39;t use the &lt;code&gt;VirtualAlloc&lt;/code&gt; function, but use the memory manager (the &lt;code&gt;GetMem&lt;/code&gt; function)? Yes, of course it will:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;GetMem(P, {...}); // actually: doesn&#39;t allocate memory
FreeMem(P);       // actually: doesn&#39;t free memory&lt;/pre&gt;
After all, memory manager functions don&#39;t actually allocate or free memory. Instead, they return a pointer to the middle of some (already allocated) memory block and mark this area as &quot;allocated&quot;, then simply mark it as &quot;free&quot; at the end. In other words:
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  P: Pointer;
begin
  GetMem(P, {...}); // &quot;allocate&quot; memory for P
  P^ := {...};      // do something (work) with P
  FreeMem(P);       // finished working, &quot;free&quot; the memory

  // BUG: accessing already freed memory
  P^ := {...};      // this line executes successfully because this memory is still allocated
end;&lt;/pre&gt;
Since the memory isn&#39;t actually freed (but only logically marked as &quot;free&quot;), we can successfully access it even after the object has been logically freed. This is the problem with using a memory manager: &lt;b&gt;a previously obvious memory bug is now hidden&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h7&quot;&gt;&lt;/a&gt;&lt;h1&gt;How do you find memory bugs in Delphi applications?&lt;/h1&gt;
There are so-called debugging memory managers for Delphi &lt;a href=&quot;#a3&quot;&gt;(***)&lt;/a&gt;. Unlike a regular memory manager, the purpose of a debugging memory manager is to help you diagnose memory problems. How do they do this?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h8&quot;&gt;&lt;/a&gt;&lt;h2&gt;Write after delete&lt;/h2&gt;
If we look at the example above, the problem is that the memory block, which is logically &quot;free&quot;, changes its contents:
&lt;pre class=&quot;brush:delphi&quot;&gt;FreeMem(P);  // the memory block is now &quot;free&quot;
P^ := {...}; // the &quot;free&quot; memory block has changed&lt;/pre&gt;
How can a memory manager detect this? For example, a memory manager may fill a &quot;free&quot; block with some known pattern (for example, the $CC byte). Then, if the memory manager allocates a new block of memory and sees that the &quot;free&quot; memory has changed (it doesn&#39;t contain the $CC byte at some location), then there was a write to &quot;freed&quot; memory bug. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  P: Pointer;
begin
  GetMem(P, {...});
  P^ := {...};
  FreeMem(P);

  // BUG: accessing already freed memory
  P^ := {...};      // this line executes successfully

  GetMem(B, {...}); // will raise a &quot;memory corruption&quot; error,
  // because the memory manager will see that the memory previously occupied by P has been modified
end;&lt;/pre&gt;
As you can see, although a memory bug can be detected, it won&#39;t be detected at the moment the bug occurred, but much later. This greatly complicates memory diagnostics. &lt;b&gt;This is a problem caused by using a debugging memory manager&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h9&quot;&gt;&lt;/a&gt;&lt;h2&gt;Buffer overflow&lt;/h2&gt;
Another common bug is writing beyond the bounds of allocated memory. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  Buffer: PInteger;
begin
  GetMem(Buffer, Count * SizeOf(Integer)); // &quot;allocate&quot; memory for Count Integers
  for X := 0 to Count do                   // BUG: should be Count - 1
  begin
    Buffer^ := 0;                          // will write data outside the &quot;allocated&quot; buffer on the last step
    Inc(Buffer);
  end;&lt;/pre&gt;
Here we allocate memory for &lt;code&gt;Count&lt;/code&gt; elements, while zeroing out &lt;code&gt;Count&lt;/code&gt; + 1 elements (one more than necessary). This means we&#39;ll be writing to &quot;free&quot; memory, which is located behind the memory block we&#39;ve &quot;allocated&quot;. The problem is that sometimes this memory isn&#39;t &quot;free&quot; but &quot;allocated&quot; by other data. This means we&#39;ll corrupt some other memory block located immediately after our memory block. This bug is called a &lt;b&gt;buffer overflow&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
How can a debugging memory manager help us with this bug? For example, it might &quot;allocate&quot; more memory than you requested. Let&#39;s say you request a 20-byte memory block, and the memory manager &quot;allocates&quot; 28 bytes. It reserves 4 bytes on each side for the template (for example, the $CC bytes). If there&#39;s anything different in this reserved area when &quot;freeing&quot; the memory - it means the memory has been overwritten. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  Buffer: PInteger;
begin
  GetMem(Buffer, Count * SizeOf(Integer)); // &quot;allocate&quot; memory for Count Integers
  for X := 0 to Count do                   // BUG: should be Count - 1
  begin
    Buffer^ := 0;                          // here: buffer overflow when X = Count
    Inc(Buffer);
  end;
  { ... do something else with Buffer }
  FreeMem(Buffer); // will trigger a buffer overflow error,
  // because the memory manager will see that the memory immediately after our block has been modified.&lt;/pre&gt;
And again, we see the same problem: &lt;b&gt;the bug will be detected long after it occurs&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h10&quot;&gt;&lt;/a&gt;&lt;h2&gt;Calling methods of a deleted object&lt;/h2&gt;
Memory isn&#39;t just for writing. Memory is often an object. What happens if we try to call a method of an already &quot;freed&quot; object?&lt;br /&gt;
&lt;br /&gt;
Well, a method can be regular (&lt;b&gt;static&lt;/b&gt;) or dynamic (&lt;b&gt;virtual&lt;/b&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h11&quot;&gt;&lt;/a&gt;&lt;h3&gt;Calling a &lt;b&gt;static&lt;/b&gt; method of a deleted object&lt;/h3&gt;
&lt;b&gt;A static&lt;/b&gt; method is essentially no different from a regular function: it has a fixed address. Therefore, calling a static method doesn&#39;t depend on the object&#39;s data. Therefore, whether the object is &quot;allocated&quot; or &quot;deallocated&quot; doesn&#39;t matter. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  L: TList;
begin
  L := TList.Create; // &quot;create&quot; the object
  L.Free;            // &quot;free&quot; the object

  I := L.IndexOf(P); // will execute successfully and return the correct result.&lt;/pre&gt;
Since the call to the &lt;code&gt;IndexOf&lt;/code&gt; method does not use the data of the &lt;code&gt;L&lt;/code&gt; object, the memory manager has no influence on the method call. Even if the memory manager frees the object, the method will still be called. Code execution will continue.&lt;br /&gt;
&lt;br /&gt;
Therefore, a &lt;b&gt;debugging memory manager will not be able to detect such a problem&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Note that we are only talking about the &lt;b&gt;call&lt;/b&gt; (invokation) of the method. How this method will be executed with an already deleted object is a separate question. We&#39;ll look at it later.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h12&quot;&gt;&lt;/a&gt;&lt;h3&gt;Calling a &lt;b&gt;virtual&lt;/b&gt; method of a deleted object&lt;/h3&gt;
The code must read the method&#39;s address from the object&#39;s data to call a &lt;b&gt;virtual&lt;/b&gt; method. If the object is &quot;freed&quot; and its data hasn&#39;t changed since it was &quot;freed&quot;, then nothing will prevent the virtual method from being called. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  S: TStringList;
begin
  S := TStringList.Create; // &quot;create&quot; the object
  // Working with S
  S.Free;              // &quot;deallocate&quot; the object

  I := S.Count;        // will execute successfully and return the correct result&lt;/pre&gt;
Here, the address of the virtual method &lt;code&gt;GetCount&lt;/code&gt; is stored in the &lt;code&gt;S&lt;/code&gt; object. When object &lt;code&gt;S&lt;/code&gt; is &quot;deleted&quot;, the memory it occupied remains accessible, so the method call succeeds.&lt;br /&gt;
&lt;br /&gt;
How can a debugging memory manager catch this problem? Well, for example, it could write a different virtual method address to the memory previously occupied by the &lt;code&gt;S&lt;/code&gt; object. And if someone tries to call the object&#39;s virtual method after it&#39;s &quot;deleted&quot;, it will call the debugging memory manager function, which will raise an error:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  S: TStringList;
begin
  S := TStringList.Create; // &quot;create&quot; the object
  // Working with S
  S.Free;              // &quot;free&quot; the object

  I := S.Count;        // will raise a &quot;method called on a deleted object&quot; error.&lt;/pre&gt;
Note: the &lt;b&gt;debugging memory manager was able to catch the error immediately&lt;/b&gt; when it occurred in this case - rather than later, as in other examples above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h13&quot;&gt;&lt;/a&gt;&lt;h2&gt;Read after delete&lt;/h2&gt;
Memory can be not only written to. You can also read from it. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  L: TList;
begin
  L := TList.Create; // &quot;create&quot; the object
  L.Free;            // &quot;free&quot; the object

  I := L.Count;      // will execute successfully and return the correct result.&lt;/pre&gt;
If the object&#39;s memory had been freed (via the &lt;code&gt;VirtualFree&lt;/code&gt; function), any attempt to write or read the object&#39;s data (fields) would immediately raise an Access Violation exception. But since the object is only &quot;freed&quot; (via the &lt;code&gt;FreeMem&lt;/code&gt; function), its data is still accessible, so attempts to read and write (modify) the object&#39;s data (fields) will succeed.&lt;br /&gt;
&lt;br /&gt;
How can a debugging memory manager help with this bug? Well, it can&#39;t help much. Yes, it can fill the &quot;freed&quot; object with some debugging pattern (the $CC byte, for example), but this won&#39;t prevent code from reading and writing the object&#39;s data:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  L: TList;
begin
  L := TList.Create; // &quot;create&quot; the object
  L.Free;            // &quot;free&quot; the object

  I := L.Count;      // will succeed, but return an incorrect result ($CCCC&#39;CCCC or -858&#39;993&#39;460)&lt;/pre&gt;
The only chance here is to hope that the data read is so erroneous that it ultimately causes some other exception. This usually happens when the address of something is read from an object.&lt;br /&gt;
&lt;br /&gt;
Therefore, a &lt;b&gt;debugging memory manager may or may not help in detecting this bug&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h14&quot;&gt;&lt;/a&gt;&lt;h2&gt;Memory reuse&lt;/h2&gt;
Let&#39;s look at this example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  S1, S2: TStringList;
begin
  S1 := TStringList.Create; // &quot;create&quot; the object
  // Working with S1
  S1.Free;                  // &quot;free&quot; the object

  S2 := TStringList.Create; // &quot;create&quot; the object

  I := S1.Count;            // logically - a bug, but will always succeed, since S1 = S2&lt;/pre&gt;
Here we &quot;delete&quot; the object, but immediately &quot;create&quot; an identical one. The addresses of both objects will be the same, i.e. &lt;code&gt;S1&lt;/code&gt; = &lt;code&gt;S2&lt;/code&gt;, since the new object will be allocated in the place of the old object. Then we access the first &quot;deleted&quot; object. Technically, &lt;code&gt;S1.Count&lt;/code&gt; is the same as &lt;code&gt;S2.Count&lt;/code&gt;. Therefore, although this is a logical error in the code, such code will execute without errors, working with &lt;code&gt;S2&lt;/code&gt; instead of &lt;code&gt;S1&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The debugging memory manager &lt;b&gt;cannot detect this problem&lt;/b&gt; for obvious reasons.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h15&quot;&gt;&lt;/a&gt;&lt;h1&gt;What is the VirtualMM debugging memory manager?&lt;/h1&gt;
How can we eliminate the shortcomings of Delphi&#39;s debugging memory managers described above? To do this: we need to understand the source of the issues with debugging memory managers. These issues arise from &lt;b&gt;software memory management&lt;/b&gt;. It means all memory problems are detected using user code.&lt;br /&gt;
&lt;br /&gt;
Unlike Delphi&#39;s memory managers, the system (&lt;code&gt;VirtualAlloc&lt;/code&gt;) uses &lt;b&gt;hardware memory management&lt;/b&gt;. It means all memory problems are detected by the CPU itself.&lt;br /&gt;
&lt;br /&gt;
Therefore, one way to address the shortcomings of Delphi&#39;s debugging memory managers is to &lt;b&gt;switch from software memory management to hardware memory management. This is precisely what the VirtualMM debugging memory manager does&lt;/b&gt;: roughly speaking, it replaces the &lt;code&gt;GetMem&lt;/code&gt; function with the &lt;code&gt;VirtualAlloc&lt;/code&gt; function. Specifically, its name is derived from the words &quot;&lt;b&gt;Virtual&lt;/b&gt;&quot; - which refers to the &lt;code&gt;&lt;b&gt;Virtual&lt;/b&gt;Alloc&lt;/code&gt; function, and &quot;&lt;b&gt;MM&lt;/b&gt;&quot; - which stands for &lt;b&gt;M&lt;/b&gt;emory &lt;b&gt;M&lt;/b&gt;anager.&lt;br /&gt;
&lt;br /&gt;
VirtualMM is a Pascal-based source code. It supports IDEs from Delphi 6 up to the latest available version (RAD Studio 13 Florence at the time of writing). Earlier versions of Delphi (5 and below) are not supported due to compiler&#39;s limitations.&lt;br /&gt;
&lt;br /&gt;
It&#39;s important to understand that VirtualMM &lt;b&gt;runs noticeably slower&lt;/b&gt; (compared to other memory managers with software allocation). This is because user code executes relatively quickly on the CPU. However, switching to kernel mode (which is necessary for hardware memory allocation) is very slow. Therefore, if your program is constantly allocating and freeing memory, be prepared for increased latency.&lt;br /&gt;
&lt;br /&gt;
Recall that the need for memory managers originally arose in Delphi due to the system&#39;s 64 KB memory allocation granularity. How does VirtualMM solve this problem?&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;If your application is 64-bit, then there is almost no problem: the 64-bit address space is 8 TB, which allows you to allocate no more than 134,217,728 memory blocks (ranging from 1 byte to 64 KB in size).&lt;/li&gt;
&lt;li&gt;If your application is 32-bit, things are much more complicated: the 32-bit address space is 2 GB (4 GB max), which allows you to allocate only 32,768 memory blocks maximum (ranging from 1 byte to 64 KB in size) – which is terribly small.&lt;/li&gt;
&lt;/ol&gt;
That&#39;s why the VirtualMM memory manager uses the concept of &quot;&lt;b&gt;small blocks&lt;/b&gt;&quot;. 64 KB is the memory &lt;b&gt;allocation&lt;/b&gt; granularity. In other words, you can&#39;t allocate memory starting at an address that isn&#39;t a multiple of 64 KB. This means you can allocate memory starting at 65,536 (64k) and 131,072 (128k), but you can&#39;t allocate memory starting at any value in between, such as 70,000. However, within these 64 KB, memory can be allocated or deallocated at a granularity of 4 KB &lt;a href=&quot;#a4&quot;&gt;(****)&lt;/a&gt;. In other words, we can allocate a 64 KB block of memory and then split it into 16 4 KB chunks. It means we can allocate 524,288 memory blocks (ranging in size from 1 byte to 4 KB) within a 2 GB address space. Of course, this isn&#39;t as good as 134,217,728 memory blocks in a 64-bit application, but it&#39;s already sufficient for practical operation of 32-bit applications - provided they don&#39;t allocate too much memory.&lt;br /&gt;
&lt;br /&gt;
Therefore, if the requested block size is smaller than the page size (4 KB), VirtualMM will classify it as a &quot;small block&quot;. These blocks will be grouped together in a single pool.&lt;br /&gt;
&lt;br /&gt;
It&#39;s important to understand that since all &quot;small blocks&quot; are grouped in a single memory area, there will be no unaccessed memory pages between them — which is the case for all other (regular, large) memory blocks. It means VirtualMM won&#39;t be able to catch buffer overflows in &quot;small blocks&quot; — at least not in hardware. However, VirtualMM adds guard values before and after &quot;small block&quot;s to catch buffer overflows in software, just like regular debugging memory managers do.&lt;br /&gt;
&lt;br /&gt;
It also means the memory consumption of your application will increase significantly when using VirtualMM — this memory will be spent on guard pages at the edges of allocated memory blocks, as well as on rounding up the block size to the allocation granularity. It means if you want to debug your application with VirtualMM, you need to ensure that any memory issue surfaces as soon as possible. The sooner the problem is detected, the less likely your application will crash with an out-of-memory error.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h16&quot;&gt;&lt;/a&gt;&lt;h1&gt;Where can I download VirtualMM?&lt;/h1&gt;
The VirtualMM debugging memory manager is included with EurekaLog. You can find it in the &lt;code&gt;\Extras&lt;/code&gt; subfolder of your EurekaLog installation.&lt;br /&gt;
&lt;br /&gt;
If you don&#39;t have EurekaLog, you can use the GetIt package manager on IDEs since RAD Studio XE8.&lt;br /&gt;
&lt;br /&gt;
If you don&#39;t have EurekaLog and you are using RAD Stduio XE7 or earlier - down to Delphi 6 (in other words, you don&#39;t have the GetIt package manager), you can &lt;a href=&quot;https://www.eurekalog.com/downloads_delphi.php&quot; title=&quot;EurekaLog Downloads&quot;&gt;download VirtualMM separately from the EurekaLog website&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;SafeMM&quot;&gt;&lt;/a&gt;&lt;blockquote&gt;It should be noted that the concepts of the VirtualMM debugging memory manager were implemented in another debugging memory manager: &lt;b&gt;SafeMM&lt;/b&gt;, presented by Mark Eddington at the DelphiLive conference. SafeMM was presented as part of the DelphiLive conference materials, so it was not possible to download it directly. The conference materials are no longer available, but its source code has been &lt;a href=&quot;http://cc.embarcadero.com/item/27241&quot; title=&quot;ID: 27241, SafeMM Debug Memory Manager for Delphi/C++&quot;&gt;posted on Code Central&lt;/a&gt;. Later, the source code &lt;a href=&quot;http://cc.embarcadero.com/item/28681&quot; title=&quot;ID: 28681, SafeMM for Delphi XE2&quot;&gt;was adapted&lt;/a&gt; to newer Delphi versions (at the time). However, SafeMM was &lt;a href=&quot;https://web.archive.org/web/20091022093257/http://blogs.embarcadero.com/medington/2009/10/16/24839&quot; title=&quot;SafeMM Debug Memory Manager available on CodeCentral&quot;&gt;written as a &quot;proof of concept&quot;&lt;/a&gt; and was not supported or developed. Therefore, if you are looking for where to download SafeMM, you can &lt;a href=&quot;https://www.eurekalog.com/downloads_delphi.php&quot; title=&quot;EurekaLog Downloads&quot;&gt;download VirtualMM&lt;/a&gt; instead. VirtualMM has more features.&lt;/blockquote&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h17&quot;&gt;&lt;/a&gt;&lt;h1&gt;How to install VirtualMM?&lt;/h1&gt;
VirtualMM does not have a dedicated installer and is distributed:&lt;br /&gt;
&lt;ol&gt;
  &lt;li&gt;[For EurekaLog 7.14 and above] with EurekaLog. In this case: &quot;installing VirtualMM&quot; consists of &quot;&lt;a href=&quot;https://www.eurekalog.com/downloads_delphi.php&quot; title=&quot;EurekaLog Downloads&quot;&gt;installing EurekaLog&lt;/a&gt;&quot;. After installation: VirtualMM can be found in the &lt;code&gt;\Extras&lt;/code&gt; subfolder of the EurekaLog installation folder. This folder will already be included in the search paths of installed IDEs; nothing else is required;&lt;/li&gt;
  &lt;li&gt;[For XE8 and above] via the GetIt package manager:&lt;br /&gt;
    &lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCFA_vbH9gOMNq3rMzGuK8riS-DOu9OfT_cEE8D-A6x_rRTEciNF4ij-FOD1i_poCAIIRHb4dggs04HmUgdBEc5yXvYCoRidQwuoQN2lkvTZBTgHO0HMAUvfueGI4SzP10_fwAw-VLMPdwRINABDuBjHPvI15GiGKx_ECKgKMbFck8gFxz_0tThkjFAW4/s1142/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;823&quot; data-original-width=&quot;1142&quot; height=&quot;289&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCFA_vbH9gOMNq3rMzGuK8riS-DOu9OfT_cEE8D-A6x_rRTEciNF4ij-FOD1i_poCAIIRHb4dggs04HmUgdBEc5yXvYCoRidQwuoQN2lkvTZBTgHO0HMAUvfueGI4SzP10_fwAw-VLMPdwRINABDuBjHPvI15GiGKx_ECKgKMbFck8gFxz_0tThkjFAW4/w400-h289/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Click on image to enlarge&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/li&gt;
  &lt;li&gt;[Universal, Delphi 6 and above] as a &lt;a href=&quot;https://www.eurekalog.com/downloads_delphi.php&quot; title=&quot;EurekaLog Downloads&quot;&gt;ZIP archive with Delphi source code files&lt;/a&gt;. To install VirtualMM in this format: simply unzip the archive to any folder and specify that folder in the search path for the project in which you want to use VirtualMM:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk_ZQEkGNbshuRV_r8uE-lshTpB50ph_9GaHS5HuR_oaxR-1onIuNenD92GRTsP0FRLh01B7uk26mFTjDAQgbtA2ZYdgl19RnPlRS0BzAEkiNfqprC2Cl4tRhpqRtXs10SZ-CTPmuL21nYZBf4kC5F7-pi_DICHwU6hiyc2V8orNPK0Bq1NQA6c3afwmWT/s1464/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;981&quot; data-original-width=&quot;1464&quot; height=&quot;429&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk_ZQEkGNbshuRV_r8uE-lshTpB50ph_9GaHS5HuR_oaxR-1onIuNenD92GRTsP0FRLh01B7uk26mFTjDAQgbtA2ZYdgl19RnPlRS0BzAEkiNfqprC2Cl4tRhpqRtXs10SZ-CTPmuL21nYZBf4kC5F7-pi_DICHwU6hiyc2V8orNPK0Bq1NQA6c3afwmWT/w640-h429/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Click the image to enlarge&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h18&quot;&gt;&lt;/a&gt;&lt;h1&gt;How do I add (connect) VirtualMM to a project?&lt;/h1&gt;
Simply add &lt;code&gt;VirtualMM&lt;/code&gt; as the &lt;b&gt;first&lt;/b&gt; module in your project&#39;s .dpr file, for example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;program Project1;

  uses
    VirtualMM, // - added
    Vcl.Forms,
    Unit1 in &#39;Unit1.pas&#39; {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.&lt;/pre&gt;
It&#39;s very important to specify &lt;code&gt;VirtualMM&lt;/code&gt; first in the &lt;code&gt;uses&lt;/code&gt; clause. If you don&#39;t specify it first, some other code will be initialized first, which will likely allocate memory through the built-in memory manager. Therefore, when VirtualMM gets to initialization, it won&#39;t be able to set itself as the memory manager, since memory was already allocated through the built-in memory manager.&lt;br /&gt;
&lt;br /&gt;
If you get an error like this when compiling your project: &lt;br /&gt;
&lt;code&gt;[dcc32 Fatal Error] Project1.dpr(4): F2613 Unit &#39;VirtualMM&#39; not found&lt;/code&gt;&lt;br /&gt;
it means you haven&#39;t added the VirtualMM source code folder to your project&#39;s (or IDE&#39;s) search path. See the &quot;How do I install VirtualMM?&quot; section above.&lt;br /&gt;
&lt;br /&gt;
The following warning will be displayed in IDE&#39;s &quot;Messages&quot; output when a project is built:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0E4F9yQV1R38ohvK1SLsit8N4b8C0r3cweYQR7C4hdblCaORyjbVx4YeMzxv-cxhuw6jP9xliJl8NA4s_yJm4XgDitSCrrwukhkGJPlWj4Vah9UfDnQi3PW-J90zRnZT01T5G7hadoWfXAgAI7OxGZPJx2qFyMe7ni2dcRPQl3hQx5vS_PGYDo0J7nUio/s575/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;175&quot; data-original-width=&quot;575&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0E4F9yQV1R38ohvK1SLsit8N4b8C0r3cweYQR7C4hdblCaORyjbVx4YeMzxv-cxhuw6jP9xliJl8NA4s_yJm4XgDitSCrrwukhkGJPlWj4Vah9UfDnQi3PW-J90zRnZT01T5G7hadoWfXAgAI7OxGZPJx2qFyMe7ni2dcRPQl3hQx5vS_PGYDo0J7nUio/s16000/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;code&gt;[DCC Warning] VirtualMMOptions.inc(51): W1054 WARNING: VirtualMM is ON, do not use this build on production&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
This is an additional warning reminder that you built the program with VirtualMM. This reminder is needed so you don&#39;t accidentally submit this version of the program to production. In other words, it is &quot;as designed&quot;. This is how it should be.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h19&quot;&gt;&lt;/a&gt;&lt;h1&gt;How to configure VirtualMM?&lt;/h1&gt;
VirtualMM consists of three files:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;VirtualMM.pas&lt;/code&gt; - the main source code. You include this unit in the project (see above). This file does not need to be edited.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VirtualMMDefs.inc&lt;/code&gt; - contains conditional symbols required for proper compilation in all supported IDEs (from Delphi 6 to the latest available IDE, which at the time of writing is RAD Studio 13 Florence). This file does not need to be edited.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VirtualMMOptions.inc&lt;/code&gt; - contains user options that allow you to change the behavior of the memory manager. This is the file you need to edit to configure VirtualMM.&lt;/li&gt;
&lt;/ol&gt;
The option is &lt;b&gt;enabled&lt;/b&gt; if you uncomment its line, for example:
&lt;pre class=&quot;brush:delphi&quot;&gt;{$DEFINE USE_SMALL_BLOCKS}&lt;/pre&gt;
The option is &lt;b&gt;disabled&lt;/b&gt; if you comment its line, for example:
&lt;pre class=&quot;brush:delphi&quot;&gt;// {$DEFINE USE_SMALL_BLOCKS}&lt;/pre&gt;
&lt;br /&gt;
Specifically, VirtualMM supports the following options:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;USE_SMALL_BLOCKS&lt;/code&gt; (enabled by default): Enables support for &quot;small blocks&quot;, as discussed above. Enabling this option allows you to conserve memory, which is necessary for running 32-bit applications. 64-bit applications have a huge address space, so running out of blocks is less of a problem. It&#39;s worth noting that &quot;small blocks&quot; are a compromise, a workaround. Not all types of checks can be implemented with &quot;small blocks&quot;, as discussed above. Usage recommendations: enable this option for 32-bit applications, disable it for 64-bit applications. Enable it if your 64-bit application starts crashing due to out-of-memory errors.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROTECT_OVERFLOW&lt;/code&gt; (enabled by default): instructs VirtualMM to protect against buffer overflows. In this case, all allocated memory will be aligned so that unavailable memory begins after the &lt;b&gt;end&lt;/b&gt; of the allocated block. Thus, writing beyond the end of the allocated buffer will immediately throw an Access Violation exception. Disable this option only for special cases (see below). Only one of the &lt;code&gt;PROTECT_*&lt;/code&gt; options can be enabled at a time.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROTECT_UNDERFLOW&lt;/code&gt;: Instructs VirtualMM to protect against buffer underflows. In this case, all allocated memory will be aligned so that unavailable memory begins immediately before the &lt;b&gt;beginning&lt;/b&gt; of the allocated block. Therefore, writing beyond the allocated buffer will immediately throw an Access Violation exception. Enable this option only to detect problems with writing before the block. Such problems are quite rare; usually, the opposite occurs. Only one of the &lt;code&gt;PROTECT_*&lt;/code&gt; options can be enabled at a time.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROTECT_RANDOM&lt;/code&gt;: Instructs VirtualMM to randomly enable &lt;code&gt;PROTECT_OVERFLOW&lt;/code&gt; or &lt;code&gt;PROTECT_UNDERFLOW&lt;/code&gt; (on each memory allocation). This is rarely needed — only if you have multiple buffer overflow/underflow issues (on both sides of memory blocks). Probably the best solution is to look for problems one at a time: first &lt;code&gt;PROTECT_UNDERFLOW&lt;/code&gt;, then &lt;code&gt;PROTECT_OVERFLOW&lt;/code&gt; (or vice versa). Only one of the &lt;code&gt;PROTECT_*&lt;/code&gt; options can be enabled at a time.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ALLOCATE_TOP_DOWN&lt;/code&gt; (enabled by default): Tells VirtualMM to allocate memory from top to bottom. Enabling this option slows down performance slightly, but allows you to catch &lt;code&gt;Integer&lt;/code&gt;/&lt;code&gt;Pointer&lt;/code&gt; conversion bugs faster. Typically, you should leave this option enabled. Note: a 32-bit application must be marked as high-address aware (the &lt;code&gt;IMAGE_FILE_LARGE_ADDRESS_AWARE&lt;/code&gt; flag must be set) for this option to be useful.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CATCH_USE_AFTER_FREE&lt;/code&gt;: This is a special option that allows you to find the bug of accessing freed memory in an environment where memory is frequently reused. We will discuss it in more detail below. Enabling this option tells VirtualMM to &lt;b&gt;never to free memory&lt;/b&gt; &lt;a href=&quot;#a1&quot;&gt;(*)&lt;/a&gt;. As you can imagine, enabling this option will lead to catastrophic memory usage growth, especially if you frequently &quot;allocate&quot; and &quot;free&quot; memory. For this reason, this option is useless in 32-bit applications: a 32-bit application will crash with an &quot;out of memory&quot; error before it even gets to the problem. Enable this option only in 64-bit applications and &lt;b&gt;only to detect issues accessing freed memory that are otherwise undetectable&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NeverUninstall&lt;/code&gt;: Tells VirtualMM not to uninstall itself when the application exits. This option is only needed for compatibility with some older IDEs that have a bug: attempting to free memory after the application has &quot;terminated.&quot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VirtualMMAlign&lt;/code&gt; (16 by default): Specifies the alignment of all allocated memory. In other words, the allocation granularity. This &lt;a href=&quot;https://docwiki.embarcadero.com/RADStudio//en/Memory_Management&quot; title=&quot;Memory Management&quot;&gt;value must be a multiple of 8 bytes for 32-bit applications and 16 bytes for 64-bit applications&lt;/a&gt;: i.e. 8 (32-bit only), 16, 24 (32-bit only), 32, 40 (32-bit only), 48, etc. Larger values will result in increased memory usage, since some memory will be wasted filling the gaps between blocks. Furthermore, large values will also significantly worsen buffer overflow detection (the &lt;code&gt;PROTECT_OVERFLOW&lt;/code&gt; option) - see discussion below. We recommend setting this to the minimum value your application can handle. This option also has two special values: 1 to disable alignment completely (formally prohibited in Delphi, as this may crash the application) and 0 to use dynamic alignment obtained from the &lt;a href=&quot;https://docwiki.embarcadero.com/Libraries/en/System.GetMinimumBlockAlignment&quot; title=&quot;System.GetMinimumBlockAlignment&quot;&gt;&lt;code&gt;System.GetMinimumBlockAlignment&lt;/code&gt; function&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h20&quot;&gt;&lt;/a&gt;&lt;h1&gt;What problems does VirtualMM solve?&lt;/h1&gt;
Let&#39;s look at how VirtualMM can help us diagnose the memory bugs we discussed above.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h21&quot;&gt;&lt;/a&gt;&lt;h2&gt;Referencing already freed memory&lt;/h2&gt;
We had the following examples:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  P: Pointer;
begin
  P := VirtualAlloc({...}); // &quot;allocate&quot; memory for P
  P^ := {...};              // do something (work) with P
  VirtualFree(P);           // finished working, &quot;free&quot; memory

  // BUG: accessing already freed memory
  P^ := {...}; // this line will ALWAYS throw an Access Violation exception with VirtualMM
end;&lt;/pre&gt;
and&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  L: TList;
begin
  L := TList.Create; // &quot;create&quot; the object
  L.Free;            // &quot;free&quot; the object

  I := L.Count;      // this line will ALWAYS raise an Access Violation exception with VirtualMM&lt;/pre&gt;
Since VirtualMM actually frees memory when it &quot;frees&quot; it, the memory becomes inaccessible after it&#39;s &quot;deleted&quot;, so any attempt to access such memory will result in an Access Violation exception — whether it&#39;s a write or (more interestingly) a read. Note that VirtualMM will allow you to detect memory bugs immediately, in situ, rather than much later, as would happen with a regular debugging memory manager. Furthermore, VirtualMM will also catch attempts to &lt;b&gt;read&lt;/b&gt; from already deleted memory, whereas typical debugging memory managers usually can&#39;t help with this bug.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h22&quot;&gt;&lt;/a&gt;&lt;h2&gt;Buffer overflow&lt;/h2&gt;
We had this example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  Buffer: PInteger;
begin
  GetMem(Buffer, Count * SizeOf(Integer)); // &quot;allocate&quot; memory for Count Integers
  for X := 0 to Count do                   // BUG: should be Count - 1
  begin
    Buffer^ := 0;                          // the last step will raise an Access Violation exception with VirtualMM in PROTECT_OVERFLOW mode
    Inc(Buffer);
  end;&lt;/pre&gt;
&lt;br /&gt;
&lt;a id=&quot;h23&quot;&gt;&lt;/a&gt;&lt;h3&gt;If the &lt;code&gt;USE_SMALL_BLOCKS&lt;/code&gt; option is &lt;b&gt;disabled&lt;/b&gt;&lt;/h3&gt;
If the &lt;code&gt;PROTECT_&lt;b&gt;OVERFLOW&lt;/b&gt;&lt;/code&gt; option is enabled in VirtualMM (or this option was selected with the &lt;code&gt;PROTECT_RANDOM&lt;/code&gt; option), VirtualMM will allocate one inaccessible memory &lt;b&gt;page&lt;/b&gt; immediately &lt;b&gt;after&lt;/b&gt; the memory block, so an attempt to read or write beyond the block will throw an Access Violation exception.&lt;br /&gt;
&lt;br /&gt;
Of course, if the &lt;code&gt;PROTECT_&lt;b&gt;UNDERFLOW&lt;/b&gt;&lt;/code&gt; option was enabled (or this option was selected with the &lt;code&gt;PROTECT_RANDOM&lt;/code&gt; option), VirtualMM will allocate one inaccessible page immediately &lt;b&gt;before&lt;/b&gt; the memory block. Since the memory page granularity is 4 KB, the end of the allocated block will most likely not fall exactly on a page boundary. It means the code will execute successfully in this case, without raising an Access Violation exception. The exception will only occur if you continue &quot;stepping&quot; forward and reach the end of the current page.&lt;br /&gt;
&lt;br /&gt;
There&#39;s one subtlety here. Delphi has a convention that any memory manager, whether standard or custom, must return memory aligned to at least 8 bytes. More is possible (for example, 32), but not less than 8. For example, Delphi 7 aligns memory to 8 bytes, while RAD Studio 13 Florence aligns it to 16 bytes. VirtualMM, however, aligns memory to whatever you specify. It could be 8, 16, or 32. But the default is 16 (see the &lt;code&gt;VirtualMMAlign&lt;/code&gt; option above).&lt;br /&gt;
&lt;br /&gt;
Why are we saying this? Obviously, if you align the start of a block to a certain boundary, the end of that block will be &quot;random&quot;. For example, if you align to 8 bytes and allocate a 1-byte block, you could allocate it at, say, 16k - 8, but then the memory block would span addresses from 16k - 8 to 16k - 7 (exactly 1 byte) – which is 7 bytes less than the nearest page boundary (16k). It means these seven bytes of &quot;padding&quot; will have the same hardware protection attributes as the allocated memory block, i.e., they will be readable and writable. In other words, a buffer overflow of up to 7 bytes cannot be immediately detected. However, a buffer overflow of 8 or more bytes will affect the next memory page (without access), which will trigger an Access Violation exception.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;For this reason, it is important to use the smallest possible memory allocation granularity&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
However, VirtualMM always places guard values before and after a memory block (if there is the aforementioned empty space), so although an attempt to read immediately after a block will always succeed, an attempt to write after a block will be detected when the memory block is freed. Thus, VirtualMM will behave like a regular debugging memory manager. In this case, an &lt;code&gt;EAssertionFailed&lt;/code&gt; exception will be raised with the message &lt;code&gt;ReleaseLargeBlock: Block Overflow&lt;/code&gt; (or &quot;Block Underflow&quot;). In general, any assert exceptions from VirtualMM indicate a memory bug: they indicate that someone has violated (overwritten) some protective or control structures (headers) of memory blocks, i.e., it is a clear bug of writing to an invalid address. Just in case, we&#39;ll clarify again that we&#39;re only talking about accesses within the memory block alignment padding. Access attempts outside the alignment padding will be detected immediately (by hardware).&lt;br /&gt;
&lt;br /&gt;
If you can&#39;t find a buffer overflow problem, you can try using formally unsupported align values, such as 1 (i.e., no alignment at all), and hope that your application can run in this mode. In this case, there will be no alignment padding, and therefore, hardware protection will operate immediately at the memory block boundary.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h24&quot;&gt;&lt;/a&gt;&lt;h3&gt;If the &lt;code&gt;USE_SMALL_BLOCKS&lt;/code&gt; option is &lt;b&gt;enabled&lt;/b&gt;&lt;/h3&gt;
If the &lt;code&gt;PROTECT_&lt;b&gt;OVERFLOW&lt;/b&gt;&lt;/code&gt; option is enabled in VirtualMM (or this option was selected with the &lt;code&gt;PROTECT_RANDOM&lt;/code&gt; option) and the block size is &lt;b&gt;less&lt;/b&gt; than the page size (4 KB), then VirtualMM will place the guard &lt;b&gt;value&lt;/b&gt; immediately &lt;b&gt;after&lt;/b&gt; the memory block, so that although an attempt to read beyond the block will always succeed, an attempt to write beyond the block will be detected when the memory block is &quot;freed&quot;. Thus, VirtualMM will behave like a regular debugging memory manager. In this case, an &lt;code&gt;EAssertionFailed&lt;/code&gt; exception will be raised with the message &lt;code&gt;ReleaseSmallBlock: Block Overflow&lt;/code&gt; (or &quot;Block Underflow&quot;).&lt;br /&gt;
&lt;br /&gt;
If the block size is &lt;b&gt;larger&lt;/b&gt; than the page size (4 KB), VirtualMM will behave as in the case &quot;If the &lt;code&gt;USE_SMALL_BLOCKS&lt;/code&gt; option is &lt;b&gt;disabled&lt;/b&gt;&quot; above, i.e., it will allocate guard pages and detect a read/write beyond the block immediately (with the above-mentioned adjustment for the memory block alignment padding).&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h25&quot;&gt;&lt;/a&gt;&lt;h2&gt;Calling a &lt;b&gt;static&lt;/b&gt; method of a deleted object&lt;/h2&gt;
Since calling a &lt;b&gt;static&lt;/b&gt; method does not depend on the object&#39;s data, VirtualMM cannot help with this case. However, since the called static method most likely does something with the object (otherwise it would be a function, not a method), the first attempt to read or write to the object will throw an Access Violation exception. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  L: TList;
begin
  L := TList.Create; // &quot;create&quot; the object
  L.Free;            // &quot;free&quot; the object

  I := L.IndexOf(P); // will always raise an Access Violation within a method with VirtualMM&lt;/pre&gt;
Therefore, VirtualMM &lt;b&gt;will help detect the bug as close to its origin as possible&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h26&quot;&gt;&lt;/a&gt;&lt;h2&gt;Calling a &lt;b&gt;dynamic&lt;/b&gt; method of a deleted object&lt;/h2&gt;
Calling &lt;b&gt;virtual&lt;/b&gt; methods is simpler: the object&#39;s memory will not be accessible after the object is &quot;deleted&quot;, so calling a virtual method will always raise an Access Violation when attempting to read the method&#39;s address from the object. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  S: TStringList;
begin
  S := TStringList.Create; // &quot;create&quot; the object
  // Working with S
  S.Free;                  // &quot;free&quot; the object

  I := S.Count;            // will always raise an Access Violation when calling a virtual method with VirtualMM&lt;/pre&gt;
&lt;br /&gt;
&lt;a id=&quot;h27&quot;&gt;&lt;/a&gt;&lt;h2&gt;Memory reuse&lt;/h2&gt;
We had this code:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  S1, S2: TStringList;
begin
  S1 := TStringList.Create; // &quot;create&quot; the object
  // Working with S1
  S1.Free;                  // &quot;free&quot; the object

  S2 := TStringList.Create; // &quot;create&quot; the object

  I := S1.Count;            // logically - a bug, but will always succeed since S1 = S2&lt;/pre&gt;
Can VirtualMM do anything in this case? Yes, it can. But only in 64-bit applications.&lt;br /&gt;
&lt;br /&gt;
To do this, you need to enable the &lt;code&gt;CATCH_USE_AFTER_FREE&lt;/code&gt; option. Enabling this option will prevent VirtualMM from ever freeing memory when &quot;freeing&quot;. It means that if we immediately &quot;create&quot; a second object (&lt;code&gt;S2&lt;/code&gt; in the example above), it will never be allocated at the same address as the first object (&lt;code&gt;S1&lt;/code&gt; in the example above), since the memory for the first object will be forever occupied. In other words, &lt;code&gt;S1&lt;/code&gt; is no longer equal to &lt;code&gt;S2&lt;/code&gt;, and therefore, calling &lt;code&gt;S1.Count&lt;/code&gt; will throw an Access Violation exception, since the memory of &lt;code&gt;S1&lt;/code&gt; will be inaccessible.&lt;br /&gt;
&lt;br /&gt;
As you can imagine, never freeing memory is a very aggressive strategy that can only work if you have a HUGE amount of free memory. In particular, enabling the &lt;code&gt;CATCH_USE_AFTER_FREE&lt;/code&gt; option in 32-bit applications is pointless, since a 32-bit application will crash almost immediately with an out-of-memory error, before reaching the code that contains the bug. But even in 64-bit applications, it makes sense to try to ensure that the memory bug you&#39;re trying to catch occurs as early as possible.&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  S1, S2: TStringList;
begin
  S1 := TStringList.Create; // &quot;create&quot; the object
  // Working with S1
  S1.Free;                  // &quot;free&quot; the object

  S2 := TStringList.Create; // &quot;create&quot; the object

  I := S1.Count;            // will always raise an Access Violation when calling a virtual method with VirtualMM and the CATCH_USE_AFTER_FREE option is enabled.&lt;/pre&gt;
Recommendation for this option: keep it disabled. If you can find a bug without enabling this option - do so. Enable this option only in 64-bit applications and only if you can&#39;t find the bug otherwise.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h28&quot;&gt;&lt;/a&gt;&lt;h1&gt;When should you use VirtualMM?&lt;/h1&gt;
Since VirtualMM is a rather special debugging memory manager, it should only be used in extreme cases where you can&#39;t find a memory bug otherwise. Most often, these will be situations where you mistakenly &lt;b&gt;read&lt;/b&gt; from &quot;freed&quot; memory.&lt;br /&gt;
&lt;br /&gt;
Less often, but also frequently, these will be situations where you write to &quot;freed&quot; memory. The main reason for choosing VirtualMM in these cases would be that debugging memory managers detect the problem too late (after it occurs), while VirtualMM can do this immediately.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h29&quot;&gt;&lt;/a&gt;&lt;h1&gt;When &lt;b&gt;NOT&lt;/b&gt; to use VirtualMM?&lt;/h1&gt;
Since VirtualMM is a rather special debugging memory manager, &lt;b&gt;it should never be used in a release (production)&lt;/b&gt; for the following reasons:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Slow operation due to the need to make a kernel call for each memory &quot;allocation&quot; and &quot;freeing&quot;;&lt;/li&gt;
&lt;li&gt;High memory consumption:
&lt;ul&gt;
&lt;li&gt;Rounding up to the page size (4 KB);&lt;/li&gt;
&lt;li&gt;Allocation of guard pages (buffer overflow/underflow protection);&lt;/li&gt;
&lt;li&gt;Memory only grows, but does not shrink, when the &lt;code&gt;CATCH_USE_AFTER_FREE&lt;/code&gt; option is enabled.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ol&gt;
This is why, when building a project with VirtualMM, the following message will appear in the message window:&lt;br /&gt;
&lt;code&gt;[DCC Warning] VirtualMMOptions.inc(51): W1054 WARNING: VirtualMM is ON, do not use this build on production&lt;/code&gt;&lt;br /&gt;
This is done intentionally to prevent you from accidentally compiling your program for production with VirtualMM.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h30&quot;&gt;&lt;/a&gt;&lt;h1&gt;Using VirtualMM with EurekaLog&lt;/h1&gt;
Although VirtualMM is part of EurekaLog, it is not related to EurekaLog and does not share source code with it. It is a standalone product that can also be downloaded &lt;a href=&quot;https://www.eurekalog.com/downloads_delphi.php&quot; title=&quot;EurekaLog Downloads&quot;&gt;as a standalone download&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
EurekaLog includes a &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/memory_leaks_page.php&quot; title=&quot;Memory problems page&quot;&gt;memory manager filter (add-on)&lt;/a&gt; that performs several memory checks. These checks largely overlap with VirtualMM, so while they can be used together, there&#39;s little point in doing so. After all, the point of VirtualMM is to place hardware protection at memory block boundaries, and by enabling memory checks in EurekaLog, you&#39;ll be pushing those boundaries to accommodate EurekaLog debug data — which, as you might guess, will impair VirtualMM&#39;s ability to immediately report memory bugs (as soon as they occur). Since this ability is the primary reason you&#39;d want to use VirtualMM, it shouldn&#39;t be interfered with. That is: if you use VirtualMM in a project with EurekaLog, all memory checks in EurekaLog must be disabled (the &quot;Enable extended memory manager&quot; option must be disabled, and the &quot;When memory is released&quot; option must be set to &quot;Do nothing&quot;).&lt;br /&gt;
&lt;br /&gt;
On the other hand, note that VirtualMM is not a replacement for memory checks in EurekaLog:
&lt;ul&gt;
&lt;li&gt;First, EurekaLog has a memory leak detection feature, which VirtualMM lacks;&lt;/li&gt;
&lt;li&gt;Second, EurekaLog can report memory issues in a more accessible way (for example, two call stacks will be shown for memory double free errors);&lt;/li&gt;
&lt;li&gt;Third, EurekaLog is designed for reporting from user machines (release/production), while VirtualMM can only be used for development.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Note that VirtualMM never explicitly reports memory bugs as &quot;this memory bug&quot; exception: it will always be either &lt;code&gt;EAccessViolation&lt;/code&gt; or &lt;code&gt;EAssertionFailed&lt;/code&gt; - unlike EurekaLog, which always tries to report precise information, for example, &lt;code&gt;EUseAfterFree&lt;/code&gt;, &lt;code&gt;EBufferOverflowError&lt;/code&gt; or &lt;code&gt;EDoubleFreeError&lt;/code&gt;. Actually, the essence of using VirtualMM is precisely the hardware protection (i.e. the &lt;code&gt;EAccessViolation&lt;/code&gt; exception). At the same time, in some cases, &lt;code&gt;EAccessViolation&lt;/code&gt;/&lt;code&gt;EAssertionFailed&lt;/code&gt; exceptions will be raised within the code of VirtualMM itself (i.e. the memory manager). For example, when an invalid or corrupted block of memory is passed. The problem here is that the Delphi debugger is not always able to correctly build the call stack if you are inside a memory manager function. For example, if VirtualMM detects a buffer overflow when freeing a block of memory, it will throw an &lt;code&gt;EAssertionFailed&lt;/code&gt; exception with &#39;Block Overflow&#39;, but the IDE may show an incomplete or truncated call stack, like this:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;KERNELBASE.RaiseException&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@Assert&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VirtualMM.ReleaseLargeBlock&lt;/code&gt; or &lt;code&gt;VirtualMM.ReleaseSmallBlock&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VirtualMM.VirtualFreeMem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@FreeMem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;and here the call stack either ends or contains functions further up the stack, and the one that directly calls &lt;code&gt;FreeMem&lt;/code&gt; is missing.&lt;/li&gt;
&lt;/ul&gt;
Needless to say, this makes it very difficult to find the problem.&lt;br /&gt;
&lt;br /&gt;
Yes, if you&#39;re proficient in debugging, you can build the call stack manually. But not everyone can do this, and it&#39;s quite a complex operation. Or you can use EurekaLog: &lt;code&gt;EAccessViolation&lt;/code&gt;/&lt;code&gt;EAssertionFailed&lt;/code&gt; - these are regular exceptions that will be caught by EurekaLog, and if you don&#39;t suppress them with code like:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;try
  FreeMem(P);
except
  // Do nothing
end;&lt;/pre&gt;
then they will be processed by EurekaLog, which will be able to show an exception report with the full stack (unless you &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/call_stack_page.php&quot; title=&quot;Call Stack page&quot;&gt;changed the stack tracing method&lt;/a&gt; to &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/stack_tracing.php&quot; title=&quot;Stack tracing: RAW method and frame-based method&quot;&gt;any frame-based method&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
So, the recommended workflow is:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;DEBUG/Development stage:
&lt;ol&gt;
&lt;li&gt;You turn off all memory checking in EurekaLog (or disable EurekaLog altogether, although this is not recommended for the reason stated above);&lt;/li&gt;
&lt;li&gt;You add VirtualMM to the project and configure it;&lt;/li&gt;
&lt;li&gt;You search for and fix memory bugs in the application (e.g., stress/load testing).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;RELEASE/Production stage:
&lt;ol&gt;
&lt;li&gt;You remove VirtualMM from the project;&lt;/li&gt;
&lt;li&gt;You enable and configure memory checks in EurekaLog;&lt;/li&gt;
&lt;li&gt;You test the application (test exceptions and memory bugs);&lt;/li&gt;
&lt;li&gt;You deploy the application;&lt;/li&gt;
&lt;li&gt;You collect bug reports and fix any bugs found. If you find a memory bug that you can&#39;t diagnose, you&#39;re using VirtualMM again (while debugging).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h32&quot;&gt;&lt;/a&gt;&lt;h1&gt;What should I do if my app crashes with out-of-memory errors when using VirtualMM?&lt;/h1&gt;
As we&#39;ve said many times before: using VirtualMM leads to increased memory usage. If your app throws an &lt;code&gt;EOutOfMemory&lt;/code&gt; exception, how can you fix it?&lt;br /&gt;
&lt;br /&gt;
Follow these steps from top to bottom until the error disappears:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Modify the app to 64-bit. This is the most reliable method;&lt;/li&gt;
&lt;li&gt;Rearrange the code so that the memory bug occurs as early as possible. Remove all non-essential code;&lt;/li&gt;
&lt;li&gt;Disable &lt;code&gt;CATCH_USE_AFTER_FREE&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;USE_SMALL_BLOCKS&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Revise your application logic. Allocate fewer memory blocks. For example, use a dynamic array of blocks instead of allocating single small blocks.&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a id=&quot;h31&quot;&gt;&lt;/a&gt;&lt;h1&gt;Notes&lt;/h1&gt;
&lt;a id=&quot;a1&quot;&gt;&lt;/a&gt;(*) When we talk about memory, we greatly simplify things. By &quot;memory&quot; we mean the application&#39;s address space. For simplicity, we don&#39;t distinguish between reserved (RESERVE) and committed (COMMIT) memory. For example, the words &quot;allocated memory&quot; can mean either &quot;reserved memory&quot; or &quot;committed memory&quot; - depending on the context.&lt;br /&gt;
&lt;a id=&quot;a2&quot;&gt;&lt;/a&gt;(**) 64 KB is the default value on many systems. However, this value may be different for your specific system. It is obtained from the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info&quot; title=&quot;SYSTEM_INFO structure (sysinfoapi.h)&quot;&gt;&lt;code&gt;dwAllocationGranularity&lt;/code&gt; field of the &lt;code&gt;SYSTEM_INFO&lt;/code&gt;&lt;/a&gt; structure. Throughout the text, we talk about 64 KB, but you should understand that it is not a constant.&lt;br /&gt;
&lt;a id=&quot;a3&quot;&gt;&lt;/a&gt;(***) Since VirtualMM is also a debugging memory manager, from here on, by &quot;debugging memory managers&quot; we will only mean &quot;typical&quot;/&quot;regular&quot;/&quot;all other&quot; memory managers, not including VirtualMM. To avoid having to write the exception &quot;typical debugging memory managers other than VirtualMM&quot; every time, we&#39;ll simply write &quot;debugging memory managers&quot; when referring to all other managers excluding VirtualMM.&lt;br /&gt;
&lt;a id=&quot;a4&quot;&gt;&lt;/a&gt;(****) 4 KB is the default value on many systems. However, this value may be different for your specific system. It is obtained from the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info&quot; title=&quot;SYSTEM_INFO structure (sysinfoapi.h)&quot;&gt;dwPageSize&lt;/a&gt; field of the &lt;code&gt;SYSTEM_INFO&lt;/code&gt; structure. Everywhere in the text it says 4 KB, but you should understand that it is not a constant.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/3714843665136326343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/3714843665136326343'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/10/introducing-VirtualMM-debugging-memory-manager.html' title='Introducing VirtualMM debugging memory manager'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCFA_vbH9gOMNq3rMzGuK8riS-DOu9OfT_cEE8D-A6x_rRTEciNF4ij-FOD1i_poCAIIRHb4dggs04HmUgdBEc5yXvYCoRidQwuoQN2lkvTZBTgHO0HMAUvfueGI4SzP10_fwAw-VLMPdwRINABDuBjHPvI15GiGKx_ECKgKMbFck8gFxz_0tThkjFAW4/s72-w400-h289-c/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-1048882973997488657</id><published>2025-09-15T20:19:00.003+01:00</published><updated>2025-09-15T20:19:53.011+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="Releases"/><title type='text'>EurekaLog 7.14 is out</title><content type='html'>We are pleased to announce the availability of the new 7.14 version.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;EurekaLog Logo&quot;&gt;&lt;img alt=&quot;EurekaLog Logo&quot; src=&quot;https://www.eurekalog.com/images/logo.png&quot; height=&quot;68&quot; width=&quot;295&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
EurekaLog is a tool for Delphi and C++Builder that gives your application the power to catch every exception and memory/resource leak, generating a detailed log of the call stack (with unit, procedure and line number), optionally sending you a copy of each log entry via email or the Web via the most used Web Bug-Tracking tools (as Mantis, BugZilla, FogBugz, JIRA, YouTrack, Redmine, Exceptionless, GitLab, and GitHub).&lt;/blockquote&gt;
EurekaLog represents the most advanced exception and memory leaks logger technology available for Delphi/C++Builder developers.&lt;br /&gt;
&lt;br /&gt;
To learn more about EurekaLog, please visit our website at:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com&quot;&gt;https://www.eurekalog.com&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
To download a demo, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/downloads.php&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/downloads.php&quot;&gt;https://www.eurekalog.com/downloads.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you would like to purchase a new license, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/buy.php&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Changes in 7.14 build:&lt;/strong&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Added: Added RAD Studio 13 Florence support&lt;/li&gt;
&lt;li&gt;Added: Workaround to report exceptions with invalid CPU context&lt;/li&gt;
&lt;li&gt;Added: The new &quot;VirtualMM&quot; debugging memory manager (replacement for SafeMM), see \Source\Extras\&lt;/li&gt;
&lt;li&gt;Fixed: Bugzilla sender fail to post new bug&lt;/li&gt;
&lt;li&gt;Fixed: Few multithreading issues&lt;/li&gt;
&lt;li&gt;Fixed: Minor call stack improvements&lt;/li&gt;
&lt;li&gt;Fixed: Minor performance improvements&lt;/li&gt;
&lt;/ol&gt;
&lt;a href=&quot;https://www.eurekalog.com/showchangelog.php&quot; title=&quot;EurekaLog: Full Change Log&quot;&gt;See full change log here&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/1048882973997488657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/1048882973997488657'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/09/EurekaLog-7-14.html' title='EurekaLog 7.14 is out'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-4807596157169364045</id><published>2025-08-28T10:58:00.000+01:00</published><updated>2025-08-28T10:58:17.234+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="how it works"/><category scheme="http://www.blogger.com/atom/ns#" term="technical"/><title type='text'>EurekaLog and Windows Service Applications</title><content type='html'>We were contacted by a customer who wondered how EurekaLog works in service applications, considering it has options like freeze detection, while service apps don&#39;t have main thread.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
A service application has main thread like any other application. Main thread is a thread which executes app&#39;s entry point (&lt;code&gt;begin&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt; in your .dpr file). Its ID is stored in the RTL&#39;s &lt;code&gt;MainThreadID&lt;/code&gt; global variable.&lt;br /&gt;
&lt;br /&gt;
Now, if we are talking about &lt;b&gt;VCL&lt;/b&gt;&#39;s service apps - the main thread is not doing any heavy lifting in these apps. It runs the &lt;code&gt;TServiceApplication.Run&lt;/code&gt;, which basically waits for service to exit.&lt;br /&gt;
&lt;br /&gt;
There are two ways to implement a service:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Either a loop inside the &lt;a href=&quot;https://docwiki.embarcadero.com/Libraries/en/Vcl.SvcMgr.TService.OnExecute&quot; title=&quot;Vcl.SvcMgr.TService.OnExecute&quot;&gt;&lt;code&gt;ServiceExecute&lt;/code&gt; event&lt;/a&gt; (with calling the &lt;a href=&quot;https://docwiki.embarcadero.com/Libraries/en/Vcl.SvcMgr.TServiceThread.ProcessRequests&quot; title=&quot;Vcl.SvcMgr.TServiceThread.ProcessRequests&quot;&gt;&lt;code&gt;TServiceThread.ProcessRequests&lt;/code&gt; method&lt;/a&gt; in the loop);&lt;/li&gt;
&lt;li&gt;Or spawn worker background threads inside the &lt;a href=&quot;https://docwiki.embarcadero.com/Libraries/en/Vcl.SvcMgr.TService.OnStart&quot; title=&quot;Vcl.SvcMgr.TService.OnStart&quot;&gt;&lt;code&gt;ServiceStart&lt;/code&gt; event&lt;/a&gt; and stop them in the &lt;a href=&quot;https://docwiki.embarcadero.com/Libraries/en/Vcl.SvcMgr.TService.OnStop&quot; title=&quot;Vcl.SvcMgr.TService.OnStop&quot;&gt;&lt;code&gt;ServiceStop&lt;/code&gt; event&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;  
&lt;br /&gt;
However, no matter which design you choose - all events (&lt;code&gt;ServiceExecute&lt;/code&gt;, &lt;code&gt;ServiceStart&lt;/code&gt;, etc.) are already called from a background thread. Main thread does not call any events.&lt;br /&gt; 
&lt;br /&gt; 
The picture is like this (it is not a call stack, it is more like sequence of calls):&lt;br /&gt; 
&lt;b&gt;[Main thread]&lt;/b&gt; -&gt; &lt;code&gt;Application.Run&lt;/code&gt; -&gt; &lt;b&gt;[&lt;code&gt;TServiceStartThread&lt;/code&gt;]&lt;/b&gt; -&gt; &lt;code&gt;StartServiceCtrlDispatcher&lt;/code&gt; -&gt; &lt;code&gt;Application.DispatchServiceMain&lt;/code&gt; -&gt; &lt;code&gt;TService.Main&lt;/code&gt; -&gt; &lt;code&gt;RegisterServiceCtrlHandler&lt;/code&gt; -&gt; &lt;code&gt;TService.DoStart&lt;/code&gt; -&gt; &lt;b&gt;[&lt;code&gt;TServiceThread&lt;/code&gt;]&lt;/b&gt; -&gt; &lt;code&gt;TServiceThread.OnStart&lt;/code&gt; -&gt; &lt;code&gt;.OnExecute&lt;/code&gt; -&gt; &lt;code&gt;.OnStop&lt;/code&gt;&lt;br /&gt;
Things in [] indicate a new thread was spawned. As you can see, its [Main thread] -&gt; [&lt;code&gt;TServiceStartThread&lt;/code&gt;] -&gt; [&lt;code&gt;TServiceThread&lt;/code&gt;] -&gt; your code. You have at least two background threads.&lt;br /&gt; 
&lt;br /&gt; 
From &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-startservicectrldispatcherw&quot; title=&quot;StartServiceCtrlDispatcher function (winsvc.h)&quot;&gt;MSDN&lt;/a&gt;:&lt;br /&gt; 
&lt;blockquote&gt;&lt;i&gt;When the service control manager starts a service process, it waits for the process to call the &lt;code&gt;StartServiceCtrlDispatcher&lt;/code&gt; function. The &quot;main thread&quot; of a service process should make this call as soon as possible after it starts up (within 30 seconds). If &lt;code&gt;StartServiceCtrlDispatcher&lt;/code&gt; succeeds, it connects the calling thread to the service control manager and does not return until all running services in the process have entered the &lt;code&gt;SERVICE_STOPPED&lt;/code&gt; state. The service control manager uses this connection to send control and service start requests to the main thread of the service process. The &quot;main thread&quot; acts as a dispatcher by invoking the appropriate &lt;code&gt;HandlerEx&lt;/code&gt; function to handle control requests, or by creating a new thread to execute the appropriate &lt;code&gt;ServiceMain&lt;/code&gt; function when a new service is started.&lt;/i&gt;&lt;/blockquote&gt;
&lt;br /&gt; 
As you can see: the VCL does not follow this description toe to toe - it uses one additional intermediate background thread to call the &lt;code&gt;StartServiceCtrlDispatcher&lt;/code&gt; function. However, even if VCL would call the &lt;code&gt;StartServiceCtrlDispatcher&lt;/code&gt; function on its main thread, the &lt;code&gt;TService&lt;/code&gt; still creates a &lt;code&gt;TServiceThread&lt;/code&gt; background thread to call your events. So your code is never executed in the main thread in VCL service apps.&lt;br /&gt; 
&lt;br /&gt; 
But if your code is never executed in the main thread - it does not mean there is no main thread! There is - it is just not calling your code.&lt;br /&gt; 
&lt;br /&gt; 
Since even empty VCL service application has at least 3 threads (main thread, &lt;code&gt;TServiceStartThread&lt;/code&gt;, &lt;code&gt;TServiceThread&lt;/code&gt;): it is already a multithreaded application - even if you don&#39;t create your own (additional) threads. Usually EurekaLog has to be &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/multithreading_enable.php&quot; title=&quot;Enabling EurekaLog for background threads&quot;&gt;enabled for each background thread that you want to handle exceptions&lt;/a&gt;. If you are using default settings (like not disabling &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/external_tools_page.php&quot; title=&quot;External tools settings&quot;&gt;low-level hooks&lt;/a&gt;), the &lt;code&gt;EAppService&lt;/code&gt; unit would detect when a new &lt;code&gt;TServiceThread&lt;/code&gt; starts and enable EurekaLog for it automatically. However, it will &lt;b&gt;not&lt;/b&gt; know about your own background threads.&lt;br /&gt;
&lt;br /&gt;
Therefore:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;[Optional] If you disable the low-level hooks in EurekaLog - you have to manually activate EurekaLog for &lt;code&gt;TServiceThread&lt;/code&gt; like so:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  {$IFDEF EUREKALOG}
  SetEurekaLogStateInThread(0, True); // activate EurekaLog for this thread
  {$ENDIF}
  
  // your own code here
end;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;[Mandatory] If you spawn your own background threads (like in the &lt;code&gt;ServiceStart&lt;/code&gt; event) - you have to activate EurekaLog for these threads by &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/multithreading_enable.php&quot; title=&quot;Enabling EurekaLog for background threads&quot;&gt;any available means&lt;/a&gt;. One &lt;b&gt;possible&lt;/b&gt; example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  {$IFDEF EUREKALOG}
  SetEurekaLogStateInThread(0, True); // activate EurekaLog for the TServiceThread thread
  {$ENDIF}
  
  FWorkerThread := TMyThread.Create(False); // create a background worker thread
end;

procedure TMyThread.Execute;
begin
  {$IFDEF EUREKALOG}
  SetEurekaLogStateInThread(0, True); // activate EurekaLog for the TMyThread thread
  {$ENDIF}
  
  // your own code here
end;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;  
&lt;br /&gt;
Now, about the freeze detection feature. It works by sending a message to the main thread and checking if it gets a reply in time. It uses the main thread specifically because it is the thread which performs the message loop processing in VCL Forms apps. If app&#39;s UI is hang, then the main thread will not reply in time, so the freeze detection can say &quot;app is not responding&quot;.&lt;br /&gt; 
&lt;br /&gt; 
As you can see, this logic is completely useless in service apps, because service apps don&#39;t have windows (UI). It is even indicated in the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/hang_detection_page.php&quot; title=&quot;Hang detection&quot;&gt;option&#39;s name&lt;/a&gt;: &quot;Activate &lt;b&gt;UI&lt;/b&gt; hang detection&quot;.&lt;br /&gt; 
&lt;br /&gt; 
However, the freeze detection can also use new Windows Vista API to detect explicit deadlocks between two threads. It will work in any app, assuming it runs on Windows Vista or later. So, this feature can be useful even in service apps. Well, assuming your freeze detection timeout is less than system timeout for services. Otherwise the system would terminate the service before freeze detection would have time to react.&lt;br /&gt;
&lt;br /&gt;
Read more about &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/hangs_and_deadlocks.php&quot; title=&quot;Hangs and deadlocks&quot;&gt;hangs and deadlocks with EurekaLog&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4807596157169364045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4807596157169364045'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/08/windows-service-applications.html' title='EurekaLog and Windows Service Applications'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-8380930635538593554</id><published>2025-08-13T15:29:00.004+01:00</published><updated>2025-10-02T11:14:47.160+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Access Violation"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><category scheme="http://www.blogger.com/atom/ns#" term="Stories"/><title type='text'>How to read bug reports</title><content type='html'>We were contacted by a customer who used a previous major version of EurekaLog and upgraded to the EurekaLog 7 recently. He said his application crashes on exit. He attached a bug report file produced by EurekaLog. The bug report was about access violation exception.&lt;br /&gt;
&lt;br /&gt;
The customer did not actually ask any question. He just stated his app crashes. It was unclear if the customer is seeking help fixing a bug in his application, asking why there was no bug reported previosly, or he thinks it is some sort of &quot;false-positive&quot; bug report.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Well, any access violation exception is a definitive bug in the application, which can not be false-positive. It is the issue that needs to be fixed.&lt;br /&gt;
&lt;br /&gt;
As for why there was no bug report before: many memory-related bugs are sensitive to how memory is allocated, so some changes in code may hide or reveal a memory bug. EurekaLog 7 does have memory checks - and these are enabled by default, unlike previous EurekaLog major versions. If you did not run your application with memory checks before - your application may have memory bugs hidden. So if you add EurekaLog 7 to your application, a previosly hidden bug may be revealed. We have many examples shown in &lt;a href=&quot;https://blog.eurekalog.com/search/label/Stories&quot;&gt;stories from our customers&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Now, about troubleshooting the issue: we don&#39;t actually provide such a service. Yes, we offer support for our own product (EurekaLog), but debugging bugs in your application is outside the scope of our support. Currently we don&#39;t offer such consulting service. However, if it is not much trouble - we would gladly assist you &lt;i&gt;unofficially&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
So, we started by looking at the call stack from customer&#39;s bug report. Scrolling down the call stack - we can see his application is shutting down and cleaning up components:&lt;br /&gt;
&lt;code&gt;...&lt;br /&gt;
Classes.TComponent.DestroyComponents&lt;br /&gt;
Forms.DoneApplication&lt;br /&gt;
SysUtils.DoExitProc&lt;br /&gt;
System._Halt0&lt;br /&gt;
Unit1.Form1.btnCloseClick 3854[1]&lt;br /&gt;
...&lt;/code&gt;&lt;br /&gt;
(all names from customer&#39;s code here and below were replaced with generic names)&lt;br /&gt;
First thing to notice is that functions from RTL do not have line numbers. Typically this happens because you &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/configuring_project_itself.php&quot; title=&quot;Configuring project for EurekaLog&quot;&gt;did not configure your project for debugging&lt;/a&gt;. Specifically, you need to enable the &quot;Use Debug DCUs&quot; option. However, the customer uses Delphi 5, which does not have line number information for debug DCUs, so there is nothing he can do.&lt;br /&gt;
&lt;br /&gt;
Next, walking the stack up, we can see some &lt;code&gt;TContosoViewPanel&lt;/code&gt; class is being deleted as part of this process:&lt;br /&gt;
&lt;code&gt;...&lt;br /&gt;
Forms.TScrollingWinControl.Destroy&lt;br /&gt;
Contoso.TContosoViewPanel.Destroy 291 [16]&lt;br /&gt;
Controls.TWinControl.Destroy&lt;br /&gt;
...&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;TContosoViewPanel&lt;/code&gt; class deletes some other class/component as part of its destruction:&lt;br /&gt;
&lt;code&gt;...&lt;br /&gt;
Controls.TControl.Destroy&lt;br /&gt;
System.TObject.Free&lt;br /&gt;
Controls.TWinControl.Destroy&lt;br /&gt;
...&lt;/code&gt;&lt;br /&gt;
Unfortunately, the call stack misses line numbers, but since the &lt;code&gt;Free&lt;/code&gt; method is called from the &lt;code&gt;TWinControl.Destroy&lt;/code&gt; destructor - we are talking about deleting owned components. In other words, the &lt;code&gt;TContosoViewPanel&lt;/code&gt; class owns some component, and it deletes this component as part of its own destruction.&lt;br /&gt;
&lt;br /&gt;
When a component is deleted, it sends a notification about it being deleted:&lt;br /&gt;
&lt;code&gt;...&lt;br /&gt;
Forms.TCustomForm.Notification&lt;br /&gt;
Classes.TComponent.RemoveComponent&lt;br /&gt;
Classes.TComponent.Destroy // owned component&lt;br /&gt;
...&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
This notification is being received by the (same?) &lt;code&gt;TContosoViewPanel&lt;/code&gt; class:&lt;br /&gt;
&lt;code&gt;...&lt;br /&gt;
Classes.TList.Get // crashes here&lt;br /&gt;
Contoso.TContosoViewPanel.Notification 1732 [25]&lt;br /&gt;
Classes.TComponent.Notification&lt;br /&gt;
...&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
Summary: it is a pretty clear picture indicating a bug in the &lt;code&gt;TContosoViewPanel&lt;/code&gt; class. Looks like the &lt;code&gt;TContosoViewPanel&lt;/code&gt; class owns some other component, but it does not expect this component to be deleted at the &lt;code&gt;Contoso.TContosoViewPanel.Notification&lt;/code&gt; 1732 [25] line - which can happen during shutdown.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;TList.Get&lt;/code&gt; method is really simple:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function TList.Get(Index: Integer): Pointer;
begin
  if (Index &lt; 0) or (Index &gt;= FCount) then
    Error(@SListIndexError, Index);
  Result := FList^[Index];
end;&lt;/pre&gt;
Let&#39;s take a look at the &quot;Assembler&quot; tab from the bug report to see if we can squeeze some extra information out of bug report:&lt;br /&gt;
&lt;pre class=&quot;brush:asm&quot;&gt;00459C30 53 PUSH EBX ; start of the TList.Get
00459C31 56 PUSH ESI
00459C32 8BF2 MOV ESI, EDX
00459C34 8BD8 MOV EBX, EAX ; the EAX register is Self, it is stored into the EBX register
...
; Result := FList^[Index];
00459C4E 8B4304 MOV EAX, [EBX+4] ; the FList field is read into the EAX register
00459C51 8B04B0 MOV EAX, [EAX+ESI*4] ; -- EXCEPTION&lt;/pre&gt;
So, the code from the &lt;code&gt;TList.Get&lt;/code&gt; reads its &lt;code&gt;FList&lt;/code&gt; field (a pointer to an array) - which succeeds. And then it tries to dereference it - which fails.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s also take a look at the exception&#39;s message: &quot;Access violation at address 00459C51 in module &#39;Project1.exe&#39;. Read of address 042C7D58&quot;. Match the exception message with disassembler output: obviosly the 042C7D58 address from the message is the result of [EAX + ESI * 4] from the disassember. We can learn what these registers are from the bug report&#39;s &quot;CPU&quot; tab:&lt;br /&gt;
&lt;code&gt;EAX: 00D5B2AC&lt;br /&gt;
ESI: 00D5B2AB&lt;/code&gt;&lt;br /&gt;
The &lt;code&gt;EAX&lt;/code&gt; is supposed to be an address of the dynamically allocated memory (array). The &lt;code&gt;ESI&lt;/code&gt; is supposed to be an index in the array. As you can see, the &lt;code&gt;ESI&lt;/code&gt; is clearly incorrect (it should be something like 0, 1, 2, etc.). Additionally, these two values seems to be aweful close to each other, which may indicate both &lt;code&gt;EAX&lt;/code&gt; and &lt;code&gt;ESI&lt;/code&gt; are incorrect and are part of something else.&lt;br /&gt;
&lt;br /&gt;
We can take a look at the &quot;Modules&quot; tab: the Project1.exe is loaded at $00400000 and have a size of 14749696 or $00E11000. $00400000 + $00E11000 = $01211000 - which is more than $00D5B2AC. It means that $00D5B2AC and $00D5B2AB are code addresses. In other words, $00D5B2AC can&#39;t be address of dynamic memory, because it is a pointer to code in the app.&lt;br /&gt;
&lt;br /&gt;
Сorollary: both &lt;code&gt;EAX&lt;/code&gt; (pointer to an array) and &lt;code&gt;ESI&lt;/code&gt; (index of the array) are incorrect and are, in fact, pointers to code. Possibly - functions.&lt;br /&gt;
&lt;br /&gt;
That&#39;s about all info that you can extract from the bug report. What&#39;s next? Well, assuming you have the same application that was used to produce the bug report:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;You can run your program;&lt;/li&gt;
&lt;li&gt;Pause it;&lt;/li&gt;
&lt;li&gt;Use View / Debug Windows / CPU;&lt;/li&gt;
&lt;li&gt;Right click on the code (disassembler view);&lt;/li&gt;
&lt;li&gt;Select &quot;Goto address...&quot;;&lt;/li&gt;
&lt;li&gt;Enter $00D5B2AC (or $00D5B2AB);&lt;/li&gt;
&lt;li&gt;See where this will take you.&lt;/li&gt;
&lt;/ol&gt;
Alternatively, you can use the &lt;a href=&quot;https://blog.eurekalog.com/2012/03/what-does-this-address-mean-introducing.html&quot; title=&quot;What does this address mean? (introducing Address Lookup Tool)&quot;&gt;Start / Programs / EurekaLog / Tools / Address Lookup&lt;/a&gt; tool to see what source code corresponds to the mentioned addresses.&lt;br /&gt;
&lt;br /&gt;
While we don&#39;t have the customer&#39;s source code, we can make a guess: both &lt;code&gt;TList&lt;/code&gt; and its index are read by the &lt;code&gt;Contoso.TContosoViewPanel.Notification&lt;/code&gt; from some object. And that object is already deleted at this moment. The customer has access to its source code, so he can easily check where these values are coming from.&lt;br /&gt;
&lt;br /&gt;
When an object is deleted, EurekaLog fills that object with some values. Some of these values are pointers to the &lt;code&gt;DeadObjectVirtualMethodCall&lt;/code&gt; function from the &lt;code&gt;EMemLeaks&lt;/code&gt; unit. This function will be called when someone will try to call a virtual method on the deleted object. Since $00D5B2AC and $00D5B2AB values are code addresses - I would speculate one of them is a pointer to the &lt;code&gt;DeadObjectVirtualMethodCall&lt;/code&gt; function, which further confirms the &lt;code&gt;Contoso.TContosoViewPanel.Notification&lt;/code&gt; accesses an already deleted object.&lt;br /&gt;
&lt;br /&gt;
Another way to tackle this is to place breakpoints on the mentioned lines 291 and 1732 of the Contoso.pas file. And walk step-by-step watching for the mentioned bug.&lt;br /&gt;
&lt;br /&gt;
The customer replied back informing us he was able to find and fix the bug in his application armed with this information.&lt;br /&gt;
&lt;br /&gt;
See also: &lt;a href=&quot;https://blog.eurekalog.com/2023/08/what-to-do-with-assembler.html&quot; title=&quot;What can you do with the &#39;Assember&#39; tab in bug reports? Is it even useful?&quot;&gt;What can you do with the &quot;Assember&quot; tab in bug reports? Is it even useful?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
P.S. &lt;a href=&quot;https://blog.eurekalog.com/search/label/Stories&quot; title=&quot;EurekaLog Blog: Stories&quot;&gt;Read more stories like this one&lt;/a&gt; or &lt;a href=&quot;https://www.eurekalog.com/casestudies.php&quot; title=&quot;Case Studies and User Reviews&quot;&gt;read feedback from our customers&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/8380930635538593554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/8380930635538593554'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/08/how-to-read-bug-reports.html' title='How to read bug reports'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-3082145196517727676</id><published>2025-07-22T20:58:00.006+01:00</published><updated>2026-07-17T13:23:34.090+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="external"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><title type='text'>Using EurekaLog with FastMM&#39;s full debug mode</title><content type='html'>Were were contacted by a customer who claimed adding EurekaLog to his application prevented FastMM from doing its job. Specifically, FastMM could detect a problem like this:
&lt;pre class=&quot;brush:delphi&quot;&gt;SomeObj.Free;
SomeObj.SomeField := 1; // here: writing into released memory&lt;/pre&gt;
but only in an application without EurekaLog. Once EurekaLog is added to an application, the FastMM no longer detects the &quot;bad&quot; code.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
First of all: these kinds of checks highly depend on order in which memory operations are performed. FastMM does not detect writes into released memory as soon as the write operation completes. Instead, FastMM perform memory scanning check when your app request memory to be allocated in the same place - which can happen a lot later; or it could not happen at all.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;SomeObj.Free;
SomeObj.SomeField := 1; // this will not be detected

TSomeObj.Create; // here: FastMM will allocate the object over the old memory block 
                 // and it will detect the old memory block was changed
                 // FastMM will crash here, even though there is no problem with the TSomeObj.Create&lt;/pre&gt;
&lt;br /&gt;
Additionally, you can tell FastMM to force-scan all released memory via the &lt;code&gt;FastMM_ScanDebugBlocksForCorruption&lt;/code&gt; function at any time.&lt;br /&gt;
&lt;br /&gt;
So one might think adding EurekaLog to an application changed the order of memory operations, so app&#39;s behaviour changed. However, that was not the case.&lt;br /&gt;
&lt;br /&gt;
EurekaLog does not have its own memory manager. EurekaLog uses memory manager of your app - whatever it is set to. EurekaLog just adds some extra features to perform few memory checks. It is all done as layer on top of your existing memory manager. So, if you add FastMM to your project and then enable EurekaLog with memory checks - your app will use FastMM to allocate memory, while EurekaLog will use that memory to store extra data for memory checks.&lt;br /&gt;
&lt;br /&gt;
The customer used FastMM 5. Unlike FastMM 4 (which enables full debug mode via conditional symbols), FastMM 5 can enable full debug mode by calling the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function. This function is so convenient, most people use it everytime, without realizing what it actually does.&lt;br /&gt;
&lt;br /&gt;
The way full debug mode in FastMM works is that it is actually &lt;b&gt;two different memory managers&lt;/b&gt;: the &quot;release&quot; one (&lt;code&gt;FastMM_GetMem&lt;/code&gt;) and full debug mode one (&lt;code&gt;FastMM_DebugGetMem&lt;/code&gt;). This was more obvious in FastMM 4, as enabling full debug mode required changing conditional symbols and a standalone DLL. So the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function simply switches the &lt;code&gt;FastMM_GetMem&lt;/code&gt; to the &lt;code&gt;FastMM_DebugGetMem&lt;/code&gt;. What people don&#39;t realize is that it is only possible if your app still uses &lt;code&gt;FastMM_GetMem&lt;/code&gt;. Indeed: that way full debug mode will know how to release memory allocated by the &lt;code&gt;FastMM_GetMem&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
However, if memory manager was already set to something else (like EurekaLog&#39;s memory filter), the full debug mode would have no idea how to work with EurekaLog headers. Thus it won&#39;t be able to properly free memory allocated via EurekaLog. That is why the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function will no nothing when called in an application with EurekaLog (well, assuming it is called after EurekaLog&#39;s initialization). You can verify this:
&lt;pre class=&quot;brush:delphi&quot;&gt;if FastMM_EnterDebugMode then
  OutputDebugString(&#39;FastMM5: FULL DEBUG MODE ENABLED&#39;);&lt;/pre&gt;
You will see this output in an app without EurekaLog, but not in app with EurekaLog.&lt;br /&gt;
&lt;br /&gt;
That was exactly what customer did: he called the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function as first action in his project file (.dpr), but it is already too late: EurekaLog (as well as RTL and VCL) is already initialized at this point, so EurekaLog&#39;s memory filter is already set, therefore the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function will do nothing.&lt;br /&gt;
&lt;br /&gt; 
That was the reason: FastMM 5 did not detect anything, because &lt;b&gt;FastMM&#39;s full debug mode was not enabled in the first place&lt;/b&gt;.&lt;br /&gt; 
&lt;br /&gt; 
Is it possible to use FastMM 5 in full debug mode with EurekaLog?&lt;br /&gt;
&lt;br /&gt;
Yes! All you need to do is to enable FastMM&#39;s full debug mode &lt;b&gt;before&lt;/b&gt; EurekaLog sets its memory manager. In other words, the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function should be called before &lt;code&gt;EMemLeaks&lt;/code&gt; unit initializes. Order of initialization matters. You can do this in at least two different ways:&lt;br /&gt; 
&lt;ol&gt;&lt;li&gt;If you open the &lt;code&gt;FastMM5.pas&lt;/code&gt; file, you would see documentation for FastMM. It mentions this among other things:
&lt;blockquote&gt;  The following conditional defines are supported:&lt;br /&gt;
&lt;blockquote&gt;- &lt;code&gt;FastMM_FullDebugMode&lt;/code&gt; (or &lt;code&gt;FullDebugMode&lt;/code&gt;) - If defined then &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; will be called on startup so that the memory manager starts in debug mode.  If &lt;code&gt;FastMM_FullDebugMode&lt;/code&gt; is defined and &lt;code&gt;FastMM_DebugLibraryDynamicLoading&lt;/code&gt; (or &lt;code&gt;LoadDebugDLLDynamically&lt;/code&gt;) is not defined then &lt;code&gt;FastMM_DebugLibraryStaticDependency&lt;/code&gt; is implied.&lt;br /&gt;
&lt;br /&gt;
- &lt;code&gt;FastMM_FullDebugModeWhenDLLAvailable&lt;/code&gt; (or &lt;code&gt;FullDebugModeWhenDLLAvailable&lt;/code&gt;) - If defined an attempt will be made to load the debug support library during startup.  If successful then &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; will be called so that the memory manager starts up in debug mode.&lt;/blockquote&gt;&lt;/blockquote&gt;
which means you can add the &lt;code&gt;FastMM_FullDebugMode&lt;/code&gt; (or &lt;code&gt;FastMM_FullDebugModeWhenDLLAvailable&lt;/code&gt;) conditional symbol to your project&#39;s options. You probably want to add it into your &quot;Debug&quot; build configuration only. Now, build your project, and it will have FastMM 5 in full debug mode for the &quot;Debug&quot; build configuration, but production version of FastMM 5 for the &quot;Release&quot; build configuration.&lt;br /&gt; 
&lt;br /&gt;
That way the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function will be called when the &lt;code&gt;FastMM5&lt;/code&gt; unit is initialized - i.e. before the &lt;code&gt;EMemLeaks&lt;/code&gt; unit is initialized.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Another way would be creating a &lt;code&gt;FastMM5_FDM.pas&lt;/code&gt; unit like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;unit FastMM5_FDM;

interface

implementation

uses
  FastMM5,
  Windows;

initialization
  if FastMM_EnterDebugMode then
  begin
    OutputDebugString(&#39;FastMM5: Full Debug Mode is enabled&#39;);
    
    {$IFDEF EnableMemoryLeakReporting}
    // Add the &quot;EnableMemoryLeakReporting&quot; symbol to your conditional defines to enable leaks checking
    FastMM_LogToFileEvents  := FastMM_LogToFileEvents  + [mmetUnexpectedMemoryLeakDetail];
    FastMM_MessageBoxEvents := FastMM_MessageBoxEvents + [mmetUnexpectedMemoryLeakSummary];
    ReportMemoryLeaksOnShutdown := True;
    OutputDebugString(&#39;FastMM5: Leaks Checks are enabled&#39;);
    {$ENDIF}
    
  end
  else
    OutputDebugString(&#39;FastMM5: Full Debug Mode is NOT enabled!&#39;);

finalization
  // Whatever sets these callbacks is already finalized at this point,
  // so we just clear/reset the callbacks just in case
  // (to avoid calling already finalized code).
  FastMM5.FastMM_GetStackTrace           := nil;
  FastMM5.FastMM_ConvertStackTraceToText := nil;
end.&lt;/pre&gt;
Place this unit as your first unit:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  FastMM5_FDM,
  {$IFDEF EurekaLog}
  EMemLeaks,
  EResLeaks,
  // ...&lt;/pre&gt;
&lt;br /&gt;
That way the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function will be called when the &lt;code&gt;FastMM5_FDM&lt;/code&gt; unit is initialized - i.e. before the &lt;code&gt;EMemLeaks&lt;/code&gt; unit is initialized.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Finally, if you disable EurekaLog&#39;s memory filter (the &quot;Enable extended memory manager&quot; option): EurekaLog will not change app&#39;s memory manager, so the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function can switch it at any time.&lt;/li&gt;
&lt;/ol&gt;
All mentioned methods will call the &lt;code&gt;FastMM_EnterDebugMode&lt;/code&gt; function first, and then initialize EurekaLog&#39;s memory filter:
&lt;ul&gt;
  &lt;li&gt;The &lt;code&gt;FastMM_FullDebugMode&lt;/code&gt; will enable full debug mode at design-time;&lt;/li&gt;
  &lt;li&gt;The &lt;code&gt;FastMM_FullDebugModeWhenDLLAvailable&lt;/code&gt; will enable full debug mode depending if the debug support library is present;&lt;/li&gt;
  &lt;li&gt;The &lt;code&gt;FastMM5_FDM&lt;/code&gt; unit will enable full debug mode at the run-time.&lt;/li&gt;
&lt;/ul&gt;
except the last method, which will disable EurekaLog&#39;s memory filter entirely.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/3082145196517727676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/3082145196517727676'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/07/EurekaLog-FastMM5-FDM.html' title='Using EurekaLog with FastMM&#39;s full debug mode'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-4800884562248254742</id><published>2025-07-08T12:47:00.006+01:00</published><updated>2025-09-16T13:36:55.478+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="debugging"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="Stories"/><title type='text'>EurekaLog-enabled application starts up x2 times slower on Windows 2016</title><content type='html'>We were contacted by a customer who claimed adding EurekaLog to his application increased application startup time by a factor of 2 - but only on PCs running Windows 2016. In other words, application startup takes about 6 seconds on Windows 2016, while taking only 3 seconds on any other OS.&lt;br /&gt;
&lt;br /&gt;
The customer used a Process Monitor tool to observe that EurekaLog-enabled application creates a lot of *.tmp files. He wondered why that is, and if it could be a source of the issue.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Generally speaking, using a file in Windows is not slower than using a memory: that is because file operations are cached in memory. Writing, say, 1 Mb to a file does not mean your HDD will spin up to write 1 Mb of data, and your code continues only after the HDD finished his work. If you write some data into a file - it will be written in a memory buffer and dumped to a hard disk in background. In other words, if your application runs slower when creating/using a file - it is not because it is using a file, but because data size for the file is too large to fit in memory. So, if you replace a file with memory - the end effect would be the same: memory would be paged to disk because there is not enough RAM.&lt;br /&gt;
&lt;blockquote&gt;(There is an additional aspect when working with files: if you read/write very small chunks of data, so you call a lot of kernel&#39;s file functions. Calls to the kernel are slow - that is what may be causing the performance loss: it is about kernel calls, not about using a file. External factors may also play a role: for example, anti-virus could affect your file operations.)&lt;/blockquote&gt;
EurekaLog uses temp files to offload large chunks of data from your address space so your application would have more free address space to run. For example, there is a lot of DLLs loaded into your process. EurekaLog has to provide debug information for each DLL in order to build reports including these DLLs. Naturally, this information has to be stored somewhere in a ready-to-use form. If we would store this info in memory, your application would have much less memory to run, as debug information tends to be very large. That is why EurekaLog creates debug information in temp files. However, this has nothing to do with performace. If we switch from files to memory - the end performance would be almost the same, but your own code would have less memory to execute.&lt;br /&gt;
&lt;br /&gt;
We have the &lt;a href=&quot;https://support.eurekalog.com/Knowledgebase/Article/View/92/0/7x-my-application-runs-very-slowly-with-eurekalog&quot; title=&quot;My application runs (very) slowly with EurekaLog&quot;&gt;guide on troubleshooting performance issues in EurekaLog&lt;/a&gt;, so we naturally asked the customer to walk through this guide to check if startup on Windows 2016 is somehow different from startups on other OS - for example, perhaps there are additional exceptions being thrown when run on Windows 2016. However, the customer reported the guide did not help.&lt;br /&gt;
&lt;br /&gt;
We asked the customer to &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/fix_runtime_issues.php&quot; title=&quot;EurekaLog run-time problems&quot;&gt;capture a run-time log from EurekaLog&lt;/a&gt; for the &quot;slow&quot; PC and the &quot;fast&quot; PC, so we can compare two runs together. Basically, it means: compile your app with debug version of precompiled files (*.dcu) - which is already a default on modern IDEs. And pass the &lt;code&gt;--el_debug&lt;/code&gt; command line switch. An &lt;code&gt;el_debug.csl&lt;/code&gt; file will be created in app&#39;s folder. The file will contain everything EurekaLog is doing in the app. See the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/fix_runtime_issues.php&quot; title=&quot;EurekaLog run-time problems&quot;&gt;above mentioned article&lt;/a&gt; for more details on how to enable and use the run-time logging.&lt;br /&gt;
&lt;br /&gt;
The produced *.csl files are compatible with the CodeSite file format, so you can use the CodeSite File Viewer tool - available as part of the &lt;a href=&quot;https://raize.com/codesite/&quot; title=&quot;CodeSite&quot;&gt;freeware CodeSite Tools package&lt;/a&gt; (scroll to the bottom to find download link for the tools). Now you can open both *.csl files. This may take a while, since the CodeSite log format is mostly textual, so the CodeSide File Viewer will need some time to parse it. Typical logs from EurekaLog on application startup start from 2 Mb and are usually up to 10 Mb - depending on what your application is doing on startup. Opening a 10 Mb text file in the CodeSize File Viewer tool could easily take a few minutes.&lt;br /&gt;
&lt;br /&gt;
Once the log is opened - you can use the &quot;View&quot; / &quot;Select Columns&quot; menu item to display Time Offsets:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVavXE62ueBpS0MhzlvleLpr6eHteDCeuXISyip6tpEB3cPLh70ePktUzFEM90g_DdgDeLpSnYKhrE_kKFOvu-ZFfofblo-hVtLCAxr-KqvROI8JXzfkmHjKpSJK0A2EV3hFbvxrzB9QvBbsvuK7XBLidgR6bcGFV2ZkkyOdQvcJbLAy13XBUTiXn7oRQ/s1600/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;412&quot; data-original-width=&quot;535&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVavXE62ueBpS0MhzlvleLpr6eHteDCeuXISyip6tpEB3cPLh70ePktUzFEM90g_DdgDeLpSnYKhrE_kKFOvu-ZFfofblo-hVtLCAxr-KqvROI8JXzfkmHjKpSJK0A2EV3hFbvxrzB9QvBbsvuK7XBLidgR6bcGFV2ZkkyOdQvcJbLAy13XBUTiXn7oRQ/s1600/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Configuring the &quot;Time&quot; column&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
Since any Windows application runs multiple threads (even if your own code does not spawn any threads - the system will create few threads), it is a good idea to organize messages into individual threads, so you can inspect the main thread isolated from background threads:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFe7DqptXoIaNvx945OLBIG2-cEH7312M5-4TEIKoR8uE_p6Kfa9anlOeH-tPqdS9l3hZi8oFXuugElcygUfOTIUD9wftHUqcM7ZS993qExgwaBZihJmR5opT9f7Vp62DeUHFqBOYBv8n8A5qWhyphenhyphen9u4kXsUOo62a1si0d1YcD_hZxGUNJ4nolFvDzA6EM/s571/1.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;422&quot; data-original-width=&quot;571&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFe7DqptXoIaNvx945OLBIG2-cEH7312M5-4TEIKoR8uE_p6Kfa9anlOeH-tPqdS9l3hZi8oFXuugElcygUfOTIUD9wftHUqcM7ZS993qExgwaBZihJmR5opT9f7Vp62DeUHFqBOYBv8n8A5qWhyphenhyphen9u4kXsUOo62a1si0d1YcD_hZxGUNJ4nolFvDzA6EM/s16000/1.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Organizing log messages&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
Organizing messages could also take some time, be patient. Once messages are organized by threads - switch to the main thread tab:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWHLWkPeCq0tGrf1G_kDVeXS3hCD184ewBz-k4V-ud2OqLL-zuPDZrS1FXHRuU6WLsMyCBDQ1Y9h3ABRk4TOaBiBGpfFRDDWb7UG6R2HqAA3Znnk1ZwsswVlR4YGBr2Zib611_CmtVyAV0ipRZm17fvjo7F776GQBKjE5uDgvaFYzXpSPHWZno2WLdmJo/s1600/7.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;169&quot; data-original-width=&quot;474&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWHLWkPeCq0tGrf1G_kDVeXS3hCD184ewBz-k4V-ud2OqLL-zuPDZrS1FXHRuU6WLsMyCBDQ1Y9h3ABRk4TOaBiBGpfFRDDWb7UG6R2HqAA3Znnk1ZwsswVlR4YGBr2Zib611_CmtVyAV0ipRZm17fvjo7F776GQBKjE5uDgvaFYzXpSPHWZno2WLdmJo/s1600/7.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Threads tabs will appear after organizing messages&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
Finally, we need to collapse all functions to make it easier to navigate the log. You can right-click on any log message inside log&#39;s view and select the &quot;Collapse all&quot; from the pop-up menu:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguFxtkZJEOXMcv0EygPVWuO_-OTjzexnDaw7kGV9Bo2VTzABsjkV6l9O2Yn05h8-xog3T7ATcJODzJ7khtVX1eteZEPebAFQDe6WOSq4ki8pL6v1Fp8isw-CYe6bEi6qR7eMLrclpwMtmhZL2ZMGax-e6zjxYlY5rNBGsyzcqGv0vxeH6qDgkRVp3AMSA/s1600/2.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;343&quot; data-original-width=&quot;542&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguFxtkZJEOXMcv0EygPVWuO_-OTjzexnDaw7kGV9Bo2VTzABsjkV6l9O2Yn05h8-xog3T7ATcJODzJ7khtVX1eteZEPebAFQDe6WOSq4ki8pL6v1Fp8isw-CYe6bEi6qR7eMLrclpwMtmhZL2ZMGax-e6zjxYlY5rNBGsyzcqGv0vxeH6qDgkRVp3AMSA/s1600/2.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Collapsing all functions&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
This could take some time too.&lt;br /&gt;
&lt;br /&gt;
Now we are ready to analyze the logs. Let&#39;s compare two startups:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimR_p4gjK0TElnlzzh300EIcTB7c8nhyphenhyphenjidwO1SOBcsgPeToPA15k2NUYNK3czoIuUrhcsW0kc_DparTplEtAs0U4YWnG855jn8VC5bk99yaPMWYXDRvjPxfNH0v-I5iVt4so6oY0px2xQ_PStkflXSUoIBrrs1abxHqzNcO-eP9In24zPY0oxKaUB4BE/s1600/test2.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;144&quot; data-original-width=&quot;1130&quot; height=&quot;82&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimR_p4gjK0TElnlzzh300EIcTB7c8nhyphenhyphenjidwO1SOBcsgPeToPA15k2NUYNK3czoIuUrhcsW0kc_DparTplEtAs0U4YWnG855jn8VC5bk99yaPMWYXDRvjPxfNH0v-I5iVt4so6oY0px2xQ_PStkflXSUoIBrrs1abxHqzNcO-eP9In24zPY0oxKaUB4BE/w640-h82/test2.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Startup on the &quot;Fast&quot; PC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ7llksDmJThGkUjpptIWf0hSBxLxehm1j4d27kgaoR4FvK6Cp8bUGWoPjImNrEwAj6Hvh5sbHVbi8heCrdbSlqLi9LjXN2LpmxheCZCeCAFSz1HNap_yYfZcEaeAcaCXpW_kBP7hAscyf5NvunVzfJF8hL0vzF2T2nZTU7Fr290xRwI537MjaxOLDCic/s1600/test1.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;145&quot; data-original-width=&quot;1130&quot; height=&quot;82&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ7llksDmJThGkUjpptIWf0hSBxLxehm1j4d27kgaoR4FvK6Cp8bUGWoPjImNrEwAj6Hvh5sbHVbi8heCrdbSlqLi9LjXN2LpmxheCZCeCAFSz1HNap_yYfZcEaeAcaCXpW_kBP7hAscyf5NvunVzfJF8hL0vzF2T2nZTU7Fr290xRwI537MjaxOLDCic/w640-h82/test1.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Startup on the &quot;Slow&quot; PC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
As you can see: EurekaLog completed the startup in about 5 seconds on the &quot;Fast&quot; PC, and in about 11 seconds on the &quot;Slow&quot; PC. These values are slower than values reported by the customer, but it is because logging itself takes some time. Nevertheless you still can observe x2 time difference.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s open (expand) the &quot;EurekaLog.Initialization&quot; function to see what exactly it is doing:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifZUf6FsmGHIaU3za98rb1cledttSAeLNj-lih-Q_4h6qjFAmxeUl1KTBrczsR6CSEJYQVOrOfQ5WsuxNemdVEBg3PzlHQm_or0gexmHZMxS8-OlXASStr_3Rsp2COpKCvOlis_0L8_EK6TUroWi4PO-gyDtzqNICSDULqwZy4ktGw53STPwCxAHlbdNA/s1600/3.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;182&quot; data-original-width=&quot;844&quot; height=&quot;138&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifZUf6FsmGHIaU3za98rb1cledttSAeLNj-lih-Q_4h6qjFAmxeUl1KTBrczsR6CSEJYQVOrOfQ5WsuxNemdVEBg3PzlHQm_or0gexmHZMxS8-OlXASStr_3Rsp2COpKCvOlis_0L8_EK6TUroWi4PO-gyDtzqNICSDULqwZy4ktGw53STPwCxAHlbdNA/w640-h138/3.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Startup on the &quot;Fast&quot; PC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1bfUOwFD_wS-ueREFfTeTObtRSl_1IsnzTSC_j3xUmAb-2eG4qW-oWkfvLbRhK1FiroKb5kV8cB0UewxI5lKFLtwsr1d6fTv0qygQcj8hhdSmRzgKt31TQUhuUALKOJWgEGY9UypLWf11ydf9C2uVD87-sFwa1dVB9KFsqyoMdZ3Vi7n5PMZ5TXNyXX8/s1600/4.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;183&quot; data-original-width=&quot;847&quot; height=&quot;138&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1bfUOwFD_wS-ueREFfTeTObtRSl_1IsnzTSC_j3xUmAb-2eG4qW-oWkfvLbRhK1FiroKb5kV8cB0UewxI5lKFLtwsr1d6fTv0qygQcj8hhdSmRzgKt31TQUhuUALKOJWgEGY9UypLWf11ydf9C2uVD87-sFwa1dVB9KFsqyoMdZ3Vi7n5PMZ5TXNyXX8/w640-h138/4.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Startup on the &quot;Slow&quot; PC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
Now we see something interesting: the &quot;Slow&quot; PC is actually running sligtly faster than the &quot;Fast&quot; PC! Notice how everything is completed in 0.343 seconds on the &quot;Slow&quot; PC compared to the 0.575 seconds on the &quot;Fast&quot; PC. However, there is still something on the &quot;Slow&quot; PC that causes the major delay. We need to dig deeper.&lt;br /&gt; 
&lt;br /&gt; 
We would need to expand the &quot;ExceptionLog7.Init&quot; function, and then open the &quot;EurekaLogInitialization&quot; function, etc. We open a lot of functions until we arrive at the bottleneck code:&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb1pYA4ccpuPR9JMhVo254qtrA-HV68dpI2JU9DPoOGtTYC4i90IoCk_8TBPZ0WliKC98NRzIoW3Rdt6U_MeWwCJwUjdVHTadaFe5zqq_8SJKMHvZr-v6U8-c9cW5bB3dBvbvoMproEajwxChB9LuX1QQ7zA2Ko_4eOEj3NNTwuyHtDz1rduMQBv_28vE/s1600/6.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;520&quot; data-original-width=&quot;1113&quot; height=&quot;299&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb1pYA4ccpuPR9JMhVo254qtrA-HV68dpI2JU9DPoOGtTYC4i90IoCk_8TBPZ0WliKC98NRzIoW3Rdt6U_MeWwCJwUjdVHTadaFe5zqq_8SJKMHvZr-v6U8-c9cW5bB3dBvbvoMproEajwxChB9LuX1QQ7zA2Ko_4eOEj3NNTwuyHtDz1rduMQBv_28vE/w640-h299/6.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;No bottleneck on the &quot;Fast&quot; PC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3pcW90ZtPZaI2I4teBg8c4NFhftTVWpIMWVNGyakeJVn9823TSDCJs1gx5K_8NSCc-YV_g-wVMFyuNZr5g9Wo1Q4WLZQ36mLcJju6Dg84Xw8YY-u1Ir89s-TydfDVBVxsRZilHSwCeWknjAi3mupHCrRJrkitTl4J-bkX-Q9EpmVZ3OonNd0rZhjTtnk/s1600/5.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;523&quot; data-original-width=&quot;1103&quot; height=&quot;303&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3pcW90ZtPZaI2I4teBg8c4NFhftTVWpIMWVNGyakeJVn9823TSDCJs1gx5K_8NSCc-YV_g-wVMFyuNZr5g9Wo1Q4WLZQ36mLcJju6Dg84Xw8YY-u1Ir89s-TydfDVBVxsRZilHSwCeWknjAi3mupHCrRJrkitTl4J-bkX-Q9EpmVZ3OonNd0rZhjTtnk/w640-h303/5.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Bottleneck on the &quot;Slow&quot; PC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
And again you can see the &quot;Slow&quot; PC is sligntly faster than the &quot;Fast&quot; PC, but not when inside the bottleneck code. The bottleneck code is the &lt;code&gt;TELDebugInfoExports.GetExportList&lt;/code&gt; function from the &lt;code&gt;EDebugExportBase&lt;/code&gt; unit. It takes about 0.5 seconds on the &quot;Fast&quot; PC and almost 4 seconds on the &quot;Slow&quot; PC - causing a major x8 times difference.&lt;br /&gt; 
&lt;br /&gt; 
As you can see: this code loads debug information from the &lt;code&gt;rtl290.bpl&lt;/code&gt; package. In this case: EurekaLog creates new debug information from the DLL (BPL) exports table since this package does not have any other debug information. If you scroll further - you will find a similar picture for the &lt;code&gt;vcl290.bpl&lt;/code&gt; package. There will be more BPLs and DLLs listed, but times for these BPLs/DLLs will be similar.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;EDebugExportBase.TELDebugInfoExports.GetExportList&lt;/code&gt; function is essentialy a copy cycle:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;  Count := ExportDir.NumberOfNames;
  SetLength(FNames.FNames, Count);
  for I := 0 to Count - 1 do
  begin
    Address := PDWORD(PtrUInt(Functions) + NameOrdinals^ * SizeOf(DWORD))^;
    UTF8Name := PAnsiChar(ABorImage.RvaToVa(Names^));
    if not TryUTF8ToString(UTF8Name, ExportName) then
      ExportName := String(UTF8Name);
    FNames.Add(ConvertAddress(Pointer(PAddress(Module) + Address)), ExportName);
    Inc(NameOrdinals);
    Inc(Names);
  end;&lt;/pre&gt;
This cycle copies function names from the BPL/DLL exports table into a temp file buffer.&lt;br /&gt;
&lt;br /&gt;
Well, assuming the &lt;code&gt;rtl290.bpl&lt;/code&gt; package is the same on both &quot;Slow&quot; and &quot;Fast&quot; PC - it is not obvious how this code could produce such a large difference (almost x8 times). We asked the customer to rule out any possible external factors: such as anti-virus, but the customer reported he did not find anything.&lt;br /&gt; 
&lt;br /&gt; 
One option is to pursue the issue further: launch a proper performance profiler to see what is taking so long in the code above. But there is another way: this bottleneck code is called because a package does not have a prepared debug information, so EurekaLog has to create one (from exports table). As you know: the fastest code is the one that is never called. The way to never call this code is to supply debug information for the package. Thankfully, modern IDEs come with *.jdbg files for each *.bpl file. *.jdbg file contains debug information in the JCL (JEDI) format. EurekaLog is able to read this format if you &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/debug_information_page_code.php&quot; title=&quot;Options: Debug information page&quot;&gt;enable the corresponding debug information provider in EurekaLog&#39;s project options&lt;/a&gt;.&lt;br /&gt; 
&lt;br /&gt; 
Once the customer deployed *.jdbg files to Windows 2016 PC - the performance problem disappeared. EurekaLog no longer needs to analyze the package and can use the prepared debug information from *.jdbg files.&lt;br /&gt;
&lt;br /&gt;
P.S. &lt;a href=&quot;https://blog.eurekalog.com/search/label/Stories&quot; title=&quot;EurekaLog Blog: Stories&quot;&gt;Read more stories like this one&lt;/a&gt; or &lt;a href=&quot;https://www.eurekalog.com/casestudies.php&quot; title=&quot;Case Studies and User Reviews&quot;&gt;read feedback from our customers&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4800884562248254742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4800884562248254742'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/07/slow-startup-on-Windows-2016.html' title='EurekaLog-enabled application starts up x2 times slower on Windows 2016'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVavXE62ueBpS0MhzlvleLpr6eHteDCeuXISyip6tpEB3cPLh70ePktUzFEM90g_DdgDeLpSnYKhrE_kKFOvu-ZFfofblo-hVtLCAxr-KqvROI8JXzfkmHjKpSJK0A2EV3hFbvxrzB9QvBbsvuK7XBLidgR6bcGFV2ZkkyOdQvcJbLAy13XBUTiXn7oRQ/s72-c/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-5646867015750471871</id><published>2025-05-13T13:26:00.004+01:00</published><updated>2025-11-14T10:55:44.178+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best practice"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><title type='text'>&quot;Invalid EurekaLog configuration found&quot; error</title><content type='html'>Users of recent EurekaLog versions could receive the &quot;Invalid EurekaLog configuration found&quot; error message when they run their EurekaLog-enabled applications. The message looks like this:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;Invalid EurekaLog configuration found.

Exception from the following module:
full-file-name-to-your-EXE-or-DLL
was catched in the following module:
full-file-name-to-your-DLL-or-EXE
Both modules have EurekaLog enabled, but EurekaLog code is not shared.

This is essentially the same error as:
- &quot;Can&#39;&#39;t assign a TFont to a TFont&quot;;
- &quot;Invalid class typecast&quot;;
- reInvalidCast;
- &quot;Run-time error 10&quot;.
etc.

Possible solutions:
- Do not allow exceptions from one module escape into another (recommended)
- Compile DLLs with Lightweight DLL profile, so your DLL will not have EurekaLog&#39;s code
- Compile EXE and DLL with run-time packages, so EXE and DLL will share EurekaLog&#39;s code

See our help for more information:
https://www.eurekalog.com/help/eurekalog/dll_using_eurekalog.php&lt;/pre&gt;&lt;/blockquote&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX9UV-2KIl0fekVcgkQ35uHJZsRkobe4bnT_4PLK-SI5jgVMEAuAP-8xrdysanc8Fjk38EDj1LxajWtIXdkDa2HiDhNnBdgen2Ihqc5GfZZR98IjsOcnViPz-a-_GAfdqovwIKdr2qJCg1MF2FwHdHb6QqqvyWJscwoNLWtcFkkVP7P3ncGgY1tGVk-kA/s1600/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;504&quot; data-original-width=&quot;443&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX9UV-2KIl0fekVcgkQ35uHJZsRkobe4bnT_4PLK-SI5jgVMEAuAP-8xrdysanc8Fjk38EDj1LxajWtIXdkDa2HiDhNnBdgen2Ihqc5GfZZR98IjsOcnViPz-a-_GAfdqovwIKdr2qJCg1MF2FwHdHb6QqqvyWJscwoNLWtcFkkVP7P3ncGgY1tGVk-kA/s1600/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h1&gt;What this check is&lt;/h1&gt;
Previous versions of EurekaLog did not have this check, so users could configure their projects incorrectly and then contact us asking why various bad things happens or why EurekaLog behave incorrectly. That is why recent versions of EurekaLog perform this check explicitly to let users know they are doing something wrong.&lt;br /&gt;
&lt;br /&gt;
The check is triggered when EurekaLog catches exception, which has EurekaLog information assigned, but this EurekaLog information does not come from EurekaLog itself! In other word, it comes from (a different) EurekaLog in a different module (EXE or DLL).&lt;br /&gt;
&lt;br /&gt;
Naturally, you &lt;b&gt;may&lt;/b&gt; get this error message only when you have EXE with EurekaLog and DLL with EurekaLog. You will never see this message if you have EXE without EurekaLog or DLL without EurekaLog.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;What the problem is&lt;/h1&gt;
We think that the message is pretty self-explanatory, but here is another example (without using EuerkaLog): suppose you have a function in DLL like this:
&lt;pre class=&quot;brush:delphi&quot;&gt;function CreateForm: TForm; stdcall; export;
begin
  Result := TMyForm.Create(nil);
end;&lt;/pre&gt;
which is being called from EXE. As you should know, EXE and DLL both have each own code of VCL (by default). Which means there are &lt;code&gt;DLL.TForm&lt;/code&gt; and &lt;code&gt;EXE.TForm&lt;/code&gt; - two &lt;b&gt;different&lt;/b&gt; things. So essentially you create &quot;something&quot; (object) in DLL and pass it back to EXE, which expects it to be &lt;code&gt;EXE.TForm&lt;/code&gt; - but it is not: it is &lt;code&gt;DLL.TForm&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The same thing is true for the reverse case: when EXE pass objects into DLL. For example, one may choose to pass EXE&#39;s &lt;code&gt;Application&lt;/code&gt; or &lt;code&gt;MainForm&lt;/code&gt; to DLL in order for DLL to display owned/child forms.&lt;br /&gt;
&lt;br /&gt;
Such code is a bad design. You should &lt;b&gt;never&lt;/b&gt; write such code.&lt;br /&gt;
&lt;br /&gt;
We should note: such code &lt;i&gt;can&lt;/i&gt; work - by &lt;b&gt;coincedence&lt;/b&gt;: when all of the below conditions are true:
&lt;ol&gt;
&lt;li&gt;If both DLL and EXE are compiled with exactly the same version of VCL (or another code which implements passed objects) - so the object&#39;s layout in memory is the same for EXE and DLL;&lt;/li&gt;
&lt;li&gt;If the caller does not check for the class explicitly (not using &lt;code&gt;InheritsFrom&lt;/code&gt;, &lt;code&gt;as&lt;/code&gt; operator, &lt;code&gt;is&lt;/code&gt; operator, etc) on returned object and all of its properties (and properties of properties).&lt;/li&gt;
&lt;li&gt;When at least one (any) condition is met:
&lt;ul&gt;  
&lt;li&gt;If the caller does not modify dynamic properties (strings, dynamic arrays, interfaces, etc.) of the returned object and does not call non-virtual object&#39;s methods that could do so. Basically, no (re)allocating memory in returned object.&lt;/li&gt;
&lt;li&gt;EXE and DLL share memory manager.&lt;/li&gt;
&lt;/ul&gt;  
&lt;/li&gt;
&lt;/ol&gt;  
Did you notice? This list is basically saying that this code is &lt;b&gt;WRONG&lt;/b&gt;:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure FuncInDLL; stdcall;
begin
  // your code
end;&lt;/pre&gt;
Why? Well, imagine an exception happens in this function. What is exception in Delphi? Well, it is an object derived from the &lt;code&gt;Exception&lt;/code&gt; class. In other words, an object (exception) created in DLL will be passed to EXE.&lt;br /&gt; 
&lt;br /&gt;
As you may suspect: most (?) Delphi DLLs are written in this way. And authors of these DLLs are saying: &quot;this code works&quot;. &lt;br /&gt;
&lt;br /&gt;
What they don&#39;t realize is that this code &quot;works&quot; by coincedence. For example, if a DLL will be loaded in EXE compiled with a different IDE version, memory layout of the &lt;code&gt;Exception&lt;/code&gt; class may be different, so either EXE will crash trying to access properties of exception or it will corrupt exception object on write - leading to access violation and/or memory leaks. Another problem if EXE will decide to do something like:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;try
  FuncInDLL;
except
  // This filter will not work, 
  // as the DLL.Exception class will not match the EXE.Exception class
  on E: Exception do 
  begin
    ShowMessage(E.Message);
    Exit;
  end;  
end;&lt;/pre&gt;
or:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;try
  FuncInDLL;
except
  // This will not work,
  // as EXE will manipulate memory for DLL
  Exception(ExceptObj).Message := &#39;Error in My DLL: &#39; + Exception(ExceptObj).Message;
  raise;
end;&lt;/pre&gt;
&lt;br /&gt;
So, when you have code like:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure FuncInDLL; stdcall;
begin
  // your code
end;&lt;/pre&gt;
and it &quot;works&quot;, and then you add EurekaLog to your DLL and EXE. EurekaLog creates objects containing collected information about exception (the &lt;code&gt;TEurekaExceptionInfo&lt;/code&gt; class from the &lt;code&gt;EException&lt;/code&gt; unit) and associate these exception info objects with exceptions.&lt;br /&gt;
&lt;br /&gt; 
So if you pass exception between DLL and EXE - you also pass EurekaLog&#39;s exception info object between DLL and EXE. And these objects are way more complex than exception objects, and EurekaLog do way more things that your code do with exceptions. So EurekaLog&#39;s code will certainly check exception info object&#39;s class (to see if an exception has associated exception&#39;s info) as well as modify exception info object. Which means: access violation, leaks, incorrect behaviour.&lt;br /&gt;  
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Solutions&lt;/h1&gt;
The exact solution depends on what kind of DLL you are developing:&lt;br /&gt;
&lt;br /&gt;  
&lt;h3&gt;&quot;Pure DLL&quot;&lt;/h3&gt;  
If you are developing a pure DLL - that is: a DLL which can be loaded by any application - then the solution is to never let exceptions escape your DLL. In other words, each and every exported function &lt;b&gt;must&lt;/b&gt; have an &lt;code&gt;except&lt;/code&gt; block. An &lt;code&gt;except&lt;/code&gt; block can be explicit like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function FuncInDLL: BOOL; stdcall;
begin
  try
    // your code
    Result := True; // only as an example
  except
    // handle exception here
    Result := False; // only as an example
  end;
end;&lt;/pre&gt;
or it can be implicit like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure FuncInDLL; safecall; // notice &quot;safecall&quot;
begin
  // your code
end;&lt;/pre&gt;  
In either way, any DLL exception will be handled in the DLL itself and will not leave the DLL. The caller (EXE) will never receive exceptions from DLL, but rather some &quot;failure&quot; flag.&lt;br /&gt;
&lt;br /&gt;
Read &lt;a href=&quot;https://blog.eurekalog.com/2023/09/creating-DLL-api.html&quot; title=&quot;Creating an API (contract) for your own DLL&quot;&gt;this article&lt;/a&gt; for more details.&lt;br /&gt;  
&lt;br /&gt;  
&lt;h3&gt;&quot;Delphi DLL&quot;&lt;/h3&gt;
If you are developing a Delphi DLL which can be used in Delphi applications only - then the solution is to use packages (BPLs).&lt;br /&gt;
&lt;br /&gt;
One option is to convert your DLL to a BPL package. Another option is to compile EXE and DLL with BPL packages. The end result will be the same: both DLL and EXE will share common code and memory manager, therefore eliminating the problem.&lt;br /&gt;
&lt;br /&gt;
So what you should do is to enable the &quot;Link with runtime packages&quot; option in your project&#39;s settings and ensure at least the &lt;code&gt;rtl&lt;/code&gt;, &lt;code&gt;vcl&lt;/code&gt; and &lt;code&gt;EurekaLogCore&lt;/code&gt; packages are listed in the &quot;Runtime packages&quot; list. Using the &lt;code&gt;rtl&lt;/code&gt; package will ensure using shared memory manager (so you can remove a dedicated shared memory manager from your DLL/EXE if you set up it previosly), while using the &lt;code&gt;EurekaLogCore&lt;/code&gt; package will ensure sharing EurekaLog&#39;s code between EXE and DLL.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Important note:&lt;/b&gt; 
&lt;blockquote&gt;Since your DLL will call EurekaLog from the &lt;code&gt;EurekaLogCore&lt;/code&gt; package, the &lt;code&gt;EurekaLogCore&lt;/code&gt; package will not know who is calling it (EXE or DLL). Therefore, it does not know where it should look for EurekaLog&#39;s options. That is why the &lt;code&gt;EurekaLogCore&lt;/code&gt; package will always load EurekaLog&#39;s options (settings) from EXE. EurekaLog&#39;s settings from your DLL will be ignored.&lt;br /&gt;   
&lt;br /&gt;  
This is kinda obvious, if you think about it: using the &lt;code&gt;EurekaLogCore&lt;/code&gt; package means there is only one instance of EurekaLog in your process, so you can&#39;t have two EurekaLog&#39;s options (from EXE and DLL) at the same time. &lt;br /&gt;  
&lt;br /&gt;    
In other words, configure EurekaLog in EXE. For DLL: you can configure EurekaLog as a &quot;package&quot; (if you don&#39;t call EurekaLog&#39;s code from your DLL) or as &quot;Standalone DLL&quot; (if you do call EurekaLog&#39;s code from your DLL).&lt;/blockquote&gt;
&lt;br /&gt;
&lt;h3&gt;&quot;Application DLL&quot;&lt;/h3&gt;
If you are developing a Delphi DLL which will be used in your specific application only - then the solution is to configure your DLL as lightweight DLL.&lt;br /&gt;
&lt;br /&gt;
Note: the solution from the &quot;Delphi DLL&quot; above will also work, but if you don&#39;t want to compile with packages (while you definitely should), using &quot;Lightweight DLL&quot; configuration is a possible alternative.&lt;br /&gt;
&lt;br /&gt;
When you configure your DLL as a &quot;Lightweight DLL&quot;: EurekaLog&#39;s code will not be included in your DLL. Instead: your DLL will make a calls to EurekaLog in your main EXE. Therefore, your process will have only one instance of EurekaLog (in your EXE), while your DLLs will call into EurekaLog from EXE.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Legacy applications&lt;/h3&gt;
If you can&#39;t apply a real solution from above (for example, you are working on a legacy application and can&#39;t introduce changes) - &lt;a href=&quot;https://www.eurekalog.com/support.php&quot; title=&quot;EurekaLog Support&quot;&gt;contact our support&lt;/a&gt; to learn how to disable this check.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
We understand that this error message could be frustrated for users which were using previous versions of EurekaLog. Especially considering the message does not appear at design-time, and it does not appear on startup at run-time, but it rather appears when a specific exception is thrown, which can be detected a lot later.&lt;br /&gt; 
&lt;br /&gt; 
However, you should realize the error dialog is shown to clearly indicate an issue in your application rather than letting the application silently run further and produce hard to diagnose issues later.&lt;br /&gt;
&lt;br /&gt;
We offer possible solutions, among simplest of which is reconfiguring EurekaLog in DLL as &quot;Lightweigh DLL&quot; - without touching your existing EXE/DLL code.&lt;br /&gt;
&lt;br /&gt;
P.S. &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/dll_using_eurekalog.php&quot; title=&quot;Using EurekaLog in DLL&quot;&gt;Read more about using EurekaLog in DLLs&lt;/a&gt;.&lt;br /&gt;
P.P.S. &lt;a href=&quot;https://blog.eurekalog.com/2023/09/creating-DLL-api.html&quot; title=&quot;Creating an API (contract) for your own DLL&quot;&gt;Read more about writing pure DLLs in Delphi&lt;/a&gt;.&lt;br /&gt;
P.P.P.S. We should note using a dedicated shared memory manager is a hack (crutch) from Delphi 2. &lt;a href=&quot;https://blog.eurekalog.com/2023/09/creating-DLL-api.html#t4&quot; title=&quot;Shared Memory Manager (and why you shouldn&#39;t use it)&quot;&gt;New code should &lt;b&gt;never&lt;/b&gt; use it&lt;/a&gt;. Using BPL packages (when developing a Delphi DLL) or following the &quot;whoever allocates memory - frees it&quot; rule (when developing a pure DLL) is a correct way.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/5646867015750471871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/5646867015750471871'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/05/invalid-EurekaLog-configuration-found.html' title='&quot;Invalid EurekaLog configuration found&quot; error'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX9UV-2KIl0fekVcgkQ35uHJZsRkobe4bnT_4PLK-SI5jgVMEAuAP-8xrdysanc8Fjk38EDj1LxajWtIXdkDa2HiDhNnBdgen2Ihqc5GfZZR98IjsOcnViPz-a-_GAfdqovwIKdr2qJCg1MF2FwHdHb6QqqvyWJscwoNLWtcFkkVP7P3ncGgY1tGVk-kA/s72-c/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-2790590358760871064</id><published>2025-05-11T21:20:00.002+01:00</published><updated>2025-06-30T13:42:03.118+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><category scheme="http://www.blogger.com/atom/ns#" term="technical"/><title type='text'>Using EurekaLog with GMail (Google Mail) after May 1, 2025 and after May 14, 2025</title><content type='html'>Google has announced that it’s disabling the &lt;a href=&quot;https://support.google.com/a/answer/14114704&quot; title=&quot;Transition from less secure apps to OAuth&quot;&gt;less secure apps, third-party apps, or devices that ask you to sign in to your Google Account with your username and password&lt;/a&gt; on Google Workspace accounts from May 1st or May 14, 2025:
&lt;blockquote&gt;Starting March 14, 2025, you and your users must use OAuth with third-party apps to access Gmail, Google Calendar, and Google Contacts.You will no longer use a password for access (with the exception of app passwords). Google is turning off access to less secure apps—non-Google apps that can access Google Accounts with a username and password (basic authentication).&lt;/blockquote&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
This is the same change that &lt;a href=&quot;https://blog.eurekalog.com/2022/05/using-EurekaLog-.html&quot; title=&quot;Using EurekaLog with GMail (Google Mail) after May 30, 2022&quot;&gt;Google did in 2022 for most user accounts&lt;/a&gt;. But this time the same change is applied to Google Workspace accounts.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;This change will affect you if you are using EurekaLog to send bug reports via GMail while &lt;b&gt;using your GMail password&lt;/b&gt;. This method is considered unsecure and &lt;b&gt;&lt;font color=&quot;red&quot;&gt;will no longer work on all Google accounts starting May 14, 2025&lt;/font&gt;&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;This change will &lt;b&gt;not&lt;/b&gt; affect you if you are using EurekaLog to send bug reports via GMail while using application-specific passwords.&lt;/li&gt;
&lt;/ul&gt;
While we’ve &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/tracker_security.php&quot; title=&quot;Security Considerations&quot;&gt;recommended using application-specific passwords instead of your GMail passwords&lt;/a&gt; for quite some time, but some customers have chosen to use the &quot;Less Secure Apps&quot; feature since it’s a little more straightforward.&lt;br /&gt;
&lt;br /&gt; 
In other words:
&lt;ol&gt;
&lt;li&gt;If you don&#39;t already have 2-Step Verification (also known as two-factor authentication) enabled for your account - you must &lt;a href=&quot;https://support.google.com/accounts/answer/185839&quot; title=&quot;Turn on 2-Step Verification&quot;&gt;turn it on&lt;/a&gt;. (Application-specific passwords can only be used with accounts that have 2-Step Verification turned on.) You can do it here: &lt;a href=&quot;https://myaccount.google.com/security&quot; title=&quot;Security Settings for your Google Account&quot;&gt;https://myaccount.google.com/security&lt;/a&gt;. Once enabled - you will have to confirm each sign in using your GMail password via a promt on already signed device, a physical security key, a code from a verification app, a phone call/SMS, or a backup code.&lt;/li&gt;
&lt;li&gt;(Optional) Turn off/disable the &quot;Less secure app access&quot; feature.&lt;/li&gt;
&lt;li&gt;You should &lt;a href=&quot;https://support.google.com/accounts/answer/185833&quot; title=&quot;Sign in with App Passwords&quot;&gt;create an application-specific password&lt;/a&gt; for your application with EurekaLog. You can do it here: &lt;a href=&quot;https://myaccount.google.com/apppasswords&quot; title=&quot;Application Passwords Settings for your Google Account&quot;&gt;https://myaccount.google.com/apppasswords&lt;/a&gt;. An App Password is a 16-digit (random) passcode that gives your app permission to access your Google Account.
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR0BZ93nzwceklhaR3xEMwze8QFv0SufWd2c-fglzcflR59HHc_sR86RJFigz5TPWgN4k34eu9Md0XzSvVrKbOGhltZps_xwQaIQQVpdBu44zrJT_B2Q1ZREuHgeXYU077BF5TTfZ_vRmUfrmoLssNQjVgYvUUS9WBOQJhxSTV31b-AI7yWbLip38s/s1116/generated-app-password.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;995&quot; data-original-width=&quot;1116&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR0BZ93nzwceklhaR3xEMwze8QFv0SufWd2c-fglzcflR59HHc_sR86RJFigz5TPWgN4k34eu9Md0XzSvVrKbOGhltZps_xwQaIQQVpdBu44zrJT_B2Q1ZREuHgeXYU077BF5TTfZ_vRmUfrmoLssNQjVgYvUUS9WBOQJhxSTV31b-AI7yWbLip38s/s400/generated-app-password.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
Basically, you can use App Password instead of your (main/primary) GMail password. Most of the time, you’ll only have to enter an App Password once (per application), so don’t worry about memorizing it. You can always create a new/additional one.&lt;/li&gt;  
&lt;/ol&gt;  
&lt;br /&gt;
Please note that we also recommend to use web-based bug tracker instead of e-mails for many reasons:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/reporting.php&quot; title=&quot;Reporting&quot;&gt;Using the large number&#39;s law and Exception Driven Development - EDD&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using HTTP(S) typically avoids all pitfalls with firewalls and security.&lt;/li&gt;
&lt;li&gt;Finer control over access rights.&lt;/li&gt;
&lt;li&gt;Better UI and report management.&lt;/li&gt;
&lt;li&gt;Etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
P.S. There are &lt;a href=&quot;https://support.google.com/a/answer/176600&quot; title=&quot;Send email from a printer, scanner, or app&quot;&gt;two other alternative methods&lt;/a&gt;:
&lt;ol&gt;
&lt;li&gt;SMTP relay service&lt;/li&gt;
&lt;li&gt;Restricted GMail SMTP server&lt;/li&gt;
&lt;/ol&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2790590358760871064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2790590358760871064'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/05/using-GMail-1-14-May-2025.html' title='Using EurekaLog with GMail (Google Mail) after May 1, 2025 and after May 14, 2025'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR0BZ93nzwceklhaR3xEMwze8QFv0SufWd2c-fglzcflR59HHc_sR86RJFigz5TPWgN4k34eu9Md0XzSvVrKbOGhltZps_xwQaIQQVpdBu44zrJT_B2Q1ZREuHgeXYU077BF5TTfZ_vRmUfrmoLssNQjVgYvUUS9WBOQJhxSTV31b-AI7yWbLip38s/s72-c/generated-app-password.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-4323342630323043439</id><published>2025-01-27T19:01:00.009+00:00</published><updated>2026-03-29T12:03:49.306+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best practice"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><title type='text'>Disable visual error dialogs for background apps</title><content type='html'>Typically, background applications run as system services. In this case, fatal errors cause the application to close and possibly restart automatically if specified in the service&#39;s settings.&lt;br /&gt;
&lt;br /&gt;
But sometimes, for some reason, you may want to make a background application as a regular user application. In this case, you would not want your application to show any error messages.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
&lt;h1&gt;Test code&lt;/h1&gt;
To test your application&#39;s behavior with fatal errors - you can use the following code:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// A thread function
function Crash(Arg: Pointer): Integer; stdcall;
begin
  // Artificially raise a test exception
  PInteger(nil)^ := 0;
  Result := 0; 
end;

function TForm1.Button1Click(Sender: TObject);
var
  TID: Cardinal;
begin
  // Create a new thread with the entry point specified above
  CloseHandle(CreateThread(nil, 0, @Crash, nil, 0, TID));
end;&lt;/pre&gt;
This code creates a new &lt;code&gt;Crash&lt;/code&gt; thread using the &lt;a title=&quot;CreateThread Function&quot; href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread&quot;&gt;&lt;code&gt;CreateThread&lt;/code&gt; system function&lt;/a&gt;. Threads created by the &lt;code&gt;CreateThread&lt;/code&gt; function do not have any exception handlers, so if such a thread throws an exception and your thread&#39;s function code does not handle it, the exception is propagated to the system (a global handler), which results in a fatal exception.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg26_YqgjxZTfqxvIcOP03_z6Ehw95TOksq9i8mcfp953IpC2VCqLe55XAbqJWrtxdXc4YrFeq42KLngX8J2qjoF0cu1K_1vpBZsnYC0H_xTZWjUBVqprZ9So9mGLu1CkGLgoLCNpbreufDnWJzNMycMvbcKhRwXUMTHyNVUlHe8OozKesEaJndybZ940w/s1600/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;179&quot; data-original-width=&quot;352&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg26_YqgjxZTfqxvIcOP03_z6Ehw95TOksq9i8mcfp953IpC2VCqLe55XAbqJWrtxdXc4YrFeq42KLngX8J2qjoF0cu1K_1vpBZsnYC0H_xTZWjUBVqprZ9So9mGLu1CkGLgoLCNpbreufDnWJzNMycMvbcKhRwXUMTHyNVUlHe8OozKesEaJndybZ940w/s1600/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
In this case, our test code throws an &lt;code&gt;EAccessViolation&lt;/code&gt; exception, trying to write an integer (0) to a null pointer (nil).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Apps with EurekaLog&lt;/h1&gt;
If your project has EurekaLog on board, your application will behave differently, because EurekaLog will install its own global exception handler. It is much more difficult to get a fatal exception in a project with EurekaLog, so it is better to disable EurekaLog for testing.&lt;br /&gt;
&lt;br /&gt;
Note that adding EurekaLog to your application does &lt;b&gt;not&lt;/b&gt; guarantee that the dialog shown above will never appear for your application. EurekaLog runs &lt;b&gt;from within&lt;/b&gt; the process. It means that there will &lt;b&gt;always&lt;/b&gt; be a chance of a situation so bad that exception handlers inside the application cannot be called, which will lead to the process being closed from outside (by the system). For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Calls a test exception
procedure Kaboom;
begin
  raise Exception.Create(&#39;Error Message&#39;);
end;

// Damages CPU stack and calls the Kaboom function
procedure Test; assembler;
asm
  // We simulate a &quot;buffer overflow&quot; bug
  mov [esp+4],  0;
  mov [esp+8],  0;
  mov [esp+12], 0;

  call Kaboom;
end;

// A test thread&#39;s function
function T(I: Integer): Integer; stdcall;
begin
  try
    Test;
  except
    // Does not matter
  end;
  Result := 0;
end;&lt;/pre&gt;
This code will crash the application even with EurekaLog on board, since this code corrupts the system records of exception handlers. Therefore, when the &lt;code&gt;Kaboom&lt;/code&gt; function raises a test exception, the system cannot call the exception handler (marked as &quot;Does not matter&quot; in the code example) and has no choice but to close the process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Disabling system reports&lt;/h1&gt;
So, to prevent your application from showing the &quot;Program has stopped working&quot; dialog, you can disable the WER (Windows Error Reporting) service for your application. You can do this by calling the following code when your application starts:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;SetErrorMode(GetErrorMode or SEM_NOGPFAULTERRORBOX);&lt;/pre&gt;
&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode&quot; title=&quot;SetErrorMode Function&quot;&gt;The &lt;code&gt;SetErrorMode&lt;/code&gt;&lt;/a&gt; function specifies how the system or process should handle fatal errors. In particular, the &lt;code&gt;SEM_NOGPFAULTERRORBOX&lt;/code&gt; flag specifies that the system should not generate Windows error reports.&lt;br /&gt;
&lt;br /&gt;
Note that you &lt;b&gt;must&lt;/b&gt; modify the already defined process mode, you cannot rewrite it completely from scratch, removing flags that you know nothing about. For example, the following code is &lt;b&gt;WRONG&lt;/b&gt;:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;SetErrorMode(SEM_NOGPFAULTERRORBOX);&lt;/pre&gt;
If you need to &lt;b&gt;turn off&lt;/b&gt; this mode, you should use a code like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;SetErrorMode(GetErrorMode and (not SEM_NOGPFAULTERRORBOX));&lt;/pre&gt;
Note that enabling this mode will completely disable the WER (Windows Error Reporting) service for your application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Disabling only visual dialogs&lt;/h1&gt;
If you do not want to completely disable WER for your application (for example, if you want to use the WER system logs to view the list of &quot;crashes&quot; of your application), you can disable only visual dialogs. This can be done by calling the following code:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  Flags: Cardinal;
begin
  if Failed(WerGetFlags(GetCurrentProcess, Flags)) then
    Flags := 0;
  WerSetFlags((Flags or WER_FAULT_REPORTING_NO_UI) and (not WER_FAULT_REPORTING_ALWAYS_SHOW_UI));&lt;/pre&gt;
The &lt;a title=&quot;WerSetFlags Function&quot; href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/werapi/nf-werapi-wersetflags&quot;&gt;&lt;code&gt;WerSetFlags&lt;/code&gt; function&lt;/a&gt; sets the Windows Error Reporting (WER) options for the current process.&lt;br /&gt;
&lt;br /&gt;
As with &lt;code&gt;SetErrorMode&lt;/code&gt;, you should not call &lt;code&gt;WerSetFlags&lt;/code&gt; blindly, overriding already set modes of operation, you should only change those modes that you want to change. Specifically, the code in the example above sets the &lt;code&gt;WER_FAULT_REPORTING_NO_UI&lt;/code&gt; flag and clears the &lt;code&gt;WER_FAULT_REPORTING_ALWAYS_SHOW_UI&lt;/code&gt; flag. The &lt;code&gt;WER_FAULT_REPORTING_NO_UI&lt;/code&gt; flag tells WER to never show the error reporting user interface for this process. The flag &lt;code&gt;WER_FAULT_REPORTING_ALWAYS_SHOW_UI&lt;/code&gt;, on the contrary, asks WER to always show the error reporting user interface for this process.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;WerSetFlags&lt;/code&gt; function, and the &lt;code&gt;WER_FAULT_REPORTING_NO_UI&lt;/code&gt;, &lt;code&gt;WER_FAULT_REPORTING_ALWAYS_SHOW_UI&lt;/code&gt; flags are not declared in the standard Delphi header files, but you can declare them yourself, import them from the JEDI Windows API Library or from EurekaLog. For example (for EurekaLog):&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  EWER;

var
  Flags: Cardinal;
begin
  // You should call this function before using anything from EWER unit
  InitWER; // imports WER functions
  
  // Will be False for very old Windows (like 2000 and XP)
  if Assigned(WerSetFlags) then
  begin
    if Failed(WerGetFlags(GetCurrentProcess, Flags)) then
      Flags := 0;
    WerSetFlags((Flags or WER_FAULT_REPORTING_NO_UI) and (not WER_FAULT_REPORTING_ALWAYS_SHOW_UI));
  end;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Disabling dialogs in EurekaLog&lt;/h1&gt;
If EurekaLog is added to your project, it can also show dialogs about errors. You can disable these dialogs. First, of course, you should &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/dialogs_page.php&quot; title=&quot;Dialogs page&quot;&gt;switch the main visual dialog&lt;/a&gt; to &quot;None&quot; in the project settings (disables the exception dialog).&lt;br /&gt;
&lt;br /&gt;
If you have configured sending reports in your project, then you should &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/sending_options_page.php&quot; title=&quot;Sending options page&quot;&gt;disable visual support for sending reports&lt;/a&gt; by disabling the &quot;Show send progress&quot;, &quot;Show success message&quot;, and &quot;Show failure message&quot; options.&lt;br /&gt;
&lt;br /&gt;
Finally, in some rare cases, EurekaLog can show simple messages (via &lt;code&gt;MessageBox&lt;/code&gt;). These dialogs can also be disabled. To do this, you need to enable hooks for non-visual applications (&quot;[Non-visual] Hides MessageBoxes&quot;) in the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/hooks_page_code.php&quot; title=&quot;Hooks page&quot;&gt;hooks settings&lt;/a&gt;. This option is enabled automatically if you select a suitable profile (for example, a system service) during the initial project setup for EurekaLog, but if you select the standard &quot;VCL Forms application&quot; profile, you need to enable this setting manually.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Important note:&lt;/b&gt; technically there is a callback in EurekaLog, which implements a MessageBox. Every time EurekaLog wants to show a simple message, this callback will be called. This callback can be set to show a visual dialog (like the usual MessageBox), it can be set to output to the concole, it can be set to do nothing. The &quot;None&quot; dialog sets the &quot;[Non-visual] Hides MessageBoxes&quot; setting, which sets the callback to do nothing. While the &quot;MS Classic&quot; dialog sets the callstack to show visual message box. Obviosly, you can&#39;t have both at the same time - you have either one or another.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Mixed mode apps or exceptions&lt;/h1&gt;
Sometimes your application has to work in two modes:&lt;br /&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;b&gt;Visual&lt;/b&gt;. For example, when launched by a user from the Start menu.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Non-visual&lt;/b&gt;. For example, when launched as a service or from autorun/Task Sheduler.&lt;/li&gt;
&lt;/ol&gt;
And sometimes your application wants to handle exceptions differently (even if it was visually launched by a user):&lt;br /&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;b&gt;Show exception&lt;/b&gt;. For example, when an exception happens in the main (UI) thread.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Hide exception&lt;/b&gt;. For example, when an exception happens in a service background thread.&lt;/li&gt;
&lt;/ol&gt;
If this is the case - you would need to switch at run-time between being visual and hiding visuals.&lt;br /&gt;
&lt;br /&gt;
First, you need to configure your application to be visual - set dialog to an appropriate option (such as &quot;MS Classic&quot;). Enable all other visual behaviour, like if your app would be run by a user.&lt;br /&gt;
&lt;br /&gt;
Once you set up your application to behave correctly when interacting with a user - write some code to turn off visual behaviour at run-time. For example, if you write a mixed mode app, you can decice how it should run at startup. If it should run in the visual mode - do nothing. If it should run in non-visual mode - turn off visuals:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;initialization
  if RunAsService then // your function to decide how to launch your app
  begin
    CurrentEurekaLogOptions.ExceptionDialogType := edtNone;
    CurrentEurekaLogOptions.sndSendInSeparatedThread := False;
    CurrentEurekaLogOptions.sndShowSendDialog := False;
    CurrentEurekaLogOptions.sndShowSuccessMsg := False;
    CurrentEurekaLogOptions.sndShowSuccessBugClosedOnlyMsg := False;
    CurrentEurekaLogOptions.sndShowFailureMsg := False;
    CurrentEurekaLogOptions.boSaveCompressedCopyInCaseOfError := False;
    CurrentEurekaLogOptions.boCopyLogInCaseOfError := False;
    CurrentEurekaLogOptions.sndScreenshot := ssNone;
  end;
end.&lt;/pre&gt;
Please see: &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_change_settings&quot;&gt;How to change EurekaLog&#39;s settings at run-time?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you write an app, which handles various exceptions differently - then you have to decide which exceptions you want to hide. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  EEvents; // for RegisterEventExceptionNotify
 
// Custom handler to customize exception&#39;s handling 
procedure HideBackgroundExceptions(const ACustom: Pointer; 
  AExceptionInfo: TEurekaExceptionInfo; 
  var AHandle: Boolean; 
  var ACallNextHandler: Boolean);
begin
  // If the exception is coming from a background thread
  // (this is only an example, you can/should replace it with your own code)
  if AExceptionInfo.ThreadID &amp;lt;&amp;gt; MainThreadID then
  begin
    // Disable all visual feedback for this exception
    AExceptionInfo.Options.ExceptionDialogType := edtNone;
    AExceptionInfo.Options.sndShowSendDialog := False;
    AExceptionInfo.Options.sndShowSuccessMsg := False;
    AExceptionInfo.Options.sndShowSuccessBugClosedOnlyMsg := False;
    AExceptionInfo.Options.sndShowFailureMsg := False;
    AExceptionInfo.Options.boSaveCompressedCopyInCaseOfError := False;
    AExceptionInfo.Options.boCopyLogInCaseOfError := False;
    AExceptionInfo.Options.sndScreenshot := ssNone;
    AExceptionInfo.Options.sndSendInSeparatedThread := False;
  end;  
end;
 
initialization
  // Ask EurekaLog to call our code for each exception
  RegisterEventExceptionNotify(nil, HideBackgroundExceptions);
end.&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
P.S. If you are developing a service, you will probably want to use the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/system_logging_setup.php&quot; title=&quot;System logging setup&quot;&gt;system logging setup dialog&lt;/a&gt; instead of disabling the dialog completely.&lt;br /&gt;
&lt;br /&gt;
P.P.S. Since you are writing a background application which runs as a standard user application, system restart settings will not be applied to your application. So it will be simply terminated in case of a crash. You may set up &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/restart_recovery_page.php&quot; title=&quot;Restart&amp;Recovery page&quot;&gt;restart settings in EurekaLog&lt;/a&gt;, but you should be aware that these settings will only work if your application shut downs more or less gracefully (under control of EurekaLog). For example, we had a code example above that illustrates how EurekaLog-enabled application can perform a fatal crash. Your application will be closed by an external process (the system) in that case, so any user code from within your process won&#39;t be called (including EurekaLog). It means that if you want a reliable way to restart your application - you have to have some sort of external monitor process, which will restart your application in case of fatal crash.&lt;br /&gt;
&lt;br /&gt;
See also:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.eurekalog.com/2017/05/threads-snapshot-as-postmortem-debugger.html&quot; title=&quot;Using Threads Snapshot tool as postmortem debugger&quot;&gt;Using Threads Snapshot tool as postmortem debugger&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.eurekalog.com/2020/03/how-to-find-out-why-your-application.html&quot; title=&quot;How to find out why your application suddenly closes?&quot;&gt;How to find out why your application suddenly closes?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4323342630323043439'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4323342630323043439'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2025/01/disable-visual-error-dialogs-for-background-apps.html' title='Disable visual error dialogs for background apps'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg26_YqgjxZTfqxvIcOP03_z6Ehw95TOksq9i8mcfp953IpC2VCqLe55XAbqJWrtxdXc4YrFeq42KLngX8J2qjoF0cu1K_1vpBZsnYC0H_xTZWjUBVqprZ9So9mGLu1CkGLgoLCNpbreufDnWJzNMycMvbcKhRwXUMTHyNVUlHe8OozKesEaJndybZ940w/s72-c/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-4242986201963170762</id><published>2024-11-28T20:39:00.003+00:00</published><updated>2025-01-27T16:43:58.885+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="non-technical"/><title type='text'>Black Friday and Cyber Monday Sale</title><content type='html'>Neos Eureka s.r.l. is proud to announce our Black Friday / Cyber Monday sale!&lt;br /&gt;
&lt;br /&gt;
Use the following 30% off coupon code when checking out/paying to take advantage of this sale:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;BFCM2024&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
This coupon code is valid from 29 November through 6 December 2024.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;hr /&gt;
&lt;br /&gt;
Existing customers should log in to their account at:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/login.php&quot; title=&quot;Purchase EurekaLog License&quot;&gt;https://www.eurekalog.com/login.php&lt;/a&gt;&lt;br /&gt;
And select the license they wish to upgrade or extend. Enter the above code into the coupon code field at checkout time.&lt;br /&gt;
&lt;br /&gt;
New customers can purchase licenses here:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; title=&quot;Purchase EurekaLog License&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;&lt;br /&gt;
Enter the coupon code at checkout.&lt;br /&gt;
&lt;br /&gt;
The coupon code is valid once per user account.&lt;br /&gt;
&lt;br /&gt;
First, you need to select which option you want to purchase:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBu1KCATqHmQLkP5IhRTQbSQr3WO-VPj6H6g5J4mDsYX23eR4X8rRUDGkENZhxuepXSl6GOz77urxluHlTGSP10H1UqCXB6pBvc81ua18qzU6rvLn7EK3YEqjUPy3ZS4_YJPhrQuQM95gbl8m_j90qIbpLGxv7hcWZTXlpvJIobi030P6_vQHfl6WSjqg/s1600/1.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;297&quot; data-original-width=&quot;796&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBu1KCATqHmQLkP5IhRTQbSQr3WO-VPj6H6g5J4mDsYX23eR4X8rRUDGkENZhxuepXSl6GOz77urxluHlTGSP10H1UqCXB6pBvc81ua18qzU6rvLn7EK3YEqjUPy3ZS4_YJPhrQuQM95gbl8m_j90qIbpLGxv7hcWZTXlpvJIobi030P6_vQHfl6WSjqg/s1600/1.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Second, you enter your billing details:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge0F4MMjIsR_6vVc0lxrcUoYEt6_zPFXf3uBOLmcQ7RIH7rX4HP4ZC40cws_7499DmXi0GbH2U63fLQPxIQKuRUtXclVmRDp7DKZc5X1b0gTJVL7IHxEcXNRcsHd5_NkJBC3Ng3mmSsCBj2UFMLUNF_XmDbxYyS4TJ3uHu3nnHtgOoJWYfMN8_nw-8jfY/s1600/2.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;763&quot; data-original-width=&quot;625&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge0F4MMjIsR_6vVc0lxrcUoYEt6_zPFXf3uBOLmcQ7RIH7rX4HP4ZC40cws_7499DmXi0GbH2U63fLQPxIQKuRUtXclVmRDp7DKZc5X1b0gTJVL7IHxEcXNRcsHd5_NkJBC3Ng3mmSsCBj2UFMLUNF_XmDbxYyS4TJ3uHu3nnHtgOoJWYfMN8_nw-8jfY/s1600/2.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
Don&#39;t forget to &lt;b&gt;enter your Tax / VAT ID&lt;/b&gt;! And, of course, enter the discount coupon code.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_rZn4t0otdlk5rjmLSQbmBOSxjkOXZk7230ZbDSeXHtp3aosuQaqvEp8IwUHJqaToX747lKmsi6N2PiHGS5cu8RKOoCT7utdTeHSzlhBQ_x2svik2WhmTFMs7VpnZIONhaHH5TxBYIwfnokQyKuCXqWkQS46v9IPMspApPMzlp3uB0eF1rf0Iltj78YI/s1600/3.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img width=&quot;700&quot; alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;635&quot; data-original-width=&quot;1059&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_rZn4t0otdlk5rjmLSQbmBOSxjkOXZk7230ZbDSeXHtp3aosuQaqvEp8IwUHJqaToX747lKmsi6N2PiHGS5cu8RKOoCT7utdTeHSzlhBQ_x2svik2WhmTFMs7VpnZIONhaHH5TxBYIwfnokQyKuCXqWkQS46v9IPMspApPMzlp3uB0eF1rf0Iltj78YI/s1600/3.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Ensure the discount is applied and the tax looks correct. Finally, make a payment.&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
EurekaLog detects exceptions, memory leaks and other problems in your Delphi or C++ Builder application.&lt;br /&gt;
&lt;br /&gt;
When trouble is found, it creates a bug report and sends it to your development team via Email or your web server. The bug report contains a stack trace that shows the place in your application where the crash occurred. The report can contain an optional screen shot, assembly code, configuration of the user&#39;s computer and many other factors.&lt;br /&gt;
&lt;br /&gt;
If you have questions or comments then please contact us at:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/support.php&quot; title=&quot;Contact Support&quot;&gt;https://www.eurekalog.com/support.php&lt;/a&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4242986201963170762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4242986201963170762'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2024/11/black-friday-and-cyber-monday-sale.html' title='Black Friday and Cyber Monday Sale'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBu1KCATqHmQLkP5IhRTQbSQr3WO-VPj6H6g5J4mDsYX23eR4X8rRUDGkENZhxuepXSl6GOz77urxluHlTGSP10H1UqCXB6pBvc81ua18qzU6rvLn7EK3YEqjUPy3ZS4_YJPhrQuQM95gbl8m_j90qIbpLGxv7hcWZTXlpvJIobi030P6_vQHfl6WSjqg/s72-c/1.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-6962069267997950072</id><published>2024-03-07T18:44:00.006+00:00</published><updated>2025-09-16T13:47:42.701+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="installation"/><category scheme="http://www.blogger.com/atom/ns#" term="Stories"/><title type='text'>The Secret of Access Denied</title><content type='html'>A client contacted us and said that he could not install EurekaLog. More precisely: EurekaLog is installed, but subsequent launch of the IDE raises an error:
&lt;blockquote&gt;Can&#39;t load package C:\Program Files (x86)\Neos Eureka S.r.l\EurekaLog 7\Packages\Studio25\EurekaLogExpert250.bpl.
Access is denied.&lt;/blockquote&gt;
Moreover, the client claimed that the file exists, there is access to it, reinstalling EurekaLog does not help.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Additionally, it was found that the code from the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; package actually does not receive control (is not executed).&lt;br /&gt;
&lt;br /&gt;
The IDE uses the same &lt;code&gt;LoadPackage&lt;/code&gt; function from the &lt;code&gt;SysUtils&lt;/code&gt; unit that you (your code) use. The IDE does not execute any secret code. The &lt;code&gt;LoadPackage&lt;/code&gt; function is implemented as follows:
&lt;pre class=&quot;brush:delphi&quot;&gt;function LoadPackage(const Name: string; AValidatePackage: TValidatePackageProc): HMODULE;
begin
  Result := SafeLoadLibrary(Name);
  if Result = 0 then
    raise EPackageError.CreateResFmt(@sErrorLoadingPackage, [Name, SysErrorMessage(GetLastError)]); // -----
  try
    InitializePackage(Result, AValidatePackage);
  except
    FreeLibrary(Result);
    raise;
  end;
end;&lt;/pre&gt;
where the &lt;code&gt;InitializePackage&lt;/code&gt; function is implemented as:
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure InitializePackage(Module: HMODULE; AValidatePackage: TValidatePackageProc);
type
  TPackageLoad = procedure;
var
  PackageLoad: TPackageLoad;
begin
  CheckForDuplicateUnits(Module, AValidatePackage);
  @PackageLoad := GetProcAddress(Module, &#39;Initialize&#39;); //Do not localize
  if Assigned(PackageLoad) then
    PackageLoad
  else
    raise EPackageError.CreateFmt(sInvalidPackageFile, [GetModuleName(Module)]);
end;&lt;/pre&gt;
As you can see: the only place where an exception with an error code from the OS (5 = &lt;code&gt;ERROR_ACCESS_DENIED&lt;/code&gt;) can be raised when loading a package is the marked line. This means that an Access Denied error when loading a package can only occur if the call to the &lt;code&gt;LoadLibrary&lt;/code&gt; function fails.&lt;br /&gt;
&lt;br /&gt;
Therefore, the client was asked to create a new empty VCL application with the following test code:
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure TForm1.Button1Click(Sender: TObject);
var
  Lib: HMODULE;
begin
  Lib := LoadPackage(&#39;C:\Program Files (x86)\Neos Eureka S.r.l\EurekaLog 7\Packages\Studio25\EurekaLogExpert250.bpl&#39;);
  if Lib = 0 then
    RaiseLastOSError
  else
    MessageBox(0, &#39;The package was loaded!&#39;, &#39;Test&#39;, 0);
end;&lt;/pre&gt;
As it turns out, the test application loads the package successfully.&lt;br /&gt;
&lt;br /&gt;
To be sure that the IDE was not executing any additional hidden code, we asked the client to create a new Design-Time package with a new unit containing only the test code:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;unit Unit1;

procedure Register;

implementation

uses
  Windows, SysUtils;

procedure Register;
var
  Lib: HMODULE;
begin
  Lib := LoadPackage(&#39;C:\Program Files (x86)\Neos Eureka S.r.l\EurekaLog 7\Packages\Studio25\EurekaLogExpert250.bpl&#39;);
  if Lib = 0 then
    RaiseLastOSError
  else
    MessageBox(0, &#39;The package was loaded!&#39;, &#39;Test&#39;, 0);
end;

end.&lt;/pre&gt;
And when the client tried to load this test package, the IDE again gave an error message.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s make an intermediate result:
&lt;ul&gt;&lt;li&gt;The test application successfully loads the package;&lt;/li&gt;&lt;li&gt;The test package (and IDE) cannot load the package.&lt;/li&gt;&lt;/ul&gt;
This strongly suggests that the problem is not with the package, but with the IDE itself.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;We also tried the following:
&lt;ol&gt;
   &lt;li&gt;Launch the IDE (without EurekaLog);&lt;/li&gt;
   &lt;li&gt;Open the Run / Load Process menu item;&lt;/li&gt;
   &lt;li&gt;Specify &lt;code&gt;bds.exe&lt;/code&gt; as the target process for debugging;&lt;/li&gt;
   &lt;li&gt;Launch the second instance of the IDE. In this case: the first instance will be the debugger, and the second will be the debugged one;&lt;/li&gt;
   &lt;li&gt;In the process being debugged: try to add/load the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; package;&lt;/li&gt;
   &lt;li&gt;In the debugger: monitor for exceptions that occur.&lt;/li&gt;
&lt;/ol&gt;
This algorithm did not provide any new information. In the debugger: we saw that in the process being debugged, the &lt;code&gt;LoadLibrary&lt;/code&gt; function returns 0, which leads to an error being raised - as we assumed above.&lt;/blockquote&gt;
Since both we and the client had already checked the access rights to the file &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; a hundred times and saw no problems, we had to do something else.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;EurekaLog&lt;b&gt;Expert&lt;/b&gt;250.bpl&lt;/code&gt; package is a Design-Time package. It depends on the Run-Time package &lt;code&gt;EurekaLog&lt;b&gt;Core&lt;/b&gt;250.bpl&lt;/code&gt;. This means that if the &lt;code&gt;LoadLibrary&lt;/code&gt; function loads the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; package, it will see that the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; package references the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; file and will try to load it too. And if a problem arose while loading the &lt;code&gt;EurekaLog&lt;b&gt;Core&lt;/b&gt;250.bpl&lt;/code&gt; package, it will bubble up to the top level and will be returned to the caller by the &lt;code&gt;LoadLibrary&lt;/code&gt; function.&lt;br /&gt;
&lt;br /&gt;
Therefore, the client also checked the file &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; (located in the &lt;code&gt;C:\Windows\System32&lt;/code&gt; folder) and found no problems.&lt;br /&gt;
&lt;br /&gt;
Next, we asked the client to use Microsoft/SysInternals Process Monitor tool to monitor how the IDE was accessing files. It was necessary to launch the IDE, open the adding component dialog, then launch the Process Monitor tool and specify the &quot;Process Name=bds.exe =&gt; include&quot; filter. Then try to add the package, see the error message and save the resulting report.&lt;br /&gt;
&lt;br /&gt;
In the resulting report, we ran a search for the &quot;Eureka&quot; word and immediately saw that the &lt;code&gt;bds.exe&lt;/code&gt; was able to open and read the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; file - which further confirms that there is nothing wrong with the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; file.&lt;br /&gt;
&lt;br /&gt;
But the lines immediately below confused us: Process Monitor reported that the &lt;code&gt;bds.exe&lt;/code&gt; is trying to access the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; from IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder. This is strange because modern versions of EurekaLog do not touch IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder.&lt;br /&gt;
&lt;blockquote&gt;(Very) old versions of EurekaLog installed the &lt;code&gt;ecc32.exe&lt;/code&gt; file in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder. We had to remove this behavior because &lt;a title=&quot;Starting Delphi Or C++ Builder Results In Product Or License Validation Error&quot; href=&quot;https://blogs.embarcadero.com/starting-delphi-or-c-builder-results-in-product-or-license-validation-error/&quot;&gt;Modern versions of the IDE may throw an integrity/license verification error if there are third-party files in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder&lt;/a&gt;:&lt;br / &gt;
&lt;blockquote&gt;Question: I’ve installed and registered C++ Builder or Delphi, yet when it starts I am brought to a web page with the error “Product or License Validation Error”. How can I fix this?&lt;br /&gt;
Answer: By far the most common cause for this error is having files or applications not provided by Embarcadero that are copied into the bin folder below where RAD Studio is installed. Only files provided by Embarcadero may reside in the bin folder.&lt;/blockquote&gt;
Therefore, modern versions of EurekaLog do not copy any files to IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder.&lt;/blockquote&gt;
But when we tried to check the access rights to the &lt;code&gt;\bin\EurekaLogCore250.bpl&lt;/code&gt; file, we... did not find such file!&lt;br /&gt;
&lt;br /&gt;
As it turns out: the &lt;code&gt;\bin\EurekaLogCore250.bpl&lt;/code&gt; is, in fact, a directory! The mystery is solved!&lt;br /&gt;
&lt;br /&gt;
So, the following happened:&lt;br /&gt;
&lt;ol&gt;
   &lt;li&gt;The IDE calls the &lt;code&gt;LoadPackage&lt;/code&gt; function to load the package;&lt;/li&gt;
   &lt;li&gt;The &lt;code&gt;LoadPackage&lt;/code&gt; function calls the &lt;code&gt;LoadLibrary&lt;/code&gt; function to load the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; file;&lt;/li&gt;
   &lt;li&gt;The &lt;code&gt;LoadLibrary&lt;/code&gt; function (successfully) opens and reads the &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; file;&lt;/li&gt;
   &lt;li&gt;The &lt;code&gt;LoadLibrary&lt;/code&gt; function sees that the &lt;code&gt;EurekaLog&lt;b&gt;Expert&lt;/b&gt;250.bpl&lt;/code&gt; file contains a link/reference to the &lt;code&gt;EurekaLog&lt;b&gt;Core&lt;/b&gt;250.bpl&lt;/code&gt; file;&lt;/li&gt;
   &lt;li&gt;The &lt;code&gt;LoadLibrary&lt;/code&gt; function tries to load the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; file;&lt;/li&gt;
   &lt;li&gt;The &lt;code&gt;LoadLibrary&lt;/code&gt; function uses &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order&quot; title=&quot;Dynamic-link library search order&quot;&gt;standard operating system&#39;s library search rules&lt;/a&gt; and sees that &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; is located right here: in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder;&lt;/li&gt;
   &lt;li&gt;The &lt;code&gt;LoadLibrary&lt;/code&gt; function tries to load &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; from IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder;&lt;/li&gt;
   &lt;li&gt;The &lt;code&gt;LoadLibrary&lt;/code&gt; function returns error 5 (Access Denied) because the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; &lt;b&gt;folder&lt;/b&gt; in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder does not have the &quot;EXECUTE&quot; access right&quot;.&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
And, indeed, everything worked after deleting the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; folder from IDE&#39;s &lt;code&gt;\bin&lt;/code&gt;  folder.&lt;br /&gt;
&lt;br /&gt;
But where did the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; folder in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder came from? I don&#39;t know. The EurekaLog installer is made using InnoSetup. Of course, the InnoSetup installation file for EurekaLog has no idea about IDEs you have installed and folders in which they are installed. And therefore the InnoSetup installation file for EurekaLog will not be able to create any folder there - simply because it does not know where it is.&lt;br /&gt;
&lt;br /&gt;
Registration of EurekaLog in the IDE is carried out by the standalone registration program: &lt;code&gt;.exe&lt;/code&gt; file compiled in Delphi from our source code. The problem is that in our source code for EurekaLog registration does not have a single call to &lt;code&gt;MkDir&lt;/code&gt; or &lt;code&gt;ForceDirectories&lt;/code&gt; functions: the EurekaLog registration application does not create directories, it copies files and enters data about them into Windows registry.&lt;br /&gt;
&lt;br /&gt;
Even if the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder was created by our installer: why is it a directory and not a file? And why is it alone? Why are there no &lt;code&gt;EurekaLogExpert250.bpl&lt;/code&gt; and &lt;code&gt;EurekaLogComponent250.bpl&lt;/code&gt; - after all, these three files go together.&lt;br /&gt;
&lt;br /&gt;
So far it looks like &lt;i&gt;someone&lt;/i&gt; has created a &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; directory in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder. Perhaps it was some kind of IDE expert?&lt;br /&gt;
&lt;br /&gt;
If you are our client, if you saw this error and know who creates the &lt;code&gt;EurekaLogCore250.bpl&lt;/code&gt; directory in IDE&#39;s &lt;code&gt;\bin&lt;/code&gt; folder - please &lt;a href=&quot;https://www.eurekalog.com/support.php&quot; title=&quot;Contact Us&quot;&gt;let us know&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
P.S. &lt;a href=&quot;https://blog.eurekalog.com/search/label/Stories&quot; title=&quot;EurekaLog Blog: Stories&quot;&gt;Read more stories like this one&lt;/a&gt; or &lt;a href=&quot;https://www.eurekalog.com/casestudies.php&quot; title=&quot;Case Studies and User Reviews&quot;&gt;read feedback from our customers&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/6962069267997950072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/6962069267997950072'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2024/03/access-denied.html' title='The Secret of Access Denied'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-128808246645321237</id><published>2023-12-07T17:14:00.002+00:00</published><updated>2023-12-07T17:14:41.463+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best practice"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><category scheme="http://www.blogger.com/atom/ns#" term="technical"/><category scheme="http://www.blogger.com/atom/ns#" term="tips"/><title type='text'>Beware of secondary exceptions</title><content type='html'>We were contacted by a client who complained that EurekaLog was generating an error report in the wrong place. In fact, the client had an &lt;b&gt;expected&lt;/b&gt; exception that he wanted to hide by showing a simple message instead. The client kindly showed his code:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;try
  Query.Delete; // - an exception is raised here
except
  Query.Transaction.Rollback;
  ShowMessage(&#39;Sorry, could not delete the report&#39;);
  Exit;
end;&lt;/pre&gt;
What&#39;s happening? Does EurekaLog really ignore user code?&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
If the client code were written like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;try
  Query.Delete; // - an exception is raised here
except
  Exit;
end;&lt;/pre&gt;
then there would be no problem: &lt;code&gt;Query.Delete&lt;/code&gt; raises an exception, the exception is caught by the &lt;code&gt;except&lt;/code&gt; block, which does nothing, and execution continues further, EurekaLog does not react (*). &lt;br /&gt;
&lt;br /&gt;
However, the client code looks different: its &lt;code&gt;except&lt;/code&gt; block does some (complex) exception recovery.&lt;br /&gt;
&lt;br /&gt;
In any case, such exceptions are called handled because they are handled by the user&#39;s (client&#39;s) code. A handled exception is handled by cleanup code, which performs some recovery/rollback actions, after which program execution continues and the exception itself is removed.&lt;br /&gt;
&lt;br /&gt;
In general, any cleanup code (be it a destructor or a rollback/recovery code) should be written in a such way so it will not raise exceptions. It seems to me that the logic here is obvious: if your &lt;i&gt;normal&lt;/i&gt; code raises an exception, then you can always suggest a possible recovery actions (the so-called “Plan B”). For example, if an exception occurs when opening a document - then the document needs to be closed; If an exception occurs while accessing the printer - the print job must be canceled. If there is an error opening a file - you can try to open it again after a pause or user&#39;s action. And so on.&lt;br /&gt;
&lt;br /&gt;
But what will you do if an error occurs in the “Plan B” itself? You wanted to cancel the print job, but it... is not cancelled. So what now? You will not be able to offer any reasonable recovery actions. Because you don&#39;t know what happened. Your variables may be corrupted. Hell, you might not even be able to allocate a block of memory right now! The only possible clean way is to kill/restart the application.&lt;br /&gt;
&lt;br /&gt;
This is why the cleanup code must NOT raise exceptions. Because you can&#39;t do anything meaningful with these exceptions. In other words: the cleanup code must handle all exceptions that it knows how to handle.&lt;br /&gt;
&lt;br /&gt;
But in practice, things may not be the same as in an ideal world. &lt;br /&gt;
&lt;br /&gt;
In this case, the client&#39;s code raises a &lt;b&gt;second&lt;/b&gt; exception inside &lt;code&gt;Query.Transaction.Rollback&lt;/code&gt;. It means that the execution of the &lt;code&gt;except&lt;/code&gt; block is interrupted and execution moves to the exception handler higher up the stack. It turns out that there are no more &lt;code&gt;try&lt;/code&gt; blocks higher up the stack in the client&#39;s code, so execution is transferred to the global exception handler, which calls EurekaLog to generate a bug report.&lt;br /&gt;
&lt;br /&gt;
Correcting the code will involve following the rule “cleanup code must not raise exceptions” (“cleanup code must handle known exceptions”). For example, as follows:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;try
  Query.Delete; // - an exception is raised here
except
  try
    Query.Transaction.Rollback;
  except
    on EDatabaseError do; // - ignore database errors
  end;
  ShowMessage(&#39;Sorry, could not delete the report&#39;);
  Exit;
end;&lt;/pre&gt;
In this case, the second exception will be handled in place and will not &quot;bubble up&quot; to the next exception handler (be it a &lt;code&gt;try&lt;/code&gt; block or a global exception handler). It is important to write the correct code - i.e. so that it doesn&#39;t block exceptions that you don&#39;t know how to handle. For example, the following code is incorrect:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;
  try
    Query.Transaction.Rollback;
  except
    // - ignore ALL errors
  end;&lt;/pre&gt;
It is incorrect because you do not know how to properly handle/rollback/recover from, for example, an &lt;code&gt;EAccessViolation&lt;/code&gt; exception. It is best to write the code so that it takes into account the most narrow conditions for exceptions - only those that you know exactly how to handle. For example, if you know a specific type/code of the error when canceling a transaction - then write like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;
  try
    Query.Transaction.Rollback;
  except
    on E: ESomeSpecificDatabaseException do
      if E.ErrorCode = 1234 then
      begin
        // - ignore only this specific error
      end
      else
        raise;
  end;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remarks:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;(*) ...unless the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/advanced_page.php&quot; title=&quot;EurekaLog Advanced Options&quot;&gt;Catch Handled Exceptions option is not enabled in EurekaLog&lt;/a&gt;. When the Catch Handled Exceptions option is enabled - EurekaLog will show bug reports about all exceptions, even if they were handled by &lt;code&gt;except&lt;/code&gt; blocks. This option is intended for local debugging, as an aid in finding a &quot;bad&quot; code. It should not be used in production.&lt;/li&gt;
&lt;li&gt;If you don&#39;t need to do special cleanup after an exception, but need to block an expected exception, then you can &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_ignore_particular_exception.php&quot; title=&quot;How to ignore a particular exception&quot;&gt;ignore it&lt;/a&gt;, or &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_mark_exception_as_expected.php&quot; title=&quot;How to mark exceptions as expected&quot;&gt;mark it as &quot;expected&quot;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you do not fix your code so that your recovery/handling code does not raise exceptions, then you will have secondary exceptions. EurekaLog has &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/nested_exceptions_page.php&quot; title=&quot;Exceptions Options&quot;&gt;a set of options that control EurekaLog&#39;s behavior for such exceptions&lt;/a&gt;.&lt;/li &gt;
&lt;/ul&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/128808246645321237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/128808246645321237'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2023/12/beware-secondary-chained-exceptions.html' title='Beware of secondary exceptions'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-4413275403827533003</id><published>2023-11-17T08:59:00.001+00:00</published><updated>2023-11-17T08:59:43.298+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="non-technical"/><title type='text'>Black Friday and Cyber Monday Sale</title><content type='html'>We are pleased to announce Black Friday and Cyber Monday &lt;b&gt;30% discount&lt;/b&gt; on any of our EurekaLog products using the coupon code found below.&lt;br /&gt;
&lt;br /&gt;
The sale starts on Black Friday (November, 24) and ends at the end of Cyber Monday (November, 27).&lt;br /&gt;
&lt;br /&gt;
Enter this code when paying for the item on our web site:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;BFCM2023&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
Existing customers with valid or expired licenses can log in and purchase upgrades, new licenses and extensions here:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/login.php&quot; title=&quot;Log in to customer control panel&quot;&gt;https://www.eurekalog.com/login.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Use the login credentials we sent you at purchase time.&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
New customers (without existing licenses) can use the discount code here:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; title=&quot;Purchase EurekaLog&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4413275403827533003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4413275403827533003'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2023/11/black-friday-and-cyber-monday-sale.html' title='Black Friday and Cyber Monday Sale'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-7613977302579038776</id><published>2023-11-10T13:39:00.002+00:00</published><updated>2023-11-10T13:39:35.432+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="Releases"/><title type='text'>EurekaLog 7.12 is out</title><content type='html'>We are pleased to announce the availability of the new 7.12 version.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;EurekaLog Logo&quot;&gt;&lt;img alt=&quot;EurekaLog Logo&quot; src=&quot;https://www.eurekalog.com/images/logo.png&quot; height=&quot;68&quot; width=&quot;295&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
EurekaLog is a tool for Delphi and C++Builder that gives your application the power to catch every exception and memory/resource leak, generating a detailed log of the call stack (with unit, procedure and line number), optionally sending you a copy of each log entry via email or the Web via the most used Web Bug-Tracking tools (as Mantis, BugZilla, FogBugz, JIRA, YouTrack, Redmine, Exceptionless, GitLab, and GitHub).&lt;/blockquote&gt;
EurekaLog represents the most advanced exception and memory leaks logger technology available for Delphi/C++Builder developers.&lt;br /&gt;
&lt;br /&gt;
To learn more about EurekaLog, please visit our website at:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com&quot;&gt;https://www.eurekalog.com&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
To download a demo, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/downloads.php&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/downloads.php&quot;&gt;https://www.eurekalog.com/downloads.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you would like to purchase a new license, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/buy.php&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Changes in 7.12 build:&lt;/strong&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Added: &lt;b&gt;[IMPORTANT]&lt;/b&gt; Added workflow path from CLOSED to NEW/OPEN/REOPENED for some bug trackers. &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/bug_tracker_workflow.php&quot; title=&quot;Bug Tracker Setup / Issue workflow&quot;&gt;Please see&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Added: &lt;b&gt;[IMPORTANT]&lt;/b&gt; Added ability to catch non-exception crashes in C++ Builder, such as Abnormal Program Termination. &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/configuring_cppbuilder.php&quot; title=&quot;Configuring C++ Builder project for EurekaLog&quot;&gt;Please see&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Added: Support for RAD Studio 12&lt;/li&gt;
&lt;li&gt;Added: Recompiled for RAD Studio 11 update 3&lt;/li&gt;
&lt;li&gt;Added: New option for EurekaLog&#39;s behaviour when processing secondary exceptions during exception&#39;s processing&lt;/li&gt;
&lt;li&gt;Added: Completely reworked internals of calling default RTL handler to allow proper feature functioning in all cases&lt;/li&gt;
&lt;li&gt;Added: EurekaLog will now show call stack dump when EIP/RIP is invalid&lt;/li&gt;
&lt;li&gt;Added: New variable: bug location (address); variable name: _Location&lt;/li&gt;
&lt;li&gt;Added: &quot;Reserve lower memory&quot; option&lt;/li&gt;
&lt;li&gt;Added: Support for mORMot2 debug info format&lt;/li&gt;
&lt;li&gt;Added: Now user&#39;s address will be used as &quot;Reply-to&quot; header when sending bug reports&lt;/li&gt;
&lt;li&gt;Added: --el_wait command line switch with default value of 60 seconds. Now EurekaLog will wait for executable file to become unlocked/writable (to workaround anti-viruses locking the compiled file)&lt;/li&gt;
&lt;li&gt;Added: AddCustomDataToBugReport and AddCustomWebField functions (EEvents unit)&lt;/li&gt;
&lt;li&gt;Added: &quot;Largest Available Memory Block&quot; value to bug reports&lt;/li&gt;
&lt;li&gt;Fixed: BugZilla API was upgraded to a modern JSON API&lt;/li&gt;
&lt;li&gt;Fixed: Possible rare range-check error&lt;/li&gt;
&lt;li&gt;Fixed: Sending to Exceptionless fails with EVariantOverflowError on old IDEs&lt;/li&gt;
&lt;li&gt;Fixed: Possible memory leaks when SMTP send fails&lt;/li&gt;
&lt;li&gt;Fixed: Very rare memory errors for TEurekaExceptionInfo&lt;/li&gt;
&lt;li&gt;Fixed: Various improvements for handling stack overflows, synchronize exceptions, multi-threaded call stacks&lt;/li&gt;
&lt;li&gt;Fixed: Possible access violation when taking call stack of already shutdown thread&lt;/li&gt;
&lt;li&gt;Fixed: Various multithreading issues&lt;/li&gt;
&lt;li&gt;Fixed: [SMTP] name in the HELO command is now a fully qualified domain name&lt;/li&gt;
&lt;li&gt;Fixed: Possible EurekaLog dialog hang when main window is not responding&lt;/li&gt;
&lt;li&gt;Fixed: [x64] Debug info providers may fail to obtain information from modules loaded at high addresses (above 4 Gb)&lt;/li&gt;
&lt;li&gt;Fixed: Rare hang in MS debug info provider startup&lt;/li&gt;
&lt;li&gt;Fixed: Very rare memory leak&lt;/li&gt;
&lt;li&gt;Fixed: Very rare range-check error&lt;/li&gt;
&lt;li&gt;Fixed: EurekaLog may fail to read map file property (very rare)&lt;/li&gt;
&lt;li&gt;Fixed: Empty network information in leak reports&lt;/li&gt;
&lt;li&gt;Fixed: Old IDEs fail to decrypt passwords from options during leak reporting, leading to various issues&lt;/li&gt;
&lt;li&gt;Fixed: Dialog, Log Builder and Send Engines classes will now expand exception-specific variables&lt;/li&gt;
&lt;li&gt;Fixed: EurekaLog dialog may change DPI awareness of the thread&lt;/li&gt;
&lt;li&gt;Fixed: [C++ Builder] TLS detection code could produce incorrect results in some cases; this leads to EurekaLog being disabled&lt;/li&gt;
&lt;li&gt;Fixed: [Viewer] Possible Access Violation when there are unsaved changes&lt;/li&gt;
&lt;li&gt;Fixed: [Viewer] Very rare range check error on opening some reports&lt;/li&gt;
&lt;li&gt;Fixed: [Regression] Possible memory leak when both memory leaks and resource leaks are enabled&lt;/li&gt;
&lt;li&gt;Fixed: [Obsolete] Rare Access Violation when using EAppMultiThreaded (e.g. &quot;Auto-handle TThread exception&quot; option is enabled)&lt;/li&gt;
&lt;li&gt;Fixed: Various minor and internal improvements&lt;/li&gt;
&lt;li&gt;Changed: Signature for OnPasswordRequest event&lt;/li&gt;
&lt;/ol&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/7613977302579038776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/7613977302579038776'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2023/11/EurekaLog-7-12.html' title='EurekaLog 7.12 is out'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-2213521636129759219</id><published>2023-09-06T22:26:00.008+01:00</published><updated>2025-05-13T15:20:51.245+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best practice"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><category scheme="http://www.blogger.com/atom/ns#" term="technical"/><title type='text'>Creating an API (contract) for your own DLL</title><content type='html'>&lt;h3&gt;Or: don&#39;t create your own DLLs without reading this article!&lt;/h3&gt;
&lt;br /&gt;
&lt;b&gt;This article is not about EurekaLog, but about writing your own DLLs in general&lt;/b&gt;. This article is based on questions on the forums: &quot;How do I return a string from a DLL?&quot;, &quot;How do I pass and return an array of records?&quot;, &quot;How do I pass a form to a DLL?&quot;.&lt;br /&gt;
&lt;br /&gt;
So that you do not spend half your life figuring it out - in this article I will bring everything on a silver platter.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;font color=&quot;red&quot;&gt;Important note&lt;/font&gt;:&lt;/b&gt; the article must be read &lt;b&gt;sequentially&lt;/b&gt;. Code examples are given only as &lt;i&gt;examples&lt;/i&gt;: the code of examples is added with new details at each step of the article. For example, at the very beginning of the article there is no error handling, &quot;classic&quot; methods are used (such as using &lt;code&gt;GetLastError&lt;/code&gt;, the &lt;code&gt;sdtcall&lt;/code&gt; convention, etc.), which are replaced by more adequate ones in the course of the article. This is done for the reason that &quot;new&quot; (&quot;unusual&quot;) designs do not raise questions. Otherwise, with each example, one would have to insert a note like: &quot;this is discussed in that paragraph below, but that - in this one here.&quot; In any case, at the end of the article there is a link to the sample code written by taking into account everything said in the article. You can just grab it and use it. And the article explains why it is created the way it is. If you are not interested in &quot;why&quot; - scroll to the end to the conclusion and find a link to download the example.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
&lt;h1&gt;Content&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#t1&quot;&gt;General Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t2&quot;&gt;Data Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t3&quot;&gt;String Data and Encodings&lt;/a&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#t3_1&quot;&gt;ANSI and Unicode&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t4&quot;&gt;Shared Memory Manager (and why you shouldn&#39;t use it)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t5&quot;&gt;API Memory Management&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#t5_1&quot;&gt;Incorrect Way&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t5_2&quot;&gt;Strings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t5_3&quot;&gt;System Memory Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t5_4&quot;&gt;Dedicated Wrapper Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t5_5&quot;&gt;Interfaces&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t6&quot;&gt;Error Handling (and calling convention)&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#t6_1&quot;&gt;Error Codes (and why you shouldn&#39;t use them)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t6_2&quot;&gt;Exceptions (and why you shouldn&#39;t use them)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t6_3&quot;&gt;What can and should be used (and which calling convention to use)&lt;/a&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#t6_3_1&quot;&gt;How to work with &lt;code&gt;safecall&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t7&quot;&gt;DllMain Workaround&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t8&quot;&gt;Callback Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t9&quot;&gt;Other Rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#t10&quot;&gt;Conclusion (and code examples)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t1&quot;&gt;&lt;/a&gt;&lt;h1&gt;General Concepts&lt;/h1&gt;
When you develop your own DLL, you must come up with the prototypes of the functions exported from it (i.e. &quot;headers&quot;), as well as the contract based on them (i.e. calling rules). Together, this forms your DLL&#39;s &lt;b&gt;API&lt;/b&gt;. API or Application Programming Interface (Application Programming Interface) is a description of the ways in which one code can interact with another. In other words, API is a tool for application integration.&lt;br /&gt;
&lt;br /&gt;
When you develop your DLL, you must determine under what conditions it will be used:
&lt;ol&gt;
&lt;li&gt;Can it be used by applications written in another programming language (for example, Microsoft Visual C++) - &quot;pure DLL&quot; (&quot;universal DLL&quot;, &quot;generic DLL&quot;);&lt;/li&gt;
&lt;li&gt;Or the DLL can only be used by applications written in the same language, &quot;Delphi DLL&quot;.&lt;/li&gt;
&lt;/ol&gt;
This is a fundamental point that you should decide first of all: even before you start writing code and even designing the API of your DLL. The fact is that you can use all the features of your programming language when creating an API in the second case (&quot;Delphi DLL&quot;). For example, for Delphi this means the ability to use strings, objects (in particular, forms, components), dynamic arrays, specialized simple types (&lt;code&gt;Extended&lt;/code&gt;, sets, etc.) - in general, all that does not exist in other languages. It also means the ability to share memory, do transparent error handling (cross-module exceptions).&lt;br /&gt;
&lt;br /&gt;
If you go this route, then you should consider using run-time packages (BPLs) instead of DLLs. BPL packages are specialized DLLs that are specifically tailored for use only in Delphi, which gives you a lot of goodies. But more on that later.&lt;br /&gt;
&lt;br /&gt;
On the other hand, if you&#39;re developing a &quot;pure DLL&quot; then you can&#39;t use features in your language that don&#39;t exist in other programming languages. And in this case, you can only use &quot;well-known&quot; data types. But more on that below.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;This article is mainly about &quot;pure DLLs&quot; in Windows.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
What you will need to create when developing your DLL API:
&lt;ol&gt;
&lt;li&gt;Headers, header files - a set of source files that contain declarations of structures and functions used in the API. As a rule, they do not contain implementations. Header files are provided in several languages - as a rule, this is the language in which the program is written (in our case, Delphi), C++ (as a standard) and some additional ones (Basic, etc.). All of these files are equivalent and are simply a translation from one programming language to another. The more languages included, the better. If you do not provide header files for a particular language, programmers in that language will not be able to use your DLL unless they themselves translate the files from the provided language (Delphi or C++) into their language. Therefore, the absence of headings in some language is not a red &quot;stop&quot;, but a sufficient obstacle.&lt;/li&gt;
&lt;li&gt;Documentation - is a verbal description of the API and it should specify additional rules not included in the header syntax. For example, the &quot;specific function can be called by passing a number to it&quot; fact is an information from the headers. And &quot;before calling this function you need to call another function&quot; fact is an information from the documentation. Such documentation should at least contain a formal description of the API - a listing of all functions, methods, interfaces and data types with explanations of &quot;how&quot; and &quot;why&quot; (the so-called Reference). Additionally, the documentation may contain an informal description of the process of using the DLL (guide, how-to, etc.). In the simplest cases, documentation is written directly in headers (comments), but most often it is a standalone file (or files) in chm, html or pdf format.&lt;/li&gt;
&lt;/ol&gt;
SDK (Software Development Kit) - a set of headers + documentation. The SDK is what a third party developer needs to use your DLL. An SDK is something you must create and publicly distribute to anyone who wants to use your DLL.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t2&quot;&gt;&lt;/a&gt;&lt;h1&gt;Data Types&lt;/h1&gt;
If you want a &quot;pure DLL&quot;, then you can&#39;t use Delphi-specific data types in your API because they don&#39;t have a counterpart in other languages. For example, &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;array of&lt;/code&gt;, &lt;code&gt;TObject&lt;/code&gt;, &lt;code&gt;TForm&lt;/code&gt; (and in general - any objects, and even more so components) and so on. &lt;br /&gt;
&lt;br /&gt;
What can be used? Integer types (&lt;code&gt;Integer&lt;/code&gt;, &lt;code&gt;Cardinal&lt;/code&gt;, &lt;code&gt;Int64&lt;/code&gt;, &lt;code&gt;UInt64&lt;/code&gt;, &lt;code&gt;NativeInt&lt;/code&gt;, &lt;code &gt;NativeUInt&lt;/code&gt;, &lt;code&gt;Byte&lt;/code&gt;, &lt;code&gt;Word&lt;/code&gt; etc., I wouldn&#39;t recommend using &lt;code&gt;Currency&lt;/code&gt; unless you really need it), real types (&lt;code&gt;Single&lt;/code&gt; and &lt;code&gt;Double&lt;/code&gt;; I would recommend avoiding the &lt;code&gt;Extended&lt;/code&gt; and &lt;code&gt;Comp&lt;/code&gt; types unless you really need them), &lt;code&gt;TDateTime&lt;/code&gt; (it is alias for &lt;a title=&quot;MSDN: Date Type&quot; href=&quot;https://learn.microsoft.com/en-us/cpp/atl-mfc-shared/date-type?view=msvc-170&quot;&gt;system&#39;s &lt;code&gt;OLEDate&lt;/code&gt;&lt;/a&gt;), enumerated and subrange types (with some caveats), character types (&lt;code&gt;AnsiChar&lt;/code&gt; and &lt;code&gt;WideChar&lt;/code&gt; - but not the &lt;code&gt;Char&lt;/code&gt;), strings (only as &lt;code&gt;WideString&lt;/code&gt;/&lt;code&gt;BSTR&lt;/code&gt;), boolean (&lt;code&gt;BOOL&lt;/code&gt;, but not the &lt;code&gt;Boolean&lt;/code&gt;), interfaces (&lt;code&gt;interface&lt;/code&gt;) whose methods use valid types, records (&lt;code&gt;record&lt;/code&gt;) from the above types, and pointers to them (including pointers to arrays of the above types, but not dynamic arrays). Arrays are usually passed as two parameters: a pointer to the first element of the array and the number of elements in the array.&lt;br /&gt;
&lt;br /&gt;
How do you know which type can be used and which can&#39;t? A relatively simple rule - if you don&#39;t see a type in this list, and the type is not in the &lt;code&gt;Windows&lt;/code&gt; unit (&lt;code&gt;Winapi.Windows&lt;/code&gt; unit since Delphi XE2), then that type cannot be used. If the type is listed above or it is in the &lt;code&gt;Windows&lt;/code&gt;/&lt;code&gt;Winapi.Windows&lt;/code&gt; unit, use it. This is a rather rough rule, but it will do for a start.&lt;br /&gt;
&lt;br /&gt;
In case of using records (&lt;code&gt;record&lt;/code&gt;) - you need to specify data alignment. Use either the &lt;code&gt;packed&lt;/code&gt; keyword (no alignment) or the {$A8} directive (8-byte alignment) at the beginning of the header file.&lt;br /&gt;
&lt;br /&gt;
In case of using enumerated types (&lt;code&gt;Color = (clRed, clBlue, clBlack);&lt;/code&gt;) - add the &lt;code&gt;{$MINENUMSIZE 4}&lt;/code&gt; directive to the beginning of the headers (the size of the enumerated type is at least 4 bytes ).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t3&quot;&gt;&lt;/a&gt;&lt;h1&gt;String Data and Encodings&lt;/h1&gt;
If you need to pass strings to the DLL or return strings from the DLL - &lt;b&gt;use only the &lt;code&gt;BSTR&lt;/code&gt;&lt;/b&gt; type. Why?
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;BSTR&lt;/code&gt; type &lt;a href=&quot;https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/string-manipulation-functions&quot; title=&quot; MSDN: String Manipulation Functions&quot;&gt;available in all programming languages&lt;/a&gt;.&lt;br /&gt;
Note: For historical reasons, the &lt;code&gt;BSTR&lt;/code&gt; type is called &lt;code&gt;WideString&lt;/code&gt; in Delphi. Therefore, to make the contents of your Delphi headers more understandable to developers in other languages, add the following code to their beginning:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
   BSTR = WideString;&lt;/pre&gt;
and then use the &lt;code&gt;BSTR&lt;/code&gt; everywhere in API/headers.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;BSTR&lt;/code&gt; (&lt;code&gt;WideString&lt;/code&gt;) type is one of Delphi&#39;s automagic types, i.e. you don&#39;t have to manually allocate and deallocate memory. The compiler will automatically do everything for you;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;BSTR&lt;/code&gt; type has a fixed encoding: Unicode. Therefore you won&#39;t have problems with a wrong code page;&lt;/li&gt;
&lt;li&gt;Delphi compiler magic allows you to simply assign &lt;code&gt;BSTR&lt;/code&gt; (via the assignment operator &lt;code&gt;:=&lt;/code&gt;) to any Delphi string and vice versa. All necessary conversions will be done automatically under the hood of the language, no conversion functions need to be called;&lt;/li&gt;
&lt;li&gt;Memory for &lt;code&gt;BSTR&lt;/code&gt; strings &lt;a href=&quot;https://docs.microsoft.com/en-us/cpp/atl-mfc-shared/allocating-and-releasing-memory-for-a-bstr&quot; title=&quot;MSDN: Allocating and Releasing Memory for a BSTR&quot;&gt;is always allocated through the same memory manager&lt;/a&gt; so you will never have problems transferring memory between executable modules (see below);&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
If for some reason you can&#39;t use the &lt;code&gt;BSTR&lt;/code&gt; type then use &lt;code&gt;PWideChar&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;Don&#39;t use &lt;code&gt;PAnsiChar&lt;/code&gt; because it&#39;s 2023, not 1995. Using &lt;code&gt;PAnsiChar&lt;/code&gt; gives you a lot of encoding headaches;&lt;/li&gt;
&lt;li&gt;Do not use &lt;code&gt;PChar&lt;/code&gt; because it is not uniquely defined: it can be either &lt;code&gt;PAnsiChar&lt;/code&gt; or &lt;code&gt;PWideChar&lt;/code&gt; (depending on compiler version).&lt;/li&gt;
&lt;/ol&gt;
Similar to using the system&#39;s &lt;code&gt;BSTR&lt;/code&gt; name instead of the Delphi&#39;s &lt;code&gt;WideString&lt;/code&gt; name: you can also do this for the &lt;code&gt;PWideChar&lt;/code&gt; type:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
   LPWSTR = PWideChar;&lt;/pre&gt;
and then use &lt;code&gt;LPWSTR&lt;/code&gt;. The &lt;code&gt;LPWSTR&lt;/code&gt; is the name of the system data type, which is called &lt;code&gt;PWideChar&lt;/code&gt; in Delphi.&lt;br /&gt;
&lt;br /&gt;
Of course, you get a bunch of cons when using &lt;code&gt;LPWSTR&lt;/code&gt;/&lt;code&gt;PWideChar&lt;/code&gt; instead of &lt;code&gt;WideString&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;You need to manually allocate and deallocate memory for &lt;code&gt;PWideChar&lt;/code&gt;, which increases the chances of memory leak problems;&lt;/li&gt;
&lt;li&gt;While in &lt;i&gt;some&lt;/i&gt; cases you can make direct assignments (e.g. &lt;code&gt;PWideChar&lt;/code&gt; to a string), more often you can&#39;t. You will have to call conversion functions and/or memory allocation/copy functions;&lt;/li&gt;
&lt;li&gt;Memory for &lt;code&gt;PWideChar&lt;/code&gt; strings is allocated as usual (without a dedicated memory manager), i.e. you have a problem with passing memory across a module boundary (see below);&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PWideChar&lt;/code&gt; has no field for length. So if you want to pass strings with &lt;code&gt;#0&lt;/code&gt; inside and/or you want to pass large strings, then you have to explicitly pass the length of the string along with the string (two parameters instead of one).&lt;/li&gt;
&lt;/ol&gt;
Read more: &lt;a href=&quot;http://rvelthuis.de/articles/articles-pchars.html&quot; title=&quot;PChars: no strings attached&quot;&gt;String and PChar&lt;/a&gt;.&lt;br / &gt;
&lt;br /&gt;
&lt;a name=&quot;t3_1&quot;&gt;&lt;/a&gt;&lt;h2&gt;ANSI and Unicode&lt;/h2&gt;
From the above, it directly follows that all your exported functions must be in Unicode. Do not just look at the Windows API to make two versions of functions (with -A and -W suffixes) - just make one version (no suffix, just Unicode). Yes, do that even if you are developing on the ANSI version of Delphi (like Delphi 7): you don&#39;t need to make ANSI versions of the exported functions. It&#39;s not 1995 now.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t4&quot;&gt;&lt;/a&gt;&lt;h1&gt;Shared Memory Manager&lt;/h1&gt;
&lt;h2&gt;(and why you shouldn&#39;t use it)&lt;/h2&gt;
In programming languages, dynamic memory is allocated and deallocated by a special code in the program - the so-called memory manager. For example, the memory manager implements functions like &lt;code&gt;GetMem&lt;/code&gt; and &lt;code&gt;FreeMem&lt;/code&gt; in Delphi. All other memory management methods (&lt;code&gt;New&lt;/code&gt;, &lt;code&gt;SetLength&lt;/code&gt;, &lt;code&gt;TForm.Create&lt;/code&gt;, etc.) are just adapters (i.e. somewhere internally they call &lt;code&gt;GetMem&lt;/code&gt; and &lt;code&gt;FreeMem&lt;/code&gt;).&lt;br /&gt;
&lt;br /&gt;
The problem is that each executable module (be it a DLL or an exe) has its own memory manager code, and, for example, the Delphi memory manager does not know anything about the Microsoft C++ memory manager (and vice versa). Therefore, if you allocate memory in Delphi and, for example, try to transfer it to Visual C++ code, then nothing good will happen. Moreover, even if you allocate memory in Delphi DLL and return it to Delphi exe, things will be even worse: both executable modules use two &lt;i&gt;different&lt;/i&gt;, but &lt;i&gt;same type&lt;/i&gt; memory managers. The exe memory manager will look at the memory and it will seem to him that this is his memory (after all, it is allocated by a similar memory manager), he will try to free it, but only damage the accounting data.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The solution to this problem is simple - you need to use the rule: whoever allocates memory, frees it.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
This rule can be enforced in a number of ways. Often mentioned method: using the so-called shared memory manager. The essence of the method is that several modules &quot;agree&quot; to use the same memory manager.&lt;br /&gt;
&lt;br /&gt;
When you create a DLL, you are told about this feature by a comment at the beginning of the .dpr file of the DLL:
&lt;pre class=&quot;brush:delphi&quot;&gt;{ Important note about DLL memory management: ShareMem must be the
   first unit in your library&#39;s USES clause AND your project&#39;s (select
   Project-View Source) USES clause if your DLL exports any procedures or
   functions that pass strings as parameters or function results. This
   applies to all strings passed to and from your DLL--even those that
   are nested in records and classes. ShareMem is the interface unit to
   the BORLNDMM.DLL shared memory manager, which must be deployed along
   with your DLL. To avoid using BORLNDMM.DLL, pass string information
   using PChar or ShortString parameters. }&lt;/pre&gt;
This is a monstrously wrong comment:
&lt;ol&gt;
&lt;li&gt;The comment talks about the need to use a shared memory manager as if it is the only way to solve the memory sharing problem - which is fundamentally wrong (see below);&lt;/li&gt;
&lt;li&gt;The comment only talks about strings, although the problem described applies to any data with dynamic memory allocation: objects, dynamic arrays, pointers;&lt;/li&gt;
&lt;li&gt;The comment does not mention in any way what to do with non-string data;&lt;/li&gt;
&lt;li&gt;Using a shared memory manager does not correlate in any way with using a standalone DLL. This is just one of the possible implementations;&lt;/li&gt;
&lt;li&gt;The comment requires the use of the &lt;code&gt;PChar&lt;/code&gt; type to avoid the described problem - which is also wrong (see our discussion above about encodings);&lt;/li&gt;
&lt;li&gt;The comment requires the use of the &lt;code&gt;ShortString&lt;/code&gt; - which, again, is wrong from a &quot;pure DLL&quot; point of view (&lt;code&gt;ShortString&lt;/code&gt; is a Delphi-specific type). Although, this is already a nitpick, since the use of Delphi strings and Delphi DLL as a common memory manager already puts an end to the &quot;universal DLL&quot; concept.&lt;/li&gt;
&lt;/ol&gt;
Unfortunately, this comment &quot;from the creators of Delphi&quot; has spawned a huge amount of myths and bad practices.&lt;br /&gt;
&lt;br /&gt;
What&#39;s wrong with using a shared memory manager?&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Other programming languages know nothing about Delphi&#39;s memory manager;&lt;/li&gt;
&lt;li&gt;And since you&#39;re targeting only Delphi, why do you need a DLL? Build the program with run-time packages (BPL) - this will automatically give you:
&lt;ul&gt;
&lt;li&gt;Shared memory manager in &lt;code&gt;rtl.bpl&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Guaranteeing the compatibility of the structure of objects, since all modules will be assembled by one compiler;&lt;/li&gt;
&lt;li&gt;No duplication of RTL and VCL (errors like &quot;&lt;code&gt;TForm&lt;/code&gt; is not compatible with &lt;code&gt;TForm&lt;/code&gt;&quot;, two &lt;code&gt;Application&lt;/code&gt; objects, etc.); &lt;/li&gt;
&lt;li&gt;Easy error handling with exceptions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The shared memory manager makes it very difficult to find memory leaks, because a module can load, allocate memory, unload, and the created leak will only be found during the finalization of the memory manager when the program exits.&lt;/li&gt;
&lt;/ol&gt;
To summarize: a shared memory manager is a crutch. You don&#39;t have to use it. What should be used? See sections below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t5&quot;&gt;&lt;/a&gt;&lt;h1&gt;API Memory Management&lt;/h1&gt;
So how do you transfer memory from the DLL to the caller and vice versa? There are several ways.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t5_1&quot;&gt;&lt;/a&gt;&lt;h2&gt;Incorrect Way&lt;/h2&gt;
First, what not to do.&lt;br /&gt;
&lt;br /&gt;
First, don&#39;t &quot;do it like Delphi&quot;: don&#39;t use a shared memory manager - for the reasons mentioned above.&lt;br /&gt;
&lt;br /&gt;
Secondly, don&#39;t &quot;do it like Windows&quot;: many look at the Windows API and do the same. But they miss the fact that this API was created in 1995, and many functions come from even earlier: 16-bit Windows. Those environments and conditions for which these functions were created no longer exist today. Today there are much simpler and more convenient ways.&lt;br /&gt;
&lt;br /&gt;
For example, here is a typical Windows function:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetUserName(lpBuffer: PWideChar; var nSize: DWORD): BOOL; stdcall;&lt;/pre&gt;
&lt;blockquote&gt;&lt;h3&gt;Parameters&lt;/h3&gt;
&lt;b&gt;lpBuffer&lt;/b&gt;&lt;br /&gt;
A pointer to the buffer to receive the user&#39;s logon name. If this buffer is not large enough to contain the entire user name, the function fails.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;pcbBuffer&lt;/b&gt;&lt;br /&gt;
On input, this variable specifies the size of the lpBuffer buffer, in TCHARs. On output, the variable receives the number of TCHARs copied to the buffer, including the terminating null character.&lt;br /&gt;
&lt;br /&gt;
If lpBuffer is too small, the function fails and GetLastError returns ERROR_INSUFFICIENT_BUFFER. This parameter receives the required buffer size, including the terminating null character.&lt;/blockquote&gt;
To get a result from such a Windows function, it must be called &lt;b&gt;twice&lt;/b&gt;. First you call the function to determine the size of the buffer, then you allocate the buffer, and only then you call the function again. But what if the data changes during this time? Function may run out of space again (on the second call). Thus, to reliably get the complete data, you have to write a loop. This is horror. Don&#39;t do that.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t5_2&quot;&gt;&lt;/a&gt;&lt;h2&gt;Strings&lt;/h2&gt;
Strings are easy - just use the &lt;code&gt;BSTR&lt;/code&gt; (which is &lt;code&gt;WideString&lt;/code&gt;). We have discussed this above in details.&lt;br /&gt;
&lt;br /&gt;
Note that in some cases you can return complex structured data (objects) as JSON or a similar way of packing the data into a string. And if this is your case - you can also use the &lt;code&gt;BSTR&lt;/code&gt; type.&lt;br /&gt;
&lt;br /&gt;
In all other cases, you need to use one of the three methods below.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t5_3&quot;&gt;&lt;/a&gt;&lt;h2&gt;System Memory Manager&lt;/h2&gt;
You can fulfill the &quot;who allocates memory - releases it&quot; rule as follows: ask a third party to allocate and release memory, which both the called and the caller know about. For example, such a third party could be any system memory manager. This is exactly how &lt;code&gt;BSTR&lt;/code&gt;/&lt;code&gt;WideString&lt;/code&gt; works. Here are some options you can use:
&lt;ol&gt;
&lt;li&gt;Process system heap:
&lt;ul&gt;
&lt;li&gt;The &lt;a title=&quot;MSDN: HeapAlloc Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapalloc&quot;&gt;&lt;code&gt;HeapAlloc&lt;/code&gt;&lt;/a&gt; and &lt;a title=&quot;MSDN: HeapFree&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapfree&quot;&gt;&lt;code&gt;HeapFree&lt;/code&gt;&lt;/a&gt; called for the &lt;a title=&quot;MSDN: GetProcessHeap Function&quot; href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-getprocessheap&quot;&gt;&lt;code&gt;GetProcessHeap&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;The &lt;a title=&quot;MSDN: GlobalAlloc Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-globalalloc&quot;&gt;&lt;code&gt;GlobalAlloc&lt;/code&gt;&lt;/a&gt; and &lt;a title=&quot;MSDN: GlobalFree Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-globalfree&quot;&gt;&lt;code&gt;GlobalFree&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;The &lt;a title=&quot;MSDN: LocalAlloc Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-localalloc&quot;&gt;&lt;code&gt;LocalAlloc&lt;/code&gt;&lt;/a&gt; and &lt;a title=&quot;MSDN: LocalFree Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-localfree&quot;&gt;&lt;code&gt;LocalFree&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
All of these functions allocate memory from the same process dynamic heap. Several variants appeared &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20041101-00/?p=37433&quot; title=&quot;What was the difference between LocalAlloc and GlobalAlloc?&quot;&gt;for historical reasons&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;COM-like memory managers:
&lt;ul&gt;
&lt;li&gt;The &lt;a title=&quot;MSND: CoTaskMemAlloc Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-cotaskmemalloc&quot;&gt;&lt;code&gt;CoTaskMemAlloc&lt;/code&gt;&lt;/a&gt; and &lt;a title=&quot;MSDN: CoTaskMemFree Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-cotaskmemfree&quot;&gt;&lt;code&gt;CoTaskMemFree&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;The &lt;a title=&quot;MSND: SHAlloc Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-shalloc&quot;&gt;&lt;code&gt;SHAlloc&lt;/code&gt;&lt;/a&gt; and &lt;a title=&quot;MSDN: SHFree Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-shfree&quot;&gt;&lt;code&gt;SHFree&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;
And again: all these functions are equivalent today. Several feature options appeared &lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20040705-00/?p=38573&quot; title=&quot;What&#39;s the difference between SHGetMalloc, SHAlloc, CoGetMalloc, and CoTaskMemAlloc&quot;&gt;for historical reasons&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a title=&quot;MSDN: VirtualAlloc Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc&quot;&gt;&lt;code&gt;VirtualAlloc&lt;/code&gt;&lt;/a&gt; and &lt;a title=&quot;MSDN: VirtualFree Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualfree&quot;&gt;&lt;code&gt;VirtualFree&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
Note: COM memory manager and Shell functions can be called immediately, without COM/OLE initialization.&lt;br /&gt;
&lt;br /&gt;
Pretty big list. Which one is better to use?&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;VirtualAlloc&lt;/code&gt;/&lt;code&gt;VirtualFree&lt;/code&gt; allocate memory &lt;a title=&quot;Why is address space allocation granularity 64KB?&quot; href=&quot;https://devblogs.microsoft.com/oldnewthing/20031008-00/?p=42223&quot;&gt;with a granularity of 64Kb&lt;/a&gt;, so you should only use them if you need to exchange huge data;&lt;/li &gt;
&lt;li&gt;&lt;code&gt;GlobalAlloc&lt;/code&gt;/&lt;code&gt;GlobalFree&lt;/code&gt; and &lt;code&gt;LocalAlloc&lt;/code&gt;/&lt;code&gt;LocalFree&lt;/code&gt; are pretty much outdated and have more overhead than &lt;code&gt;HeapAlloc&lt;/code&gt;/&lt;code&gt;HeapFree&lt;/code&gt;, so you don&#39;t need to use them;&lt;/li&gt;
&lt;/ul&gt;
That leaves us with the &lt;code&gt;HeapAlloc&lt;/code&gt;/&lt;code&gt;HeapFree&lt;/code&gt; and COM. The Heap option may very well be the default. COM memory managers may be more familiar to some programming languages. In addition, there is a ready-made memory manager interface (see below). In general, it is &lt;a title=&quot;Allocating and freeing memory across module boundaries&quot; href=&quot;https://devblogs.microsoft.com/oldnewthing/20060915-04/?p=29723&quot;&gt;more like taste choice, not much real difference&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Here is an example of how it might look in code. In DLL (simplified code without error handling):&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  ActiveX; // or uses OLE2;

function GetDynData(const AFlags: DWORD; out AData: Pointer; out ADataSize: DWORD): BOOL; stdcall;
var
  P: array of Something;
begin
  P := { ... prepare data to return ... };

  ADataSize := Length(P) * SizeOf(Something);
  AData := CoTaskMemAlloc(ADataSize);
  Move(Pointer(P)^, AData^, ADataSize);

  Result := True;
end;&lt;/pre&gt;
exe:
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  ActiveX; // or uses OLE2;

var
  P: array of Something;  
  Data: Pointer;
  DataSize: DWORD;
begin
  GetDynData(0, Data, DataSize);

  SetLength(P, DataSize div SizeOf(Something));
  Move(Data^, Pointer(P)^, DataSize);
  CoTaskMemFree(Data);
  
  // Work with P
end;&lt;/pre&gt;
Note: it is just an example. In real applications, you can (on the callee side) both prepare data immediately in the returned buffer (provided that you know its size in advance), and (on the caller side) work with the returned data directly, without copying it to another type of buffer. &lt;br /&gt;
&lt;br /&gt;
Of course, at the same time, your SDK should have documentation on the &lt;code&gt;GetDynData&lt;/code&gt; function, which will explicitly say that the returned memory must be freed by calling &lt;code&gt;CoTaskMemFree&lt;/code&gt;, like this: &lt;br /&gt;
&lt;blockquote&gt;
&lt;h1&gt;
&lt;span lang=&quot;EN-US&quot;&gt;GetDynData&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Returns &lt;i&gt;XYZ&lt;/i&gt;.&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Syntax&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;&lt;b&gt;function&lt;/b&gt; GetDynData(&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;&amp;nbsp; &lt;b&gt;const&lt;/b&gt;
AFlags: DWORD; &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;&amp;nbsp; &lt;b&gt;out&lt;/b&gt;
AData: Pointer;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;&amp;nbsp; &lt;b&gt;out&lt;/b&gt;
ADataSize: DWORD&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;): BOOL; &lt;b&gt;stdcall&lt;/b&gt;;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Parameters&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;AFlags&lt;/span&gt;&lt;/i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;&amp;nbsp;[&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;in, optional&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;]&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Type&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;:&amp;nbsp;&lt;b&gt;DWORD&lt;/b&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Optional flags: ...&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;AData&lt;/span&gt;&lt;/i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;&amp;nbsp;[&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;out&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;]&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Type&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;:&amp;nbsp;&lt;b&gt;Pointer&lt;/b&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Pointer to a data of the &lt;code&gt;ADataSize&lt;/code&gt; bytes. The caller should free this data by calling the &lt;code&gt;CoTaskMemFree&lt;/code&gt; function.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;ADataSize&lt;/span&gt;&lt;/i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;&amp;nbsp;[&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;out&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;]&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Type&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;:&amp;nbsp;&lt;b&gt;DWORD&lt;/b&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Size of the &lt;code&gt;AData&lt;/code&gt; in bytes.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Returns&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;If the function succeeds - it returns &lt;code&gt;True&lt;/code&gt;.&lt;br /&gt;If the function fails - it returns &lt;code&gt;False&lt;/code&gt;. You can call the &lt;code&gt;GetLastError&lt;/code&gt; function to learn the failure reason.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Remarks&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;...&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Examples&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;A usage example can be found in the &lt;a href=&quot;http://www.example.com/&quot;&gt;Getting the data sample&lt;/a&gt; example code.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Requirements&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;table border=&quot;0&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;MsoNormalTable&quot; style=&quot;border-collapse: collapse; mso-padding-bottom-alt: 15.0pt; mso-padding-top-alt: 15.0pt; mso-yfti-tbllook: 1184;&quot;&gt;
 &lt;tbody&gt;
&lt;tr&gt;
  &lt;td style=&quot;background: #EDEDED; border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;API version&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/td&gt;
  &lt;td style=&quot;border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot; valign=&quot;top&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 7.5pt; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 7.5pt;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;1&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td style=&quot;background: #EDEDED; border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Headers&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/td&gt;
  &lt;td style=&quot;border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot; valign=&quot;top&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;MyDll.pas&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
 &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;br /&gt;
Note: of course, the &lt;code&gt;CoTaskMemAlloc&lt;/code&gt;/&lt;code&gt;CoTaskMemFree&lt;/code&gt; calls can be replaced with &lt;code&gt;HeapAlloc&lt;/code&gt;/&lt;code&gt;HeapFree&lt;/code&gt; or any other option convenient for you. &lt;br /&gt;
&lt;br /&gt;
Note that with this method, you typically need to copy the data twice: in the callee (to copy the data from the prepared location to a location suitable for return to the caller) and possibly in the caller (to copy the returned data into structures suitable for further use). Sometimes you can get away with a single copy if the caller can use the data right away. But it is rare to get rid of copying data in the callee.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t5_4&quot;&gt;&lt;/a&gt;&lt;h2&gt;Dedicated Wrapper Functions&lt;/h2&gt;
Another option is to wrap your preferred memory manager in an exportable function. Accordingly, the documentation for the function should indicate that to free the returned memory, you need to call not &lt;code&gt;CoTaskMemFree&lt;/code&gt; (or whatever you used there), but your wrapper function. Then you can simply return prepared data immediately, without copying. For example, in a DLL (simplified code without error handling):&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetDynData(const AFlags: DWORD; out AData: Pointer; out ADataSize: DWORD): BOOL; stdcall;
var
  P: array of Something;
begin
  P := { ... prepare your data ... };

  ADataSize := Length(P) * SizeOf(Something);
  Pointer(AData) := Pointer(P); // copy the pointer, not the data itself
  Pointer(P) := nil; // block the auto-release

  Result := True;
end;

procedure DynDataFree(var AData: Pointer); stdcall;
var
  P: array of Something;
begin
  if AData = nil then
    Exit;

  Pointer(P) := Pointer(AData); // and again: copy just the pointer
  AData := nil; 

  Finalize(P); // a matching release function
  // (it is optional in this particular case)
end;&lt;/pre&gt;
exe:
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  P: array of Something;  
  Data: Pointer;
  DataSize: DWORD;
begin
  GetDynData(0, Data, DataSize);

  SetLength(P, DataSize div SizeOf(Something));
  Move(Data^, Pointer(P)^, DataSize);
  DynDataFree(Data);
  
  // Work with P
end;&lt;/pre&gt;
Note: we can&#39;t just copy the pointer to the array on the caller&#39;s side because the &lt;code&gt;GetDynData&lt;/code&gt; contract says nothing about the compatibility of the returned data with Delphi&#39;s dynamic array. Indeed, a DLL can be written in MS Visual C++, which does not have dynamic arrays.&lt;br /&gt;
&lt;br /&gt;
As in the previous case, this contract must also be explicitly stated in your SDK&#39;s documentation:&lt;br /&gt;
&lt;blockquote&gt;&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;AData&lt;/span&gt;&lt;/i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;&amp;nbsp;[&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;out&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;]&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Type&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;:&amp;nbsp;&lt;b&gt;Pointer&lt;/b&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Pointer to a data of the &lt;code&gt;ADataSize&lt;/code&gt; bytes. The caller should free this data by calling the &lt;code&gt;DynDataFree&lt;/code&gt; function.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;
Note that by using a wrapper function you can reduce the amount of data copying, because now you don&#39;t need to copy the data on the caller&#39;s side, because you use the same memory manager for both calculations and for returning the data. The disadvantage of this method is the need to write additional wrapper functions. Sometimes you can get away with one generic wrapper function common to all exported functions. But more often than not, you will need an individual cleanup function for each exported function (returning data) if you want to use &quot;just one copy&quot; advantage.&lt;br /&gt;
&lt;br /&gt;
If you use one generic cleanup function, you can return it as the &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nn-objidl-imalloc&quot; title=&quot;MSDN: IMalloc Interface&quot;&gt;&lt;code&gt;IMalloc&lt;/code&gt;&lt;/a&gt; interface. This will be more familiar to those familiar with the basics of COM. But it will also allow you not only to return memory to the caller, but also to accept memory from the caller with transfer of ownership. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  ActiveX; // or Ole2

type
  TAllocator = class(TInterfacedObject, IMalloc)
    function Alloc(cb: Longint): Pointer; stdcall;
    function Realloc(pv: Pointer; cb: Longint): Pointer; stdcall;
    procedure Free(pv: Pointer); stdcall;
    function GetSize(pv: Pointer): Longint; stdcall;
    function DidAlloc(pv: Pointer): Integer; stdcall;
    procedure HeapMinimize; stdcall;
  end;

{ TAllocator }

function TAllocator.Alloc(cb: Integer): Pointer;
begin
  Result := AllocMem(cb);
end;

function TAllocator.Realloc(pv: Pointer; cb: Integer): Pointer;
begin
  ReallocMem(pv, cb);
  Result := pv;
end;

procedure TAllocator.Free(pv: Pointer);
begin
  FreeMem(pv);
end;

function TAllocator.DidAlloc(pv: Pointer): Integer;
begin
  Result := -1;
end;

function TAllocator.GetSize(pv: Pointer): Longint;
begin
  Result := -1;
end;

procedure TAllocator.HeapMinimize;
begin
  // does nothing
end;

function GetMalloc(out AAllocator: IMalloc): BOOL; stdcall;
begin
  AAllocator := TAllocator.Create;
  Result := True;
end;

//_______________________________________

function GetDynData(const AOptions: Pointer; out AData: Pointer; out ADataSize: DWORD): BOOL; stdcall;
var
  P: array of Something;
begin
  P := { ... prepare the data with AOptions ... };
  // Assume the AOptions was passed with ownership
  FreeMem(AOptions); 

  ADataSize := Length(P) * SizeOf(Something);
  AData := GetMem(ADataSize);
  Move(Pointer(P)^, Pointer(AData)^, ADataSize);

  Result := True;
end;&lt;/pre&gt;
exe:
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  A: IMalloc;
  Options: Pointer;
  P: array of Something;
  Data: Pointer;
  DataSize: DWORD;
begin
  GetMalloc(A);

  Options := A.Alloc({ options&#39; size });
  { Preparing Options }
  GetDynData(Options, Data, DataSize);
  // Do not free Options, because we have passed ownership to the GetDynData

  SetLength(P, DataSize div SizeOf(Something));
  Move(Data^, Pointer(P)^, DataSize);
  A.Free(Data);

  // Work with P
end;&lt;/pre&gt;
Note: of course, it is a bit of a nonsensical example, because in this particular case there is no need to pass the ownership of the &lt;code&gt;AOptions&lt;/code&gt; to the &lt;code&gt;GetDynData&lt;/code&gt; function: the caller can clean up the memory himself, then the callee will not need to free the memory. But it is just an example. In real applications, you may need to keep &lt;code&gt;AOptions&lt;/code&gt; inside the DLL for longer than the function&#39;s call. The example shows how this can be done by hiding the memory manager behind a facade.&lt;br /&gt;
&lt;br /&gt;
Also note that if you implement the &lt;code&gt;TAllocator.GetSize&lt;/code&gt; method, then the &lt;code&gt;ADataSize&lt;/code&gt; parameter can be removed.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t5_5&quot;&gt;&lt;/a&gt;&lt;h2&gt;Interfaces&lt;/h2&gt;
Instead of using the system memory manager and/or special export functions (two ways above), it is much more convenient to use &lt;code&gt;interface&lt;/code&gt;s for the following reasons:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;An interface is a record with function pointers, an analogue of a class with virtual functions. Due to this, each method automatically becomes a wrapper function from the previous paragraph, i.e. always works with the right memory manager. In other words, there is no need to use a fixed third-party memory manager, nor to introduce wrapper functions;&lt;/li&gt;
&lt;li&gt;Any programming languages can understand interfaces;&lt;/li&gt;
&lt;li&gt;Interfaces can pass complex data (objects);&lt;/li&gt;
&lt;li&gt;Interfaces are self-cleanup types (in Delphi), no need to explicitly call cleanup functions;&lt;/li&gt;
&lt;li&gt;Interfaces can be easily modified by extending them in future versions of your DLL;&lt;/li&gt;
&lt;li&gt;The way Delphi implements interfaces using compiler magic makes it easy to implement proper error handling (see the next section below).&lt;/li&gt;
&lt;/ol&gt;
The previous example can be implemented with interfaces like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  IData = interface
  [&#39;{C79E39D8-267C-4726-98BF-FF4E93AE1D44}&#39;]
    function GetData: Pointer; stdcall;
    function GetDataSize: DWORD; stdcall;

    property Data: Pointer read GetData; 
    property DataSize: DWORD read GetDataSize; 
  end;

  TData = class(TInterfacedObject, IData) 
  private
    FData: Pointer;
    FDataSize: DWORD;
  protected
    function GetData: Pointer; stdcall;
    function GetDataSize: DWORD; stdcall;
  public
    constructor Create(const AData: Pointer; const ADataSize: DWORD);
  end;

constructor TData.Create(const AData: Pointer; const ADataSize: DWORD);
begin
  inherited Create;
  if ADataSize &gt; 0 then
  begin
    GetMem(FData, ADataSize);
    Move(AData^, FData^, ADataSize);
  end;
end;

function TData.GetData: Pointer; stdcall;
begin
  Result := FData;
end;

function TData.GetDataSize: DWORD; stdcall;
begin
  Result := FDataSize;
end;

//________________________________

function GetDynData(const AFlags: DWORD; out AData: IData): BOOL; stdcall;
var
  P: array of Something;
begin
  P := { ... preparing the data ... };

  AData := TData.Create(Pointer(P), Length(P) * SizeOf(Something)); 

  Result := True;
end;&lt;/pre&gt;
exe:
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  P: array of Something;  
  Data: IData;
begin
  GetDynData(0, Data);

  SetLength(P, Data.DataSize div SizeOf(Something));
  Move(Data^, Data.Data^, Data.DataSize);
  
  // Work with P
end;&lt;/pre&gt;
In this case, we have made one universal &lt;code&gt;IData&lt;/code&gt; interface that can be written once and be used in all functions. Although it does not require writing special code for every function, it also results in data copying on the side of the callee, as well as lack of typing. Here&#39;s what an improved DLL might look like:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  IData = interface
  [&#39;{C79E39D8-267C-4726-98BF-FF4E93AE1D44}&#39;]
    function GetData: Pointer; stdcall;
    function GetDataSize: DWORD; stdcall;

    property Data: Pointer read GetData; 
    property DataSize: DWORD read GetDataSize; 
  end;

  TSomethingArray = array of Something;

  TSomethingData = class(TInterfacedObject, IData) 
  private
    FData: TSomethingArray;
    FDataSize: DWORD;
  protected
    function GetData: Pointer; stdcall;
    function GetDataSize: DWORD; stdcall;
  public
    constructor Create(var AData: TSomethingArray);
  end;

constructor TSomethingData.Create(var AData: TSomethingArray);
begin
  inherited Create;
  FDataSize := Length(AData) * SizeOf(Something);
  if FDataSize &gt; 0 then
  begin
    Pointer(FData) := Pointer(AData);
    Pointer(AData) := nil;
  end;
end;

function TSomethingData.GetData: Pointer; stdcall;
begin
  Result := Pointer(FData);
end;

function TSomethingData.GetDataSize: DWORD; stdcall;
begin
  Result := FDataSize;
end;

function GetDynData(const AFlags: DWORD; out AData: IData): BOOL; stdcall;
var
  P: TSomethingArray;
begin
  P := { ... preparing the data ... };

  AData := TSomethingData.Create(P); 

  Result := True;
end;&lt;/pre&gt;
In this case, the outer wrapper (that is, the interface) remains unchanged, only the DLL code changes. So the caller&#39;s code (in the exe) doesn&#39;t change either. But if you change the contract (interface), then you can do the following:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  ISomethingData = interface
  [&#39;{CF8DF791-1E8D-4363-94A2-9FF035A9015A}&#39;]
    function GetData: Pointer; stdcall;
    function GetDataSize: DWORD; stdcall;
    function GetCount: Integer; stdcall;
    function GetItem(const AIndex: Integer): Something; stdcall;

    property Data: Pointer read GetData; 
    property DataSize: DWORD read GetDataSize; 
    property Count: Integer read GetCount;
    property Items[const AIndex: Integer]: Something read GetItem; default; 
  end;

  TSomethingArray = array of Something;

  TSomethingData = class(TInterfacedObject, ISomethingData) 
  private
    FData: TSomethingArray;
    FDataSize: DWORD;
  protected
    function GetData: Pointer; stdcall;
    function GetDataSize: DWORD; stdcall;
    function GetCount: Integer; stdcall;
    function GetItem(const AIndex: Integer): Something; stdcall;
  public
    constructor Create(var AData: TSomethingArray);
  end;

constructor TSomethingData.Create(var AData: TSomethingArray);
begin
  inherited Create;
  FDataSize := Length(AData) * SizeOf(Something);
  if FDataSize &gt; 0 then
  begin
    Pointer(FData) := Pointer(AData);
    Pointer(AData) := nil;
  end;
end;

function TSomethingData.GetData: Pointer; stdcall;
begin
  Result := Pointer(FData);
end;

function TSomethingData.GetDataSize: DWORD; stdcall;
begin
  Result := FDataSize;
end;

function TSomethingData.GetCount: Integer; stdcall; 
begin
  Result := Length(FData);
end;

function TSomethingData.GetItem(const AIndex: Integer): Something; stdcall;
begin
  Result := FData[AIndex];
end;

function GetDynData(const AFlags: DWORD; out AData: ISomethingData): BOOL; stdcall;
var
  P: TSomethingArray;
begin
  P := { ... preparing the data ... };

  AData := TSomethingData.Create(P); 

  Result := True;
end;&lt;/pre&gt;
and then the caller turns into this:
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  Data: ISomethingData;
begin
  GetDynData(0, Data);

  // No need to copy, just work with Data:
  for X := 0 to Data.Count do
    AddToList(Data[X]);
end;&lt;/pre&gt;
In general, there are quite wide possibilities, you can do almost anything you want. And even if you first made a contract through &lt;code&gt;IData&lt;/code&gt;, then later you can add &lt;code&gt;ISomethingData&lt;/code&gt; by simply extending the interface with inheritance. However, older clients of your version 1 DLL will use &lt;code&gt;IData&lt;/code&gt;, while version 2 clients may request the more convenient &lt;code&gt;ISomethingData&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
As you can see from the code above, interfaces are more useful the more complex the returned data. It is very easy to return complex objects as interfaces, while returning a simple block of memory means writing a lot of code.&lt;br /&gt;
&lt;br /&gt;
The obvious downside is the need to write more code for interfaces, since you need a thunk object to implement the interface. But this minus is easily neutralized by the next paragraph (see &quot;Error Handling&quot; below). It&#39;s also partially removed if you originally need to return an object (because it doesn&#39;t require a thunk object, the returned object itself can implement the interface).&lt;br /&gt;
&lt;br /&gt;
Note: the code above is just an example. In real code, you need to add error handling and move the &lt;code&gt;IData&lt;/code&gt;/&lt;code&gt;ISomethingData&lt;/code&gt; interface definitions to separate files (your SDK headers).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t6&quot;&gt;&lt;/a&gt;&lt;h1&gt;Error Handling&lt;/h1&gt;
&lt;h2&gt;(and calling convention)&lt;/h2&gt;
When a programmer writes code, he determines the sequence of actions in the program, placing operators, function calls, and so on in the right order. At the same time, the implemented sequence of actions corresponds to the logic of the algorithm: first we do this, then this, and finally this. The main code corresponds to the &quot;ideal&quot; situation, when all files are in their places, all variables have valid values, and so on. But during the actual operation of the program, situations inevitably occur when the code written by the programmer will operate in an unacceptable (and sometimes unforeseen) environment. Such (and some other) situations are called by the generalized word &quot;error&quot;. Therefore, the programmer must somehow determine what he will do in such situations. How will he determine the admissibility of the situation, how to react to it, etc.&lt;br /&gt;
&lt;br /&gt;
As a rule, the minimum blocks subject to control are a function or procedure (subroutine). Each subroutine performs a specific task. And we can expect a different level of &quot;success&quot; for this task: the task was successful or an error occurred during its execution. To write reliable code, we absolutely need a way to detect error situations - how do we determine that an error has occurred in a function? And responding to them is the so-called &quot;error recovery&quot; (i.e.: what will we do when an error occurs?). Traditionally, there are two main ways to handle errors: error codes and exceptions.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t6_1&quot;&gt;&lt;/a&gt;&lt;h2&gt;Error codes&lt;/h2&gt;
&lt;h3&gt;(and why not to use them)&lt;/h3&gt;
Error codes are perhaps the easiest way to respond to errors. Its essence is simple: the subroutine must return some sign of the success of the task. There are two options here: either it will return a simple sign (success/failure), or it will return the execution status (in other words - &quot;error description&quot;), i.e. a certain code (number) of one of several predefined situations: the function parameters are incorrectly set, the file is not found, etc. In the first case, there may be an additional function that returns the execution status of the last function called. With this approach, errors found in the function are usually passed up (to the calling function). Each function must check the results of other function calls for errors and perform appropriate processing. Most often, the processing is simply passing the error code even higher, to the &quot;higher&quot; calling function. For example: function A calls B, B calls C, C detects an error and returns an error code to B. B checks the return code, sees that an error occurred, and returns an error code to A. A checks the return code and issues an error message (or decides to do something else).&lt;br /&gt;
&lt;br /&gt;
For example, here is a typical Windows API function:&lt;br /&gt;
&lt;blockquote&gt;&lt;h1&gt;
&lt;span lang=&quot;EN-US&quot;&gt;RegisterClassEx&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Registers a window class for subsequent use in calls to the &lt;a title=&quot;MSDN: CreateWindow Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-createwindoww&quot;&gt;&lt;code&gt;CreateWindow&lt;/code&gt;&lt;/a&gt; or &lt;a title=&quot;MSDN: CreateWindowEx Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-createwindowexw&quot;&gt;&lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt; function.&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Syntax&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;&lt;b&gt;function&lt;/b&gt; RegisterClassEx(&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;&amp;nbsp; &lt;b&gt;const&lt;/b&gt;
AClass: TWndClassEx&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: Consolas; font-size: 10pt;&quot;&gt;): ATOM; &lt;b&gt;stdcall&lt;/b&gt;;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; word-break: break-all;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Parameters&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;AClass&lt;/span&gt;&lt;/i&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;&amp;nbsp;[&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;in&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;font-family: &#39;Segoe UI&#39;, sans-serif; font-size: 9pt;&quot;&gt;]&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Type&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;:&amp;nbsp;&lt;b&gt;TWndClassEx&lt;/b&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;A pointer to a &lt;code&gt;WNDCLASSEX&lt;/code&gt; structure. You must fill the structure with the appropriate class attributes before passing it to the function.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 36.0pt; margin-right: 0cm; margin-top: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Returns&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;If the function succeeds, the return value is a class atom that uniquely identifies the class being registered. This atom can only be used by the &lt;a title=&quot;MSDN: CreateWindow Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-createwindoww&quot;&gt;&lt;code&gt;CreateWindow&lt;/code&gt;&lt;/a&gt;, &lt;a title=&quot;MSDN: CreateWindowEx Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-createwindowexw&quot;&gt;&lt;code&gt;CreateWindowEx&lt;/code&gt;&lt;/a&gt;, &lt;a title=&quot;MSDN: GetClassInfo Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getclassinfow&quot;&gt;&lt;code&gt;GetClassInfo&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getclassinfow&quot; title=&quot;MSDN: GetClassInfoEx&quot;&gt;&lt;code&gt;GetClassInfoEx&lt;/code&gt;&lt;/a&gt;, &lt;a title=&quot;MSDN: FindWindow Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindoww&quot;&gt;&lt;code&gt;FindWindow&lt;/code&gt;&lt;/a&gt;, &lt;a title=&quot;MSDN: FindWindowEx Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindowexw&quot;&gt;&lt;code&gt;FindWindowEx&lt;/code&gt;&lt;/a&gt;, and &lt;a title=&quot;MSDN: UnregisterClass Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-unregisterclassw&quot;&gt;&lt;code&gt;UnregisterClass&lt;/code&gt;&lt;/a&gt; functions and the &lt;a title=&quot;MSDN: IActiveIMMap::FilterClientWindows Method&quot; href=&quot;https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa768087(v%3Dvs.85)&quot;&gt;&lt;code&gt;IActiveIMMap.FilterClientWindows&lt;/code&gt;&lt;/a&gt; method.&lt;br /&gt;&lt;br /&gt;If the function fails, the return value is zero. To get extended error information, call &lt;a title=&quot;MSDN: GetLastError Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/errhandlingapi/nf-errhandlingapi-getlasterror&quot;&gt;&lt;code&gt;GetLastError&lt;/code&gt;&lt;/a&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Remarks&lt;/span&gt;&lt;span lang=&quot;EN-US&quot; style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-ansi-language: EN-US; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;... cut ...&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Examples&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;For an example, see &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/winmsg/using-window-classes&quot; title=&quot;MSDN: Using Window Classes&quot;&gt;Using Window Classes&lt;/a&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;

&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;span style=&quot;color: #db7100; font-family: &amp;quot;Segoe UI Light&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 13.5pt; mso-bidi-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Requirements&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;table border=&quot;0&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;MsoNormalTable&quot; style=&quot;border-collapse: collapse; mso-padding-bottom-alt: 15.0pt; mso-padding-top-alt: 15.0pt; mso-yfti-tbllook: 1184;&quot;&gt;
 &lt;tbody&gt;
&lt;tr&gt;
  &lt;td style=&quot;background: #EDEDED; border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Minimum supported client&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/td&gt;
  &lt;td style=&quot;border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot; valign=&quot;top&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Windows 95&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
 &lt;/tr&gt;

&lt;tr&gt;
  &lt;td style=&quot;background: #EDEDED; border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Headers&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/td&gt;
  &lt;td style=&quot;border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot; valign=&quot;top&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Winuser.h&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td style=&quot;background: #EDEDED; border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;line-height: 13.5pt; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;Library&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/td&gt;
  &lt;td style=&quot;border-bottom: solid #DBDBDB 1.0pt; border: none; mso-border-bottom-alt: solid #DBDBDB .75pt; padding: 7.5pt 6.0pt 7.5pt 6.0pt;&quot; valign=&quot;top&quot;&gt;
  &lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt; margin-left: 7.5pt; margin-right: 7.5pt; margin-top: 0cm;&quot;&gt;
&lt;span lang=&quot;EN-US&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;Segoe UI&amp;quot;,&amp;quot;sans-serif&amp;quot;; font-size: 9.0pt; mso-ansi-language: EN-US; mso-fareast-font-family: &amp;quot;Times New Roman&amp;quot;; mso-fareast-language: RU;&quot;&gt;User32.dll&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
 &lt;/tr&gt;

&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0.0001pt;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;
It is a typical way to handle errors in the classic Windows API. In this case: the so-called Win32 error codes. The Win32 error code is a usual &lt;code&gt;DWORD&lt;/code&gt; number. Error codes are fixed and declared in the &lt;code&gt;Windows&lt;/code&gt; unit. The absence of an error is taken as &lt;code&gt;ERROR_SUCCESS&lt;/code&gt; or &lt;code&gt;NO_ERROR&lt;/code&gt; (equal to 0). Constants are defined for all possible errors. Those begin (usually) with the &lt;code&gt;ERROR_&lt;/code&gt; word, for example:
&lt;pre class=&quot;brush:delphi&quot;&gt;  { Incorrect function. }
  ERROR_INVALID_FUNCTION = 1;   { dderror }

  { The system cannot find the file specified. }
  ERROR_FILE_NOT_FOUND = 2;

  { The system cannot find the path specified. }
  ERROR_PATH_NOT_FOUND = 3;

  { The system cannot open the file. }
  ERROR_TOO_MANY_OPEN_FILES = 4;

  { Access is denied. }
  ERROR_ACCESS_DENIED = 5;

  { The handle is invalid. }
  ERROR_INVALID_HANDLE = 6;
  // ... and so on&lt;/pre&gt;
A description of the Win32 error can be obtained via the &lt;a title=&quot;MSDN: FormatMessage Function&quot; href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-formatmessage&quot;&gt;&lt;code&gt;FormatMessage&lt;/code&gt;&lt;/a&gt; function. There is (specifically for our case) a more convenient wrapper in Delphi for this system function with a bunch of parameters: the &lt;a title=&quot;DocWiki: System.SysUtils.SysErrorMessage&quot; href=&quot;https://docwiki.embarcadero.com/Libraries/en/System.SysUtils.SysErrorMessage&quot;&gt;&lt;code&gt;SysErrorMessage&lt;/code&gt;&lt;/a&gt; function. It returns the human-readable description of the passed Win32 error code. By the way, note that messages are returned localized. In other words, if you have Russian Windows, then the messages will be in Russian. If English - in English.&lt;br /&gt;
&lt;br /&gt;
Summarizing what has been said, you have to call such functions like this: &lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;{ prepare the WndClass }
ClassAtom := RegisterClassEx(WndClass);
if ClassAtom = 0 then
begin
  // some error occurred, failure reason is indicated by GetLastError
  Application.MessageBox(
    PChar(&#39;There was an error: &#39; + SysErrorMessage(GetLastError)), 
    PChar(&#39;Error&#39;), MB_OK or MB_ICONSTOP);
  Exit;
end;
// ... continue normal execution
&lt;/pre&gt;
&lt;br /&gt;
As in the memory management case - it is the same: &lt;b&gt;do not follow the example of Windows&lt;/b&gt;. This style has long been outdated. And here&#39;s what&#39;s wrong with him (it is not a complete list):&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;To call a function, two calls are required: the function itself and the &lt;code&gt;GetLastError&lt;/code&gt; (add to this the need to call the function itself twice to get memory from it - it turns out to be a terrible horror as much as four function calls instead of one);&lt;/li&gt;
&lt;li&gt;You need to explicitly write a check like &lt;code&gt;&lt;b&gt;if&lt;/b&gt; &lt;i&gt;something&lt;/i&gt; &lt;b&gt;then&lt;/b&gt; &lt;i&gt;error&lt;/i&gt;&lt;/code&gt;. And if you forget to write this code, you will get a bug: your program will continue executing ignoring the error. Probably corrupting the data and making it difficult to localize the bug (the visible problem will happen later);
&lt;ul&gt;&lt;li&gt;If-like checks also clog the code visually;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;If in case of an error you need to free some resources, and even if there are several of them and there are also several function calls, then the correct code for freeing resources can become very non-trivial;&lt;/li&gt;
&lt;li&gt;You can not pass any additional information. For example, you can not specify in any way which argument is incorrect, or which file you do not have access to;
&lt;ul&gt;&lt;li&gt;You have no way of knowing which function failed, whether it was the function you called, or maybe some other function that the one you called might have called;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;The debugger will not notify you of the problem in any way (although, hypothetically, you could put a breakpoint on the &lt;code&gt;GetLastError&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
Despite all the disadvantages, error codes have a plus: since they are just numbers, they are understandable to any programming language. In other words, error codes are compatible between different languages.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t6_2&quot;&gt;&lt;/a&gt;&lt;h2&gt;Exceptions&lt;/h2&gt;
&lt;h3&gt;(and why not to use them)&lt;/h3&gt;
Exceptions don&#39;t have many of the disadvantages of error codes:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Exceptions do not need to be explicitly checked, the default reaction is an error response;
&lt;ul&gt;&lt;li&gt;The program is not &quot;polluted&quot; with the check code, it is taken out of your main code;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Easy to release resources (via &lt;code&gt;try&lt;/code&gt;-&lt;code&gt;finally&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;Exceptions are easy to extend, inherit, add additional fields, make nested exceptions;&lt;/li&gt;
&lt;li&gt;The debugger will notify you when an exception occurs;
&lt;ul&gt;&lt;li&gt;You can assign your code to diagnose exceptions (the so-called exception tracer).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
But despite all the pluses, exceptions have one significant minus, which crosses out all the pluses (in relation to the DLL API).&lt;br /&gt;
&lt;br /&gt;
Recall how exceptions are raised in Delphi:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  E: Exception;
begin
  E := EMyExceptionClass.Create(&#39;Something&#39;);
  raise E;
end;&lt;/pre&gt;
I split the typical &quot;&lt;code&gt;raise EMyExceptionClass.Create(&#39;Something&#39;);&lt;/code&gt;&quot; line into two to make the problem even more obvious. We create a &lt;b&gt;Delphi object&lt;/b&gt; (exception) and &quot;throw&quot; it. And whoever wants to handle this exception does this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;except
  on E: EMyException do
  begin
    ShowMessage(E.Message);
  end; // - E will be deleted here
end;&lt;/pre&gt;
It means that the Delphi object is passed from the callee (DLL) where the exception is thrown to the caller (exe) where the exception is handled. As we learned earlier (see the Data Types section above), this is a problem. Other programming languages don&#39;t know what a Delphi object is, nor how to read it, nor how to delete it. Even Delphi itself doesn&#39;t always know this (for example, if an exception is thrown by code built on Delphi 7 but caught by code built on Delphi XE, or vice versa). Other programming languages use similar constructs: an exception is represented by an object. Accordingly, Delphi code has no idea how to work with objects in other languages.&lt;br /&gt;
&lt;br /&gt;
In other words, exceptions should not be used due to language incompatibilities.&lt;br /&gt;
&lt;br /&gt;
Corollary 1: Exceptions should not leave your DLL.&lt;br /&gt;
&lt;br /&gt;
Corollary 2: You must catch all exceptions in your exported functions.&lt;br /&gt;
&lt;br /&gt;
Corollary 3: &lt;b&gt;all exported functions must have the global construct &lt;code&gt;try&lt;/code&gt;-&lt;code&gt;except&lt;/code&gt;&lt;/b&gt;.&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetDynData(const AFlags: DWORD; out AData: IData): BOOL; stdcall;
begin
  try
    // ... function&#39;s code, useful payload ... 
    Result := True;
  except
    // ... exception handling ...
    Result := False;
  end;
end;&lt;/pre&gt;
&lt;br /&gt;
&lt;a name=&quot;t6_3&quot;&gt;&lt;/a&gt;&lt;h2&gt;What can and should be used&lt;/h2&gt;
&lt;h3&gt;(and which calling convention to use)&lt;/h3&gt;
If we can&#39;t use error codes and we can&#39;t use exceptions, then what should we use? Well, we need to use their combination - and here is how it looks.&lt;br /&gt;
&lt;br /&gt;
Delphi has built-in compiler magic that can wrap any function in a hidden &lt;code&gt;try&lt;/code&gt;-&lt;code&gt;except&lt;/code&gt; block with an automatic (hidden) call to the processing function. And there is a compiler magic that works the other way around: on the returned error code, it automatically raises the appropriate exception.&lt;br /&gt;
&lt;br /&gt;
Before we get to know this magic, we need to get acquainted with error codes in the form of the &lt;code&gt;HRESULT&lt;/code&gt; type. The &lt;code&gt;HRESULT&lt;/code&gt; is also a number, but now of the &lt;code&gt;Integer&lt;/code&gt; type. The &lt;code&gt;HRESULT&lt;/code&gt; is no longer just an error code, it consists of several parts, which we will not go into in detail, but suffice it to say that they include the error code itself (what used to be Win32 code), a sign success or failure, identifier of the error agent. Error codes typically start with the &lt;code&gt;E_&lt;/code&gt; prefix (for example, &lt;code&gt;E_FAIL&lt;/code&gt;, &lt;code&gt;E_UNEXPECTED&lt;/code&gt;, &lt;code&gt;E_ABORT&lt;/code&gt; or &lt;code&gt;E_ACCESSDENIED&lt;/code&gt;), and success codes typically start with the &lt;code&gt;S_&lt;/code&gt; prefix (for example, &lt;code&gt;S_OK&lt;/code&gt; or &lt;code&gt;S_FALSE&lt;/code&gt;). It is easy to determine the success of the &lt;code&gt;HRESULT&lt;/code&gt; code by comparing it with zero: &lt;code&gt;HRESULT&lt;/code&gt; error codes must be less than zero.&lt;br /&gt;
&lt;br /&gt;
Highlighting the success/error indicator means that now there is no need for the function to return only the success/failure (via &lt;code&gt;BOOL&lt;/code&gt;), and the error code itself - through a separate function (&lt;code&gt;GetLastError&lt;/code&gt;). Now the function can return both information at once, in one call:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetDynData(const AFlags: DWORD; out AData: IData): HRESULT; stdcall;
begin
  try
    // ... function&#39;s code, useful payload ... 
    Result := S_OK;
  except
    // ... exception handling ...
    Result := E_FAIL; // some error code
  end;
end;&lt;/pre&gt;
&lt;br /&gt;
Along with the introduction of the &lt;code&gt;HRESULT&lt;/code&gt; type, the &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/nn-oaidl-ierrorinfo&quot; title= &quot;MSDN: IErrorInfo Interface&quot;&gt;&lt;code&gt;IErrorInfo&lt;/code&gt;&lt;/a&gt; interface was added - which allows you to associate additional information with the returned &lt;code&gt;HRESULT&lt;/code&gt;: arbitrary description, GUID of the raising party (interface), the location of the error (arbitrary line), help. You don&#39;t even need to implement this interface, the system already has an object ready - returned by the &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/desktop/api/oleauto/nf-oleauto-createerrorinfo&quot; title=&quot;MSDN: CreateErrorInfo Function&quot;&gt;&lt;code&gt;CreateErrorInfo&lt;/code&gt;&lt;/a&gt; function.&lt;br /&gt;
&lt;br /&gt;
Finally, Delphi has the already mentioned compiler magic that can make writing such code easier. To do this, the function must have a calling convention &lt;code&gt;stdcall&lt;/code&gt; and return the &lt;code&gt;HRESULT&lt;/code&gt; type. If before the function returned some &lt;code&gt;Result&lt;/code&gt;, then this &lt;code&gt;Result&lt;/code&gt; should be converted to the last out-parameter, for example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Was:
function GetDynData(const AFlags: DWORD): IData;

// Became:
function GetDynData(const AFlags: DWORD; out AData: IData): HRESULT; stdcall;&lt;/pre&gt;
If a function satisfies these requirements, then you can declare it like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetDynData(const AFlags: DWORD): IData; safecall;&lt;/pre&gt;
This would be binary equivalent (i.e. fully compatible) to:
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetDynData(const AFlags: DWORD; out AData: IData): HRESULT; stdcall;&lt;/pre&gt;
&lt;br /&gt;
By declaring a function as &lt;code&gt;safecall&lt;/code&gt; you turn on compiler magic for it, namely:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;The returned result will be automatically converted to the last out parameter;&lt;/li&gt;
&lt;li&gt;The function will implicitly return the &lt;code&gt;HRESULT&lt;/code&gt; (and possibly &lt;code&gt;IErrorInfo&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;The function&#39;s call will be wrapped in an if-check on the return code. If an erroneous &lt;code&gt;HRESULT&lt;/code&gt; is received, an exception will be raised:
  &lt;pre class=&quot;brush:delphi&quot;&gt;var
  Data: IData;
begin
  Data := GetDynData(Flags); // throwns exception when error occurs
  // execution continues only when there is no error&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;The function itself will be wrapped in a hidden &lt;code&gt;try&lt;/code&gt;-&lt;code&gt;except&lt;/code&gt; block that converts the exception to &lt;code&gt;HRESULT&lt;/code&gt; (and possibly to &lt;code&gt;IErrorInfo&lt;/code&gt;):
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetDynData(const AFlags: DWORD): IData; safecall;
begin 
  // ... function&#39;s code, useful payload ...
end; // - a hidden try-except block&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
As you can see, with this compiler support, you can write code almost as if it was a regular function in a regular Delphi unit. And the best part is that other programming languages can use a similar approach. Of course, other programming language may not have a similar compiler magic, but any language is perfectly capable of taking the &lt;code&gt;HRESULT&lt;/code&gt; of the &lt;code&gt;stdcall&lt;/code&gt; function and parsing it (perhaps along with &lt;code&gt;IErrorInfo&lt;/code&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t6_3_1&quot;&gt;&lt;/a&gt;&lt;h3&gt;How to use &lt;code&gt;safecall&lt;/code&gt; correctly&lt;/h3&gt;
Now that we&#39;ve covered the benefits of the &lt;code&gt;safecall&lt;/code&gt;, it&#39;s time for a fly in the ointment. The fact is that the &lt;code&gt;safecall&lt;/code&gt; magic works in a minimal mode &quot;out of the box&quot;. And to get maximum benefit from it, we need to take additional steps. Luckily, they only need to be made once and can be reused in the future.&lt;br /&gt;
&lt;br /&gt;
Item number one: simple exported functions:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure DoSomething; safecall;
begin
  // ... function&#39;s code, useful payload ...
end;

exports
  DoSomething;&lt;/pre&gt;
Unfortunately, the compiler does not allow customizing the process of converting an exception to &lt;code&gt;HRESULT&lt;/code&gt; for ordinary functions, always returning a fixed code and losing additional error information. Therefore, instead of exported functions, you need to use interfaces with methods. Before:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure DoSomething; safecall;
begin
  // ... function&#39;s code, useful payload ...
end;

function GetDynData(const AFlags: DWORD): IData; safecall;
begin 
  // ... function&#39;s code, useful payload ...
end;

function DoSomethingElse(AOptions: IOptions): BSTR; safecall;
begin 
  // ... function&#39;s code, useful payload ...
end;

exports
  DoSomething,
  GetDynData,
  DoSomethingElse;&lt;/pre&gt;
After:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  IMyDLL = interface 
  [&#39;{C5DBE4DC-B4D7-475B-9509-E43193796633}&#39;]
    procedure DoSomething; safecall;
    function GetDynData(const AFlags: DWORD): IData; safecall;
    function DoSomethingElse(AOptions: IOptions): BSTR; safecall;
  end;

  TMyDLL = class(TInterfacedObject, IMyDLL)
  protected
    procedure DoSomething; safecall;
    function GetDynData(const AFlags: DWORD): IData; safecall;
    function DoSomethingElse(AOptions: IOptions): BSTR; safecall;
  end;

procedure TMyDLL.DoSomething; safecall;
begin
  // ... function&#39;s code, useful payload ...
end;

function TMyDLL.GetDynData(const AFlags: DWORD): IData; safecall;
begin 
  // ... function&#39;s code, useful payload ...
end;

function TMyDLL.DoSomethingElse(AOptions: IOptions): BSTR; safecall;
begin 
  // ... function&#39;s code, useful payload ...
end;

function GetFunctions(out AFunctions: IMyDLL): HRESULT; stdcall;
begin
  try
    AFunctions := TMyDLL.Create;
    Result := S_OK;
  except
    on E: Exception do
      Result := HandleSafeCallException(E, ExceptAddr);
  end;
end;

exports
  GetFunctions;&lt;/pre&gt;
where &lt;code&gt;HandleSafeCallException&lt;/code&gt; is our function, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
As you can see, we have placed all exported functions in a single interface (object) - this will allow us to set up/control the process of converting exceptions to &lt;code&gt;HRESULT&lt;/code&gt;. In this case, the DLL exports the only function that we had to write manually, without &lt;code&gt;safecall&lt;/code&gt; - which also allowed us to control the conversion process. Don&#39;t forget that it is binary compatible with &lt;code&gt;safecall&lt;/code&gt;, so if you want to use this DLL in Delphi you can do this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function GetFunctions: IMyDLL; safecall; external &#39;MyDLL.dll&#39;;&lt;/pre&gt;
and it will work just fine.&lt;br /&gt;
&lt;br /&gt;
For objects, when an exception is thrown in a &lt;code&gt;safecall&lt;/code&gt; method, the compiler calls the &lt;a title=&quot;DocWiki: System.TObject.SafeCallException&quot; href=&quot;https://docwiki.embarcadero.com/Libraries/en/System.TObject.SafeCallException&quot;&gt;&lt;code&gt;TObject.SafeCallException&lt;/code&gt;&lt;/a&gt; virtual method which does nothing useful by default and which we can replace with our own method:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;
type
  TMyDLL = class(TInterfacedObject, IMyDLL)
  protected
    procedure DoSomething; safecall;
    function GetDynData(const AFlags: DWORD): IData; safecall;
    function DoSomethingElse(AOptions: IOptions): BSTR; safecall;
  public
    function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; override;
  end;

function TMyDLL.SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; 
begin
  Result := HandleSafeCallException(ExceptObject, ExceptAddr);
end;&lt;/pre&gt;
&lt;br /&gt;
Further, when the code calls a &lt;code&gt;safecall&lt;/code&gt; method, the compiler wraps the method&#39;s call in a &lt;code&gt;CheckAutoResult&lt;/code&gt; wrapper, which (in case of erroneous code) raises an exception through the &lt;code&gt;SafeCallErrorProc&lt;/code&gt; global variable function, which, again, we can replace with our own:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure RaiseSafeCallException(ErrorCode: HResult; ErrorAddr: Pointer);
begin
  // ... our code ...
end;

initialization
  SafeCallErrorProc := RaiseSafeCallException;
end.&lt;/pre&gt;
Now we just need to make our &lt;code&gt;HandleSafeCallException&lt;/code&gt; and &lt;code&gt;RaiseSafeCallException&lt;/code&gt; work as a pair and do something useful.&lt;br /&gt;
&lt;br /&gt;
First we need two helper wrapper functions:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  ActiveX; // or Ole2

function SetErrorInfo(const ErrorCode: HRESULT; const ErrorIID: TGUID; 
  const Source, Description, HelpFileName: WideString;
  const HelpContext: Integer): HRESULT;
var
  CreateError: ICreateErrorInfo;
  ErrorInfo: IErrorInfo;
begin
  Result := E_UNEXPECTED;
  if Succeeded(CreateErrorInfo(CreateError)) then
  begin
    CreateError.SetGUID(ErrorIID);
    if Source &lt;&gt; &#39;&#39; then
      CreateError.SetSource(PWideChar(Source));
    if HelpFileName &lt;&gt; &#39;&#39; then
      CreateError.SetHelpFile(PWideChar(HelpFileName));
    if Description &lt;&gt; &#39;&#39; then
      CreateError.SetDescription(PWideChar(Description));
    if HelpContext &lt;&gt; 0 then
      CreateError.SetHelpContext(HelpContext);
    if ErrorCode &lt;&gt; 0 then
      Result := ErrorCode;
    if CreateError.QueryInterface(IErrorInfo, ErrorInfo) = S_OK then
      ActiveX.SetErrorInfo(0, ErrorInfo);
  end;
end;

procedure GetErrorInfo(out ErrorIID: TGUID; out Source, Description, HelpFileName: WideString; out HelpContext: Longint);
var
  ErrorInfo: IErrorInfo;
begin
  if ActiveX.GetErrorInfo(0, ErrorInfo) = S_OK then
  begin
    ErrorInfo.GetGUID(ErrorIID);
    ErrorInfo.GetSource(Source);
    ErrorInfo.GetDescription(Description);
    ErrorInfo.GetHelpFile(HelpFileName);
    ErrorInfo.GetHelpContext(HelpContext);
  end
  else
  begin
    FillChar(ErrorIID, SizeOf(ErrorIID), 0);
    Source := &#39;&#39;;
    Description := &#39;&#39;;
    HelpFileName := &#39;&#39;;
    HelpContext := 0;
  end;
end;&lt;/pre&gt;
As you can easily imagine, they are intended to pass and receive additional information along with &lt;code&gt;HRESULT&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Next, we need a way to somehow pass the class name of the exception. You can do this in different ways. For example, pass it directly to &lt;code&gt;HRESULT&lt;/code&gt;. To do this, it needs to be encoded. For example, like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;uses
  ComObj, // for the EOleSysError and EOleException
  VarUtils; // for the ESafeArrayError

const
  // ID for our DLL API rules
  ThisDllIID: TGUID   = &#39;{AA76E538-EF3C-4F35-9914-B4801B211A6D}&#39;;
  // &quot;Customer&quot; bit, it is always 0 for Microsoft-defined codes
  CUSTOMER_BIT        = 1 shl 29;
  // Delphi uses this value to pass EAbort
  // It is assumed that E_Abort should show &quot;Aborted&quot; message,
  // while EAbortRaisedHRESULT should be handled silently.
  EAbortRaisedHRESULT = HRESULT(E_ABORT or CUSTOMER_BIT);

function Exception2HRESULT(const E: TObject): HRESULT;

  function NTSTATUSFromException(const E: EExternal): DWORD;
  begin
    // ...
  end;

begin
  if E = nil then
    Result := E_UNEXPECTED
  else
  if not E.InheritsFrom(Exception) then
    Result := E_UNEXPECTED
  else
  if E.ClassType = Exception then
    Result := E_FAIL
  else
  if E.InheritsFrom(ESafecallException) then
    Result := E_FAIL
  else
  if E.InheritsFrom(EAssertionFailed) then
    Result := E_UNEXPECTED
  else
  if E.InheritsFrom(EAbort) then
    Result := EAbortRaisedHRESULT
  else
  if E.InheritsFrom(EOutOfMemory) then
    Result := E_OUTOFMEMORY
  else
  if E.InheritsFrom(ENotImplemented) then
    Result := E_NOTIMPL
  else
  if E.InheritsFrom(ENotSupportedException) then
    Result := E_NOINTERFACE
  else
  if E.InheritsFrom(EOleSysError) then
    Result := EOleSysError(E).ErrorCode
  else
  if E.InheritsFrom(ESafeArrayError) then
    Result := ESafeArrayError(E).ErrorCode
  else
  if E.InheritsFrom(EOSError) then
    Result := HResultFromWin32(EOSError(E).ErrorCode)
  else
  if E.InheritsFrom(EExternal) then
    if Failed(HRESULT(EExternal(E).ExceptionRecord.ExceptionCode)) then
      Result := HResultFromNT(Integer(EExternal(E).ExceptionRecord.ExceptionCode))
    else
      Result := HResultFromNT(Integer(NTSTATUSFromException(EExternal(E))))
  else
    Result := MakeResult(SEVERITY_ERROR, FACILITY_ITF, Hash(E.ClassName)) or CUSTOMER_BIT;
end;&lt;/pre&gt;
Here we are checking for a few special predefined classes, and we also have the ability to pass Win32 codes and hardware exception codes directly. For all other (Delphi specific) exception classes, we use the hash on the class name along with &lt;code&gt;FACILITY_ITF&lt;/code&gt;. As a hash, you can use, for example, SDBM - this is a very simple hash function with good randomization of the result. Of course, you can use any other method - for example, just manually extract and fix the codes for each exception class.&lt;br /&gt;
&lt;blockquote&gt;&lt;code&gt;HRESULT&lt;/code&gt;s with &lt;code&gt;FACILITY_NULL&lt;/code&gt; and &lt;code&gt;FACILITY_RPC&lt;/code&gt; codes have a generic value because they are defined by Microsoft. &lt;code&gt;HRESULT&lt;/code&gt; with &lt;code&gt;FACILITY_ITF&lt;/code&gt; code are defined by the interface function or method from which they are returned. This means that the same 32-bit value in &lt;code&gt;FACILITY_ITF&lt;/code&gt; but returned by two different interfaces can have different meanings. In this way, Microsoft can define multiple generic error codes while still allowing other programmers to define new error codes without fear of conflict. The coding convention looks like this:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HRESULT&lt;/code&gt; with codes other than &lt;code&gt;FACILITY_ITF&lt;/code&gt; can only be defined by Microsoft;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HRESULT&lt;/code&gt; of &lt;code&gt;FACILITY_ITF&lt;/code&gt; are defined solely by the implementer of the interface or function that returns &lt;code&gt;HRESULT&lt;/code&gt;. To avoid conflicting &lt;code&gt;HRESULT&lt;/code&gt;, whoever defines an interface is responsible for coordinating and publishing the &lt;code&gt;HRESULT&lt;/code&gt; codes associated with that interface;&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;HRESULT&lt;/code&gt; defined by Microsoft have an error code value in the $0000-$01FF range. Although you can use any code with &lt;code&gt;FACILITY_ITF&lt;/code&gt;, it is recommended to use values in the $0200-$FFFF range. This recommendation is intended to reduce confusion with Microsoft codes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
That&#39;s why in the code above we also defined &lt;code&gt;ThisDllIID&lt;/code&gt; which is an &quot;interface&quot; identifier that gives meaning to returned codes of type &lt;code&gt;FACILITY_ITF&lt;/code&gt;. This value must be passed as &lt;code&gt;ErrorIID&lt;/code&gt; to the &lt;code&gt;SetErrorInfo&lt;/code&gt; defined above.&lt;br /&gt;
&lt;br /&gt;
The 29th &quot;Customer&quot; bit was originally a reserved bit, which was later allocated to be used as a flag indicating whether the code is defined by Microsoft (0) or by a third party (1). In a way, this bit duplicates &lt;code&gt;FACILITY_ITF&lt;/code&gt;. Usually even third party developers only use &lt;code&gt;FACILITY_ITF&lt;/code&gt;. In this case, we set it to reduce possible problems with bad code (which does not take into account the GUID of the interface).&lt;br /&gt;
&lt;br /&gt;
Wverything is a little more complicated with the reverse conversion (code to exception): we need tables to search for the exception class by code. A simple implementation might look like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function HRESULT2Exception(const E: HRESULT): Exception;

  function MapNTStatus(const ANTStatus: DWORD): ExceptClass;
  begin
    // ...
  end;

  function MapException(const ACode: DWORD): ExceptClass;
  begin
    // ...
  end;

var
  NTStatus: DWORD;
  ErrorIID: TGUID;
  Source: WideString;
  Description: WideString;
  HelpFileName: WideString;
  HelpContext: Integer;
begin
  if GetErrorInfo(ErrorIID, Source, Description, HelpFileName, HelpContext) then
  begin
    if Pointer(StrToInt64Def(Source, 0)) &lt;&gt; nil then
      ErrorAddr := Pointer(StrToInt64(Source));
  end
  else
    Description := SysErrorMessage(DWORD(E));

  if (E = E_FAIL) or (E = E_UNEXPECTED) then
    Result := Exception.Create(Description)
  else
  if E = EAbortRaisedHRESULT then
    Result := EAbort.Create(Description)
  else
  if E = E_OUTOFMEMORY then
  begin
    OutOfMemoryError;
    Result := nil;
  end
  else
  if E = E_NOTIMPL then
    Result := ENotImplemented.Create(Description)
  else
  if E = E_NOINTERFACE then
    Result := ENotSupportedException.Create(Description)
  else
  if HResultFacility(E) = FACILITY_WIN32 then
  begin
    Result := EOSError.Create(Description);
    EOSError(Result).ErrorCode := HResultCode(E);
  end
  else
  if E and FACILITY_NT_BIT &lt;&gt; 0 then
  begin
    // Get exception&#39;s class by code
    NTStatus := Cardinal(E) and (not FACILITY_NT_BIT);
    Result := MapNTStatus(NTStatus).Create(Description);

    // Create a dummy ExceptionRecord just in case
    ReallocMem(Pointer(Result), Result.InstanceSize + SizeOf(TExceptionRecord));
    EExternal(Result).ExceptionRecord := Pointer(NativeUInt(Result) + Cardinal(Result.InstanceSize));
    FillChar(EExternal(Result).ExceptionRecord^, SizeOf(TExceptionRecord), 0);

    EExternal(Result).ExceptionRecord.ExceptionCode := cDelphiException;
    EExternal(Result).ExceptionRecord.ExceptionAddress := ErrorAddr;
  end
  else
  if (E and CUSTOMER_BIT &lt;&gt; 0) and
     (HResultFacility(E) = FACILITY_ITF) and
     CompareMem(@ThisDllIID, @ErrorIID, SizeOf(ErrorIID)) then
    Result := MapException(HResultCode(E)).Create(Description)
  else
    Result := EOleException.Create(Description, E, Source, HelpFileName, HelpContext);
end;&lt;/pre&gt;
In general, the code is fairly straightforward, with the exception of hardware exceptions. We make emulation for them.&lt;br /&gt;
&lt;br /&gt;
Also note that the &lt;code&gt;Source&lt;/code&gt; field of the &lt;code&gt;IErrorInfo&lt;/code&gt; interface must point to the location where the error occurred. This field is arbitrary and is determined by the interface developer (ie, again, by GUID). In this case, we just write the address of the exception there. But, for example, if you use an exception tracer (such as EurekaLog), you can write the call stack there.&lt;br /&gt;
&lt;br /&gt;
Then with the above helper functions, our &lt;code&gt;HandleSafeCallException&lt;/code&gt; and &lt;code&gt;RaiseSafeCallException&lt;/code&gt; become trivial:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function HandleSafeCallException(ExceptObj: TObject; ErrorAddr: Pointer): HRESULT;
var
  ErrorMessage: String;
  HelpFileName: String;
  HelpContext: Integer;
begin
  if ExceptObj is Exception then
    ErrorMessage := Exception(ExceptObj).Message
  else
    ErrorMessage := SysErrorMessage(DWORD(E_FAIL));
  if ExceptObj is EOleException then
  begin
    HelpFileName := EOleException(ExceptObj).HelpFile;
    HelpContext := EOleException(ExceptObj).HelpContext;
  end
  else
  begin
    HelpFileName := &#39;&#39;;
    if ExceptObj is Exception then
      HelpContext := Exception(ExceptObj).HelpContext
    else
      HelpContext := 0;
  end;

  Result := SetErrorInfo(Exception2HRESULT(ExceptObj), ThisDllIID,
    &#39;$&#39; + IntToHex(NativeUInt(ErrorAddr), SizeOf(ErrorAddr) * 2), ErrorMessage,
    HelpFileName, HelpContext);
end;

procedure RaiseSafeCallException(ErrorCode: HResult; ErrorAddr: Pointer);
var
  E: Exception;
begin
  E := HRESULT2Exception(ErrorCode, ErrorAddr);
  raise E at ErrorAddr;
end;&lt;/pre&gt;
Note: in our model, we do not use help fields of the &lt;code&gt;IErrorInfo&lt;/code&gt; interface.&lt;br /&gt;
&lt;br /&gt;
It should be noted that if an interface uses &lt;code&gt;HRESULT&lt;/code&gt; and &lt;code&gt;IErrorInfo&lt;/code&gt; together, then it should also implement the &lt;code&gt;ISupportErrorInfo&lt;/code&gt; interface. Some programming languages require this. By calling &lt;code&gt;ISupportErrorInfo.InterfaceSupportsErrorInfo&lt;/code&gt;, the client side can determine that an object supports additional information.&lt;br /&gt;
&lt;br /&gt;
And the last point - in the Delphi implementation for Windows 32-bit there is &lt;a title=&quot;Quality Portal: [RSP-24652] TObject.SafeCallException might be NOT called in certain cases&quot; href=&quot;https://quality.embarcadero.com/browse/RSP-24652&quot;&gt;nasty bug&lt;/a&gt; that doesn&#39;t exist in 64-bit RTL, as well as on other platforms. The fix for this bug is included in the code examples at the link at the end of the article.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t7&quot;&gt;&lt;/a&gt;&lt;h1&gt;DllMain Workaround&lt;/h1&gt;
The &lt;code&gt;DllMain&lt;/code&gt; is a special function in a DLL that is called by the system when the DLL is loaded into, unloaded from a process (and attached/detached to/from a thread). For example, the &lt;code&gt;initialization&lt;/code&gt; and &lt;code&gt;finalization&lt;/code&gt; sections of your Delphi modules are executed inside &lt;code&gt;DllMain&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The problem is that &lt;code&gt;DllMain&lt;/code&gt; is a very special function. It is called while holding the critical section of the loader (modules) of the operating system. In long and detailed terms - see the links at the end of this paragraph, and in short: &lt;b&gt;&lt;code&gt;DllMain&lt;/code&gt; is a weapon from which you can easily shoot yourself&lt;/b&gt;. There are not many things that can be done legally in &lt;code&gt;DllMain&lt;/code&gt;. But it&#39;s incredibly easy to do something forbidden - you constantly need to be sure that this very function that you just called can never, under any circumstances, do something forbidden. This makes it incredibly difficult to use code written elsewhere. The compiler won&#39;t tell you anything. And the code will most of the time work like it should... but sometimes it will crash or freeze.&lt;br /&gt;
&lt;br /&gt;
The solution to the problem is to do nothing in &lt;code&gt;DllMain&lt;/code&gt; (read: don&#39;t write code in &lt;code&gt;initialization&lt;/code&gt; and &lt;code&gt;finalization&lt;/code&gt; sections of your units when you create a DLL).&lt;br /&gt;
&lt;br /&gt;
Instead, you need to &lt;b&gt;make separate DLL initialization and finalization functions&lt;/b&gt;. You need to do them even if your DLL doesn&#39;t need any initialization or cleanup steps. After all, such a need may arise in the future, and if you do not provide separate initialization and finalization functions in your API, you will not be able to solve this problem later.&lt;br /&gt;
&lt;br /&gt;
Here is the code template:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// In headers:
type
  IMyDll = interface
  [&#39;{C5DBE4DC-B4D7-475B-9509-E43193796633}&#39;]
    procedure InitDLL(AOptional: IUnknown = nil); safecall;
    procedure DoneDLL; safecall;
    // ...
  end;

// In DLL:

type
  TInitFunc = procedure(const AOptional: IUnknown);
  TDoneFunc = procedure;
  TInitDoneFunc = record
    Init: TInitFunc;
    Done: TDoneFunc; 
  end;

procedure RegisterInitFunc(const AInitProc: TInitFunc; const ADoneFunc: TDoneFunc = nil);

// ...

var
  GInitDoneFuncs: array of TInitDoneFunc;

procedure RegisterInitFunc(const AInitProc: TInitFunc; const ADoneFunc: TDoneFunc);
begin
  SetLength(GInitDoneFuncs, Length(GInitDoneFuncs) + 1);
  GInitDoneFuncs[High(GInitDoneFuncs)].Init := AInitProc;
  GInitDoneFuncs[High(GInitDoneFuncs)].Done := ADoneFunc;
end;

procedure TMyDLL.InitDLL(AOptional: IUnknown); safecall;
var
  X: Integer;
begin
  for X := 0 to High(GInitDoneFuncs) do
    if Assigned(GInitDoneFuncs[X].Init) then
      GInitDoneFuncs.Init(AOptional);
end;

procedure TMyDLL.DoneDLL; safecall;
var
  X: Integer;
begin
  for X := 0 to High(GInitDoneFuncs) do
    if Assigned(GInitDoneFuncs[X].Done) then
      GInitDoneFuncs.Done;
end;

// In your units:

procedure InitUnit(const AOptional: IUnknown);
begin
  // ... code from unit&#39;s initialization sections
end;

procedure DoneUnit;
begin
  // ... code from unit&#39;s finalization section
end;

initialization
  RegisterInitFunc(InitUnit, DoneUnit);
end;&lt;/pre&gt;
The &lt;code&gt;AOptional&lt;/code&gt; parameter is designed for possible future use. It is not used in the code above, but later (in the next version of the DLL) you can use it to pass initialization parameters. &lt;code&gt;IUnknown&lt;/code&gt; is the base interface from which all other interfaces are inherited (i.e. some analogue of &lt;code&gt;TObject&lt;/code&gt; for interfaces).&lt;br /&gt;
&lt;br /&gt;
I hope this code is clear enough. Of course, it must be distributed among different units and sections. Interface - in headers, &lt;code&gt;RegisterInitFunc&lt;/code&gt; declaration - in &lt;code&gt;interface&lt;/code&gt; of the common DLL module, you need to call it from the &lt;code&gt;initialization&lt;/code&gt; section of other units.&lt;br /&gt;
&lt;br /&gt;
Of course, your SDK documentation should say that the user (client) of your DLL must call the &lt;code&gt;InitDLL&lt;/code&gt; method immediately after loading your DLL with the &lt;code&gt;LoadLibrary&lt;/code&gt; function and call the &lt;code&gt;DoneDLL&lt;/code&gt; just before the DLL is unloaded by &lt;code&gt;FreeLibrary&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;var
  DLL: HMODULE;
  DLLApi: IMyDll;
begin
  DLL := LoadLibrary(&#39;MyDLL.dll&#39;);
  Win32Check(DLL &lt;&gt; 0);
  try
    DLLApi.InitDLL(nil);

    // working with DLL, for example, calling DLLApi.GetDynData

  finally
    DLLApi.DoneDLL;
    DLLApi := nil;
    FreeLibrary(DLL);
  end;
end;&lt;/pre&gt;
&lt;br /&gt;
More information about the &lt;code&gt;DllMain&lt;/code&gt;:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/archive/blogs/oleglv/dllmain-and-life-before-birth&quot; title=&quot;DllMain and life before birth&quot;&gt;DllMain and life before birth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/archive/blogs/oleglv/dllmain-a-horror-story&quot; title=&quot;DllMain : a horror story&quot;&gt;DllMain : a horror story&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20040127-00/?p=40873&quot; title=&quot;Some reasons not to do anything scary in your DllMain&quot;&gt;Some reasons not to do anything scary in your DllMain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/oldnewthing/20040128-00/?p=40853&quot; title=&quot;Another reason not to do anything scary in your DllMain: Inadvertent deadlock&quot;&gt;Another reason not to do anything scary in your DllMain: Inadvertent deadlock&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t8&quot;&gt;&lt;/a&gt;&lt;h1&gt;Callback Functions&lt;/h1&gt;
A callback function - passing the executable code as one of the parameters of another code. For example, if you want to set a timer using the Windows API, you can call &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx&quot; title=&quot;MSDN: SetTimer Function&quot;&gt;&lt;code&gt;SetTimer&lt;/code&gt;&lt;/a&gt; function, passing it a pointer to your own function, which will be the callback function. The system will call your function every time the timer fires:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;procedure MyTimerHandler(Wnd: HWND; uMsg: UINT; idEvent: UINT_PTR; dwTime: DWORD); stdcall;
begin
  // Will be called after 100 ms timeout
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetTimer(Handle, 1, 100, @MyTimerHandler);
end;&lt;/pre&gt;
Here&#39;s another example: if you want to find all windows on the desktop, you can use the &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/ms633497(v=vs.85 ).aspx&quot; title=&quot;MSDN: EnumWindows Function&quot;&gt;&lt;code&gt;EnumWindows&lt;/code&gt;&lt;/a&gt; function:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;function MyEnumFunc(Wnd: HWND; lpData: LPARAM): Bool; stdcall;
begin
  // Will be called for each found window
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  EnumWindows(@MyEnumFunc, 0);
end;&lt;/pre&gt;
Since the callback function usually performs the same task as the code that sets it up, it turns out that both pieces of code need to work with the same data. Therefore, the data from the setting code must somehow be passed to the callback function (or visa versa). For this purpose, so-called user parameters are provided in the callback functions: it is either a pointer or an integer (necessarily of the &lt;code&gt;Native(U)Int&lt;/code&gt; type, but not just &lt;code&gt;(U)Int&lt;/code&gt;), which are not used by the API itself in any way and are transparently passed to the callback function. Or (in rare cases) it can be some value that uniquely identifies the function&#39;s call.&lt;br /&gt;
&lt;br /&gt;
For example, &lt;code&gt;SetTimer&lt;/code&gt; has &lt;code&gt;idEvent&lt;/code&gt; and &lt;code&gt;EnumWindows&lt;/code&gt; has &lt;code&gt;lpData&lt;/code&gt;. We can use these parameters to pass arbitrary data. For example, here is how you can find all windows of a given class:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  PEnumArgs = ^TEnumArgs;
  TEnumArgs = record
    ClassName: String;
    Windows: TStrings;
  end;

function FindWindowsOfClass(Wnd: HWND; lpData: LPARAM): Bool; stdcall;
var
  Args: PEnumArgs;
  WndClassName, WndText: String;
begin
  Args := Pointer(lpData);

  SetLength(WndClassName, Length(Args.ClassName) + 2);
  SetLength(WndClassName, GetClassName(Wnd, PChar(WndClassName), Length(WndClassName)));
  if WndClassName = Args.ClassName then
  begin
    SetLength(WndText, GetWindowTextLength(Wnd) + 1);
    SetLength(WndText, GetWindowText(Wnd, PChar(WndText), Length(WndText)));
    Args.Windows.Add(Format(&#39;%8x : %s&#39;, [Wnd, WndText]));
  end;

  Result := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Args: TEnumArgs;
begin
  // Edit can contain values like:
  // &#39;TForm1&#39;, &#39;IME&#39;, &#39;MSTaskListWClass&#39;, &#39;Shell_TrayWnd&#39;, &#39;TTOTAL_CMD&#39;, &#39;Chrome_WidgetWin_1&#39;
  Args.ClassName := Edit1.Text;  
  Args.Windows   := Memo1.Lines;

  Memo1.Lines.BeginUpdate;
  try
    Memo1.Lines.Clear;
    EnumWindows(@FindWindowsOfClass, LPARAM(@Args));
  finally
    Memo1.Lines.EndUpdate;
  end;
end;&lt;/pre&gt;
&lt;br /&gt;
Note: here&#39;s another example of how not to do it - don&#39;t do it like in Windows. If you just need to get a list of something - don&#39;t make a callback, just return the list in an array (wrap it in an interface or pass it as a block of memory - as discussed above). The callback function should only be used if creating the list can take a long time and you don&#39;t need all the elements. Then the callback function can return the &quot;stop&quot; flag without completing the list to the end.&lt;br /&gt;
&lt;br /&gt;
Note: some analogue of user-parameters are &lt;a title=&quot;DocWiki: System.Classes.TComponent.Tag&quot; href=&quot;https://docwiki.embarcadero.com/Libraries/en/System.Classes.TComponent.Tag&quot;&gt;&lt;code&gt;Tag&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TTreeNode.Data&quot; title=&quot;DocWiki: Vcl.ComCtrls.TTreeNode .Data&quot;&gt;&lt;code&gt;Data&lt;/code&gt;&lt;/a&gt; properties, although their use is not always ideologically correct (correct: create a derived class).&lt;br /&gt;
&lt;br /&gt;
The conclusion follows from the above: if your API needs to make a callback function, then &lt;b&gt;it must have a custom &lt;code&gt;Pointer&lt;/code&gt; size parameter that will not be used by your API&lt;/b&gt;. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Incorrect!
type
  TNotifyMeProc = procedure; safecall;

  IMyDllAPI = interface 
    // ...
    procedure NotifyMe(const ANotifyEvent: TNotifyMeProc); safecall;
  end;&lt;/pre&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Correct
type
  TNotifyMeProc = procedure(const AUserArg: Pointer); safecall;

  IMyDllAPI = interface 
    // ...
    procedure NotifyMe(const ANotifyEvent: TNotifyMeProc; const AUserArg: Pointer = nil); safecall;
  end;&lt;/pre&gt;
And if you forget to do this, the caller will have to use ugly hacks to get around your bad API design.&lt;br /&gt;
&lt;br /&gt;
Naturally, instead of a function + parameter, you can just use an interface:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Correct
type
  INotifyMe = interface
  [&#39;{07FA30E4-FE9B-4ED2-8692-1E5CFEE4CF3F}&#39;]
    procedure Notify; safecall;
  end;

  IMyDllAPI = interface 
    // ...
    procedure NotifyMe(const ANotifyEvent: INotifyMe); safecall;
  end;&lt;/pre&gt;
This is preferable, because error handling via &lt;code&gt;safecall&lt;/code&gt; in interfaces is simpler, and the interface can contain as many parameters as you like, and it is even more convenient to integrate with objects (form). For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  TForm1 = class(TForm, INotifyMe)
    // ...
    procedure Notify; safecall;
  private
    FAPI: IMyDllAPI;
  public
    function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; override;
  end;

// ...

procedure TForm1.FormCreate(Sender: TObject); 
begin
  // ... load DLL, get API

  FAPI.NotifyMe(Self); // Ask DLL to call us on some event 
end;

procedure TForm1.Notify; 
begin
  ShowMessage(&#39;Something just happenned&#39;);
  // Our form is available here (in the callback),
  // so we can just use it, no need to pass it manually
end;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t9&quot;&gt;&lt;/a&gt;&lt;h1&gt;Other Rules&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;If you are not only developing but also using a DLL, then load the DLL correctly. I won&#39;t go into details here, as it deserves its own article, but you can take a look at the &lt;code&gt;LoadDLL&lt;/code&gt; function from the sample code below: &lt;b&gt;do NOT use &lt;code&gt;LoadLibrary&lt;/code&gt;&lt;/b&gt;!;&lt;/li&gt;
&lt;li&gt;If for some reason you don&#39;t use &lt;code&gt;safecall&lt;/code&gt; then don&#39;t return complex types via &lt;code&gt;Result&lt;/code&gt;, make it an out parameter. The problem is that Delphi and MS Visual C++ disagree on how to interpret the result returned by reference by a stdcall function: as &lt;code&gt;var&lt;/code&gt; or as &lt;code&gt;out&lt;/code&gt;. Accordingly, for &lt;code&gt;safecall&lt;/code&gt; there is no such problem, since &lt;code&gt;Result&lt;/code&gt; for it is always &lt;code&gt;Integer&lt;/code&gt; (&lt;code&gt;HRESULT&lt;/code&gt;) - a simple type, for which &lt;code&gt;var&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt; are equivalent;&lt;/li&gt;
&lt;li&gt;All APIs must have a unique IID/GUID (non-API interfaces (not mentioned in headers) &lt;i&gt;might&lt;/i&gt; not have a GUID, although I would recommend always specifying an IID). You can create a GUID to use as an IID (Interface ID) by pressing &lt;code&gt;Ctrl + Shift + G&lt;/code&gt; in the Delphi code editor - this combination will insert an expression like &lt;code&gt;[&#39;{C5DBE4DC-B4D7-475B-9509- E43193796633}&#39;]&lt;/code&gt; (of course, each time with a unique GUID) directly below the cursor in the editor;&lt;/li&gt;
&lt;li&gt;Once you have published some type (interface), i.e. released your DLL with this interface - you shouldn&#39;t change it. If you need to expand or change it, you introduce a new interface (a new version of the interface), but do not change the old one&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Was:
type
  IMyDLL = interface
  [&#39;{C5DBE4DC-B4D7-475B-9509-E43193796633}&#39;]
    procedure InitDLL(AOptional: IUnknown = nil); safecall;
    procedure DoneDLL; safecall;
    function GetDynData(const AFlags: DWORD): IData; safecall;
  end;
  
// You can&#39;t do that after publishing a production build:
type
  IMyDLL = interface
  [&#39;{C5DBE4DC-B4D7-475B-9509-E43193796633}&#39;]
    procedure InitDLL(AOptional: IUnknown = nil); safecall;
    procedure DoneDLL; safecall;
    procedure DoSomething; safecall; // was added
    function GetDynData(const AFlags: DWORD): IData; safecall;
  end;

// However, you can do that:
type
  IMyDLLv1 = interface
  [&#39;{C5DBE4DC-B4D7-475B-9509-E43193796633}&#39;]
    procedure InitDLL(AOptional: IUnknown = nil); safecall;
    procedure DoneDLL; safecall;
    function GetDynData(const AFlags: DWORD): IData; safecall;
  end;

  IMyDLLv2 = interface(IMyDLLv1)
  [&#39;{69E77989-64DC-4177-975C-487818598C70}&#39;]
    procedure DoSomething; safecall; // added
  end;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;If a function or method returns an interface, then don&#39;t do this:
&lt;pre class=&quot;brush:delphi&quot;&gt;// Incorrect!
function GetSomething: ISomething; safecall;

// ...

var
  Something: ISomething;
begin
  Something := GetSomething;&lt;/pre&gt;
Surely, it is a convenient solution in the beginning: you can call functions &quot;as usual&quot; and even chain them into chains like &lt;code&gt;Control&lt;/code&gt;.&lt;code&gt;GetPicture&lt;/code&gt;.&lt;code&gt;GetImage&lt;/code&gt;.&lt;code&gt;GetColorInfo&lt;/code&gt;.&lt;code&gt;GetBackgroundColor&lt;/code&gt;. However, this state of affairs will exist only in the very first version of the system. As soon as you start developing the system, you will start to have new interfaces. In the not so distant future, you will have a bunch of advanced interfaces, and the basic interfaces that were in the program initially, at the time of its birth, will implement only trivially uninteresting functions. As a result, very often the calling code will need new interfaces, not the original ones. What does it mean? This means that the code needs to call the original function, get the original interface, then ask it for a new one (via &lt;code&gt;Supports&lt;/code&gt;/&lt;code&gt;QueryInterface&lt;/code&gt;) and only then use the new interface. It turns out not so convenient, even rather inconvenient: we have a triple call (original + conversion + required). The best solution is for the calling code to tell the called function which interface it is interested in: the new one or the old one:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Correct
procedure GetSomething(const AIID: TGUID; out Intf); safecall;

// ... 

var
  Something: ISomething;
begin
  GetSomething(ISomething, Something);&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;If an object implements an interface, then your code should not contain variables of this class. I.e.:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  TSomeObject = class(TSomeOtherClass, ISomeInterface)
  // ... 
  end;

var
  Obj: TSomeObject; // - incorrect!
  Obj: ISomeInterface; // - correct
begin
  Obj := TSomeObject.Create;
  // ...&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;If you&#39;re implementing an interface extension by inheritance, don&#39;t forget to explicitly list all of its ancestors in the implementing object. For example:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  ISomeInterfaceV1 = interface
  [&#39;{A80A78ED-5836-49C4-B6C2-11F531103FE7}&#39;]
    procedure A;
  end;
 
  ISomeInterfaceV2 = interface(ISomeInterfaceV1) // ISomeInterfaceV2 is inherited from ISomeInterfaceV1
  [&#39;{EBDD52A1-489B-4564-998E-09FCCF923F48}&#39;]
    procedure B;
  end;
 
  // Incorrect!
  TObj = class(TInterfacedObject, ISomeInterfaceV2) // ISomeInterfaceV2 is mentioned, but not ISomeInterfaceV1
  protected
    procedure A; 
    procedure B;
  end;

var
  SI1: ISomeInterfaceV1;
  SI2: ISomeInterfaceV2;
begin
  Supports(SI2, ISomeInterfaceV1, SI1);
  Assert(Assigned(SI1)); // will fire, since SI1 = nil (Supports returned False)
end;&lt;/pre&gt;
A correct way would be:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;  // Correct
  TObj = class(TInterfacedObject, ISomeInterfaceV1, ISomeInterfaceV2)
  // ...&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;It is not necessary to make the implementation of interface methods virtual:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  ISomeInterfaceV1 = interface
  [&#39;{C25F72B0-0BC9-470D-8F43-6F331473C83C}&#39;]
    procedure A;
  end;

  TObj = class(TInterfacedObject, ISomeInterfaceV1)
  protected
    // Incorrect
    procedure A; virtual;
  end;&lt;/pre&gt;
Do like so instead:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;  TObj = class(TInterfacedObject, ISomeInterfaceV1)
  protected
    // Correct
    procedure A;
  end;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Do not use the &lt;code&gt;const&lt;/code&gt; modifier with interface parameters:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;// Incorrect!
procedure DoSomething(const AArg: ISomething); safecall;

// Correct
procedure DoSomething(AArg: ISomething); safecall;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title=&quot;Basic ground rules for programming – function parameters and how they are used&quot; href=&quot;https://devblogs.microsoft.com/oldnewthing/20060320-13/?p=31853&quot;&gt;Other unspoken rules&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;t10&quot;&gt;&lt;/a&gt;&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;a href=&quot;https://www.eurekalog.com/getfile.php?id=92&quot; title=&quot;Download SampleDllAPI.zip&quot;&gt;Download sample DLL API here&lt;/a&gt;. The archive contains a group of two projects (a DLL and an application that uses it). The DLL implements an sample API with example functions. The SDK folder contains the SDK, which consists of:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;SDK:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SampleDLLHeaders.pas&lt;/code&gt; header file;&lt;/li&gt;
&lt;li&gt;CHM and PDF documentation (and project sources &lt;a title=&quot;Help + Manual Website&quot; href=&quot;https://www.helpandmanual.com/&quot;&gt;Help&amp;Manual&lt;/a&gt;);&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;As well as the Delphi-specific support file &lt;code&gt;DelphiSupport.pas&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
Developers in other programming languages can use &lt;code&gt;SampleDLLHeaders.pas&lt;/code&gt; and Delphi developers can use &lt;code&gt;SampleDLLHeaders.pas&lt;/code&gt; + &lt;code&gt;DelphiSupport.pas&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Headers are presented only in the form of Delphi code. Translation into other programming languages left as homework.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;DelphiSupport.pas&lt;/code&gt; module can be included both in the DLL and in applications that use it. It contains:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Processing &lt;code&gt;safecall&lt;/code&gt; (along with fixing &lt;a href=&quot;https://quality.embarcadero.com/browse/RSP-24652&quot; title=&quot;Quality Portal: [RSP-24652] TObject. SafeCallException might be NOT called in certain cases&quot;&gt;RSP-24652&lt;/a&gt;);&lt;/li&gt;
&lt;li&gt;A base object &lt;code&gt;TBaseObject&lt;/code&gt; for implementing interfaces with &lt;code&gt;safecall&lt;/code&gt; processing support and debugging checks;&lt;/li&gt;
&lt;li&gt;Prebuilt classes: &lt;code&gt;TMalloc&lt;/code&gt; allocator and &lt;code&gt;TNotify&lt;/code&gt; wrapper;&lt;/li&gt;
&lt;li&gt;The function &lt;code&gt;RegisterInitFunc&lt;/code&gt; for registering the initialization of modules in the DLL;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LoadDLL&lt;/code&gt; function for correct loading of the DLL.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
The DLL API has sample functions:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Returning an array of strings &lt;code&gt;GetData&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Returning dynamic memory &lt;code&gt;GetMemory&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Callback function set by &lt;code&gt;NotifyMe&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Error/Exception Test &lt;code&gt;TryAbort&lt;/code&gt;, &lt;code&gt;TryAccessViolation&lt;/code&gt;, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
The calling application shows both &quot;load-use-upload&quot; and &quot;load, use, upload&quot;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2213521636129759219'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2213521636129759219'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2023/09/creating-DLL-api.html' title='Creating an API (contract) for your own DLL'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-2150963204376445237</id><published>2023-08-12T16:51:00.006+01:00</published><updated>2025-09-16T13:32:32.751+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Access Violation"/><category scheme="http://www.blogger.com/atom/ns#" term="bug reports"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="how-to"/><category scheme="http://www.blogger.com/atom/ns#" term="Stories"/><category scheme="http://www.blogger.com/atom/ns#" term="technical"/><title type='text'>What can you do with the &quot;Assember&quot; tab in bug reports? Is it even useful?</title><content type='html'>We were contacted by a customer that claimed that his application worked fine until he added EurekaLog to it. Specifically, his application starts to raise an &lt;code&gt;EAccessViolation&lt;/code&gt; exception with the following message:&lt;br /&gt;
&lt;code&gt;Access violation at address 03DB472F in module &#39;Sample.exe&#39;. Read of address 5653E4CC&lt;/code&gt;&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
The customer was kind enough to share the code mentioned in the call stack of the bug report:
&lt;pre class=&quot;brush:delphi&quot;&gt;function TContosoEventCollection.FindEvent(const AValue: TContosoEvent): Integer;
var
  X: Integer;
begin
  Result := -1;

  if not Assigned(FEvents) then
    Exit;

  for X := 0 to FEvents.Count - 1 do // - crashes here
  begin
    if SameMethod(AValue, FEvents[X]) then
    begin
      Result := X;
      Break;
    end;
  end;
end;&lt;/pre&gt;
Where &lt;code&gt;FEvents&lt;/code&gt; is the &lt;code&gt;TList&lt;/code&gt; field in the &lt;code&gt;TContosoEventCollection&lt;/code&gt; class.&lt;br /&gt;
&lt;br /&gt;
As a reminder: the &lt;code&gt;TList&lt;/code&gt; class is implemented in the RTL like this:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  TList = class(TObject)
  private
    FList: TPointerList;
    FCount: Integer;
  // ...
  protected
    function Get(Index: Integer): Pointer; // returns FList[Index]
  // ...  
  public  
    property Items[Index: Integer]: Pointer read Get write Put; default;
    property Count: Integer read FCount write SetCount;
  // ...
  end;&lt;/pre&gt;  
So, the code crashes while trying to read the &lt;code&gt;FCount&lt;/code&gt; field of the &lt;code&gt;TList&lt;/code&gt; class - which probably means that the &lt;code&gt;TList&lt;/code&gt; object is not valid (in other words: trashed).&lt;br /&gt;
&lt;br /&gt;
Well, the bug report can not be false-positive. So, what happenned? And why there is no access violation in the same application compiled without EurekaLog?&lt;br /&gt;
&lt;br /&gt;
Quite often you would see message like this for memory errors:&lt;br /&gt;
&lt;code&gt;Access violation at address ... in module &#39;...&#39;. Read of address DEADBEEF&lt;/code&gt;&lt;br /&gt;
Where the data address would match or be close to the DEADBEEF value - this is the debug marker that EurekaLog fills in the memory being deleted. In addition to DEADBEEF, you can also encounter other values: $CCCCCCCC, $FEEEFEEE, $FDFDFDFD, $FADEDEAD, $00009999. EurekaLog attempts to reserve at least one of them for debugging purposes and will use the first one for which the reservation succeeds. There may also be values close to them (i.e. debug marker + offset, e.g. DEADBE8F).&lt;br /&gt;
&lt;br /&gt;
However, in this particular case: the data address is 5653E4CC - which does not match any known debugger marker. So, at the first glance it &lt;i&gt;could&lt;/i&gt; be a valid data value.&lt;br /&gt;
&lt;br /&gt;
Note that 5653E4CC is supposed to be an address of the &lt;code&gt;TList&lt;/code&gt;&#39;s &lt;code&gt;FCount&lt;/code&gt; field. Now, let&#39;s take a look at the &quot;Assember&quot; tab from EL&#39;s bug report:
&lt;pre class=&quot;brush:asm&quot;&gt;; Line=3297 - Offset=23
; ---------------------
03DB472F  8B7808          MOV  EDI, [EAX+8]                 ; &amp;lt;-- EXCEPTION&lt;/pre&gt;
Now we can see that the &lt;code&gt;TList&lt;/code&gt;&#39;s &lt;code&gt;FCount&lt;/code&gt; field has the offset of 8 from the start of the &lt;code&gt;TList&lt;/code&gt; object. In other words: it is the second field in the class. Therefore, the address of the &lt;code&gt;TList&lt;/code&gt; object itself (in other words: the &lt;code&gt;FEvents&lt;/code&gt; field in customer&#39;s &lt;code&gt;TContosoEventCollection&lt;/code&gt; object) is 5653E4CC - 8 = 5653E4C4.&lt;br /&gt;
&lt;br /&gt;
Why is this important?&lt;br /&gt;
&lt;br /&gt;
Well, the 5653E4C4 = 1448338628 and it is &lt;b&gt;NOT&lt;/b&gt; divisible by 16. In fact, it is not even disisible by 8. It matters because allocation granularity on Delphi is at least 16 bytes.&lt;br /&gt;
(Note: very old IDEs like Delphi 5 had allocation granularity of 4)&lt;br /&gt;
&lt;br /&gt;
Conclusion: 5653E4C4 can not be address of any &lt;code&gt;TList&lt;/code&gt; object in customer&#39;s application. Which means that it is a completely trash value. (Otherwise, we might suspect that it could be a value of &lt;i&gt;some&lt;/i&gt; &lt;code&gt;TList&lt;/code&gt; object - the one that was already deleted.)&lt;br /&gt;
&lt;br /&gt;
Summary: the &lt;code&gt;FEvents&lt;/code&gt; (&lt;code&gt;TList&lt;/code&gt;) field in customer&#39;s &lt;code&gt;TContosoEventCollection&lt;/code&gt; object is trashed.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s dig further. Look at the &quot;Assembler&quot; tab again:&lt;br /&gt;
&lt;pre class=&quot;brush:asm&quot;&gt;; ContosoUnit.TContosoEventCollection.FindEvent (Line=1234 - Offset=0)
; ---------------------------------------------------------
03DB4718  55              PUSH EBP
03DB4719  8BEC            MOV  EBP, ESP&lt;/pre&gt;
This sounds like a start of the &lt;code&gt;TContosoEventCollection.FindEvent&lt;/code&gt; method to me. And what do we know about Delphi methods? Well, they pass &lt;code&gt;Self&lt;/code&gt; as the implicit first argument. And what do we know about arguments passing in x86? Well, the first argument is stored in the &lt;code&gt;EAX&lt;/code&gt; CPU register. It is further confirmed by the same &quot;Assember&quot; tab:&lt;br /&gt;
&lt;pre class=&quot;brush:asm&quot;&gt;03DB471F  8BF0            MOV  ESI, EAX

; if not Assigned(FEvents) then
03DB4728  8B4604          MOV  EAX, [ESI+4]
03DB472B  85C0            TEST EAX, EAX&lt;/pre&gt;
We can clearly see as &lt;code&gt;Self&lt;/code&gt; is being copied from &lt;code&gt;EAX&lt;/code&gt; into &lt;code&gt;ESI&lt;/code&gt;, and then &lt;code&gt;ESI&lt;/code&gt; is being used to read the &lt;code&gt;FEvents&lt;/code&gt; field (which, as we remember, is trashed) - which has offset of 4 (e.g. the very first field in the class - also confirmed by customer&#39;s source code).&lt;br /&gt;
&lt;br /&gt;
So, when crash occurs, the &lt;code&gt;Self&lt;/code&gt; must be stored in the &lt;code&gt;ESI&lt;/code&gt; CPU register. Let&#39;s take a look at it: 00451C10.&lt;br /&gt;
&lt;br /&gt;
Do you see anything wrong with it?&lt;br /&gt;
&lt;br /&gt;
Well, while the 00451C10 value &lt;b&gt;is&lt;/b&gt; divisible by 16, but customer&#39;s EXE loads at the default 00400000 address - which is awfully close to the 00451C10. In fact, customer&#39;s app has crashed at the 03DB472F address (see the original exception&#39;s message) - which is well inside reach from the 00400000, given size of customer&#39;s exe being more than 100&#39;000&#39;000 bytes (as inspected from the &quot;Modules&quot; tab). 100&#39;000&#39;000 = 05F5E100, so 00400000 + 05F5E100 = 0635E100 - way more than the 03DB472F. Therefore, any valid data/objects must have addresses only above 0635E100 (or below 00400000).&lt;br /&gt;
&lt;br /&gt;
In other words: 00451C10 is a code address. It can not possibly be any data/&lt;code&gt;TObject&lt;/code&gt; address.&lt;br /&gt;
&lt;br /&gt;
Conclusion: customer&#39;s &lt;code&gt;TContosoEventCollection&lt;/code&gt; object is trashed too, so no wonder we read trash data from it, so its &lt;code&gt;FEvents&lt;/code&gt;/&lt;code&gt;TList&lt;/code&gt; field also becomes trash. Customer was unlucky in the sense that the invalid value of the &lt;code&gt;TContosoEventCollection&lt;/code&gt; object was, in fact, readable, so CPU could load the (trashed) &lt;code&gt;FEvents&lt;/code&gt; field from it and only crashed later. If customer was lucky - he would crash immediately on access to &lt;code&gt;TContosoEventCollection&lt;/code&gt;&#39;s &lt;code&gt;FEvents&lt;/code&gt; field.&lt;br /&gt;
&lt;br /&gt;
Summary: customer should look further up stack to the caller code: it is calling the &lt;code&gt;FindEvent&lt;/code&gt; method on the invalid object value. From where does it reads/takes the &lt;code&gt;TContosoEventCollection&lt;/code&gt; object? Whatever it was - it is trashed now.&lt;br /&gt;
&lt;br /&gt;
The customer has reported that he was able to find and fix the issue armed with this information.&lt;br /&gt;
&lt;br /&gt;
As for the question why there was no access violation in the same application compiled without EurekaLog - well, applications without EurekaLog have absolutely no memory checks in them! So &lt;a href=&quot;https://blog.eurekalog.com/2023/04/librariescomponentsarenottested.html&quot; title=&quot;Many libraries/components are not tested for memory bugs&quot;&gt;most memory-related errors go unnoticed&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
See also: &lt;a href=&quot;https://blog.eurekalog.com/2025/08/how-to-read-bug-reports.html&quot; title=&quot;How to read bug reports&quot;&gt;How to read bug reports&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
P.S. &lt;a href=&quot;https://blog.eurekalog.com/search/label/Stories&quot; title=&quot;EurekaLog Blog: Stories&quot;&gt;Read more stories like this one&lt;/a&gt; or &lt;a href=&quot;https://www.eurekalog.com/casestudies.php&quot; title=&quot;Case Studies and User Reviews&quot;&gt;read feedback from our customers&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2150963204376445237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2150963204376445237'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2023/08/what-to-do-with-assembler.html' title='What can you do with the &quot;Assember&quot; tab in bug reports? Is it even useful?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-2676243567742008236</id><published>2023-07-26T17:33:00.011+01:00</published><updated>2024-10-04T19:37:23.134+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bug reports"/><category scheme="http://www.blogger.com/atom/ns#" term="compiling"/><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="feature"/><category scheme="http://www.blogger.com/atom/ns#" term="technical"/><category scheme="http://www.blogger.com/atom/ns#" term="Win64"/><title type='text'>Does EurekaLog support CLang/LLVM/64-bit?</title><content type='html'>&lt;h1&gt;Short Answer&lt;/h1&gt;
Yes, EurekaLog supports CLang and LLVM in both 32 and 64 bits - with some limitations. Exception is the very recent &quot;Windows 64-bit (Modern)&quot; platform, which was added into the RAD Studio 12 Athens since the major 12.1 update. The &quot;Windows 64-bit (Modern)&quot; platform is not supported yet.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h1&gt;Long Answer&lt;/h1&gt;
&lt;h3&gt;History&lt;/h3&gt;
The 64-bit Delphi is build on the classic Borland toolchain. EurekaLog 7 supported 64-bit Delphi since the very begining: the original release of EurekaLog 7.0.0.0 in 2012. Since many of our clients were using the new 64-bit platform, we were also able to improve its support over time. It was feature-complete in somewhere near 2014. Currently it does not have any limitations.&lt;br /&gt; 
&lt;br /&gt;
On the other hand, the 64-bit C++ Builder is build on entirely different toolchain: LLVM. Classic toolchain is not used for 64-bit C++ Builder. The CLang is LLVM&#39;s frontend for C++. Initially EurekaLog 7 did not have support for LLVM.&lt;br /&gt; 
&lt;br /&gt;
While 32-bit C++ Builder was also originally build on the classic Borland toolchain, Embarcadero has released RAD Studio 10 Seattle in 2015 with an option to use the LLVM-based toolchain for 32-bit C++ Builder. In other words, starting with RAD Studio 10 Seattle (2015+):&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;32-bit Delphi: classic only&lt;/li&gt;
&lt;li&gt;64-bit Delphi: classic only&lt;/li&gt;
&lt;li&gt;32-bit C++ Builder: classic OR LLVM - your choice&lt;/li&gt;
&lt;li&gt;64-bit C++ Builder: LLVM only&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Since now CLang was accessible in 32-bit, we also started implementing support for LLVM and CLang. However, not many of our clients use C++ Builder. And very few clients immediately switched to CLang/LLVM. Therefore, we also couldn&#39;t polish our CLang/LLVM support as quickly as Delphi&#39;s 64-bit support. However, a lot of time has passed and we can say with confidence that many of our clients use EurekaLog for CLang/LLVM today.&lt;br /&gt;
&lt;br /&gt;
Support for 64-bit C++ Builder is a relatively recent addition. It was enabled in 2020. EurekaLog doesn&#39;t track usage statistics, so we don&#39;t know exactly how many of our clients are using 64-bit C++ Builder. But we can judge this indirectly: by the number of questions to our technical support. And judging by it: almost none of our clients use 64-bit C++ Builder. This means we don&#39;t get a lot of feedback and improvements to our 64-bit C++ Builder support are VERY slow.&lt;br /&gt;
&lt;br /&gt;
In summary: while EurekaLog does support CLang, LLVM and 64-bit, you must understand that some aspects had more time to polish and evolve. For these historical reasons, the levels of maturnity (in order from high to low) are:
&lt;ol&gt;
&lt;li&gt;32-bit Delphi&lt;/li&gt;
&lt;li&gt;32-bit C++ Builder on classic toolchain&lt;/li&gt;
&lt;li&gt;64-bit Delphi&lt;/li&gt;
&lt;li&gt;32-bit C++ Builder on LLVM toolchain (CLang)&lt;/li&gt;
&lt;li&gt;64-bit C++ Builder&lt;/li&gt;
&lt;li&gt;64-bit C++ Builder (Modern)&lt;/li&gt;
&lt;/ol&gt; 
&lt;br /&gt;
&lt;h3&gt;Technical details and limitations&lt;/h3&gt;
As mentioned above, 32-bit and 64-bit Delphi support and 32-bit C++ Builder on classic toolchain are feature-complete and have no limitations.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, using LLVM (CLang) for 32 or 64-bits means that some exception info is missing. LLVM (or its implementation in C++ Builder) is very unpolished for exception tracing. Specifically, it does not set an exception address, it does not set a valid stack pointer and a frame - all those necessary and important values are just zeros. We really got into the wilds of workarounds and hacks, only to extract the address of the exception. Everything really hangs on straws and assumptions.&lt;br /&gt;
&lt;br /&gt;
Classic C++ Builder is much better in this aspect. We are improving diagnostic as much as possible, so newer versions of EurekaLog should provide better info. But there may be something that is impossible to implement from our side. We highly recommend that you don&#39;t use LLVM on 32-bit, use classic compiler. Naturally, you are limited to LLVM only for 64-bit C++ Builder as there is no option to use classic compiler.&lt;br /&gt; 
&lt;br /&gt;
Therefore, stack frames based methods will not work if you are using LLVM/CLang compiler (in other words: &quot;Use classic compiler&quot; option is disabled). Select any RAW method for LLVM/CLang.&lt;br /&gt;
&lt;br /&gt;
One other point to consider is the &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/configuring_project_itself.php&quot; title=&quot;Configuring project for EurekaLog&quot;&gt;debug information&lt;/a&gt;. Delphi, C++ Builder and RAD Studio has an option to generate a text .map file, which contains debug information about the compiled module - such as unit, class, function names and line numbers. &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/eurekalog_basics.php&quot; title=&quot;EurekaLog&#39;s Basics&quot;&gt;Such .map file is used by EurekaLog to compose debug information in EurekaLog&#39;s own format&lt;/a&gt; - designed for maximum access speed or minimum space. The issue is that .map files produced by the C++ Builder do not have line numbers - just names of units, classes, and functions:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;EurekaLog is also able to read debug information in Turbo Debugger (TD32, TDS) format. If it is enabled for C++ Builder - EurekaLog will use it to extract line numbers. So, when using 32-bit C++ Builder on the classic toolchain - EurekaLog will be able to show line numbers in bug reports.&lt;/li&gt;
&lt;li&gt;If you would use LLVM/CLang on Win32 (e.g. &quot;Use Classic compiler&quot; option is disabled) or 64-bit C++ Builder (which is LLVM/CLang-only) - then no TD32/TDS debug information will be available, as LLVM/clang generates DWARF debug information, not TD32/TDS. Currently EurekaLog is unable to read DWARF debug information, so there will be no line numbers in bug reports.&lt;/li&gt;
&lt;/ol&gt; 
&lt;br /&gt;
You can help us to fix this issue by voting for this entry:&lt;br /&gt;
&lt;a title=&quot;Quality Portal: RSS-1496&quot; href=&quot;https://embt.atlassian.net/servicedesk/customer/portal/1/RSS-1496&quot;&gt;[RSS-1496] No line numbers in map files for many platforms, while classic Win32 works fine&lt;/a&gt;.&lt;br /&gt;
&lt;a title=&quot;Quality Portal: RSP-27359&quot; href=&quot;https://quality.embarcadero.com/browse/RSP-27359&quot;&gt;[RSP-27359] No line numbers in map files for LLVM-based compilers&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2676243567742008236'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/2676243567742008236'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2023/07/eurekalog-support-clang-llvm-64-bits.html' title='Does EurekaLog support CLang/LLVM/64-bit?'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-4877752996156256528</id><published>2023-04-03T21:43:00.003+01:00</published><updated>2023-04-04T09:24:47.365+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="memory leaks"/><category scheme="http://www.blogger.com/atom/ns#" term="Stories"/><title type='text'>Many libraries/components are not tested for memory bugs</title><content type='html'>We were contacted by a customer, who claimed that EurekaLog causes Access Violation in a simple sample demo application.&lt;br /&gt;
&lt;br /&gt;
Specifically, the application runs fine when compiled without EurekaLog and produces the expected results. But application crashes with &quot;Access violation at address 00410759 in module &#39;DemoApp.exe&#39;. Read of address 83EC8B59&quot; when compiled with EurekaLog.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Unfortunately, the customer did not report any additional details, such as: EurekaLog version, IDE version, OS version, bug report file nor call stack, etc. However, we did know that the access violation exception happenned inside the &lt;code&gt;System._IntfClear&lt;/code&gt; function.&lt;br /&gt;
&lt;br /&gt;
Well, it doesn&#39;t take a genius to figure out that there is most likely a memory bug in the sample app&#39;s code. And it is probably something related to mixing manual/automatic management of object&#39;s lifecycle.&lt;br /&gt;
&lt;br /&gt;
We have installed a demo version of the components in question and compiled a sample application with EurekaLog. Running the application and simply exiting it trigered the following error:&lt;br /&gt;
&lt;br /&gt;
&quot;Application made attempt to call method of already deleted object: $0430B610 OBJECT [TContosoDoc] 340 bytes&quot;&lt;br /&gt;
&lt;br /&gt;
while the call stack from the bug report looked like this:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;System._IntfClear&lt;br /&gt;
System._FinalizeRecord&lt;br /&gt;
System.TObject.CleanupInstance&lt;br /&gt;
System.TObject.FreeInstance&lt;br /&gt;
System._ClassDestroy&lt;br /&gt;
Contoso.VCL.TContosoPropertiesForm.Destroy 2893[2]&lt;br /&gt;
System.TObject.Free&lt;br /&gt;
System.Classes.TComponent.DestroyComponents&lt;br /&gt;
Vcl.Forms.DoneApplication&lt;br /&gt;
System.SysUtils.DoExitProc&lt;br /&gt;
System._Halt0&lt;br /&gt;&lt;/code&gt;
&lt;br /&gt;
Well, the error message is different, but the crash location is the same. Why is that?&lt;br /&gt;
&lt;br /&gt;
There could be few reasons for that:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;The customer did not report when and how the error occurs. Perhaps, he was seeing a different error in a different place.&lt;/li&gt;
&lt;li&gt;The client could (correctly) assume that it was a memory bug somewhere, so he tried to &quot;fix&quot; the bug by disabling some of the EurekaLog&#39;s memory check options. Therefore, the client application&#39;s configuration could differ from defaults. And we were checking using the default settings.&lt;/li&gt;
&lt;li&gt;The code that detects/shows the &quot;call method of already deleted object&quot; message relies on the fact that the released memory remains untouched. However, if some code allocates memory over this disposed memory, the check could not function, and you get a simple access violation instead. So, depending on how memory is allocated/disposed, the behaviour can change.&lt;/li&gt;
&lt;/ol&gt;  
&lt;br /&gt;
Anyway, the EurekaLog was able to show a second call stack for the same object: specifically, the second call stack shows where the object was originally destroyed:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;ContosoDoc.TContosoCustomDoc.Destroy&lt;br /&gt;
System.Classes.TComponent.DestroyComponents&lt;br /&gt;
Vcl.Forms.DoneApplication&lt;br /&gt;
System.SysUtils.DoExitProc&lt;br /&gt;
System._Halt0&lt;br /&gt;&lt;/code&gt;
&lt;br /&gt;
Just looking at these two call stack you can see the problem already: 
&lt;ol&gt;
&lt;li&gt;The second call stack (actual deletion) mentions that the object in question was deleted &quot;manually&quot; by calling its destructor when components are cleaned up on app&#39;s shutdown.&lt;/li&gt;
&lt;li&gt;The first call stack (access to already deleted object) mentions that the same object was also tried to be deleted automagically via an interface reference.&lt;/li&gt;
&lt;/ol&gt;
Thankfully, we don&#39;t need the source code for the component/library (which we don&#39;t have, because we are using demo/trial) to confirm that. The line number from the first call stack leads us directly to the problem:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;constructor TContosoPropertiesForm.Create(AOwner: TComponent);
begin
  inherited;
  FDoc := TContosoDoc.Create(Self);
end;

destructor TContosoPropertiesForm.Destroy;
begin
  inherited;
end;&lt;/pre&gt;
where the &lt;code&gt;FDoc&lt;/code&gt; field is declared as:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;private
  FDoc: IContosoDoc;&lt;/pre&gt;
Do you see the problem?&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;TContosoDoc&lt;/code&gt; will be deleted by the &lt;code&gt;TContosoPropertiesForm&lt;/code&gt;, because the &lt;code&gt;TContosoPropertiesForm&lt;/code&gt; (&lt;code&gt;Self&lt;/code&gt;) was passed as an owner to the &lt;code&gt;TContosoDoc&lt;/code&gt;. So, when the &lt;code&gt;TContosoPropertiesForm&lt;/code&gt; deletes itself - it also deletes all owned sub-components, including the &lt;code&gt;TContosoDoc&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
But the reference to the &lt;code&gt;TContosoDoc&lt;/code&gt; was also saved into the &lt;code&gt;FDoc&lt;/code&gt; field. That should not be a problem if the field has the &lt;code&gt;&lt;b&gt;T&lt;/b&gt;ContosoDoc&lt;/code&gt; type. But it has the &lt;code&gt;&lt;b&gt;I&lt;/b&gt;ContosoDoc&lt;/code&gt; type. In other words, it is an interface! When interface goes out of scope, it dereferences, and the object is deleted when the reference count reaches zero.&lt;br /&gt;
&lt;br /&gt;
You may know that the components (descendants from &lt;code&gt;TComponent&lt;/code&gt;) override the automatic inteface management by saying &quot;there is no reference counter&quot;. In other words, increasing and descreasing interface counter do absolutely nothing.&lt;br /&gt;
&lt;br /&gt;
If so - why there is the crash then? The reason is that even the simple &quot;there is no reference counter&quot; behavior requires &lt;b&gt;virtual&lt;/b&gt; method calls! Indeed, remember that &lt;code&gt;IInterface&lt;/code&gt;/&lt;code&gt;IUnknown&lt;/code&gt; is declared as:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  IInterface = interface
    [&#39;{00000000-0000-0000-C000-000000000046}&#39;]
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;
  
  IUnknown = IInterface;&lt;/pre&gt;
In other words, any interface in Delphi &lt;b&gt;must&lt;/b&gt; implement the &lt;code&gt;_AddRef&lt;/code&gt; and &lt;code&gt;_Release&lt;/code&gt; methods, because all interfaces in Delphi descent from &lt;code&gt;IInterface&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Another piece of the puzzle: the &quot;there is no reference counter&quot; behaviour is not implemented like &quot;do not call the &lt;code&gt;_AddRef&lt;/code&gt;/&lt;code&gt;_Release&lt;/code&gt; methods&quot;. Instead, this behaviour is implemented like &quot;the &lt;code&gt;_AddRef&lt;/code&gt;/&lt;code&gt;_Release&lt;/code&gt; methods do nothing&quot;. So, the &lt;code&gt;_AddRef&lt;/code&gt;/&lt;code&gt;_Release&lt;/code&gt; methods must be called.&lt;br /&gt;
&lt;br /&gt;
If so - they must be implemented (as emtpy methods). And how do you implement interface methods? By using virtual methods:&lt;br /&gt;
&lt;pre class=&quot;brush:delphi&quot;&gt;type
  TInterfacedObject = class(TObject, IInterface)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;&lt;/pre&gt;
Yes, the &lt;code&gt;_AddRef&lt;/code&gt;/&lt;code&gt;_Release&lt;/code&gt; methods are non-virtual methods for &lt;b&gt;the object (class)&lt;/b&gt;. However, remember that interface is basically an abstract class, which means all of its methods are pure virtual. Which means that the mentioned methods will be virtual methods for &lt;b&gt;the interface&lt;/b&gt; once it is implemented.&lt;br /&gt;
&lt;br /&gt;
And how do you call a virtual method? Well, you have to look it up inside object&#39;s (interface&#39;s) virtual method table. But if the object/interface was already released then its virtual method table won&#39;t be accessible anymore. That is where the bug comes from. The code is not actually trying to delete already deleted object, but it is trying to say &quot;interface goes out of scope, please decrease reference counter&quot;. Normally this would result in the &quot;do nothing&quot; behaviour, but in our case the &quot;do nothing&quot; behaviour could not be located, since its implementing object is already gone.&lt;br /&gt;
&lt;br /&gt;
So, why it was not a problem without EurekaLog on board?&lt;br /&gt;
&lt;br /&gt;
It&#39;s simple: deleting object means marking its memory as &quot;empty&quot;. The memory itself is not gone. And its content stays the same. Therefore, any futher calls to &lt;code&gt;_AddRef&lt;/code&gt;/&lt;code&gt;_Release&lt;/code&gt; methods will be successful, since virtual method table still could be located.&lt;br /&gt;
&lt;br /&gt;
Conclusion: it is a bug in library/component&#39;s demo code, which must be fixed. Simplest way is a workaround: set the &lt;code&gt;FDoc&lt;/code&gt; field to &lt;code&gt;nil&lt;/code&gt; as the first action in the &lt;code&gt;TContosoPropertiesForm&lt;/code&gt;&#39;s destructor. One correct way to fix it is to change field&#39;s type to object (class), so interfaces will be created/disposed only when used. Another way is to remove ownership and implement the reference counting, so object&#39;s lifetime will be managed by interface field only.&lt;br /&gt;
&lt;br /&gt;
Moral of the story: use either interfaces or objects, do not mix! E.g. if you use interfaces - do not store references to implementing objects. If you use objects - do not store interface references.&lt;br /&gt; 
&lt;br /&gt; 
As you can imagine, many libraries and components come with memory-related bugs, because there is no build-in tools in Delphi to diagnose such issues. You need a 3rd party tool: debugging memory manager. Not every library/component vendor will go extra length to use 3rd party tool to test his code. This is true even for Delphi itself, as both VCL and FMX has similar memory bugs which usually stays hidden. For example: &lt;a href=&quot;https://quality.embarcadero.com/browse/RSP-38694&quot; title=&quot;RSP-38694: Using &#39;with&#39; in VCL causes &#39;use after free&#39; bugs&quot;&gt;RSP-38694&lt;/a&gt;, &lt;a href=&quot;https://quality.embarcadero.com/browse/RSP-30403&quot; title=&quot;RSP-30403: Leak in TParallel.For / EAggregateException&quot;&gt;RSP-30403&lt;/a&gt;, &lt;a href=&quot;https://quality.embarcadero.com/browse/RSP-28294&quot; title=&quot;RSP-28294: Regression for RSP-10308: accessing already deleted object&quot;&gt;RSP-28294&lt;/a&gt;, &lt;a href=&quot;https://quality.embarcadero.com/browse/RSP-10308&quot; title=&quot;RSP-10308: TWebBrowser has a memory leak&quot;&gt;RSP-10308&lt;/a&gt;, ...&lt;br /&gt; 
&lt;br /&gt; 
So, what if you can&#39;t fix the 3rd party code? Well, you can hide the bug by &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/memory_leaks_page.php&quot; title=&quot;EurekaLog Memory Options&quot;&gt;disabling the memory checks in EurekaLog&lt;/a&gt;. We recommend that you keep the &quot;Enable extended memory manager&quot; option enabled and disable all other sub-options. Don&#39;t forget to set the &quot;When memory is released&quot; option to &quot;Do nothing&quot;. Please note that by doing so - you are hiding the bug, you are not actually fixing it!&lt;br /&gt;
&lt;br /&gt;
P.S. It might be counter-intuitive to some, but if you want to fix a memory bug - you need to &lt;b&gt;enable&lt;/b&gt; the &quot;Catch memory leaks&quot; option (make sure the &quot;Active only when running under debugger&quot; option is off if you are running the app outside of the debugger). Enabling memory leaks checks allows EurekaLog to allocate additional memory blocks with information about allocated memory. In these additional memory blocks, EurekaLog can store, among other things, additional call stacks and information about the memory&#39;s data type. All this additional information can help EurekaLog produce more accurate diagnostic information if a problem was found.&lt;br /&gt;
&lt;br /&gt;
P.P.S. &lt;a href=&quot;https://blog.eurekalog.com/search/label/Stories&quot; title=&quot;EurekaLog Blog: Stories&quot;&gt;Read more stories like this one&lt;/a&gt; or &lt;a href=&quot;https://www.eurekalog.com/casestudies.php&quot; title=&quot;Case Studies and User Reviews&quot;&gt;read feedback from our customers&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4877752996156256528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/4877752996156256528'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2023/04/librariescomponentsarenottested.html' title='Many libraries/components are not tested for memory bugs'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-6485040856405417648</id><published>2022-12-01T09:57:00.003+00:00</published><updated>2022-12-01T09:57:34.791+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="Releases"/><title type='text'>EurekaLog 7.11 is out</title><content type='html'>We are pleased to announce the availability of the new 7.11 version.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;EurekaLog Logo&quot;&gt;&lt;img alt=&quot;EurekaLog Logo&quot; src=&quot;https://www.eurekalog.com/images/logo.png&quot; height=&quot;68&quot; width=&quot;295&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
EurekaLog is a tool for Delphi and C++Builder that gives your application the power to catch every exception and memory/resource leak, generating a detailed log of the call stack (with unit, procedure and line number), optionally sending you a copy of each log entry via email or the Web via the most used Web Bug-Tracking tools (as Mantis, BugZilla, FogBugz, JIRA, YouTrack, Redmine, Exceptionless, GitLab, and GitHub).&lt;/blockquote&gt;
EurekaLog represents the most advanced exception and memory leaks logger technology available for Delphi/C++Builder developers.&lt;br /&gt;
&lt;br /&gt;
To learn more about EurekaLog, please visit our website at:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com&quot;&gt;https://www.eurekalog.com&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
To download a demo, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/downloads.php&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/downloads.php&quot;&gt;https://www.eurekalog.com/downloads.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you would like to purchase a new license, please visit:&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; target=&quot;_blank&quot; title=&quot;https://www.eurekalog.com/buy.php&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Changes in 7.11 build:&lt;/strong&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Added: Support for RAD Studio 11.2 (update 2 for 11 Alexandria)&lt;/li&gt;
&lt;li&gt;Added: EurekaLog can now be packaged into a custom BPL package. Options from BPL package will be used to initialize EurekaLog. EurekaLogCore continues to be a default RTL package, which will use options from main executable&lt;/li&gt;
&lt;li&gt;Added: [C++ Builder] Support for CLang/LLVM objects for memory features. &lt;b&gt;Do not forget to update your &lt;code&gt;EMemLeaksBCB.cpp&lt;/code&gt; files from EurekaLog installation folder&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Added: [C++ Builder] Support for memory features for 64-bits&lt;/li&gt;
&lt;li&gt;Added: [C++ Builder] New &lt;code&gt;_TDSComplete&lt;/code&gt; option can now be used to turn off TDS processing for faster post-processing of C++ Builder projects (no line numbers will be added), good for quick testing, not recommended for production&lt;/li&gt;
&lt;li&gt;Added: Stay On Top checkbox for MessageBox dialog&lt;/li&gt;
&lt;li&gt;Added: New &lt;code&gt;.LastSystemErrorCode&lt;/code&gt; property for exception info&lt;/li&gt;
&lt;li&gt;Added: [Viewer] EurekaLog report version is now displayed as the first value under &quot;General&quot; tab&lt;/li&gt;
&lt;li&gt;Added: A warning about saving your project before editing EurekaLog&#39;s options&lt;/li&gt;
&lt;li&gt;Added: EurekaLog options will now have an unique GUID set&lt;/li&gt;
&lt;li&gt;Added: Ability to add debug information/options to an external file (.edbg) instead of injecting inside executable. Can be used for C++ Builder when debugger is unable to recognize the modified .exe. Additionally, there is a hidden option &lt;code&gt;DoNotTouchExe&lt;/code&gt; (for testing or packers/digital signatures)&lt;/li&gt;
&lt;li&gt;Added: Ability to side-load external debug info for main module (for example, use EurekaLog options, but use JCL debug info)&lt;/li&gt;
&lt;li&gt;Added: Made &lt;code&gt;SMTPClientAdditionalHeaders&lt;/code&gt; to be additional argument for the &lt;code&gt;EurekaLogSendEmail&lt;/code&gt; functions&lt;/li&gt;
&lt;li&gt;Fixed: Custom/Help button will not be visible if no event handler is assigned&lt;/li&gt;
&lt;li&gt;Fixed: [64-bit C++ Builder] IDE debugger crash when debugging EurekaLog-enabled executables&lt;/li&gt;
&lt;li&gt;Fixed: [C++ Builder] Various memory diagnostic improvements&lt;/li&gt;
&lt;li&gt;Fixed: [C++ Builder] Proper unit handling in IDE when switching target platform&lt;/li&gt;
&lt;li&gt;Fixed: Added workaround for &lt;a title=&quot;My application starts throwing 12175/ERROR_WINHTTP_SECURE_FAILURE error after adding EurekaLog&quot; href=&quot;https://support.eurekalog.com/index.php?/Knowledgebase/Article/View/93/10/7x-my-application-starts-throwing-12175error_winhttp_secure_failure-error-after-adding-eurekalog&quot;&gt;this issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Fixed: Added support latest changes in JIRA API&lt;/li&gt;
&lt;li&gt;Fixed: Taking screenshots from non-DPI awared applications&lt;/li&gt;
&lt;li&gt;Fixed: [x64] Rare crash when following error line from report in IDE&lt;/li&gt;
&lt;li&gt;Fixed: [Regression] MS Debug Info provider may fail to obtain information&lt;/li&gt;
&lt;li&gt;Fixed: [Regression] Showing EurekaLog dialog may hung when parent window is not responding&lt;/li&gt;
&lt;li&gt;Fixed: [Regression] Rare deadlock on startup when using MS Debug Info provider&lt;/li&gt;
&lt;li&gt;Fixed: [Regression] ECC32 may fail to compile project when no .dproj/.cbproj file is specified&lt;/li&gt;
&lt;li&gt;Fixed: Improved support for non-default output file extensions&lt;/li&gt;
&lt;li&gt;Fixed: Freeze detection restart controls improvements&lt;/li&gt;
&lt;li&gt;Fixed: EurekaLog style dialog will property set a default button&lt;/li&gt;
&lt;li&gt;Fixed: Sometimes EurekaLog dialog could appear behind app&#39;s window&lt;/li&gt;
&lt;li&gt;Fixed: Rare range-check error in exception dialogs&lt;/li&gt;
&lt;li&gt;Fixed: Rare crash on startup of  RAD Studio 11 Alexandria (bug in the workaround for the RSP-36484)&lt;/li&gt;
&lt;li&gt;Fixed: [Regression] SMTP send speed improvements&lt;/li&gt;
&lt;li&gt;Fixed: [C++ Builder] Incorrect unit initialization order could cause EurekaLog to ignore exceptions&lt;/li&gt;
&lt;li&gt;Fixed: [C++ Builder] Installation of library paths for 64-bit&lt;/li&gt;
&lt;li&gt;Fixed: [C++ Builder] Crash on startup in C++ Builder 2010 only&lt;/li&gt;
&lt;li&gt;Fixed: Minor internal logging improvements&lt;/li&gt;
&lt;li&gt;Fixed: Rare issues with some SMTP servers&lt;/li&gt;
&lt;li&gt;Fixed: &quot;Include child classes&quot; option did not work correctly in some cases (mostly on older IDEs)&lt;/li&gt;
&lt;li&gt;Fixed: &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/how_to_register_event_handler_for_leaks.php&quot; title=&quot;How to register event handler for leaks&quot;&gt;OnExceptionNotify now works for leaks too&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Fixed: Various minor improvements&lt;/li&gt;
&lt;li&gt;Removed: Hook for &lt;code&gt;CreateFileA&lt;/code&gt; function as no longer used by EurekaLog&#39;s run-time code. Now this hook is used only for design-time&lt;/li&gt;  
&lt;li&gt;Removed: The &quot;Delete service files after compilation&quot; option. You can add the &quot;&lt;code&gt;del &quot;%_IDEDst%*.map&quot;;del &quot;%_IDEDst%*.drc&quot;;del &quot;%_IDEDst%*.dcu&quot;&lt;/code&gt;&quot; command (without external quotes) to &lt;a href=&quot;https://www.eurekalog.com/help/eurekalog/build_events_page.php&quot; title=&quot;Build Events&quot;&gt;EurekaLog&#39;s post-build event&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Changed: FastMM support units will no longer include debug information providers. You are supposed to manually add/enable providers that you want/need/use.&lt;/li&gt;
&lt;li&gt;Changed: [IDE] New code for EurekaLog version update checks&lt;/li&gt;
&lt;/ol&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/6485040856405417648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/6485040856405417648'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2022/12/blog-post.html' title='EurekaLog 7.11 is out'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry><entry><id>tag:blogger.com,1999:blog-6789056281440007248.post-6207437614186821560</id><published>2022-11-21T05:00:00.001+00:00</published><updated>2022-11-21T05:00:00.190+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Delphi"/><category scheme="http://www.blogger.com/atom/ns#" term="EL 7.x"/><category scheme="http://www.blogger.com/atom/ns#" term="non-technical"/><title type='text'>Black Friday and Cyber Monday Sale</title><content type='html'>We are pleased to announce Black Friday and Cyber Monday &lt;b&gt;30% discount&lt;/b&gt; on any of our EurekaLog products using the coupon code found below.&lt;br /&gt;
&lt;br /&gt;
The sale starts on Black Friday (November, 25) and ends at the end of Cyber Monday (November, 28).&lt;br /&gt;
&lt;br /&gt;
Enter this code when paying for the item on our web site:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;BFCM2022&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
Existing customers with valid or expired licenses can log in and purchase upgrades, new licenses and extensions here:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/login.php&quot; title=&quot;Log in to customer control panel&quot;&gt;https://www.eurekalog.com/login.php&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Use the login credentials we sent you at purchase time.&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
New customers (without existing licenses) can use the discount code here:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.eurekalog.com/buy.php&quot; title=&quot;Purchase EurekaLog&quot;&gt;https://www.eurekalog.com/buy.php&lt;/a&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/6207437614186821560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6789056281440007248/posts/default/6207437614186821560'/><link rel='alternate' type='text/html' href='http://blog.eurekalog.com/2022/11/black-friday-and-cyber-monday-sale.html' title='Black Friday and Cyber Monday Sale'/><author><name>GunSmoker</name><uri>http://www.blogger.com/profile/15611696588191431330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYMBDZyhmwpngoHu4MbxoNS55hTzfYX9MRyMyiL7lk8FV4zzROyizfpqf_E85yuu8-BtGtcAZg_miWOVH_8I9hdkdBcDd5UkBhBcWo1jkcyacq_Lgcmt9gECy0zV0zNo/s220/avatar.png'/></author></entry></feed>