VB does a lot of mapping of HRESULT values to Err.Number values. For instance, if the HRESULT was 0x8007002, VB breaks down them using the COM standard as follows:
Bit 31 - Severity (set to 1 in all errors)
Bit 30 - Reserved
Bit 29 - Customer Bit
Bit 28 - Reserved
Bit 27
- 16 Facility code
Bit 15
- 0 Error code
In practice the top nibble is always 8. The Facility varies, and tells you what type of error that this is. In this case, the facility code is 0x007 (FACILITY_WIN32), which means that this is a standard Win32 error. The lower two bytes represent the actual error. Looking in winerror.h, this error is 2 - ERROR_FILE_NOT_FOUND. VB is clever enough to map this error to the standard VB error 53 "File not found".
In the VB documentation, we were always encouraged to add the constant vbObjectError to our error numbers when raising them from a VB component. This was almost equivalent to ORing the 32 bit integer 0x80040000 to your error number (assuming it was <= 65535). In this case, the facility code was 0x4 (FACILITY_ITF) which indicated that the error was raised from a coclass's specific interface. In practice this just made our error numbers large and negative, and hard to understand.
What we should have really done was to ignore the documentation and just raise the straight error numbers. Behind the scenes, VB OR'd its own facility code - 0xA (FACILITY_CONTROL). However, any VB component seeing an HRESULT with that facility code would automatically clear the top two bytes, so we would see the number as being positive - not negative.
I suggest that you use FACILITY_CONTROL for your own error numbers. Other COM clients may possibly get confused, but VB clients see your error number as positive.
Alternatively, you could ignore the normal HRESULT mechanism for return values. Instead return HRESULT = S_OK, and use a [retval, out] parameter at the end of the function to make it look as if it is a VB function.