Root > Advanced topics > Using EurekaLog in DLL > What is the proper way to handle exceptions in DLL > Your own API

Your own API

Previous pageReturn to chapter overviewNext page   

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.

 

 

COM - a default solution to design API

It'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 functions

If 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;
 
uses
  Windows;
 
procedure Init; safecall
// the same as:
// function Init: HRESULT; stdcall; 
begin
  // your code
end;
 
function DoSomething(A1: Integer; const A2: WideString): Integer; safecall;
// the same as:
// function DoSomething(A1: Integer; const A2: WideString; 

//                      out AResult: Integer): HRESULT; stdcall; 
begin
  // your code

  Result := { ... };
end;
 
procedure Done; safecall
// the same as:
// function Done: HRESULT; stdcall; 
begin
  // your code
end;
 
exports
  Init, DoSomething, Done;
 
end;

 

As an alternative to a "safecall compiler magic" - you may write the same code as this:

 

library Project2;
 
uses
  Windows;
 
function Init: HRESULT; stdcall
// the same as:
// procedure Init; safecall; 
begin
  try
    // your code

 
    Result := S_OK;
  except
    on E: Exception do
      Result := ConvertExceptionToHRESULT(E);
  end;
end;
 
function DoSomething(A1: Integer; const A2: WideString; 

                     out AResult: Integer): HRESULT; stdcall
// the same as:
// function DoSomething(A1: Integer; const A2: WideString): Integer; safecall;
begin
  try
    // your code
    AResult := { ... };
 
    Result := S_OK;
  except
    on E: Exception do
      Result := ConvertExceptionToHRESULT(E);
  end;
end;
 
function Done: HRESULT; stdcall
// the same as:
// procedure Done; safecall; 
begin
  try
    // your code

 
    Result := S_OK;
  except
    on E: Exception do
      Result := ConvertExceptionToHRESULT(E);
  end;
end;
 
exports
  Init, DoSomething, Done;
 
end;

 

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.

 

Note: it'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.

 

 

Possible ways to handle exceptions within DLLs

ConvertExceptionToHRESULT is some function that you need to write which will handle exceptions and converts failure reasons to HRESULT codes. The simplest implementation may look like this:

 

function ConvertExceptionToHRESULT(E: Exception): HRESULT;
begin
  Result := E_FAIL;
end;

 

(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. 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
  ComObj;
 
function ConvertExceptionToHRESULT(E: Exception): HRESULT;
const 
begin
  Result := HandleSafeCallException(E, ExceptAddr, GUID_NULL, GUID_NULL, '');
end;

 

This example will additionally pass error message and help context via IErrorInfo interface. This example pass 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. COM applications should fill these values with actual information (this task is done automatically for VCL-based COM objects - see TComObject.SafeCallException method).

 

 

Creating bug reports for DLL exceptions

Please 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:

If exception from your DLL will be handled as error by the caller - then the error message will appear twice: first from your DLL as bug report, second - from the caller;
If exception from your DLL will be handled as non-error by the caller (i.e. the caller may re-try action or try alternative solutions) - then your DLL will show "false" error message (user will see error dialog - even though requested action will be completed by the caller).

 

Some possible solutions to this problem are explored in this article.

 

 

See also:




Send feedback... Build date: 2018-11-26
Last edited: 2018-06-14
PRIVACY STATEMENT
The documentation team uses the feedback submitted to improve the EurekaLog documentation. We do not use your e-mail address for any other purpose. We will remove your e-mail address from our system after the issue you are reporting has been resolved. While we are working to resolve this issue, we may send you an e-mail message to request more information about your feedback. After the issues have been addressed, we may send you an email message to let you know that your feedback has been addressed.


Permanent link to this article: https://www.eurekalog.com/help/eurekalog/dll_api.php