Teach Yourself Database Programming
with Visual C++ 6 in 21 days


Day 21
      OLE DB Error Handling



The focus this week has been OLE DB objects, their interfaces, and some simple OLE DB applications. OLE DB also provides a means for you to build robust applications by using mechanisms to integrate error handling into your applications. The first step in error handling is to examine the value returned by a method, which is where today's discussion of OLE DB error handling begins. OLE DB uses the basic error-handling techniques of Automation, the technology formerly known as OLE Automation, and adds another layer of error handling that can return multiple provider-specific errors. The goal of Day 21 is to show you how to integrate these techniques into your own applications.

Today you will

Basic Error Handling

Although all the OLE DB interface methods discussed this week return an HRESULT type value, the OLE DB code you have written so far assumes that all method calls always execute successfully. Obviously, this approach isn't the way to build a robust application that can handle unexpected situations. Therefore, the first step in adding robustness to OLE DB applications is to check the method's result value.

NOTE
A robust application can handle all error conditions without crashing. As an application developer, you have the responsibility of handling all errors that your application might generate.

Checking Error Results

As you know, the HRESULT type return value indicates the success or failure of a method. (Later today you will explore the status codes that a method can return.) Visual C++ provides two macros that you can use to determine generally whether a method was successful. These macros are aptly named SUCCEEDED and FAILED. The SUCCEEDED macro returns true if the call to the method was successful, and the FAILED macro returns true if the call to the method was unsuccessful. If a call to a method is successful, it returns the constant value S_OK. A method can return other success and error values as well. Typically, the success value constants begin with an S_ or DB_S_, and the error value constants begin with an E or DB_E. You will learn about these status value constants later today, also. Listing 21.1 demonstrates how to check the HRESULT value of a method to determine whether it executed successfully.

NOTE
The standard error and success return codes are defined in the Visual C++ documentation.


Listing 21.1  Checking the HRESULT Value of a Method

 1:    // Create a Command object...
 2:    if(FAILED(pCreateSession->CreateSession(NULL, IID_IDBCreateCommand,
 3:                                           (IUnknown **)
                                                    &pCreateCommand))) {
 4:       printf("The Create Session Method Failed!\n");
 5:    };

As you can see, checking the status of a call to an OLE DB method is fairly simple. That information enables your application to take the necessary steps to prevent a failure. In addition, OLE DB and Automation support interfaces that can help you retrieve more-detailed information regarding the failure of a method. The following section explains how to integrate these interfaces to retrieve additional error information. The immediate goal here is to build a procedure you can use in your application to display this additional error information.

Automation Error Objects

The OLE DB specification is an extension of the Automation interface. Automation defines two interfaces that assist in the error-handling process. These interfaces are not required by OLE DB data providers. The first interface, ISupportErrorInfo, determines whether the additional support is provided. The second interface, IErrorInfo, assists in retrieving additional specific error information, including a more complete description of the error and whether any further information is available in an associated help file.

The ISupportErrorInfo Interface

The ISupportErrorInfo interface determines whether an object supports the necessary interfaces to retrieve additional error information. The ISupportErrorInfo defines a single method, InterfaceSupportsErrorInfo, which is defined as follows:

HRESULT InterfaceSupportsErrorInfo(REFIID riid);

This method takes a single parameter, riid, which represents the interface you are checking to determine whether it supports additional error information. If the interface supports additional error information, the InterfaceSupportsErrorInfo method returns S_OK.

Listing 21.2 demonstrates the first step in creating a procedure that retrieves and displays the additional error information if it's supported. The procedure DispErrorInfo takes two parameters: a pointer to the interface that generated the error and the globally unique identifier (GUID) of that interface. Because this procedure should function for any type of OLE DB object, it will pass the pointer to the interface that generated the error as a pointer to the generic IUnknown type.

CAUTION
You must always call the InterfaceSupportsErrorInfo method before attempting to retrieve additional error information. If the interface doesn't support additional error information and you call any of the additional error-information methods, you will generate yet another error! Never create error-handling routines that generate more errors themselves.


Listing 21.2  The First Step in Building a Procedure to Handle and Display OLE DB Errors

 1:  HRESULT DispErrorInfo(IUnknown *pErrorInt, GUID ErrorIID) {
 2:    ISupportErrorInfo *pSupportErrorInfo;
 3:
 4:    // Obtain Access to the ISupportErrorInfo Interface
 5:    if(SUCCEEDED(pErrorInt->QueryInterface(IID_ISupportErrorInfo,
 6:                                    (void **) &pSupportErrorInfo))) {
 7:      // Check if Extended Error Information is Available
 8:      if(pSupportErrorInfo->InterfaceSupportsErrorInfo(ErrorIID)
         ==      S_OK) {
 9:         // Process the Error Information Available Here!...
10:       } else {
11:         cerr << "Extended Error Information Unavailable!\n";
12:       };
13:       pSupportErrorInfo->Release();
14:     } else {
15:       cerr << "Could Not Obtain Access To The ISupportErrorInfo
           Interface\n";
16:       cerr << "Additional Error Information Unavailable!\n";
17:     };
18:     return S_OK;
             };

As Listing 21.2 demonstrates, after the ISupportErrorInfo interface is successfully obtained, you can use the InterfaceSupportsErrorInfo method to determine whether the object supports the IErrorInfo interface. The next section explains the IErrorInfo interface.

The IErrorInfo Interface

The IErrorInfo interface is the generic Automation interface that retrieves extended error information about a single error. However, OLE DB objects don't use this interface directly. As you already know, the ISupportErrorInfo interface determines whether the application supports the IErrorInfo interface. The IErrorInfo interface defines five methods: GetDescription, GetGUID, GetHelpContext, GetHelpFile, and GetSource. These methods are defined as follows:

HRESULT GetDescription(BSTR *pbstrDesc);
HRESULT GetGUID(GUID *pGUID);
HRESULT GetHelpContext(DWORD *pwHelpContext);
HRESULT GetHelpFile(BSTR *pbstrHelpFile);
HRESULT GetSource(BSTR *pbstrSource);

The GetDescription method returns a string that contains a natural-language description of the error that occurred. This description is suitable to display to an end user.

The GetGUID method returns the interface ID of the interface that generated the error. If the error isn't attributable to a specific interface, the value of the GUID is set to DBGUID_NULL.

The GetHelpContext and GetHelpFile methods retrieve the name of the help file and help file lookup. This information can launch the help viewer and place the user in a context that displays additional error information. These values are returned only if the data provider supports a help file. The GetHelpContext method returns a context value, and the GetHelpFile method returns a string that contains the name of the help file, including the path.

The GetSource method returns the name of the source operating-system object that generated the error. The source name is generally the name of the dynamic link library (DLL) that encapsulates the data provider interfaces.

NOTE
The consumer must use the SysFreeString method to deallocate any string values that the IErrorInfo interfaces return.

OLE DB Error Objects

The IErrorInfo interface works quite well for standard COM objects. However, the OLE DB has two requirements that the standard IErrorInfo interface can't meet:

Therefore, OLE DB defines three additional interfaces to overcome these limitations. These interfaces are IErrorRecords, IErrorLookup, and ISQLErrorInfo. The IErrorRecords interface returns multiple errors simultaneously. The IErrorLookup interface provides extended error-description information. Finally, the ISQLErrorInfo interface returns custom SQL error information.

A detailed explanation of these interfaces and the methods they provide follows. When you understand how to use these interfaces, you can integrate them into the completed DispErrorInfo procedure started in Listing 21.1.

The IErrorRecords Interface

Rather than return one record as defined by the IErrorInfo interface, OLE DB methods can return multiple errors. The IErrorRecords interface navigates and manages this collection of error records. The reason OLE DB needs to return multiple errors is that an error can have a cascading effect. This record structure enables these multiple cascading errors to be captured and returned. Each error record contains the following information:

I explain each of the preceding components in the following discussion of the specific OLE DB error interfaces. The IErrorRecords interface defines the following methods: AddErrorRecord, GetBasicErrorInfo, GetCustomErrorObject, GetErrorInfo, GetErrorParameters, and GetRecordCount. These methods are defined as follows:

HRESULT GetBasicErrorInfo(ULONG lRecNum, ERRORINFO *pErrorInfo);
HRESULT GetCustomErrorObject(ULONG lRecNum, REFIID riid, 
                             IUnknown **ppCustomErrorInt);
HRESULT GetErrorInfo(ULONG lRecNum, LCID LocaleID, 
                     IErrorInfo **ppErrorInfoInt);
HRESULT GetErrorParameters(ULONG lRecNum, DISPPARAMS *pDispParam);
HRESULT GetRecordCount(ULONG *plNumRecs);
HRESULT AddErrorRecord(ERRORINFO *pErrorInfo, DWORD dwLookupID, 
                       DISPPARAMS *pDispParam, IUnknown *pCustomErrorInt,
                       DWORD dwDynamicID);

The GetBasicErrorInfo method retrieves the basic ERRORINFO structure for the record number specified in the lRecNum parameter. The ERRORINFO structure holds basic information about an error. The structure is defined as follows:

typedef struct tagERRORINFO
     HRESULT    hrError;
     DWORD      dwMinor;
     CLSID      clsid;
     IID        iid;
     DISPID     dispid;
} ERRORINFO;

The hrError field contains the HRESULT error value for the record specified. Each record can have an HRESULT value different from the one that the method initially returned. The dwMinor field contains a provider-specific error value. You must consult the documentation for the data provider for more information about the meanings of this value. The clsid field contains the class ID of the OLE DB object that generated this error. The iid parameter contains the interface ID of the interface that generated this error. If the method belongs to multiple interfaces, the interface ID where the method is defined first is returned. The dispid field contains the method that generated the error. This method ID may or may not be defined, depending on the data provider.

The GetCustomErrorObject method retrieves a provider-specific customer error. OLE DB provides one custom error interface, ISQLErrorInfo (discussed shortly), for providers that support the SQL command language. The GetCustomErrorObject method parameter lRecNum specifies the error record number to access. The riid parameter specifies the interface ID of the custom error object you want to retrieve. The ppCustomErrorInt parameter returns a pointer to the interface requested. If the requested custom error interface isn't available, ppCustomErrorInt will be NULL on return. If this method succeeds, ppCustomErrorInt returns S_OK.

The GetErrorInfo method retrieves an IErrorInfo interface for the specified record number. The lRecNum parameter specifies the record number to retrieve. The LocalID parameter specifies the locale ID. The locale ID specifies the current location and selects the appropriate language version of the error descriptions. The ppErrorInfoInt parameter returns a pointer to IErrorInfo interface for the selected record number.

TIP
To retrieve the default system locale ID, use the standard Visual C++ method GetSystemDefaultLCID().

The GetErrorParameters method retrieves error message parameters. Some error messages might use parameters, as in the following example: Access to table <table1> denied. In this case, <table1> is a parameter. If the parameter <table1> had the value CUSTOMERS, the error message would be automatically reformatted as follows: Access to table CUSTOMERS denied. A consumer usually won't use a parameter structure without knowing which values are available in that structure. The lRecNum parameter specifies the record number. The pDispParams parameter retrieves the error message parameters.

The GetRecordCount method returns the number of error records available. The parameter plNumRecs returns the number of error records available. Error records have the index values 0 to plNumRecs -1.

The AddErrorRecord method is used only by data providers. It adds a new error record to the error records collection. The pErrorInfo structure holds a pointer to a filled-in ERRORINFO structure, specifying information about the error. The dwLookupID specifies the error's lookup ID. The pDispParam parameter specifies the error's message parameters. The pCustomErrorInt parameter specifies the custom error object, if applicable. The dwDynamicID parameter specifies the error ID if the provider uses dynamic errors; otherwise, it has a value of 0. You will generally not use this method.

You will use the IErrorRecords interface extensively to complete the DispErrorInfo procedure from Listing 21.2. In its final form, the procedure loops through and retrieves the information in each error record. First, though, you need to look at the IErrorLookup and ISQLErrorInfo interfaces.

The IErrorLookup Interface

Data providers use the IErrorLookup interface to retrieve help file information, combine the error parameters with the error message to create a completed error message, and release dynamic error information. The IErrorLookup interface defines three methods: GetErrorDescription, GetHelpInfo, and ReleaseErrors. These methods are defined as follows:

HRESULT GetErrorDescription(HRESULT hError, DWORD dwLookupID,
                            DISPPARAMS *pDispParam, LCID lcid,
                            BSTR *pErrorObject, BSTR *pDesc);
HRESULT GetHelpInfo(HRESULT hError, dwLookupID, LCID lcid,
                    BSTR *pHeloFileName, DWORD *pContext);
HRESULT ReleaseErrors(const DWORD dwDynErrorID);

The GetDescription method retrieves the source error object string, specified in the pErrorObject parameter, and the error description, specified in the pDesc parameter. The GetHelpInfo method retrieves an error's associated help file information, if applicable. The GetErrorDescription and GetHelpInfo functions return BSTRs that must be freed by the caller, using SysFreeString.

The ISQLErrorInfo Interface

The final error-related interface covered today is the ISQLErrorInfo interface. This interface is used in the custom error object pointer of an error record. You access the ISQLErrorInfo interface by using the GetCustomErrorObject method of the IErrorRecord interface. The ISQLErrorInfo interface defines a single method: GetSQLInfo. This method is defined as follows:

HRESULT GetSQLInfo(BSTR *pSQLStateStr, LONG *plSQLErrorNum);

The pSQLStateStr parameter returns a string containing the current SQL state. The plSQLErrorNum returns the provider-specific SQL error number. The pSqlStateStr is an [out] bstr, so the caller must free it by using SysFreeString.

The Completed DispErrorInfo Source Code

Armed with the information provided by these error interfaces, you are ready to return to the DispErrorInfo procedure in Listing 21.2. The final procedure accesses the IErrorRecord interface and loops through each available error record, one at a time. DispErrorInfo retrieves an IErrorInfo interface for each error record and uses that IErrorInfo interface to access the error description, source, and GUID. The GetCustomErrorObject is accessed for each record. If a ISQLErrorInfo interface is available, the current SQL state and error number are also displayed. The complete DispErrorInfo procedure appears in Listing 21.3.

NOTE
Remember to use the SysFreeString method to release the returned string memory. The BSTR type is a Unicode type string. (Refer to Day 19, "Navigating the Result of a Query," for more information about the methods available to process Unicode strings.)


Listing 21.3  The Completed DISPERRORINFO Procedure

 1:    HRESULT DispErrorInfo(IUnknown *pErrorInt, GUID ErrorIID) {
 2:    ISupportErrorInfo *pSupportErrorInfo;
 3:    IErrorInfo        *pErrorInfo,
 4:                      *pErrorInfoRecord;
 5:    IErrorRecords     *pErrorRecords;
 6:    ISQLErrorInfo     *pSQLErrorInfo;
 7:    BSTR               pDescription = NULL;
 8:    BSTR               pSource = NULL;
 9:    BSTR               pSQLState = NULL;
10:    GUID               ErrorGUID;
11:    ULONG              i,
12:                       lNumRecs;
13:    LONG               lSQLErrorNum;
14:    HRESULT            retcode = S_OK;
15:
16:    // Obtain Access to the ISupportErrorInfo Interface
17:    if(SUCCEEDED(pErrorInt->QueryInterface(IID_ISupportErrorInfo,
18:                                      (void **) &pSupportErrorInfo))) {
19:      // Check if Extended Error Information is Available
20:      if(SUCCEEDED(pSupportErrorInfo->InterfaceSupportsErrorInfo
                 (ErrorIID))) {
21:         // Access the Error Info interface
22:         if(SUCCEEDED(GetErrorInfo(0,&pErrorInfo))) {
23:            // Retrieve the Error Records Interface
24:           if(SUCCEEDED(pErrorInfo->QueryInterface(IID_IErrorRecords,
25:                                          (void **) &pErrorRecords))) {
26:              // Retrieve the Number of Error Records
27:              if(SUCCEEDED(pErrorRecords->GetRecordCount(&lNumRecs))) {
28:                for(i=0; i < lNumRecs; i++) {
29:                   // Get the Error Info Interface
30:                   pErrorRecords-     >GetErrorInfo(i,GetSystemDefaultLCID(),
31:                                               &pErrorInfoRecord);
32:                   // Get the Error Description
33:                   pErrorInfoRecord->GetDescription(&pDescription);
34:                   // Get the Error Source
35:                   pErrorInfoRecord->GetSource(&pSource);
36:                   // Get the Error GUID
37:                   pErrorInfoRecord->GetGUID(&ErrorGUID);
38:                   // Print the Error Record Interface
39:                   fprintf(stderr,"Error GUID: %lx\n",ErrorGUID);
40:                   fprintf(stderr,"Source: %S\n",pDescription);
41:                   fprintf(stderr,"Description: %S\n\n",pDescription); 
42:                   // Free the Strings
43:                   SysFreeString(pDescription);
44:                   SysFreeString(pSource);
45:                   // Get SQL State if Available
46:                   if(SUCCEEDED(pErrorRecords->GetCustomErrorObject(i,
47:                                      IID_ISQLErrorInfo,
48:                                      (IUnknown **) &pSQLErrorInfo))) {
49:                     pSQLErrorInfo->GetSQLInfo(&pSQLState,
                                                       &lSQLErrorNum);
50:                     fprintf(stderr,"SQL State: %S\n",pSQLState);
51:                     fprintf(stderr,"SQL Error Number:
                                     %ld",lSQLErrorNum);
52:                     SysFreeString(pSQLState);
53:                     pSQLErrorInfo->Release();
54:                   };
55:                   // Release the Interface
56:                   pErrorInfoRecord->Release();
57:                 };
58:              } else {
59:                fprintf(stderr,"Can't retrieve the number
                                of error records!\n");
60:                 retcode = E_FAIL;
61:              };
62:              pErrorRecords->Release();
63:            } else {
64:              fprintf(stderr,"Can't retrieve the ErrorRecords
                              interface\n");
65:              retcode = E_FAIL;
66:            };
67:            pErrorInfo->Release();
68:         } else {
69:           fprintf(stderr,"Can't retrieve the ErrorInfo
                           interface.\n");
70:            retcode = E_FAIL;
71:         };
72:         pSupportErrorInfo->Release();
73:      } else {
74:        fprintf(stderr,"Extended Error Information Unavailable!\n");
75:        retcode = E_FAIL;
76:      };
77:    } else {
78:      fprintf(stderr,
79:                "Could Not Obtain Access To
                         The ISupportErrorInfo Interface\n");
80:      fprintf(stderr,"Additional Error Information Unavailable!\n");
81:       retcode = E_FAIL;
82:    };
83:    return(retcode);
84:  };

How to Integrate the DispErrorInfo Procedure

When you call an OLE DB method, you should check the HRESULT return value, as I mentioned at the beginning of the day. If the method fails, you can call the DispErrorInfo procedure to display any additional error information that's available. Listing 21.4 demonstrates how to integrate the DispErrorInfo method into an OLE DB method call.


Listing 21.4  How to Integrate the DISPERRORINFO Procedure into an OLE DB Method Call

 1:    if(FAILED(pCommandText->Execute(NULL, IID_Rowset, NULL, &cNumRows,
 2:                                       (IUnknown **) &pRowset))) {
 3:         // The Execute method Failed! Display Error!
 4:         DispErrorInfo(pCommandText,IID_ICommandText);
 5:         // Exit and call free pCommandText and CoUninitialize?...
 6:         exit(0);
 7:     };

Error-Handling Considerations

So far, you have learned about Automation and OLE DB error-handling interfaces, created a procedure to assist in the interpretation of errors that occurred, and integrated the error-handling interfaces into your applications. Here is a review of the basic steps in error handling:

  1. Check the HRESULT value of a method when it is called.
  2. If the method didn't succeed, possibly print some error information.
  3. If the error is critical to the execution of the application, gracefully end the application. Be sure to release any allocated memory and close any open data sources and files.
  4. If the error isn't critical (perhaps the data provider doesn't support a specific method), dynamically change the functionality of the application. For example, an application feature might not be available with lower-level data providers.

Returning Error Objects

Now that you know what a data consumer expects from a data provider, the data provider implementation requirements should be easy to understand. The final part of today's discussion of error handling explains how to implement error handling from the data provider side, including how a multithreaded environment affects data provider error handling.

Listing 21.5 demonstrates how an error is created and returned by a data provider method. The following survey of the basic steps involved in returning an error from a provider will help you understand that listing:

  1. Call the SetErrorInfo method to clear out any current errors.
  2. If the current operation fails, begin by using the GetErrorInfo method to access the IErrorInfo interface. If the interface isn't available, use the CreateInstance method of the IClassFactory interface to create a new IErrorInfo interface.
  3. Fill in the ERRORINFO structure with the appropriate information.
  4. Obtain access to the IErrrorRecords interface and use the AddErrorRecord method to add the newly created error record.
  5. Call the SetErrorInfo method to pass the error to the Automation interface, signaling an error occurred.

The SetErrorInfo method sets the current ErrorInfo object, and the GetErrorInfo method retrieves the current ErrorInfo object.


Listing 21.5  How an OLE DB Data Provider Returns an Error

 1:       IErrorInfo           pErrorInfoObj = NULL;
 2:       IErrorRecords        pErrorRecs;
 3:       ERRORINFO            ErrorInf;
 4:
 5:       // Clear any current errors
 6:       SetErrorInfo(0, NULL);
 7:
 8:       // Perform whatever is necessary to implement the method
 9:       // Save error result in hrError and set Error flag
10:
11:       // Check if an Error Occurred?
12:       if(Error) {
13:         if(FAILED(GetErrorInfo(0, &pErrorInfoObj)) ||
                 (!pErrorInfoObj)) {
14:           pClassFactoryObject->CreateInstance(NULL,
15:                CLSID_EXTENDEDERRORINFO, (void *) &pErrorInfoObj);
16:         };
17:         if (pErrorInfoObj) {
18:           // Create the ERRORINFO record
19:           pErrorInfoObj->QueryInterface(IID_IErrorRecords,
                                                 (void *) &pErrorRecs);
20:           ErrorInf.hrError = hrError;         // Set error result
21:           ErrorInf.dwMinor = INTERNAL_ERROR;  // Set Internal Error
                                                  // Number
22:           ErrorInf.clsid = CURRENT_CLSID;     // Set the Current CLSID
23:           ErrorInf.iid = IID_CURRENT;         // Set current IID
24:           ErrorInf.dispid = DISPID_CURRENT;   // Set current DISPID
25:
26:           // Add the Error Record
27:           pErrorRecs->AddErrorRecord(&ErrorInf,  ErrorInf.dwMinor,
28:                                      NULL, NULL, 0);
29:
30:           // Set the Error
31:           SetErrorInfo(0,pErrorInfo);
32:
33:           // Release Error Interfaces
34:           pErrorRecs->Release();
35:           pErrorInfo->Release();
36:           pErrorInfoObj->Release();
37:        }
38:     };

Threads

When operating in a multithreaded application, an error message generated by a previously called method might be sitting in the message queue when another message is called. (Recall that in the OLE DB environment, each application thread has only one error object.) To clear out the error message queue, the OLE data provider must call the SetErrorInfo method when a method is called. This step ensures that only the most current errors are in the queue on return.

A Summary of OLE DB HRESULT Error Codes

The first thing you did today was to check the HRESULT of a method to determine whether it succeeded. Table 21.1 lists the HRESULT values specifically defined by OLE DB. Refer also to the Visual C++ documentation for information about generic HRESULT constants.

Table 21.1  The HRESULT Values Defined by OLE DB
HRESULT Constant
Description
 Warning Results
DB_S_BADROWHANDLEA row handle is invalid.
DB_S_BOOKMARKSKIPPEDA bookmark was skipped for a row that was deleted or filtered.
DB_S_BUFFERFULLThe buffer that holds fields or parameters is full.
DB_S_CANTRELEASEA lock could not be released until the transaction is complete.
DB_S_COLUMNSCHANGEDWhile re-executing a query during a cursor reposition operation, the order or number of columns changed.
DB_S_COLUMNTYPEMISMATCHSome columns cannot be converted during the copy operation because of type-conversion incompatibilities.
DB_S_COMMANDREEXECUTEDThe command was re-executed.
DB_S_DELETEDROWA row handle refers to a previously deleted row.
DB_S_DIALECTIGNOREDThe language of the command was ignored; translated command returned.
DB_S_ENDOFROWSETThe end of the current row set was encountered.
DB_S_ERRORSINTREEAn error occurred in the validation tree.
DB_S_ERRORSOCCURREDAn unspecified error occurred.
DB_S_ERRORSRETURNEDErrors were encountered and returned.
DB_S_GOALCHANGEDThe current goal was changed to an unsupported value.
DB_S_LOCKUPGRADEDA record lock was upgraded.
DB_S_MULTIPLECHANGESThe requested change affects multiple data source rows.
DB_S_NONEXTROWSETEnd of multiple return row sets reached.
DB_S_NORESULTEnd of results reached.
DB_S_PARAMUNAVAILABLEThe specified parameter is invalid.
DB_S_PROPERTIESCHANGEDThe properties of the object were changed successfully.
 Warning Results
DB_S_ROWLIMITEXCEEDEDThe number of requested rows is greater than the number of active rows that the Rowset object supports.
DB_S_STOPLIMITREACHEDExecution of a command was halted because of a resource limit; the results returned are incomplete.
DB_S_TOOMANYCHANGESToo many changes were encountered; data must be refreshed.
DB_S_TYPEINFOOVERRIDDENThe type of the parameter was overridden.
DB_S_UNWANTEDPHASENotifications for this phase are no longer desired.
DB_S_UNWANTEDREASONNotifications for this phase are no longer desired for a specific reason.
 Error Results
DB_E_ABORTLIMITREACHEDThe command was aborted because of resource limitations; no results returned.
DB_E_ALREADYINITIALIZEDThe data source was previously initialized.
DB_E_BADACCESSORFLAGSThe Accessor flag is not valid.
DB_E_BADACCESSORHANDLEThe Accessor handle is not valid.
DB_E_BADACCESSORTYPEThe Accessor specified is invalid.
DB_E_BADBINDINFOThe binding information is not valid.
DB_E_BADBOOKMARK_The bookmark is not valid.
DB_E_BADCHAPTERThe chapter specified is not valid.
DB_E_BADCOLUMNID_The column ID specified is not valid.
DB_E_BADCONVERTFLAG_The conversion flag specified is not valid.
DB_E_BADCOPYAn error was encountered while copying.
DB_E_BADDYNAMICERRORID_The DynamicError ID specified is not valid.
DB_E_BADHRESULT_The HRESULT value specified is not valid.
DB_E_BADID_The table ID value specified is not valid.
DB_E_BADLOCKMODE_The lock mode specified is not valid.
DB_E_BADLOOKUPID_The lookup ID specified is not valid.
DB_E_BADORDINAL_The column specified is not valid.
DB_E_BADPARAMETERNAME_The specified parameter name is not valid.
DB_E_BADPRECISION_The precision value specified is not valid.
DB_E_BADPROPERTYVALUE_The property value is not valid.
 Error Results
DB_E_BADRATIO_The ratio specified is not valid (greater than 1 or undefined).
DB_E_BADRECORDNUM_The record number specified is not valid.
DB_E_BADREGIONHANDLE_The region handle specified is not valid.
DB_E_BADROWHANDLE_The row handle is not valid.
DB_E_BADSCALE_The scale value specified is not valid.
DB_E_BADSOURCEHANDLE_The source handle specified is not valid.
DB_E_BADSTARTPOSITION_The offset position specified is past the end or before the beginning of the row set; rows not retrieved.
DB_E_BADSTATUSVALUE_The status flag specified is not valid.
DB_E_BADSTORAGEFLAG_The storage flag specified is not valid.
DB_E_BADSTORAGEFLAGS_The storage flags specified are not valid._
DB_E_BADTYPE_The type specified is not valid.
DB_E_BADTYPENAME_The type name specified is not valid.
DB_E_BADVALUES_The value specified is not valid.
DB_E_BOOKMARKSKIPPED_No row was found that matched the bookmark.
DB_E_BYREFACCESSORNOTSUPPORTED_ Cannot pass Accessor by reference for this data provider.
DB_E_CANCELED_The command was canceled; changes not saved.
DB_E_CANNOTFREE_Cannot deallocate this memory.
DB_E_CANNOTRESTART_Cannot restart the new row set.
DB_E_CANTCANCEL_Cannot stop the current command.
DB_E_CANTCONVERTVALUE_Cannot convert the specified value correctly.
DB_E_CANTFETCHBACKWARDS_Cannot retrieve the row set rows backwards.
DB_E_CANTSCROLLBACKWARDS_The row set cannot scroll backwards.
DB_E_CANTTRANSLATE_Cannot translate the current command tree.
DB_E_CHAPTERNOTRELEASED_The chapter was not released.
DB_E_CONCURRENCYVIOLATION_A concurrency violation was encountered.
DB_E_COSTLIMIT_When attempting to optimize the query, the cost constraints could not be met.
DB_E_DATAOVERFLOW_A command value caused an overflow.
DB_E_DELETEDROW_The row handle points to a deleted row.
 Error Results
DB_E_DIALECTNOTSUPPORTED_The language of the command is not supported.
DB_E_DUPLICATECOLUMNID_A column was duplicated.
DB_E_DUPLICATEDATASOURCE_The data source name is already in use.
DB_E_DUPLICATEINDEXID_The index specified is already in use.
DB_E_DUPLICATETABLEID_The table specified already exists.
DB_E_ERRORSINCOMMAND_Errors were encountered in the command.
DB_E_ERRORSOCCURRED_An unspecified error occurred.
DB_E_GOALREJECTED_The goal specified was not valid; current goal unchanged.
DB_E_INDEXINUSE_The index specified is already opened.
DB_E_INTEGRITYVIOLATION_A column value violated integrity constraints for that object.
DB_E_INVALID_Cannot use bookmarks on this row set.
DB_E_INVALIDTRANSITION_The transition specified is not valid.
DB_E_LIMITREJECTED_Cost limits specified were not valid.
DB_E_MAXPENDCHANGESEXCEEDED_The maximum number of pending changes has been exceeded.
DB_E_MULTIPLESTATEMENTS_Multiple statement commands are not supported.
DB_E_MULTIPLESTORAGE_More than one storage object opened concurrently.
DB_E_NEWLYINSERTED_Cannot establish the identity of the new rows.
DB_E_NOAGGREGATION_This object does not support aggregation.
DB_E_NOCOMMAND_The command has not been specified.
DB_E_NOINDEX_The specified index is not valid.
DB_E_NOLOCALE_The locale ID specified is not valid.
DB_E_NONCONTIGUOUSRANGE_The set of rows specified are not contiguous.
DB_E_NOQUERY_The query was not defined.
DB_E_NOTABLE_The table specified does not exist.
DB_E_NOTAREFERENCECOLUMN_The column does not contain a bookmark or chapter identifier.
DB_E_NOTASUBREGION_The region specified is not a valid subregion.
DB_E_NOTFOUND_Cannot find the specified key value.
DB_E_NOTPREPARED_Cannot prepare the specified command.
DB_E_NOTREENTRANT_A method was called while another was still executing.
 Error Results
DB_E_NOTSUPPORTED_The method specified is not supported.
DB_E_NULLACCESSORNOTSUPPORTED_ Cannot pass NULL value Accessor to this data provider.
DB_E_OBJECTOPEN_Operation performed on an object that was not opened.
DB_E_PARAMNOTOPTIONAL_A value was not specified for a required parameter.
DB_E_PARAMUNAVAILABLE_The parameter specified is not available.
DB_E_PENDINGCHANGES_Changes are pending on an unreferenced row.
DB_E_PENDINGINSERT_Cannot perform the pending insert.
DB_E_READONLYACCESSOR_Accessor is read-only; write invalid.
DB_E_ROWLIMITEXCEEDED_Adding this row exceeds the number of active rows for this row set.
DB_E_ROWSETINCOMMAND_Cannot copy a command that contains row sets.
DB_E_ROWSNOTRELEASED_The current row handles were not released before retrieving new rows.
DB_E_SCHEMAVIOLATION_The values supplied are inconsistent with the data source schema.
DB_E_TABLEINUSE_The table specified is already opened.
DB_E_UNSUPPORTEDCONVERSION_Cannot perform the requested type conversion.
DB_E_WRITEONLYACCESSOR_The specified Accessor can only be written.
DB_SEC_E_AUTH_FAILED_Security error; data source access authorization was not successful.
DB_SEC_E_PERMISSIONDENIED Permission denied because of a security violation.

Summary

Day 21 explains how to integrate error handling into OLE DB applications. You learned how to implement basic error-handling techniques and how to check the HRESULT of a method with the SUCCEEDED and FAILED macros to determine whether the method executed correctly. Then you learned how to use the ISupportErrorInfo interface to determine whether an OLE DB object supports extended error information.

You used the IErrorRecords and ISQLErrorInfo methods to create the DispErrorInfo procedure, which can display extended error information for any OLE DB object.

Q&A

This section answers some common questions related to today's topics.
Q
Should my application look for and try to interpret specific HRESULT values?
A
If you look at the OLE DB error information, you will see that as each method is defined, the most common HRESULT values that the method can return are listed. A useful exercise is to try to interpret certain warning result values. You can be assured only that an OLE DB provider supports the methods and interfaces that are required. If you attempt to access an optional interface, you will need to be sure that your application can still function if the data provider doesn't support that interface.
Q
Is there any way other than checking the HRESULT value of each method call to add error handling to my OLE DB application?
A
Unfortunately, no. To ensure the highest degree of error checking in your applications, you must check each method's HRESULT value. You must realize that one failed method can cause a cascading error effect. Again, the best technique is to design your applications to be robust so that they offer only the functionality provided by the OLE DB data provider. A robust application is one that can handle all error conditions without crashing for the end user. As an application developer, you are responsible for handling all errors that your application might generate.

Workshop

The Workshop quiz questions test your understanding of today's material. (The answers appear in Appendix F, "Answers.") The exercises encourage you to apply the information you learned today to real-life situations.

Quiz

  1. Name the two macros used to check the result of an OLE DB method call.
  2. Which interface checks whether an OLE DB object supports extended error information?
  3. What are the special requirements of OLE DB error handling that aren't resolved by Automation error-handling objects?
  4. What information does the GetBasicErrorInfo method return? Describe the elements of this structure.
  5. How do you retrieve a custom error object? What custom error objects does OLE DB provide?
  6. List the basic techniques for OLE DB error handling.
  7. Which method does a data provider use to add a new error?
  8. Explain the difference between HRESULT constants with the prefix DB_S and those with the prefix DB_E.

Exercises

  1. Review the generic HRESULT return values. They can be found in the Visual C++ documentation.
  2. A sample called DECODE is available in the Microsoft Software Library. The DECODE application enables you to enter an HRESULT in a dialog and have it decoded to its text description.
  3. Integrate the DispErrorInfo procedure and error checking into one of the data consumer examples created in an earlier lesson.
  4. Listing 21.4 shows how to call the DispErrorInfo procedure from within another program.

© Copyright, Sams Publishing. All rights reserved.