12    Error Handling

COM provides a rich mechanism for allowing objects to return error information to callers. The following interfaces are used:

This chapter covers the error handling interfaces.

12.1    Returning Error Information

To return error information:

  1. Implement the ISupportErrorInfo() interface.

  2. To create an instance of the generic error object, call the CreateErrorInfo() function.

  3. To set its contents, use the ICreateErrorInfo methods.

  4. To associate the error object with the current logical thread, call the SetErrorInfo() function.

The error handling interfaces create and manage an error object, which provides information about the error. The error object is not the same as the object that encountered the error. It is a separate object associated with the current thread of execution.

12.2    Retrieving Error Information

To retrieve error information:

  1. Check whether the returned value represents an error that the object is prepared to handle.

  2. Call QueryInterface() to get a pointer to the ISupportErrorInfointerface. Then, call InterfaceSupportsErrorInfo to verify that the error was raised by the object that returned it and that the error object pertains to the current error, and not to a previous call.

  3. To get a pointer to the error object, call the GetErrorInfo() function.

  4. To retrieve information from the error object, use the IErrorInfomethods.

If the object is not prepared to handle the error, but needs to propagate the error information further down the call chain, it should simply pass the return value to its caller. Because the GetErrorInfo() function clears the error information and passes ownership of the error object to the caller, the function should be called only by the object that handles the error.

12.3    Error Handling API Descriptions

12.3.1    IErrorInfo

The IErrorInfo() interface provides detailed contextual error information.

Description

Table 12-1:  IErrorInfo Elements

Implemented by Used by Header filename Import library name
Oleaut32.dll (32-bit systems) Ole2disp.dll (16-bit systems) Applications that receive rich information. Oleauto.h Dispatch.h Oleaut32.lib Oledisp.lib

 

IErrorInfo::GetDescription()

NAME

IErrorInfo::GetDescription() - Returns a textual description of the error.

Synopsis

#include <Oaidl.h>

HRESULT GetDescription(
        BSTR * pBstrDescription );

Parameters

pBstrDescription

Pointer to a brief string that describes the error.

Return Values

S_OK

Success

The text is returned in the language specified by the locale identifier (LCID) that was passed to IDispatch::Invoke() for the method that encountered the error.  

IErrorInfo::GetGUID()

NAME

IErrorInfo::GetGUID() - Returns the globally unique identifier (GUID) of the interface that defined the error.

Synopsis

#include <Oaidl.h>

HRESULT GetGUID(
        GUID * pGUID );

Parameters

pGUID

Pointer to a GUID, or GUID_NULL, if the error was defined by the operating system.

Return Values

S_OK

Success.

IErrorInfo::GetGUID returns the GUID of the interface that defined the error. If the error was defined by the system, IErrorInfo::GetGUID returns GUID_NULL.

This GUID does not necessarily represent the source of the error. The source is the class or application that raised the error. Using the GUID, an application can handle errors in an interface, independent of the class that implements the interface.  

IErrorInfo::GetHelpContext()

NAME

IErrorInfo::GetHelpContext() - Returns the Help context identifier (ID) for the error.

Synopsis

#include <Oaidl.h>

HRESULT GetHelpContext(
        DWORD * pdwHelpContext );

Parameters

pdwHelpContext

Pointer to the Help context ID for the error.

Return Values

S_OK

Success

IErrorInfo::GetHelpContext returns the Help context ID for the error. To find the Help file to which it applies, use IErrorInfo::GetHelpFile.  

IErrorInfo::GetHelpFile()

NAME

IErrorInfo::GetHelpFile() - Returns the path of the Help file that describes the error.

Synopsis

#include <Oaidl.h>

HRESULT GetHelpFile(
        BSTR * pBstrHelpFile );

Parameters

pBstrHelpFile

Pointer to a string that contains the fully qualified path of the Help file.

Return Values

S_OK

Success

IErrorInfo::GetHelpFile returns the fully qualified path of the Help file that describes the current error. IErrorInfo::GetHelpContext should be used to find the Help context ID for the error in the Help file.  

IErrorInfo::GetSource()

NAME

IErrorInfo::GetSource() - Returns the language-dependent programmatic ID (ProgID) for the class or application that raised the error.

Synopsis

#include <Oaidl.h>

HRESULT GetSource(
        BSTR * pBstrSource );

Parameters

pBstrSource

Pointer to a string containing a ProgID, in the form progname.objectname.

Return Values

S_OK

Success

Use IErrorInfo::GetSource to determine the class or application that is the source of the error. The language for the returned ProgID depends on the locale ID (LCID) that was passed into the method at the time of invocation.

12.3.2    ICreateErrorInfo

The ICreateErrorInfo interface returns error information.

Description

Table 12-2:  ICreateErrorInfo Elements

Implemented by Used by Header filename Import library name
Oleaut32.dll (32-bit systems) Oledisp.dll (16-bit systems) Applications that return rich error information. Oleauto.h Dispatch.h Oleaut32.lib Oledisp.lib

 

ICreateErrorInfo::SetDescription()

NAME

ICreateErrorInfo::SetDescription() - Sets the textual description of the error.

Synopsis

#include <Oleauto.h>

HRESULT SetDescription(
        LPCOLESTR * szDescription );

Parameters

szDescription

A brief, zero-terminated string that describes the error.

Return Values

S_OK

Success

E_OUTOFMEMORY

Insufficient memory to complete the operation.

The text should be supplied in the language specified by the locale ID (LCID) that was passed to the method raising the error.

Examples

hr = CreateErrorInfo(&pcerrinfo);
if (m_excepinfo.bstrDescription)
pcerrinfo->SetDescription(m_excepinfo.bstrDescription);

 

ICreateErrorInfo::SetGUID()

NAME

ICreateErrorInfo::SetGUID() - Sets the globally unique identifier (GUID) of the interface that defined the error.

Synopsis

#include <Oleauto.h>

HRESULT SetGUID(
        REFGUID rguid );

Parameters

rguid

The GUID of the interface that defined the error, or GUID_NULL if the error was defined by the operating system.

Return Values

The return value obtained from the returned HRESULT is one of the following: S_OK

Success

E_OUTOFMEMORY

Insufficient memory to complete the operation.

ICreateErrorInfo::SetGUID sets the GUID of the interface that defined the error. If the error was defined by the system, set ICreateErrorInfo::SetGUID to GUID_NULL.

This GUID does not necessarily represent the source of the error; however, the source is the class or application that raised the error. Using the GUID, applications can handle errors in an interface, independent of the class that implements the interface.

Examples

hr = CreateErrorInfo(&pcerrinfo);
pcerrinfo->SetGUID(IID_IHello);

 

ICreateErrorInfo::SetHelpContext()

NAME

ICreateErrorInfo::SetHelpContext() - Sets the Help context identifier (ID) for the error.

Synopsis

#include <Oleauto.h>

HRESULT SetHelpContext(
        DWORD dwHelpContext );

Parameters

dwHelpContext

The Help context ID for the error.

Return Values

S_OK

Success

E_OUTOFMEMORY

Insufficient memory to complete the operation

ICreateErrorInfo::SetHelpContext sets the Help context ID for the error. To establish the Help file to which it applies, use ICreateErrorInfo::SetHelpFile.

Examples

hr = CreateErrorInfo(&pcerrinfo);
pcerrinfo->SetHelpContext(dwhelpcontext);

 

ICreateErrorInfo::SetHelpFile()

NAME

ICreateErrorInfo::SetHelpFile() - Sets the path of the Help file that describes the error.

Synopsis

#include <Oleauto.h>

HRESULT SetHelpFile(
        LPCOLESTR szHelpFile );

Parameters

szHelpFile

The fully qualified path of the Help file that describes the error.

Return Values

S_OK

Success

E_OUTOFMEMORY

Insufficient memory to complete the operation

ICreateErrorInfo::SetHelpFile sets the fully qualified path of the Help file that describes the current error. Use ICreateErrorInfo::SetHelpContext to set the Help context ID for the error in the Help file.

Examples

hr = CreateErrorInfo(&pcerrinfo);
pcerrinfo->SetHelpFile(<``><''>C:\myapp\myapp.hlp<``><''>);

 

ICreateErrorInfo::SetSource()

NAME

ICreateErrorInfo::SetSource() - Sets the language-dependent programmatic identifier (ProgID) for the class or application that raised the error.

Synopsis

#include <Oleauto.h>

HRESULT SetSource(
        LPCOLESTR szSource );

Parameters

szSource

A ProgID in the form progname.objectname.

Return Values

S_OK

Success

E_OUTOFMEMORY

Insufficient memory to complete the operation.

ICreateErrorInfo::SetSource should be used to identify the class or application that is the source of the error. The language for the returned ProgID depends on the locale identifier (LCID) that was passed to the method at the time of invocation.

Examples

hr = CreateErrorInfo(&pcerrinfo);
if (m_excepinfo.bstrSource) 
pcerrinfo->SetSource(m_excepinfo.bstrSource);

12.3.3    ISupportErrorInfo

The ISupportErrorInfo() interface ensures that error information can be propagated up the call chain correctly. Automation objects that use the error handling interfaces must implement ISupportErrorInfo().

Description

Table 12-3:  ISupportErrorInfo Elements

Implemented by Used by Header filename
Applications that return error information. Applications that retrieve error information. Oleauto.h (32-bit systems) Dispatch.h (16-bit systems)

 

ISupportErrorInfo::InterfaceSupportsErrorInfo()

NAME

ISupportErrorInfo::InterfaceSupportsErrorInfo() - Indicates whether or not an interface supports the IErrorInfo() interface.

Synopsis

#include <Oaidl.h>

HRESULT InterfaceSupportsErrorInfo(
        REFIID riid );

Parameters

riid

Pointer to an interface identifier (IID).

Return Values

S_OK

Interface supports IErrorInfo()

S_FALSE

Interface does not support IErrorInfo()

Objects that support the IErrorInfo() interface must also implement this interface.

Programs that receive an error return value should call QueryInterface() to get a pointer to the ISupportErrorInfo() interface, and then call InterfaceSupportsErrorInfo with the riid of the interface that returned the return value. If InterfaceSupportsErrorInfo returns S_FALSE, then the error object does not represent an error returned from the caller, but from somewhere else. In this case, the error object can be considered incorrect and should be discarded.

If ISupportErrorInfo() returns S_OK, use the GetErrorInfo() function to get a pointer to the error object.

The following example implements the ISupportErrorInfo() for the Lines sample. The IErrorInfo() implementation also supports the AddRef(), Release(), and QueryInterface() members inherited from the IUnknown() interface.

CSupportErrorInfo::CSupportErrorInfo(IUnknown() FAR* punkObject, REFIID riid)
{
    m_punkObject = punkObject;
    m_iid = riid;
}
STDMETHODIMP
CSupportErrorInfo::QueryInterface(REFIID iid, void FAR* FAR* ppv)
{
    return m_punkObject->QueryInterface(iid, ppv);
}
STDMETHODIMP_(ULONG)
CSupportErrorInfo::AddRef(void)
{
    return m_punkObject->AddRef();
}
STDMETHODIMP_(ULONG)
CSupportErrorInfo::Release(void)
{
    return m_punkObject->Release();
}
STDMETHODIMP
CSupportErrorInfo::InterfaceSupportsErrorInfo(REFIID riid)
{
    return (riid == m_iid) ? NOERROR : ResultFromScode(S_FALSE);
}

 

CreateErrorInfo()

NAME

CreateErrorInfo() - Creates an instance of a generic error object.

Synopsis

#include <oleauto.h>

HRESULT CreateErrorInfo((
        ICreateErrorInfo ** pperrinfo );

Parameters

pperrinfo

Pointer to a system-implemented generic error object.

Return Values

S_OK

Success

E_OUTOFMEMORY

Could not create the error object

This function returns a pointer to a generic error object, which you can use with QueryInterface() on ICreateErrorInfo to set its contents. You can then pass the resulting object to SetErrorInfo(). The generic error object implements both ICreateErrorInfo and IErrorInfo().

Examples

ICreateErrorInfo *perrinfo;
HRESULT hr;
hr = CreateErrorInfo(&pcerrinfo);

 

GetErrorInfo()

NAME

GetErrorInfo() - Obtains the error information pointer set by the previous call to SetErrorInfo_oa96_SetErrorInfo in the current logical thread.

Synopsis

#include <oleauto.h>

HRESULT GetErrorInfo((
        DWORD dwReserved,
        IErrorInfo ** pperrinfo );

Parameters

dwReserved

Reserved for future use. Must be zero.

pperrinfo

Pointer to a pointer to an error object.

Return Values

S_OK

Success

S_FALSE

There was no error object to return

This function returns a pointer to the most recently set IErrorInfo() pointer in the current logical thread. It transfers ownership of the error object to the caller, and clears the error state for the thread.  

SetErrorInfo()

NAME

SetErrorInfo() - Sets the error information object for the current thread of execution.

Synopsis

#include <oleauto.h>

HRESULT SetErrorInfo((
        DWORD dwReserved,
        IErrorInfo * perrinfo );

Parameters

dwReserved

Reserved for future use. Must be zero.

perrinfo

Pointer to an error object.

Return Values

S_OK

Success

This function releases the existing error information object, if one exists, and sets the pointer to perrinfo. Use this function after creating an error object that associates the object with the current thread of execution.

If the property or method that calls SetErrorInfo() is called by DispInvoke(), then DispInvoke() will fill the EXCEPINFO parameter with the values specified in the error information object. DispInvoke() will return DISP_E_EXCEPTION when the property or method returns a failure return value for DispInvoke().

Virtual function table (VTBL) binding controllers that do not use IDispatch::Invoke() can get the error information object by using GetErrorInfo(). This allows an object that supports a dual interface to use SetErrorInfo(), regardless of whether the client uses VTBL binding or IDispatch().

Examples

ICreateErrorInfo *pcerrinfo;
IErrorInfo *perrinfo;
HRESULT hr;
hr = CreateErrorInfo(&pcerrinfo);
hr = pcerrinfo->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &perrinfo);
if (SUCCEEDED(hr))
{
    SetErrorInfo(0, perrinfo);
    perrinfo->Release();
}
pcerrinfo->Release();

[Footnote 59]

12.3.4    HRESULT

The key type involved in COM error reporting is HRESULT. [Footnote 59] In addition, the COM Library provides a few functions and macros to help applications of any kind deal with error information. An HRESULT is a simple 32-bit value:

typedef LONG HRESULT;

[Footnote 60] An HRESULT is divided up into an internal structure that has four fields with the following format (numbers indicate bit positions):

S:

(1 bit) Severity field:

R:

(2 bits) Reserved for future use; must be set to zero by present programs generating HRESULTs; present code should not take action that relies on any particular bits being set or cleared this field.

Facility:

(13 bits) Indicates which group of status codes this belongs to. New facilities must be allocated by a central coordinating body since they need to be universally unique. [Footnote 60] However, the need for new facility codes is very small. Most cases can and should use FACILITY_ITF. See Section Section 12.3.4.1 below.

Code:

(16 bits) Describes what actually took place, error or otherwise.

COM presently defines the following facility codes:

Table 12-4:  HRESULT Facility Codes

Facility Name Facility Value Description
FACILITY_NULL 0 Used for broadly applicable common status codes that have no specific grouping. S_OK belongs to this facility, for example.
FACILITY_ITF 4 Used for by far the majority of result codes that are returned from an interface member function. Use of this facility indicates that the meaning of the error code is defined solely by the definition of the particular interface in question; an HRESULT with exactly the same 32-bit value returned from another interface might have a different meaning.
FACILITY_RPC 1 Used for errors that result from an underlying remote procedure call implementation. In general, this specification does not explicitly document the RPC errors that can be returned from functions, though they nevertheless can be returned in situations where the interface being used is in fact remoted
FACILITY_DISPATCH 2 Used for IDispatch-interface-related status codes.
FACILITY_STORAGE 3 Used for persistent-storage-related status codes. Status codes whose code (lower 16 bits) value is in the range of DOS error codes (less than 256) have the same meaning as the corresponding DOS error.
FACILITY_WIN32 7 Used to provide a means of mapping an error code from a function in the Win32 API into an HRESULT. The semantically significant part of a Win32 error is 16 bits large.
FACILITY_WINDOWS 8 Used for additional error codes from Microsoft-defined interfaces.
FACILITY_CONTROL 10 Used for ActiveX Controls-related error values.

A particular HRESULT value by convention uses the following naming structure:

<Facility>_<Sev>_<Reason>

where <Facility> is either the facility name or some other distinguishing identifier, <Sev> is a single letter, one of the set { S, E } indicating the severity (success or error), and <Reason> is a short identifier that describes the meaning of the code. Status codes from FACILITY_NULL omit the <Facility>_ prefix. For example, the status code E_NOMEMORY is the general out-of memory error. All codes have either S_ or E_ in them allowing quick visual determination if the code means success or failure.

The general ``success'' HRESULT is named S_OK, meaning ``everything worked'' as per the function specification. The value of this HRESULT is zero. In addition, as it is useful to have functions that can succeed but return Boolean results, the code S_FALSE is defined are success codes intended to mean ``function worked and the result is false.''

#define S_OK  0
#define S_FALSE  1

From a general interface design perspective, ``success'' status codes should be used for circumstances where the consequence of ``what happened'' in a method invocation is most naturally understood and dealt with by client code by looking at the out-values returned from the interface function: NULL pointers, etc. ``Error'' status codes should in contrast be used in situations where the function has performed in a manner that would naturally require ``out of band'' processing in the client code, logic that is written to deal with situations in which the interface implementation truly did not behave in a manner under which normal client code can make normal forward progress. The distinction is an imprecise and subtle one, and indeed many existing interface definitions do not for historical reasons abide by this reasoning. However, with this approach, it becomes feasible to implement automated COM development tools that appropriately turn the error codes into exceptions as was mentioned above.

Interface functions in general take the form:

HRESULT ISomeInteface::SomeFunction(ARG1_T arg1, ... , ARGN_T argn, RET_T * pret);

Stylistically, what would otherwise be the return value is passed as an out-value through the last argument of the function. COM development tools which map error returns into exceptions might also consider mapping the last argument of such a function containing only one out-parameter into what the programmer sees as the ``return value'' of the method invocation.

The COM remoting infrastructure only supports reporting of RPC-induced errors (such as communication failures) through interface member functions that return HRESULTs. For interface member functions of other return types (e.g.: void), such errors are silently discarded. To do otherwise would, to say the least, significantly complicate local / remote transparency.

12.3.4.1    Use of FACILITY_ITF

The use of FACILITY_ITF deserves some special discussion with respect to interfaces defined in COM and interfaces that will be defined in the future. Whereas status codes with other facilities (FACILITY_NULL, FACILITY_RPC, etc.) have universal meaning, status codes in FACILITY_ITF have their meaning completely determined by the interface member function (or API function) from which they are returned; the same 32-bit value in FACILITY_ITF returned from two different interface functions may have completely different meanings.

The reasoning behind this distinction is as follows. For reasons of efficiency, it is unreasonable to have the primary error code data type (HRESULT) be larger than 32 bits in size. 32 bits is not large enough, unfortunately, to enable COM to develop an allocation policy for error codes that will universally avoid conflict between codes allocated by different non-communicating programmers at different times in different places (contrast, for instance, with what is done with IIDs and CLSIDs). Therefore, COM structures the use of the 32 bit SCODE in such a way as to allow a central coordinating body [Footnote 60] to define some universally defined error codes while at the same time allowing other programmers to define new error codes without fear of conflict by limiting the places in which those field-defined error codes can be used. Thus:

  1. Status codes in facilities other than FACILITY_ITF can only be defined by the central coordinating body.

  2. Status codes in facility FACILITY_ITF are defined solely by the definer of the interface or API by which said status code is returned. That is, in order to avoid conflicting error codes, a human being needs to coordinate the assignment of codes in this facility, and we state that he who defines the interface gets to do the coordination.

COM itself defines a number of interfaces and APIs, and so COM defines many status codes in FACILITY_ITF. By design, none of the COM-defined status codes in fact have the same value, even if returned by different interfaces, though it would have been legal for COM to do otherwise.

Likewise, it is possible (though not required) for designers of COM interface suites to coordinate the error codes across the interfaces in that suite so as to avoid duplication. The designers of the COM interface suite, for example, ensured such lack of duplication.

Thus, with regard to which errors can be returned by which interface functions, it is the case that, in the extreme,

It is legal that any COM-defined error code may in fact be returned by any COM-defined interface member function or API function. This includes errors presently defined in FACILITY_ITF. Further, COM may in the future define new failure codes (but not success codes) that may also be so ubiquitously returned.

Designers of interface suites may if they wish choose to provide similar rules across the interfaces in their suites.

Further, any error in FACILITY_RPC or other facility, even those errors not presently defined, may be returned.

Clients must treat error codes that are unknown to them as synonymous with E_UNEXPECTED, which in general should be and is presently a legal error return value from each and every interface member function in all interfaces; interface designers and implementors are responsible to insure that any newly defined error codes they should choose to invent or return will be such that that existing clients with code treating generic cases as synonymous with E_UNEXPECTED this will have reasonable behavior.

In short, if you know the function you invoked, you know as a client how to unambiguously take action on any error code you receive. The interface implementor is responsible for maintaining your ability to do same.

Normally, of course, only a small subset of the COM-defined status codes will be usefully returned by a given interface function or API, but the immediately preceding statements are in fact the actual interoperability rules for the COM-defined interfaces. This specification endeavors to point out which error codes are particularly useful for each function, but code must be written to correctly handle the general rule.

The present document is, however, precise as to which success codes may legally be returned.

Conversely, it is only legal to return a status code from the implementation of an interface member function which has been sanctioned by the designer of that interface as being legally returnable; otherwise, there is the possibility of conflict between these returned code values and the codes in-fact sanctioned by the interface designer. Pay particular attention to this when propagating errors from internally called functions. Nevertheless, as noted above, callers of interfaces must to guard themselves from imprecise interface implementations by treating any otherwise unknown returned error code (in contrast with success code) as synonymous with E_UNEXPECTED: experience shows that programmers are notoriously lax in dealing with error handling. Further, given the third bullet point above, this coding practice is required by clients of the COM-defined interfaces and APIs. Pragmatically speaking, however, this is little burden to programmers: normal practice is to handle a few special error codes specially, but treat the rest generically.

All the COM-defined FACILITY_ITF codes will, in fact, have a code value which lies in the region 0x0000--0x01FF. Thus, while it is indeed legal for the definer of a new function or interface to make use of any codes in FACILITY_ITF that he chooses in any way he sees fit, it is highly recommended that only code values in the range 0x0200--0xFFFF be used, as this will reduce the possibility of accidental confusion with any COM-defined errors. It is also highly recommended that designers of new functions and interfaces consider defining as legal that most if not all of their functions can return the appropriate status codes defined by COM in facilities other than FACILITY_ITF. E_UNEXPECTED is a specific error code that most if not all interface definers will wish to make universally legal.

12.3.5    IErrorLog

The IErrorLog() interface is an abstraction for an error log that is used to communicate detailed error information between a client and an object. The caller of the single interface method, AddError, simply logs an error where the error is an EXCEPINFO structure related to a specific property. The implementer of the interface is responsible for handling the error in whatever way it desires. IErrorLog() is used in the protocol between a client that implements IPropertyBag() and an object that implements IPersistPropertyBag().

12.3.5.1    When to Implement

A container implements IErrorLog() to provide a control with a means of logging errors when the control is loading its properties from the container-provided property bag.

12.3.5.2    When to Use

A control logs calls the single method in this interface to log any errors that occur when it is loading its properties.

Table 12-5:  IErrorLog Methods in Vtable Order

IUnknown Methods Description
QueryInterface() Returns pointers to supported interfaces.
AddRef() Increments reference count.
Release() Decrements reference count.

Table 12-6:  IErrorLog Methods

IErrorLog() Method Description
AddError Logs an error, an EXCEPINFO structure, in the error log during the property load process for a named property.

12.3.5.3    See Also

IPersistPropertyBag(), IPropertyBag()  

IErrorLog::AddError()

NAME

IErrorLog::AddError() - Logs an error, an EXCEPINFO structure, in the error log during the property load process for a named property.

Synopsis

#include <ocidl.h>

HRESULT AddError(
        LPCOLESTR pszPropName,
        LPEXCEPINFO pException );

Description

E_NOTIMPL is not a valid return code as the method is the only one in the entire interface.

Parameters

pszPropName

[in] Pointer to the name of the property involved with the error. Cannot be NULL.

pExcepInfo

[in] Pointer to the caller-initialized EXCEPINFO structure that describes the error to log. Cannot be NULL.

Return Values

S_OK

The error was logged successfully.

E_FAIL

There was a problem logging the error.

E_OUTOFMEMORY

There was not enough memory to log the error.

E_POINTER

The address in pszPropName or pExceptInfo is not valid (such as NULL). The caller must supply both.