You can't, this appears to be by design. Details further below, but in short you have three options:
- Return no message and a VB friendly COM error, i.e. one well known by the VB runtime according to this KB article; the VB runtime will translate this 'COM error' to a VB error plus message.
- Return an error message and DISP_E_EXCEPTION; the VB runtime will pass through this 'Server error' and your custom error message. This is what's implicitly happening on your second example, see below for details.
- Return no message and any other COM error, i.e. one not known by the VB runtime; the VB runtime will resort to the raw HRESULT plus the generic message "
Method '~' of object '~' failed
".
- Please note that this runtime behavior does also apply, if you do supply an error message here, i.e. your message will simply be ignored! This is what's happening on your first example, see below for details.
For the task at hand it boils down to two choices:
- If you want to supply contextually correct 'COM errors' for automation clients like VB (and likely you should) you must omit custom error messages.
- If you want to supply custom error messages for 'Server errors' (i.e. a custom error conditions regarding the functionality within your automation server) your only option is DISP_E_EXCEPTION.
Details
The VB runtime seems to offer only very restricted handling in regard to COM errors. This is likely for historic and/or technical reasons specific to the way VB has been implemented and not of particular interest here (keywords would be IDispatch only vs dual interface and ActiveX as a 'subset' of COM).
While I've been unable to surface an explicit specification for the behavior outlined above one can figure it from digging through other sources:
From the KB article justadreamer pointed out already:
[...] a call is made to the
GetErrorInfo method to retrieve the
available error information. The
runtime then determines whether
bstrDescription has a value other than
NULL. If the runtime finds a value
other than NULL, [...], the raw HRESULT
value is used in this scenario. If the
runtime finds a NULL value, [...]
Visual Basic then uses HRESULT
to look up the corresponding Visual
Basic error.
This explains the behavior regarding your fist example: you did supply an error message, hence the runtime simply resorts to its generic message "Method '~' of object '~' failed
" plus your HRESULT
.
The behavior of your second example is also consistent once you look at the definition of the (first listed) constructor for CComCoClass::Error
: it has defaults for the non specified parameters, especially 'hRes = 0'. The 'Remarks' section further states that "If hRes is zero, then the first four versions of Error return DISP_E_EXCEPTION.". Consequently this implicitly triggers the 'Server error' pass through behavior.
Finally, for a concrete C++ implementation sample of a VB like automation client behavior see for example paragraphs 'Error handling' and the following 'Exercise 5' in Automating Microsoft Office 97 and Microsoft Office 2000.