How a Provider Returns an OLE DB Error Object

When an error occurs in a provider, the provider can return an OLE DB error object to describe that error. To do this, it makes the following calls:

  1. Calls GetErrorInfo in the Automation DLL to obtain ownership of the error object on the current thread and to retrieve an IErrorInfo interface pointer on it. Such an error object will exist only if added by a lower level provider. Thus, a provider at the bottom of a chain of providers will not find an error object, while a provider in the middle (a service component) might find such an object. In the latter case, the service component is merely gaining temporary ownership of the error object before returning it to the Automation DLL.

  2. Creates an OLE DB error object if none was retrieved in step 1. To do this, the provider calls CoCreateInstance with the CLSID_EXTENDEDERRORINFO class ID or IClassFactory::CreateInstance on a class factory object created earlier for this class. The latter method is faster and therefore preferred if the provider frequently creates error objects.

  3. Calls IErrorRecords::AddErrorRecord to add one or more error records to the error object. To AddErrorRecord it passes an ERRORINFO structure, the lookup ID, the error parameters, if any, an interface pointer on the custom error object that further describes the error, and a dynamic error ID.

  4. Calls QueryInterface to retrieve an IErrorInfo interface pointer on the error object. This interface pointer identifies the error object to all Automation components.

  5. Calls the function SetErrorInfo in the Automation DLL and passes it the IErrorInfo interface pointer. SetErrorInfo replaces the current error object on the thread, if any, with the new error object and adds a reference count to hold the new error object.

  6. Calls Release to release its reference count on the error object. This transfers ownership of the error object from the provider to the Automation DLL.

The following code shows an example of how a provider might create an OLE DB error object in response to being unable to open a table and transfer ownership of the object to the Automation DLL:

#include <oledb.h>

IClassFactory   *m_pErrorObjectFactory ;
IParseDisplayName *pppwow;
DWORD      errCantOpenTable;
IOpenRowset   *pSimpleProvider;
DBPROPSET    rgPropertySets[1];
IRowset     *pRowset;
DBID     TableID;
extern GUID    CLSID_THISISAM;
extern DISPID  DISPID_OpenRowset;
BSTR     bstrTableName;

int main() {
 ERRORINFO  ErrorInfo;
 HRESULT    hr, hrErr;
 IErrorInfo  *pErrorInfo;
 IErrorRecords *pErrorRecords;

 // Clear the current error object.
 SetErrorInfo(0, NULL);

 // Try to open the table or call another provider to do it.
 hrErr = pSimpleProvider->OpenRowset(NULL, &TableID, NULL, IID_IRowset, 1,
             rgPropertySets, (IUnknown**) &pRowset);

 if (!FAILED(hrErr))
  return hrErr;

 // An error or warning occurred while opening the table. 
 //Get the current error object. If one does not exist, create a new one.

 GetErrorInfo(0, &pErrorInfo);

 if (!pErrorInfo)
  hr = m_pErrorObjectFactory->CreateInstance(NULL, CLSID_EXTENDEDERRORINFO,
                 (void**) &pErrorInfo);

 // Get an IErrorRecords interface pointer on the error object.
 hr = pErrorInfo->QueryInterface(IID_IErrorRecords, (void**) &pErrorRecords);

 // Set up a parameter to pass to the object.
 VARIANTARG  varg;
 VariantInit (&varg);
 DISPPARAMS  dispparams = {&varg, NULL, 1, 0};

 varg.vt = VT_BSTR;
 varg.bstrVal = SysAllocString(bstrTableName);

 // Fill in the ERRORINFO structure and add the error record.

 ErrorInfo.hrError = hrErr;
 ErrorInfo.dwMinor = errCantOpenTable;
 ErrorInfo.clsid = CLSID_THISISAM;
 ErrorInfo.iid   = IID_IOpenRowset;
 ErrorInfo.dispid  = DISPID_OpenRowset;
 hr = pErrorRecords->AddErrorRecord(&ErrorInfo,ErrorInfo.dwMinor,&dispparams,NULL,0);

 VariantClear(&varg);

 // Call SetErrorInfo to pass the error object to the Automation DLL.
 hr = SetErrorInfo(0, pErrorInfo);

 // Release the interface pointers on the object to finish transferring ownership of
 // the object to the Automation DLL.

 pErrorRecords->Release();
 pErrorInfo->Release();
 return hr;
};

The following code example shows how a provider adds a record to an OLE DB error object that includes a pointer to a custom error object:

#include <oledb.h>

class CSQLStateObject:public ISQLErrorInfo {
 public:
  CSQLStateObject(OLECHAR );
  CSQLStateObject();
  HRESULT __stdcall QueryInterface(REFIID, void** );
  ULONG __stdcall AddRef(void);
  ULONG __stdcall Release(void);
  HRESULT __stdcall GetSQLInfo( 
   /* [out] */ BSTR __RPC_FAR *pbstrSQLState,
   /* [out] */ LONG __RPC_FAR *plNativeError);
};

IClassFactory *g_pErrorObjectFactory ;
extern DISPID  DISPID_GetData;
extern GUID  CLSID_THISISAM;
DWORD    errGeneralError;

int main() {
 ERRORINFO  ErrorInfo;
 HRESULT    hr, hrErr;
 IErrorInfo  *pErrorInfo;
 IErrorRecords *pErrorRecords;

 // Clear the current error object.
 SetErrorInfo(0, NULL);

 // Do something that causes an error to occur. (Not shown.)

 if (!FAILED(hrErr))
  return hrErr;

 // An error or warning occurred. Get the current error object. If one    //does not exist, create one.

 GetErrorInfo(0, &pErrorInfo);
 if (!pErrorInfo)
  hr = g_pErrorObjectFactory->CreateInstance(NULL, CLSID_EXTENDEDERRORINFO,
                 (void**) &pErrorInfo);

 // Get an IErrorRecords interface pointer on the error object.
 hr = pErrorInfo->QueryInterface(IID_IErrorRecords, (void**) &pErrorRecords);

 // Create a custom error object.
 CSQLStateObject* pMyErrObj = new CSQLStateObject(OLESTR("HY000"));

 // Fill in the ERRORINFO structure and add the error record.
 ErrorInfo.hrError = hrErr;
 ErrorInfo.dwMinor = errGeneralError;
 ErrorInfo.clsid = CLSID_THISISAM;
 ErrorInfo.iid   = IID_IRowset;
 ErrorInfo.dispid  = DISPID_GetData;
 hr = pErrorRecords->AddErrorRecord(&ErrorInfo, ErrorInfo.dwMinor, NULL,
              (void**) pMyErrObj, 0);

 // Release the interface pointer on the custom error object to finish transferring
 // ownership of it to the error object.
 pMyErrObj->Release();

 // Call SetErrorInfo to pass the error object to the Automation DLL.
 hr = SetErrorInfo(0, pErrorInfo);

 // Release the interface pointers on the object to finish transferring ownership of
 // the object to the Automation DLL.
 pErrorRecords->Release();
 pErrorInfo->Release();
 return hr;
};