Share via


Creating and Using Accessors

Consumers describe the memory structure for their buffers through a process called binding. An accessor is a group of bindings. The sample provider supports a very basic type of accessor and cannot support reference accessors, which allow the consumer direct access to the rowset's data cache.

Accessors are implemented through IAccessor. You can create accessors with IAccessor::CreateAccessor and release accessors with IAccessor::ReleaseAccessor. You can use IAccessor::GetBindings to determine the bindings in an existing accessor. IAccessor::AddRefAccessor enables the consumer to add a reference count to an existing accessor. The following sections describe these methods.

Determining Supported Conversions

Before the consumer creates an accessor, it can call IConvertType::CanConvert to determine if the provider supports a particular conversion.

The source code for IConvertType::CanConvert follows; you can find the complete source code for IConvertType in CvtType.cpp.

//----------------------------------------------------------------------------
// CImpIConvertType::CanConvert
//
// @mfunc Used by consumer to determine provider support for a conversion
//
// @rdesc HRESULT indicating the status of the method
//      @flag S_OK | Conversion supported
//      @flag S_FALSE | Conversion unsupported
//      @flag DB_E_BADCONVERTFLAG | dwConvertFlags was invalid
//      @flag DB_E_BADCONVERTFLAG | called on rowset for DBCONVERTFLAG_PARAMETER
//      @flag OTHER | HRESULTS returned from support functions
//
STDMETHODIMP CImpIConvertType::CanConvert
(
   DBTYPE         wFromType,      //@parm IN | src type
   DBTYPE         wToType,      //@parm IN | dst type
   DBCONVERTFLAGS   dwConvertFlags   //@parm IN | conversion flags
)
{
   //
   // We only support conversions on Rowsets, we only allow DBCONVERTFLAGS_COLUMN
   //
   if( (dwConvertFlags & ~(DBCONVERTFLAGS_ISLONG | 
                     DBCONVERTFLAGS_ISFIXEDLENGTH | 
                     DBCONVERTFLAGS_FROMVARIANT)) != DBCONVERTFLAGS_COLUMN )
      return (DB_E_BADCONVERTFLAG);

   //
   // Make sure that we check that the type is a variant if they say so
   //
   if( dwConvertFlags & DBCONVERTFLAGS_FROMVARIANT )
   {
      DBTYPE   wVtType = wFromType & VT_TYPEMASK;

      //
      // Take out all of the Valid VT_TYPES (36 is VT_RECORD in VC 6)
      //
      if( (wVtType > VT_DECIMAL && wVtType < VT_I1) ||
         ((wVtType > VT_LPWSTR && wVtType < VT_FILETIME) && wVtType != 36) ||
         (wVtType > VT_CLSID) )
         return (DB_E_BADTYPE);
   }

   //
   // Don't allow _ISLONG on fixed-length types
   //
   if( dwConvertFlags & DBCONVERTFLAGS_ISLONG )
      switch( wFromType & ~(DBTYPE_RESERVED|DBTYPE_VECTOR|DBTYPE_ARRAY|DBTYPE_BYREF) )
      {
      case DBTYPE_BYTES:
      case DBTYPE_STR:
      case DBTYPE_WSTR:
      case DBTYPE_VARNUMERIC:
         break;

      default:
         return (DB_E_BADCONVERTFLAG);
      }
   
   //
   // If the DC is not there, try to create it again
   if( !g_pIDataConvert )
   {
      CoCreateInstance(CLSID_OLEDB_CONVERSIONLIBRARY,
                        NULL,
                        CLSCTX_INPROC_SERVER,
                        IID_IDataConvert,
                        (void**)&g_pIDataConvert);

      //
      // Tell the DC that we are 2.5
      //
      if( g_pIDataConvert )
      {
         DCINFO rgInfo[] = {{DCINFOTYPE_VERSION,{VT_UI4, 0, 0, 0, 0x0}}};
         IDCInfo   *pIDCInfo = NULL;

         if( g_pIDataConvert->QueryInterface(IID_IDCInfo, (void **)&pIDCInfo) == S_OK && 
            pIDCInfo )
         {
            // OLE DB Version 02.50
            V_UI4(&rgInfo->vData) = 0x250;
            pIDCInfo->SetInfo(NUMELEM(rgInfo),rgInfo);
            pIDCInfo->Release();
         }
      }
      else
         return (S_FALSE);
   }

   //
   // Ask the conversion library for the answer
   //
   return (g_pIDataConvert->CanConvert(wFromType, wToType));
}

Creating an Accessor

CreateAccessor associates a set of bindings with an accessor handle that is used to send data to or fetch data from the rowset's data cache. The sample provider supports only the DBACCESSOR_ROWDATA accessor flag, which specifies that the accessor is to be used for rowset data.

The source code for IAccessor::CreateAccessor follows; you can find the complete source code for IAccessor in Accessor.cpp.

// CImpIAccessor::CreateAccessor -----------------------------------------
//
// @mfunc Creates a set of bindings that can be used to send data
// to or retrieve data from the data cache.
//
// @rdesc HRESULT
//      @flag S_OK                      | Method Succeeded
//      @flag E_FAIL                    | Provider specific Error
//      @flag E_INVALIDARG              | pHAccessor was NULL, dwAccessorFlags was
//                                        invalid, or cBindings was not 0 and
//                                        rgBindings was NULL
//      @flag E_OUTOFMEMORY             | Out of Memory
//      @flag DB_E_ERRORSOCCURRED      | dwBindPart in an rgBindings element was invalid, OR
//                               | Column number specified was out of range, OR
//                              | Requested coercion is not supported.
//      @flag OTHER                     | Other HRESULTs returned by called functions
//
STDMETHODIMP CImpIAccessor::CreateAccessor
    (
    DBACCESSORFLAGS dwAccessorFlags,
    DBCOUNTITEM     cBindings,      //@parm IN | Number of Bindings
    const DBBINDING rgBindings[],   //@parm IN | Array of DBBINDINGS
    DBLENGTH        cbRowSize,      //@parm IN | Number of bytes in consumer's buffer
    HACCESSOR*      phAccessor,     //@parm OUT | Accessor Handle
   DBBINDSTATUS   rgStatus[]      //@parm OUT   | Binding status
    )
{
    PACCESSOR   pAccessor;
    HACCESSOR   hAccessor;
    DBCOUNTITEM cBind;
    DBORDINAL   cCols;
    HRESULT     hr;


    // Check Parameters
    if( (cBindings && !rgBindings) || (phAccessor == NULL) )
        return ResultFromScode( E_INVALIDARG );

    // init out params
    *phAccessor = NULL;

   // Check if we have a correct accessor type
    if ( dwAccessorFlags & DBACCESSOR_PASSBYREF )
        return ResultFromScode( DB_E_BYREFACCESSORNOTSUPPORTED );

   // Only allow DBACCESSOR_ROWDATA and DBACCESSOR_OPTIMIZED
    if ( (dwAccessorFlags & ~DBACCESSOR_OPTIMIZED ) != DBACCESSOR_ROWDATA )
        return ResultFromScode( DB_E_BADACCESSORFLAGS );

   // Check for NULL Accessor on the Command Object
   // Also check for NULL Accessor on a read only rowset
   if( (m_pObj->GetBaseObjectType() == BOT_COMMAND && !cBindings) ||
      (m_pObj->GetBaseObjectType() == BOT_ROWSET  && !cBindings && !((CRowset *)m_pObj)->SupportIRowsetChange()) )
      return ResultFromScode( DB_E_NULLACCESSORNOTSUPPORTED );

   // Check for Optimized Accessor on the Rowset Object after Fetch
   if( (dwAccessorFlags & DBACCESSOR_OPTIMIZED) && 
      m_pObj->GetBaseObjectType() == BOT_ROWSET && ((CRowset *)m_pObj)->m_cRows )
      return ResultFromScode( DB_E_BADACCESSORFLAGS );

   // Initialize the status array to DBBINDSTATUS_OK.
   // 64 bit TODO
   if ( rgStatus )
      memset(rgStatus, 0x00, (ULONG)(cBindings * sizeof(DBBINDSTATUS)));

    // Check on the bindings the user gave us.
    for (cBind=0, hr=NOERROR; cBind < cBindings; cBind++)
    {
      // other binding problems forbidden by OLE-DB
        const DBTYPE currType = rgBindings[cBind].wType;
        const DBTYPE currTypePtr = currType &
                       (DBTYPE_BYREF|DBTYPE_ARRAY|DBTYPE_VECTOR);
      const DBTYPE currTypeBase = currType & 
                     ~(DBTYPE_BYREF|DBTYPE_ARRAY|DBTYPE_VECTOR);
        const DWORD  currFlags = rgBindings[cBind].dwFlags;

        cCols = rgBindings[cBind].iOrdinal;

      // Check for a Bad Ordinal
      if( m_pObj->GetBaseObjectType() == BOT_ROWSET )
      {
         // make sure column number is in range
         if ( !(0 < cCols && cCols <= ((CRowset *) m_pObj)->m_cCols) )
         {
            // Set Bind status to DBBINDSTATUS_BADORDINAL
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
            if ( rgStatus )
               rgStatus[cBind] = DBBINDSTATUS_BADORDINAL;
            continue;
         }
      }

      // At least one of these valid parts has to be set. In SetData I assume it is the case.
        if ( !(rgBindings[cBind].dwPart & (DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS)) )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }

      // dwPart is something other than value, length, or status
      else if ( (rgBindings[cBind].dwPart & ~(DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS)) )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }

      // wType was DBTYPE_EMPTY or DBTYPE_NULL
      else if ( (currType==DBTYPE_EMPTY || currType==DBTYPE_NULL) )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }

      // wType was DBTYPE_BYREF or'ed with DBTYPE_EMPTY, NULL, or RESERVED
      else if ( ((currType & DBTYPE_BYREF) && 
           (currTypeBase == DBTYPE_EMPTY || currTypeBase == DBTYPE_NULL || 
            currType & DBTYPE_RESERVED)) )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }

      // dwFlags was DBBINDFLAG_HTML and the type was not a String
      else if ( currFlags && (currFlags != DBBINDFLAG_HTML || 
             (currTypeBase != DBTYPE_STR  &&
              currTypeBase != DBTYPE_WSTR &&
              currTypeBase != DBTYPE_BSTR)) )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }

      // wType was used with more than one type indicators
      else if ( !(currTypePtr == 0 || currTypePtr == DBTYPE_BYREF ||
            currTypePtr == DBTYPE_ARRAY || currTypePtr == DBTYPE_VECTOR) )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }

      // wType was a non pointer type with provider owned memory
      else if ( !currTypePtr && 
          rgBindings[cBind].dwMemOwner==DBMEMOWNER_PROVIDEROWNED )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }

      // we only support client owned memory
      else if ( rgBindings[cBind].dwMemOwner != DBMEMOWNER_CLIENTOWNED )
      {
         // Set Bind status to DBBINDSTATUS_BADBINDINFO
            hr = ResultFromScode( DB_E_ERRORSOCCURRED );
         if ( rgStatus )
            rgStatus[cBind] = DBBINDSTATUS_BADBINDINFO;
        }
    }

    // Any errors amongst those checks?
    if (hr != NOERROR)
    {
        return hr;
    }

    // Make a copy of the client's binding array, and the type of binding.
   // 64 bit TODO
    pAccessor = (ACCESSOR *) new BYTE[sizeof( ACCESSOR ) + ((ULONG)cBindings - 1) *sizeof( DBBINDING )];
    if ( pAccessor == NULL )
        return ResultFromScode( E_OUTOFMEMORY );

    // We store a ptr to the newly created variable-sized ACCESSOR.
    // We have an array of ptrs (to ACCESSOR's).
    // The handle is the index into the array of ptrs.
    // The InsertIntoExtBuffer function appends to the end of the array.
    assert( m_pextbufferAccessor );
    hr = m_pextbufferAccessor->InsertIntoExtBuffer(&pAccessor, hAccessor);
    if ( FAILED( hr ) )
   {
        SAFE_DELETE( pAccessor );
        return ResultFromScode( E_OUTOFMEMORY );
   }
    assert( hAccessor );

    // Copy the client's bindings into the ACCESSOR.
    pAccessor->dwAccessorFlags   = dwAccessorFlags;
    pAccessor->cBindings      = cBindings;
    pAccessor->cRef            = 1;      // Establish Reference count.
   // 64 bit TODO
   memcpy( &(pAccessor->rgBindings[0]), &rgBindings[0], (ULONG)cBindings*sizeof( DBBINDING ));

    // fill out-param and return
    *phAccessor = hAccessor;
    return ResultFromScode( S_OK );
}

Returning Accessor Bindings

GetBindings returns the bindings in an existing accessor. The source code for IAccessor::GetBindings follows; you can find the complete source code for IAccessor in Accessor.cpp.

// CImpIAccessor::GetBindings --------------------------------------------------
//
// @mfunc Returns the bindings in an accessor
//
// @rdesc HRESULT
//      @flag S_OK                      | Method Succeeded
//      @flag E_INVALIDARG              | pdwAccessorFlags/pcBinding/prgBinding were NULL
//      @flag E_OUTOFMEMORY             | Out of Memory
//      @flag DB_E_BADACCESSORHANDLE    | Invalid Accessor given
//
STDMETHODIMP CImpIAccessor::GetBindings
    (
    HACCESSOR        hAccessor,         //@parm IN | Accessor Handle
    DBACCESSORFLAGS* pdwAccessorFlags,  //@parm OUT | Binding Type flag
    DBCOUNTITEM*     pcBindings,        //@parm OUT | Number of Bindings returned
    DBBINDING**      prgBindings        //@parm OUT | Bindings
    )
{
    // Retrieve our accessor structure from the client's hAccessor,
    // make a copy of the bindings for the user, then done.
    PACCESSOR   pAccessor;
    ULONG       cBindingSize;

    // init out-params
   if(   pdwAccessorFlags )
      *pdwAccessorFlags = 0;
   if( pcBindings )
      *pcBindings = 0;
   if ( prgBindings )
      *prgBindings = NULL;

    // check parameters
    if (!pdwAccessorFlags || !pcBindings || !prgBindings)
        return ResultFromScode( E_INVALIDARG );

    // Validate Accessor Handle
    m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
    if( !pAccessor )
        return DB_E_BADACCESSORHANDLE;

    // Allocate and return Array of bindings
   // 64 bit TODO
    cBindingSize = (ULONG)pAccessor->cBindings * sizeof( DBBINDING );
   if ( cBindingSize )
      *prgBindings = (DBBINDING *) PROVIDER_ALLOC( cBindingSize );
    
   // Check the Allocation
   if ( ( *prgBindings == NULL ) && ( cBindingSize ) )
        return ResultFromScode( E_OUTOFMEMORY );

    *pdwAccessorFlags = pAccessor->dwAccessorFlags;
    *pcBindings = pAccessor->cBindings;
    memcpy( *prgBindings, pAccessor->rgBindings, cBindingSize );

    // all went well..
    return ResultFromScode( S_OK );
}

Adding a Reference Count to an Existing Accessor

AddRefAccessor adds a reference count to an existing accessor. The source code for IAccessor::AddRefAccessor follows.

// CImpIAccessor::AddRefAccessor -----------------------------------------
//
// @mfunc Adds a reference count to an existing accessor
//
// @rdesc HRESULT
//      @flag S_OK                      | Method Succeeded
//      @flag E_FAIL                    | Provider specific Error
//
STDMETHODIMP CImpIAccessor::AddRefAccessor
    (
   HACCESSOR   hAccessor,      //@parm IN | Accessor Handle
   DBREFCOUNT*   pcRefCounts      //@parm OUT | Reference Count
    )
{
    // Retrieve our accessor structure from the client's hAccessor,
    // free it, then mark accessor ptr as unused.
    // We do not re-use accessor handles.  This way, we hope
    // to catch more client errors.  (Also, ExtBuffer doesn't
    // maintain a free list, so it doesn't know how to.)

    PACCESSOR   pAccessor;

    if( pcRefCounts )
      *pcRefCounts = 0;

   m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
    if( !pAccessor )
        return DB_E_BADACCESSORHANDLE;

   InterlockedIncrement((LONG*)&(pAccessor->cRef));

   if( pcRefCounts )
      *pcRefCounts = (DBREFCOUNT)(pAccessor->cRef);

   return ResultFromScode( S_OK );
}

Releasing an Accessor

ReleaseAccessor decrements the reference count on an accessor; when the reference count reaches zero, the accessor is released. The source code for IAccessor::ReleaseAccessor follows; you can find the complete source code for IAccessor in Accessor.cpp.

// CImpIAccessor::ReleaseAccessor ---------------------------------------
//
// @mfunc Releases an Accessor
//
// @rdesc HRESULT
//      @flag S_OK                      | Method Succeeded
//      @flag DB_E_BADACCESSORHANDLE    | hAccessor was invalid
//
STDMETHODIMP CImpIAccessor::ReleaseAccessor
    (
    HACCESSOR   hAccessor,      //@parm IN | Accessor handle to release
   DBREFCOUNT*   pcRefCounts      //@parm OUT | Reference Count
    )
{
    // Retrieve our accessor structure from the client's hAccessor,
    // free it, then mark accessor ptr as unused.
    // We do not re-use accessor handles.  This way, we hope
    // to catch more client errors.  (Also, ExtBuffer doesn't
    // maintain a free list, so it doesn't know how to.)

    PACCESSOR   pAccessor;

   if( pcRefCounts )
      *pcRefCounts = 0;

    m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
    if( !pAccessor )
        return DB_E_BADACCESSORHANDLE;

    // Free the actual structure.
   InterlockedDecrement((LONG*)&(pAccessor->cRef));
   
   if( pAccessor->cRef <= 0 )
   {
      SAFE_DELETE( pAccessor );
      if( pcRefCounts )
         *pcRefCounts = 0;

       // Store a null in our array-of-ptrs,
       // so we know next time that it is invalid.
       // (operator[] returns a ptr to the space where the ptr is stored.)
       *(PACCESSOR*) ((*m_pextbufferAccessor)[hAccessor]) = NULL;
   }
   else
   {
      if( pcRefCounts )
         *pcRefCounts = (DBREFCOUNT)(pAccessor->cRef);
   }

    return ResultFromScode( S_OK );
} 

See Also

Tasks

Writing an OLE DB Provider: An Introduction