Important: as a general rule, you should write your application in a such way that it will behave correctly without EurekaLog on board. This means that if your or 3rd party code throws an exception in a DLL - it must be handled in a way that you expect. For example, by showing an error message, canceling action, retrying, etc. Once your application behaves like it is supposed to do - then you add EurekaLog for it. EurekaLog will auto-handle all unhandled exceptions automatically (by hooking few well-known places), and for everything else - you can call EurekaLog from your own handlers when needed. In other words:
When you want to develop a new DLL which will be used by many applications ("common DLL") or if you want to write an application which may be extended with 3rd party DLLs ("plugins") - then you need to develop API, i.e. set of rules which will be used to communications between host and DLLs.
Incorrect (not recommended)
procedure DoSomething;
DoSomething function does not catch exceptions, and let exceptions escape to the caller module - which can be written in a different programming language. This is a big NO - unless you compile both DLL and caller in the very same version of the compiler.
Note: if you develop DLL that will only be used in executable compiled in exactly the same version of the compiler, and you want to pass exceptions between modules for simplicity - see this article.
COM - a default solution to design APIIt's a good idea to provide an informative and easy way to report and handle errors. An easy solution would be to use COM. That's because COM is relatively modern API, which provides a decent way to work with errors. COM also has support in many frameworks.
Second best bet - HRESULT via interfaces or functionsIf you think that COM is an "overkill" for your application, then you have to develop your own API. It would be a good idea to use HRESULT as base of error handling part in your API. That's because HRESULT offers a good range of possible error values, it has additional support in Delphi (via safecall) and it's familiar for many Windows developers.
So, functions from your DLL may looks like this:
library Project2; // out AResult: Integer): HRESULT; stdcall; Result := { ... };
As an alternative to a "safecall compiler magic" - you may write the same code as this:
library Project2; out AResult: Integer): HRESULT; stdcall;
Both implementations are binary compatible with each other and do the same thing. The difference is that second implementation allows you to control exception handling - by manually specifying a custom ConvertExceptionToHRESULT function (see below for a sample implementation).
Using interacesIt's also a good idea to use interfaces instead of simple functions in your DLLs. Interfaces allow you to customize safecall handling by overriding SafeCallException method. Interfaces also allow you to simplify memory management and avoid using shared memory manager. Example:
// Place this declaration into a common unit, which will be used by both DLL and caller (exe) // Method below are just example // You can replace with your own code // _____________________________________________ AResult := nil;
// _____________________________________________
// The code below is for application:
var // We recommend to wrap this code into sub-routine // to finalize hidden auto-managed variables before unloading DLL
Using framework(s) within your DLLYou may want to use frameworks inside your DLL. For example, you may want to display dialog with VCL within your DLL function. If this is the case - follow guidelines for frameworks.
Possible ways to handle exceptions within DLLsConvertExceptionToHRESULT in the examples above is some function that you need to write by yourself - which will handle exceptions and converts failure reasons to HRESULT codes. The simplest implementation may look like this:
function ConvertExceptionToHRESULT(E: Exception): HRESULT;
(The same function is used as default action for TObject.SafeCallException method.)
Obviously, such primitive function will ignore any exception's info and just report "something went wrong" to the caller. EurekaLog provides a ready-to-use function Exception2HRESULT which can be found in EAppDLL unit:
function ConvertExceptionToHRESULT(E: Exception): HRESULT;
This example will additionally pass error class, message, and help context via IErrorInfo interface. Of course, you can simply call Exception2HRESULT directly, there is no need for ConvertExceptionToHRESULT. It is used only as example.
If you want to have manual control - a more complex implementation of this function can be found in System.Win.ComObj unit - see HandleSafeCallException function. (This function is used as default action for TComObject.SafeCallException method.)
uses
This example will pass error message via IErrorInfo interface - same as Exception2HRESULT above. However, this example passes empty values for COM-related information (error ID, source ID and help file name). You can use these values as you want in your own API.
Creating bug reports for DLL exceptionsPlease note that no implementation of ConvertExceptionToHRESULT in the above examples creates bug report for the exception.
Creating bug reports for DLL exceptions is not an easy question - because final handling of the exception is not under your control. It is the caller of your DLL who decides what to do with the exceptions from your DLLs. Surely, exception from your DLL may be handled as usual: by showing error message to end user, asking to send bug report to developers, etc. However, exception from your DLL may also be silently handled by the caller and failed action will be repeated. Or the caller may try to execute fallback method with alternative solution (for example, the code that tries to set application for auto-launch may write to HKEY_LOCAL_MACHINE registry key. If this action will fail due to application being run under limited user account - the code may switch to HKEY_CURRENT_USER key, e.g. re-try action with other params).
For these reasons you can not simply show error dialog and ask to send bug report - because:
Some possible solutions to this problem are explored in this article.
See also:
|