Users of recent EurekaLog versions could receive the "Invalid EurekaLog configuration found" error message when they run their EurekaLog-enabled applications. The message looks like this:
Invalid EurekaLog configuration found.Exception from the following module:full-file-name-to-your-EXE-or-DLLwas catched in the following module:full-file-name-to-your-DLL-or-EXEBoth modules have EurekaLog enabled, but EurekaLog code is not shared.This is essentially the same error as:- "Can''t assign a TFont to a TFont";- "Invalid class typecast";- reInvalidCast;- "Run-time error 10".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's code- Compile EXE and DLL with run-time packages, so EXE and DLL will share EurekaLog's codeSee our help for more information:
https://www.eurekalog.com/help/eurek..._eurekalog.php

What this check is
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.
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).
What the problem is
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:function CreateForm: TForm; stdcall; export;begin Result := TMyForm.Create(nil);end;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
DLL.TForm and EXE.TForm - two
different things. So essentially you create "something" (object) in
DLL and pass it back to EXE, which expects it to be EXE.TForm - but it is not: it is
DLL.TForm.
The same thing is true for the reverse case: when EXE pass objects into
DLL. For example, one may choose to pass EXE's Application or MainForm to
DLL in order for
DLL to display owned/child forms.
Such code is a bad design. You should
never write such code.
We should note: such code
can work - by
coincedence: when all of the below conditions are true:
- If both DLL and EXE are compiled with exactly the same version of VCL (or another code which implements passed objects) - so the object's layout in memory is the same for EXE and DLL;
- If the caller does not check for the class explicitly (not using InheritsFrom, as operator, is operator, etc) on returned object and all of its properties (and properties of properties).
- When at least one (any) condition is met:
- If the caller does not modify dynamic properties (strings, dynamic arrays, interfaces, etc.) of the returned object and does not call non-virtual object's methods that could do so. Basically, no (re)allocating memory in returned object.
- EXE and DLL share memory manager.
Did you notice? This list is basically saying that this code is
WRONG:
procedure FuncInDLL; stdcall;begin // your codeend;Why? Well, imagine an
exception happens in this function. What is
exception in Delphi? Well, it is an object derived from the
Exception class. In other words, an object (
exception) created in
DLL will be passed to EXE.
As you may suspect: most (?) Delphi DLLs are written in this way. And authors of these DLLs are saying: "this code works".
What they don't realize is that this code "works" by coincedence. For example, if a
DLL will be loaded in EXE compiled with a different
IDE version, memory layout of the
Exception 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:
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;or:
try FuncInDLL;except // This will not work, // as EXE will manipulate memory for
DLL Exception(ExceptObj).Message := 'Error in My
DLL: ' +
Exception(ExceptObj).Message; raise;end;
So, when you have code like:
procedure FuncInDLL; stdcall;begin // your codeend;and it "works", and then you add EurekaLog to your
DLL and EXE.
EurekaLog creates objects containing collected information about
exception (the TEurekaExceptionInfo class from the EException
unit) and associate these
exception info objects with exceptions. So if you pass
exception between
DLL and EXE - you also pass EurekaLog'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's code will certainly check
exception info object's class (to see if an
exception has associated
exception's info) as well as modify
exception info object. Which means:
access violation, leaks, incorrect behaviour.
Solutions
The exact solution depends on what kind of
DLL you are developing:
"Pure DLL"
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
must have an except block. An except block can be explicit like this:
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;or it can be implicit like this:
procedure FuncInDLL; safecall; // notice "safecall"begin // your codeend; 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 "failure" flag.
Read
this article for more details.
"Delphi DLL"
If you are developing a Delphi
DLL which can be used in Delphi applications only - then the solution is to use packages (BPLs).
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.
So what you should do is to enable the "Link with runtime packages" option in your project's settings and ensure at least the
rtl,
vcl and EurekaLogCore packages are listed in the "Runtime packages" list. Using the
rtl 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 EurekaLogCore
package will ensure sharing EurekaLog's code between EXE and
DLL.
Important note:Since your DLL will call EurekaLog from the EurekaLogCore package, the EurekaLogCore package will not know who is calling it (EXE or DLL). Therefore, it does not know where it should look for EurekaLog's options. That is why the EurekaLogCore package will always load EurekaLog's options (settings) from EXE. EurekaLog's settings from your DLL will be ignored.
This is kinda obvious, if you think about it: using the EurekaLogCore package means there is only one instance of EurekaLog in your process, so you can't have two EurekaLog's options (from EXE and DLL) at the same time.
In other words, configure EurekaLog in EXE. For DLL: you can configure EurekaLog as a "package" (if you don't call EurekaLog's code from your DLL) or as "Standalone DLL" (if you do call EurekaLog's code from your DLL).
"Application DLL"
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.
Note: the solution from the "Delphi
DLL" above will also work, but if you don't want to compile with packages (while you definitely should), using "Lightweight
DLL" configuration is a possible alternative.
When you configure your
DLL as a "Lightweight
DLL": EurekaLog'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.
Conclusion
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.
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.
We offer possible solutions, among simplest of which is reconfiguring EurekaLog in
DLL as "Lightweigh
DLL" - without touching your existing EXE/
DLL code.
P.S.
Read more about using EurekaLog in DLLs.
P.P.S.
Read more about writing pure DLLs in Delphi.
P.P.P.S. We should note using a dedicated shared memory manager is a hack (crutch) from Delphi 2.
New code should never use it. Using
BPL packages (when developing a Delphi
DLL) or following the "whoever allocates memory - frees it" rule (when developing a pure
DLL) is a correct way.
Weiterlesen...