Partager via


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 should perform the following actions:

  1. Call GetErrorInfo in the Automation DLL to obtain ownership of the error object on the current thread and request an IErrorInfo interface pointer on it. (The error object is not required to support IErrorInfo.) Such an error object will exist only if added by a lower-level provider. Therefore, a provider at the bottom of a chain of providers will not find an error object. 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. Create 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. Call IErrorRecords::AddErrorRecord to add one or more error records to the error object. To add by using AddErrorRecord, the provider 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. Call QueryInterface to retrieve an IErrorInfo interface pointer on the error object. This interface pointer identifies the error object to all Automation components.

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

  6. Call Release to release all of its references to 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 that is unable to open a table might respond by creating an OLE DB error object and transferring ownership of the object to the Automation DLL:

#include <oledb.h>
#include <msdaguid.h>

DWORD                 errCantOpenTable;
IOpenRowset *         pSimpleProvider;
IRowset *             pRowset;
DBID                  TableID;
extern GUID           CLSID_THISISAM;
extern DISPID         DISPID_OpenRowset;
BSTR                  bstrTableName;

int main() {
   IClassFactory *    pErrorObjectFactory;
   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, 0,
                                       NULL, (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)
   {
      CoGetClassObject(CLSID_EXTENDEDERRORINFO,
         CLSCTX_INPROC_SERVER,
         NULL,                              // Reserved, must be NULL
         IID_IClassFactory,
         (LPVOID *)&pErrorObjectFactory);

      hr = pErrorObjectFactory->CreateInstance(NULL, IID_IErrorInfo,
                                               (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 ofthe object to the Automation DLL.

   pErrorRecords->Release();
   pErrorInfo->Release();
   if(pErrorObjectFactory)
      pErrorObjectFactory->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>
#include <msdaguid.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);
};

extern DISPID           DISPID_GetData;
extern GUID             CLSID_THISISAM;
DWORD                   errGeneralError;

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

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

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

   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);

   CoGetClassObject(CLSID_EXTENDEDERRORINFO,
      CLSCTX_INPROC_SERVER,
      NULL,                              // Reserved, must be NULL
      IID_IClassFactory,
      (LPVOID *)&pErrorObjectFactory);

   if (!pErrorInfo)
   hr = pErrorObjectFactory->CreateInstance(NULL, IID_IErrorInfo,
                                            (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(OLECHAR("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, (IUnknown*) 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();
   pErrorObjectFactory->Release();
   return hr;
};