Choose the narrowest change that solves the problem. A different report folder does not need a custom dialog; a special exception policy usually does not need a replacement exception manager.
| What you need | Use |
|---|---|
| Different defaults selected at startup | CurrentEurekaLogOptions (section 24.1 and chapter 17) |
| A stable policy for an exception class or source location | Filters or custom attributes (section 24.2) |
| Data or a decision for the exception being processed | An event handler and its AExceptionInfo.Options (section 24.3) |
| Behaviour with no matching event, or a new sending method | A registered descendant class (section 24.5) |
| An internal hook that has no public contract | Not a normal integration route (section 24.7) |
The code in this chapter belongs in the reporting executable or a standalone reporting module,
not in a lightweight DLL. The lightweight profile deliberately excludes EClasses, EModules
and the exception manager; make these customizations in its host (chapter 16).
24.1 Change a default before exceptions are raised
CurrentEurekaLogOptions in EModules returns the module's TEurekaModuleOptions object.
Set defaults at startup, before starting workers or accepting requests. Do not free the returned
object. This small procedure preserves local reports but stops sending:
uses
EBase, EModules, ETypes;
procedure SelectLocalReporting;
begin
if not IsEurekaLogActive then
Exit;
CurrentEurekaLogOptions.SaveLogFile := True;
CurrentEurekaLogOptions.SenderClasses := esmNoSend;
end;
Call it from startup code. IsEurekaLogActive guards the accessor's fatal inactive-module case;
it is a global status, not a per-thread status. Section 17.3 supplies the service/GUI variant,
including account-dependent output paths and both dialog choices.
Before a runtime or filter-based switch to a different dialog, open Project / EurekaLog options / Advanced / Code / Dialogs and include that implementation. Do the equivalent under Advanced / Code / Send engines for every sender you may select, then rebuild. Selecting the default in the options normally includes its code automatically; alternatives selected only by your code need explicit inclusion. A class-name string cannot load missing code.
Build options cannot change after compilation. Hooks and debug-information providers are registered at startup, not dynamically switched by this options object. Leak/memory controls have separate APIs in chapter 12, and hang detection in chapter 14.
Every raised exception captures an options copy. Changing global defaults in an exception handler
therefore affects future exceptions, not the one being processed. Use that exception's
Options, as in section 24.3. The same ownership rule applies to dialogs, senders, builders and
call stacks: their Options comes from their parent, or from the global default when no parent
is supplied.
24.2 Behaviour for one exception type: filters and custom attributes
An exception filter alters EurekaLog's behaviour based on the properties of an exception. The
documented ways to identify one are its class name (Exception, EAccessViolation, EStreamReadError,
...), its source location, the values of its properties, and whether it is handled or unhandled. Once
identified, a filter can change its handler (none, RTL or EurekaLog), its dialog class, its error message,
its action (restart or terminate), or mark it as expected. Handler none hides the exception completely
- "No error dialog will be shown. No bug report will be created" - while handler RTL leaves it to the
application's ordinary handling, "but no EurekaLog work will be done for it". Filters are applied
before the exception is processed, from top to bottom, and "first matched filter is applied"; a filter
"can also be created from code", through the
ExceptionsFiltersproperty of section 24.1.
To create a rule without code:
- Open Project / EurekaLog options / Advanced / Exception filters and enable
Activate Exceptions Filters (
ActivateFilters). With it disabled, the listed rules do nothing. - Click Add. Identify the exception by its real class name and choose the required behaviour. Use RTL when your normal application handler should show the error without a EurekaLog report; use None only when deliberately suppressing both.
- Put specific rules above broader ones with Move up / Move down. The first match wins.
By default a class-name match is exact;
+includes descendants.[I]selects an inner chained exception where present. For message matching,=means exact text and%means contains. - Save, rebuild and raise the target exception and a nearby nontarget case. Confirm the intended case changes and the other still reports normally.
Filters cannot alter final leak reporting (EMemoryLeak/EResourceLeak): their state has already
been finalized when that check runs. Use the dedicated leak controls in chapter 12.
Custom attributes express the same thing at the exception class declaration, and their declarations
are printed in full. They are declared in the EClasses unit:
type
EurekaLogHandler = class(TCustomAttribute)
strict private
FHandler: TFilterHandlerType;
public
constructor Create(Value: TFilterHandlerType);
property Handler: TFilterHandlerType read FHandler;
end;
EurekaLogMessage = class(TCustomAttribute)
strict private
FMessage: String;
public
constructor Create(Value: String);
property Message: String read FMessage;
end;
EurekaLogAction = class(TCustomAttribute)
strict private
FAction: TFilterActionType;
public
constructor Create(Value: TFilterActionType);
property Action: TFilterActionType read FAction;
end;
EurekaLogDialog = class(TCustomAttribute)
strict private
FDialog: TExceptionDialogType;
public
constructor Create(Value: TExceptionDialogType);
property Dialog: TExceptionDialogType read FDialog;
end;
EurekaLogExpected = class(TCustomAttribute)
strict private
FURL: String;
FContext: Integer;
FBugID: TBugID;
public
constructor Create(const AContextNumber: Integer = -1;
const AURL: String = ''; const ABugID: TBugID = 0);
property URL: String read FURL;
property Context: Integer read FContext;
property BugID: TBugID read FBugID;
end;
The examples attach them to the class declaration, and their comments say what each buys:
[EurekaLogHandler(fhtRTL)]- "ETestException will be ignored by EurekaLog and it always will be handled by your application (as if EurekaLog would be disabled)".[EurekaLogMessage('Sorry, there was an error.')]- "Exception message will be replaced".[EurekaLogDialog(edtEurekaLogDetailed)]- "Switch dialog to EurekaLog-Detailed style".[EurekaLogExpected()]- "Mark exception as expected". With a context number,[EurekaLogExpected(1234)]also assigns help topic ID 1234, which shows the "Help" button in dialogs;[EurekaLogExpected(0, 'http://www.example.com/kb/low_memory.asp')]assigns an online help topic instead;[EurekaLogExpected(-1, '', $C4F10001)]assigns a fixed BugID.
For example, on a Delphi compiler with custom-attribute RTTI support, put this declaration in the unit that defines the application's exception classes:
uses
SysUtils, EClasses, ETypes;
type
[EurekaLogHandler(fhtRTL)]
EUserCorrectableError = class(Exception);
Raise EUserCorrectableError for the specific condition you have chosen to handle through the
ordinary application path. Do not attach this policy to the base Exception class merely to
silence reports globally.
What to know before using them:
- They are filters. "internally custom attributes just creates new filter rule" - the same behaviour, expressed at the declaration instead of in the options.
- They need extended RTTI: "New Delphi and C++ Builder IDEs offer extended RTTI with support of custom attributes", and the code "will be ignored if there is no EurekaLog installed".
- Aliases do not work.
EMyException1 = class(Exception)can be named in a rule;EMyException2 = Exceptioncannot, "because there is no such class" - nameExceptioninstead. - A message override is visual only. It affects the error message in dialogs; bug reports "always contain information about original exception (with original error message)".
- The availability caveat of section 24.1 applies: a rule that selects a dialog class needs that dialog's code in the application.
24.3 Events: EurekaLog calling your code
Use a TEurekaLogEvents component when the handler genuinely depends on a form. Its registrations
exist only while the form exists, so they do not cover failures before form construction or after
destruction. For application-wide policy, register a procedure from a unit instead.
This complete unit keeps local reporting for EReportLocalOnly exceptions while disabling sending
for those exceptions alone. Add it to the reporting project; no form or UI synchronization is needed
because the callback only touches the supplied exception's options:
unit ReportPolicy;
interface
uses
SysUtils;
type
EReportLocalOnly = class(Exception);
implementation
uses
EEvents, EException, ETypes;
procedure ApplyLocalPolicy(const ACustom: Pointer;
AExceptionInfo: TEurekaExceptionInfo;
var AHandle, ACallNextHandler: Boolean);
begin
if AExceptionInfo.ExceptionClass = EReportLocalOnly.ClassName then
begin
AExceptionInfo.Options.SaveLogFile := True;
AExceptionInfo.Options.SenderClasses := esmNoSend;
end;
end;
initialization
RegisterEventExceptionNotify(nil, ApplyLocalPolicy);
finalization
UnRegisterEventExceptionNotify(nil, ApplyLocalPolicy);
end.
The incoming AHandle and ACallNextHandler values are deliberately left unchanged: the handler
does not cancel reporting or claim exclusive control of the callback chain. Setting AHandle
to False instead would suppress EurekaLog processing for the exception. Chapter 7 gives the
event ordering, action flags and other event contracts.
For a real application policy, raise an ordinary exception and an EReportLocalOnly from the
same controlled test path. Both should follow the normal report policy except that the latter
does not send. Do not use this exception type for failures that your operational policy requires
to be delivered automatically.
Match every dynamic registration with its corresponding UnRegisterEvent<Name> before destroying
the object or state the callback uses. The unit above unregisters during finalization; an object
method should unregister before its object is destroyed. Process-lifetime procedures with no
finalized dependencies can remain registered, but that is a lifetime decision, not a general
rule that unregistering is unnecessary. Stop possible callback producers before unloading code.
Three rules that apply to every handler.
- Never assume the thread. "NEVER assume thread in which your event handler will run. Your event handler may be called from a background thread even in single-threaded application. For example, a stack overflow exception will force all event handlers to run from a background service thread." Synchronise any access to global entities from a handler.
- Options inside a handler go to the exception. Use
AExceptionInfo.Options.CustomizedTexts[mtMSDialog_ErrorMsgCaption] := ...for the current MS Classic dialog text, not the same assignment toCurrentEurekaLogOptions. The latter changes future defaults. Do not freeAExceptionInfoor itsOptions: the callback receives them from EurekaLog. - Do not treat ordinary events as leak callbacks. The documented ordinary event registrations are not called during final leak processing. Use the dedicated lifetime/setup rules of chapters 12 and 22 instead; ordinary form-bound state may already be gone.
Handlers give "unlimited ability for customizing EurekaLog, but only within certain points of interest (i.e. events). You can't alter whole processing logic by using only event handlers" - that is section 24.5.
24.4 What the EEvents unit offers
EEvents provides a registration routine per event and a matching UnRegister routine that
removes the previously registered handler. Choose by processing stage:
- While an exception is raised and handled:
RegisterEventRaise(OnRaise),RegisterEventExceptionNotify(OnExceptionNotify),RegisterEventExceptionError(OnExceptionError),RegisterEventExceptionAction(OnExceptionAction),RegisterEventCustomBugID(OnCustomBugID),RegisterEventPasswordRequest(OnPasswordRequest). - While the report is built:
RegisterEventBeginReportGen(OnBeginReportGen),RegisterEventEndReportGen(OnEndReportGen),RegisterEventCustomDataRequest(OnCustomDataRequest),RegisterEventCustomFileName(OnCustomFileName),RegisterEventReproduceRequest(OnReproduceRequest). - While the report is packed and sent:
RegisterEventAttachedFilesRequest(OnAttachedFilesRequest),RegisterEventZippedFilesRequest(OnZippedFilesRequest),RegisterEventCustomWebFieldsRequest(OnCustomWebFieldRequest). - While the error dialog is up:
RegisterEventCustomButtonClick(OnCustomButtonClick) andRegisterEventHelpButtonClick(OnHelpButtonClick) - attaching behaviour to a custom button and to the dialog's own Help button.
OnExceptionNotify handles the general per-exception decision. OnExceptionAction gives a
TEurekaActionType and additional text for a specific processing action; for example,
atShowingExceptionInfo identifies the dialog-showing stage. Use the complete event examples in
chapter 7 rather than assuming every event has the same signature.
For data that does not need an event, AddCustomDataToBugReport adds static report data and
AddCustomWebField adds static web-request data (chapter 6). Request events supply the dynamic
counterpart. Review both for private data before publishing reports (chapter 3).
24.5 Replacing EurekaLog's own classes
Subclass only when the change is not available through an option or event. Prefer overriding one
virtual method and calling inherited to replacing the whole processing pipeline.
Registration is not the same as selection. Dialogs and send engines have registries; register
the class first, then select its name in the appropriate options. RegisterDialogClassFirst and
RegisterSenderFirst put a class at the beginning of the lookup list. Singleton extension points
such as the report builder instead use a global class variable.
Class methods evolve between EurekaLog releases more often than event contracts. The examples below use the documented overrides; after upgrading, compile and exercise your subclass against the installed version rather than assuming binary or source compatibility.
Replace the MS Classic dialog's icon
Create an icon file named Report.ico and this resource script, ReportIcons.rc:
CUSTOMICON ICON "Report.ico"
Compile it with the resource compiler supplied with the IDE (brcc32 ReportIcons.rc), making
ReportIcons.res available to the unit below. This example uses the protected LoadIcon and
SetLogoIcon extension points present in EurekaLog 7.15.0.0; verify their availability before
using it with another build. The Windows API returns a shared icon. SetLogoIcon creates the
dialog-owned bitmap; inherited painting and cleanup keep responsibility for that bitmap.
unit ReportDialogIcon;
interface
implementation
{$R ReportIcons.res}
uses
Windows, EBase, EModules, EDialog, EDialogWinAPIMSClassic;
type
TMSClassicDialogCustom = class(TMSClassicDialog)
protected
procedure LoadIcon; override;
end;
procedure TMSClassicDialogCustom.LoadIcon;
var
CustomIcon: HICON;
Size: TSize;
begin
CustomIcon := Windows.LoadIcon(HInstance, 'CUSTOMICON');
if CustomIcon = 0 then
begin
inherited;
Exit;
end;
Size.cx := 32;
Size.cy := 32;
SetLogoIcon(CustomIcon, Size);
end;
initialization
if IsEurekaLogActive then
begin
RegisterDialogClass(TMSClassicDialogCustom);
CurrentEurekaLogOptions.ExceptionDialogType := TMSClassicDialogCustom.ClassName;
end;
end.
If the named icon cannot be loaded, this descendant uses the inherited icon. The resource file must still exist at build time. Raise a controlled exception after startup and check the dialog; a successful compile does not prove that the configured application selected this class.
Overriding the original class instead of adding one
If the change should apply wherever an option already points at a built-in class, give the descendant the
same class name - qualifying the ancestor by its unit, as in
TMSClassicDialog = class(EDialogWinAPIMSClassic.TMSClassicDialog) - and register it first with
RegisterDialogClassFirst, so that a lookup by name finds your class before the built-in one. No option
has to change, so design-time settings keep working. For example, this technique can append
ExceptionInfo.BugIDStr to the dialog caption.
The same lookup rule applies to senders: a descendant named TELTrackerMantisSender, with
ancestor ESendAPIMantis.TELTrackerMantisSender, can override ComposeTitle: String and be
registered with RegisterSenderFirst. Do not casually change that title: Mantis issue
identification can depend on it. For extra version or deployment information, prefer configured
custom fields and show them in the tracker list (chapter 3). That preserves the original
sender's matching behaviour.
A log builder: replacing the report content
A singleton class is installed by assigning its class variable during initialization. The
following complete unit demonstrates the documented EurekaLog 6-format compatibility route.
Use it only when a legacy consumer requires that format, not to relabel a modern report as an
older one for its own sake. It changes the general header and stack formatter together, and
renames .el files to .elf; verify the output with the actual legacy consumer.
unit LegacyReportFormat;
interface
implementation
uses
SysUtils, EConsts, ETypes, EClasses, ECallStack, EException, EEvents, ELogBuilder;
type
TEurekaLog6Builder = class(TLogBuilder)
public
function CreateGeneralText: String; override;
function CreateCallStackText: String; override;
end;
function TEurekaLog6Builder.CreateGeneralText: String;
begin
Result := inherited;
Result := 'EurekaLog 6' + Copy(Result, Pos(sLineBreak, Result), MaxInt);
end;
function TEurekaLog6Builder.CreateCallStackText: String;
var
Stack: TEurekaCallStack;
Formatter: TEurekaBaseStackFormatter;
begin
Stack := nil;
try
if CallStack <> nil then
begin
Stack := TEurekaCallStack.Create;
Stack.Assign(CallStack);
Stack.Formatter.Assign(Options);
Stack.Formatter.CaptionHeader :=
Options.CustomizedExpandedTexts[mtDialog_CallStackHeader] + EHeaderSuffix;
end;
Formatter := TEurekaStackFormatterV6.Create;
try
Formatter.Assign(Options);
Formatter.CaptionHeader :=
Options.CustomizedExpandedTexts[mtDialog_CallStackHeader] + EHeaderSuffix;
Result := CallStackToString(Stack,
Options.CustomizedExpandedTexts[mtDialog_CallStackHeader] + EHeaderSuffix,
Formatter);
finally
Formatter.Free;
end;
finally
Stack.Free;
end;
end;
procedure UseLegacyExtension(const ACustom: Pointer;
AExceptionInfo: TEurekaExceptionInfo; const AFileType: TEurekaLogFileType;
var AFileName: String; var ACallNextHandler: Boolean);
begin
if SameText(ExtractFileExt(AFileName), '.el') then
AFileName := ChangeFileExt(AFileName, '.elf');
end;
initialization
LogBuilderClass := TEurekaLog6Builder;
RegisterEventCustomFileName(nil, UseLegacyExtension);
finalization
UnRegisterEventCustomFileName(nil, UseLegacyExtension);
end.
This unit is for a process-lifetime reporting executable. Its builder class must remain available
while EurekaLog can create reports; do not install it from an unloadable plugin. The stack copy
and formatter created by this implementation are owned and freed here; the inherited CallStack
and Options are not.
Add a sender that copies reports to a private folder
Use this when delivery means depositing the prepared files in a local folder or UNC share that another trusted process collects. It is a file-copy sender, not a network tracker integration or an automatic resend queue. Chapter 3 covers standard HTTP/mail/tracker setup and offline delivery policy.
Before using it:
- Provision a private, writable destination for the actual application/service account. Prefer a UNC path over a mapped drive for services. A local folder proves only a local copy, not that a developer received the report.
- In Advanced / Custom/Manual, add
Custom_ReportDropPathwith a full literal path. For example, the raw.eofrepresentation isCustom_ReportDropPath="C:\\ProgramData\\MySoftware\\ReportDrop". This custom key is read literally by the example; do not put%VARIABLE%placeholders in it. - Keep Save bug report to file enabled and do not delete the original after successful sending while evaluating the integration. Review report and attachment contents and destination permissions as described in chapter 3.
- Add this unit to the reporting executable. Its initialization registers the class before selecting it; selection replaces the configured sender list.
unit FolderReportSender;
interface
implementation
uses
Windows, SysUtils, EBase, ESend, EModules, ETypes;
type
TFolderReportSender = class(TELUniversalSender)
public
function SendMessage: TResponse; override;
end;
function TFolderReportSender.SendMessage: TResponse;
var
Root, Destination, Target: String;
ID: TGUID;
I: Integer;
begin
Finalize(Result);
FillChar(Result, SizeOf(Result), 0);
Result.SendResult := srUnknownError;
try
Root := Options.CustomField['Custom_ReportDropPath'];
if (Root = '') or not DirectoryExists(Root) then
raise Exception.Create('Report destination does not exist');
if AttachedFiles.Count = 0 then
raise Exception.Create('No report files were supplied');
if CreateGUID(ID) <> 0 then
raise Exception.Create('Cannot create a report directory identifier');
Destination := IncludeTrailingPathDelimiter(Root) + GUIDToString(ID);
if not CreateDir(Destination) then
RaiseLastOSError;
for I := 0 to AttachedFiles.Count - 1 do
begin
Target := IncludeTrailingPathDelimiter(Destination) +
ExtractFileName(AttachedFiles[I]);
Win32Check(CopyFile(PChar(AttachedFiles[I]), PChar(Target), True));
end;
Result.SendResult := srSent;
except
on E: EOSError do
begin
Result.ErrorCode := Integer(E.ErrorCode);
Result.IssueIDType := iitWin32;
Result.ErrorMessage := E.Message;
end;
on E: Exception do
Result.ErrorMessage := E.Message;
end;
end;
initialization
if IsEurekaLogActive then
begin
RegisterSender(TFolderReportSender);
CurrentEurekaLogOptions.SenderClasses := TFolderReportSender.ClassName;
end;
end.
AttachedFiles contains the files supplied by EurekaLog; do not assume there is exactly one.
The sender borrows those paths and copies their contents; it does not delete the originals.
Each invocation creates a new directory, and CopyFile(..., True) refuses to overwrite an
existing filename. If two attachments have the same base filename, or any copy fails, the sender
returns failure. A partially copied directory remains for inspection: this example is not an
atomic multi-file handoff and must not be used with a collector that treats directory appearance
alone as proof of a complete report.
Success is set only after all copies finish. Failure reports a message and, for OS errors, a
Win32 error code. The Finalize/FillChar pair follows the documented initialization pattern for
the response record's managed fields; do not clear a live response with FillChar alone.
Test with an actual exception and compare all prepared files with the destination, then deny write access or select a nonexistent folder and check that sending fails while the original local report remains. A built-in sender's Test command cannot prove that this custom sender was selected and exercised.
Other documented sender properties include BugID, BugType, BugText, BugReportText,
BugCallStack, BugLocation, BugAppVersion, StepsToReproduce and UserEMail. They are populated
as part of exception processing and may be empty for a manually initiated send. Work from the
sender's Options and supplied exception context, not mutable global defaults.
Registered send classes cannot be unregistered. Keep their code loaded for as long as the
registry can use it; this process-lifetime example must not be installed from an unloadable DLL.
RegisterSenderFirst also permits a same-name replacement and puts it before the built-in class;
ordinary RegisterSender is appropriate for this new, uniquely named sender.
The classes you can replace
The extension points and the units that declare them (as of EurekaLog 7.0.02) are: base
dialog class TBaseDialog and RegisterDialogClass (both EDialog); base send engine class
TELUniversalSender and RegisterSender (ESend); base debug information provider class
TELDebugInfoSource (EClasses) and RegisterDebugInfoSource (EDebugInfo); base call stack class
TEurekaBaseStackList, default call stack class EurekaCallStackClass and specific call stack classes
TracerMethodsClasses (all ECallStack); base log builder class TBaseLogBuilder and default
LogBuilderClass (ELogBuilder); base hung detection thread class TFreezeThread and default
FreezeThreadClass (EFreeze).
24.6 What a dialog class has to do
Changing the dialog class can change far more than the window. The class participates in saving, sending and termination decisions, including nonvisual modes such as service and system-log reporting. Do not replace it merely to hide a window; use the appropriate existing dialog mode or the per-exception option first.
The abstract TBaseDialog is declared in EDialog; TWinAPIDialog in EDialogWinAPI is a
base for native WinAPI dialogs. ShowModalInternal is the required abstract operation. The
documented examples include a nonvisual TNullDialog, simple message-box dialogs and
TRTLHandlerDialog. In a null dialog, a successful ShowModalInternal response means the
dialog stage completed; it is not evidence that a report was delivered.
Section 24.5 changes only the icon and deliberately preserves the rest of this lifecycle. When choosing an override, keep the following responsibilities in view rather than copying a do-nothing dialog and assuming the inherited work is unimportant.
The default Execute method - the "real entry point" - shows that order: it saves the environment, sets
up file names and detects duplicates; unless the dialog is being restarted it takes a screenshot, adds
custom data, deletes the old log when DeleteLogAtVersionChange asks for it, clears the reproduce text,
pre-saves the report when pre-saving and log saving are both on ("Pre-save to get log in case of crash in
dialog") and beeps; then it calls ShowModal, where ShowModalInternal is reached. A dialog that answers
srRestart ends the method there and hands the exception over to the RTL; otherwise the report is saved
again "to update changed fields (like reproduce text)" when log saving is on and the response allows it,
and sent when the response succeeded and sending is enabled - on failure the report is copied or packed
only if CopyLogInCaseOfError or SaveCompressedCopyInCaseOfError asks for it, on success the log is
deleted when DeleteLogAfterSuccessfulSend does - and the send result is shown to the user, and recorded
in the report when the report is being saved. When sending is skipped instead, the OnExceptionAction
event of section 24.4 is raised with atSendCancelled. The environment is restored at the end, CheckTermination
runs, and only then does the difTerminateApplication custom field decide whether the application
terminates; an exception raised anywhere inside becomes an srUnknownError response carrying
ERROR_GEN_FAILURE and the exception's message. The advice that comes with it: "You rarely need to alter
this method, rather you will override methods which are called by it."
The example's TRTLHandlerDialog is the documented bridge back to the RTL handler: it returns True from
GetCallRTLExceptionEvent and sets Result.SendResult := srRestart in ShowModalInternal, a value the
vendor's comment glosses as "means 'call RTL handler'". ThreadSafe is a threading contract rather than
cosmetics: it "should be False for VCL/CLX/FMX dialogs", while the two dialogs the example documents as
callable from any thread, TNullDialog and TMessageBoxDialog, return True.
24.7 Low-level handler variables
Some integration code refers to internal global handler variables. They are listed here so you can recognize them during maintenance, not as a supported recipe for replacing the runtime. The internal variables include:
EInject:EventUnhandledExceptionFilter,EventExceptProc,EventExceptClsProc,EventExceptObjProc,EventRaiseExceptionProc,EventGetExceptionStackInfoProc,EventGetStackInfoStringProc,EventCleanUpStackInfoProc,EventExceptionDispatcher,EventIndyThreadHandleExceptionEExceptionHook:_IsUnexpectedELowLevel:_InitDoneErrorHandler,_SafeExecLog,_ConsoleLoggerEHook:_GetSymbolAddrEBase:_InternalError,_AllowBypassInternalErrors,_RaiseExpected,_OnPanicEAppType:_CustomMessageBox,_GetMainWndEMemLeaksandEResLeaks: "Almost all routines inEMemLeaksandEResLeaksunits"EThreadsManager:_GetRealAddresses
These internal hooks have no complete public procedural-type or behavior contract. An internal override must respect initialization order, must not replace routines during normal application work, and must save the previous handler and restore it during finalization. Those precautions are necessary, not sufficient to establish a safe implementation for an arbitrary version.
Prefer the options, events and public class extensions above. Do not guess the parameters of an internal handler variable.
Directly modifying EurekaLog source is a different maintenance commitment. It requires an edition with full source; upgrades or reinstalls can overwrite the changes. Keep such a patch outside the installation directory under version control and rebuild every affected artifact: merely building an application without packages does not rebuild runtime packages or IDE tools. Chapter 2 covers build ownership. This is a last resort, not a prerequisite for any example in this chapter.
24.8 Check a customization at its actual boundary
- Options: verify a new exception after startup uses the selected value; verify an already-raised exception uses its own options copy.
- Handlers: exercise failures before/after form lifetime if that coverage matters. Do not touch VCL/FMX controls from arbitrary callback threads. Chapter 7 covers ordering and callback contracts.
- Classes: confirm the registered class is actually selected. Keep its code loaded, and preserve inherited behaviour outside the method you intended to change.
- Sending: exercise both success and failure with the real prepared files and process account; retain a local report when delivery fails (chapter 3).
- Upgrades: rebuild and exercise these paths against the installed EurekaLog version. Appendices A–D collect the documented API contracts; the internal variables in section 24.7 remain explicitly outside that public contract.
If the integration fails before your customization runs, use chapter 5 to diagnose EurekaLog's own startup/processing path instead of adding more callbacks blindly.