home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Internet 2000 May / MICD_2000_05.iso / CBuilder5 / INSTALL / DATA1.CAB / Program_Built_Files / Include / Atl / atldbcli.h < prev    next >
Encoding:
C/C++ Source or Header  |  2000-02-01  |  103.3 KB  |  3,838 lines

  1. // This is a part of the Active Template Library.
  2. // Copyright (C) 1996-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Active Template Library Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Active Template Library product.
  10.  
  11. // ATLDBCLI.H : ATL consumer code for OLEDB
  12.  
  13. #ifndef __ATLDBCLI_H_
  14. #define __ATLDBCLI_H_
  15.  
  16. #ifdef __BORLANDC__
  17. #ifdef _BCB_ATL_DEBUG
  18. #define _DEBUG
  19. #endif
  20. #endif
  21.  
  22. #ifndef __cplusplus
  23.     #error ATL requires C++ compilation (use a .cpp suffix)
  24. #endif
  25.  
  26. #ifndef _ATLBASE_H
  27. #include "atlbase.h"
  28. #endif
  29.  
  30. #ifndef __oledb_h__
  31. #include <oledb.h>
  32. #endif // __oledb_h__
  33.  
  34. #include <msdaguid.h>
  35. #include <msdasc.h>
  36.  
  37. namespace ATL
  38. {
  39.  
  40. #define DEFINE_OLEDB_TYPE_FUNCTION(ctype, oledbtype) \
  41.     inline DBTYPE _GetOleDBType(ctype&) \
  42.     { \
  43.         return oledbtype; \
  44.     }
  45.     inline DBTYPE _GetOleDBType(BYTE[])
  46.     {
  47.         return DBTYPE_BYTES;
  48.     }
  49.     inline DBTYPE _GetOleDBType(CHAR[])
  50.     {
  51.         return DBTYPE_STR;
  52.     }
  53.     inline DBTYPE _GetOleDBType(WCHAR[])
  54.     {
  55.         return DBTYPE_WSTR;
  56.     }
  57.  
  58.     DEFINE_OLEDB_TYPE_FUNCTION(signed char      ,DBTYPE_I1)
  59.     DEFINE_OLEDB_TYPE_FUNCTION(SHORT            ,DBTYPE_I2)     // DBTYPE_BOOL
  60.     DEFINE_OLEDB_TYPE_FUNCTION(int              ,DBTYPE_I4)
  61.     DEFINE_OLEDB_TYPE_FUNCTION(LONG             ,DBTYPE_I4)     // DBTYPE_ERROR (SCODE)
  62.     DEFINE_OLEDB_TYPE_FUNCTION(LARGE_INTEGER    ,DBTYPE_I8)     // DBTYPE_CY
  63.     DEFINE_OLEDB_TYPE_FUNCTION(BYTE             ,DBTYPE_UI1)
  64.     DEFINE_OLEDB_TYPE_FUNCTION(unsigned short   ,DBTYPE_UI2)
  65.     DEFINE_OLEDB_TYPE_FUNCTION(unsigned int     ,DBTYPE_UI4)
  66.     DEFINE_OLEDB_TYPE_FUNCTION(unsigned long    ,DBTYPE_UI4)
  67.     DEFINE_OLEDB_TYPE_FUNCTION(ULARGE_INTEGER   ,DBTYPE_UI8)
  68.     DEFINE_OLEDB_TYPE_FUNCTION(float            ,DBTYPE_R4)
  69.     DEFINE_OLEDB_TYPE_FUNCTION(double           ,DBTYPE_R8)     // DBTYPE_DATE
  70.     DEFINE_OLEDB_TYPE_FUNCTION(DECIMAL          ,DBTYPE_DECIMAL)
  71.     DEFINE_OLEDB_TYPE_FUNCTION(DB_NUMERIC       ,DBTYPE_NUMERIC)
  72.     DEFINE_OLEDB_TYPE_FUNCTION(VARIANT          ,DBTYPE_VARIANT)
  73.     DEFINE_OLEDB_TYPE_FUNCTION(IDispatch*       ,DBTYPE_IDISPATCH)
  74.     DEFINE_OLEDB_TYPE_FUNCTION(IUnknown*        ,DBTYPE_IUNKNOWN)
  75.     DEFINE_OLEDB_TYPE_FUNCTION(GUID             ,DBTYPE_GUID)
  76.     DEFINE_OLEDB_TYPE_FUNCTION(SAFEARRAY*       ,DBTYPE_ARRAY)
  77.     DEFINE_OLEDB_TYPE_FUNCTION(DBVECTOR         ,DBTYPE_VECTOR)
  78.     DEFINE_OLEDB_TYPE_FUNCTION(DBDATE           ,DBTYPE_DBDATE)
  79.     DEFINE_OLEDB_TYPE_FUNCTION(DBTIME           ,DBTYPE_DBTIME)
  80.     DEFINE_OLEDB_TYPE_FUNCTION(DBTIMESTAMP      ,DBTYPE_DBTIMESTAMP)
  81.      DEFINE_OLEDB_TYPE_FUNCTION(FILETIME            ,DBTYPE_FILETIME)
  82.     DEFINE_OLEDB_TYPE_FUNCTION(DBFILETIME        ,DBTYPE_DBFILETIME)
  83.     DEFINE_OLEDB_TYPE_FUNCTION(PROPVARIANT        ,DBTYPE_PROPVARIANT)
  84.     DEFINE_OLEDB_TYPE_FUNCTION(DB_VARNUMERIC    ,DBTYPE_VARNUMERIC)
  85.    
  86. // Internal structure containing the accessor handle and a flag
  87. // indicating whether the data for the accessor is automatically
  88. // retrieved
  89. struct _ATL_ACCESSOR_INFO
  90. {
  91.     HACCESSOR   hAccessor;
  92.     bool        bAutoAccessor;
  93. };
  94.  
  95. class _CNoOutputColumns
  96. {
  97. public:
  98.     static bool HasOutputColumns()
  99.     {
  100.         return false;
  101.     }
  102.     static ULONG _GetNumAccessors()
  103.     {
  104.         return 0;
  105.     }
  106.     static HRESULT _GetBindEntries(ULONG*, DBBINDING*, ULONG, bool*, BYTE* pBuffer = NULL)
  107.     {
  108.         pBuffer;
  109.         return E_FAIL;
  110.     }
  111. };
  112.  
  113. class _CNoParameters
  114. {
  115. public:
  116.     static bool HasParameters()
  117.     {
  118.         return false;
  119.     }
  120.     static HRESULT _GetParamEntries(ULONG*, DBBINDING*, BYTE* pBuffer = NULL)
  121.     {
  122.         pBuffer;
  123.         return E_FAIL;
  124.     }
  125. };
  126.  
  127. class _CNoCommand
  128. {
  129. public:
  130.     static HRESULT GetDefaultCommand(LPCTSTR* /*ppszCommand*/)
  131.     {
  132.         return S_OK;
  133.     }
  134. };
  135.  
  136. typedef _CNoOutputColumns   _OutputColumnsClass;
  137. typedef _CNoParameters      _ParamClass;
  138. typedef _CNoCommand         _CommandClass;
  139.  
  140. #define BEGIN_ACCESSOR_MAP(x, num) \
  141.     public: \
  142.     typedef x _classtype; \
  143.     typedef x _OutputColumnsClass; \
  144.     static ULONG _GetNumAccessors() { return num; } \
  145.     static bool HasOutputColumns() { return true; } \
  146.     /* If pBindings == NULL means we only return the column number */ \
  147.     /* If pBuffer != NULL then it points to the accessor buffer and */ \
  148.     /* we release any appropriate memory e.g. BSTR's or interface pointers */ \
  149.     inline static HRESULT _GetBindEntries(ULONG* pColumns, DBBINDING *pBinding, ULONG nAccessor, bool* pAuto, BYTE* pBuffer = NULL) \
  150.     { \
  151.         ATLASSERT(pColumns != NULL); \
  152.         DBPARAMIO eParamIO = DBPARAMIO_NOTPARAM; \
  153.         ULONG nColumns = 0; \
  154.         pBuffer;
  155.  
  156. #define BEGIN_ACCESSOR(num, bAuto) \
  157.     if (nAccessor == num) \
  158.     { \
  159.         if (pBinding != NULL) \
  160.             *pAuto = bAuto;
  161.  
  162. #define END_ACCESSOR() \
  163.     } \
  164.     else
  165.  
  166. #define END_ACCESSOR_MAP() \
  167.         ; \
  168.         *pColumns = nColumns; \
  169.         return S_OK; \
  170.     }
  171.  
  172. #define BEGIN_COLUMN_MAP(x) \
  173.     BEGIN_ACCESSOR_MAP(x, 1) \
  174.         BEGIN_ACCESSOR(0, true)
  175.  
  176. #define END_COLUMN_MAP() \
  177.         END_ACCESSOR() \
  178.     END_ACCESSOR_MAP()
  179.  
  180. #define offsetbuf(m) offsetof(_classtype, m)
  181. #define _OLEDB_TYPE(data) _GetOleDBType(((_classtype*)0)->data)
  182. #define _SIZE_TYPE(data) sizeof(((_classtype*)0)->data)
  183.  
  184. #define _COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, nPrecision, nScale, dataOffset, lengthOffset, statusOffset) \
  185.     if (pBuffer != NULL) \
  186.     { \
  187.         CAccessorBase::FreeType(wType, pBuffer + dataOffset); \
  188.     } \
  189.     else if (pBinding != NULL) \
  190.     { \
  191.         CAccessorBase::Bind(pBinding, nOrdinal, wType, nLength, nPrecision, nScale, eParamIO, \
  192.             dataOffset, lengthOffset, statusOffset); \
  193.         pBinding++; \
  194.     } \
  195.     nColumns++;
  196.  
  197. #define COLUMN_ENTRY_EX(nOrdinal, wType, nLength, nPrecision, nScale, data, length, status) \
  198.     _COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, nPrecision, nScale, offsetbuf(data), offsetbuf(length), offsetbuf(status))
  199.  
  200. #define COLUMN_ENTRY_TYPE(nOrdinal, wType, data) \
  201.     COLUMN_ENTRY_TYPE_SIZE(nOrdinal, wType, _SIZE_TYPE(data), data)
  202.  
  203. #define COLUMN_ENTRY_TYPE_SIZE(nOrdinal, wType, nLength, data) \
  204.     _COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, 0, 0, offsetbuf(data), 0, 0)
  205.  
  206.  
  207. // Standard macros where type and size is worked out
  208. #define COLUMN_ENTRY(nOrdinal, data) \
  209.     COLUMN_ENTRY_TYPE(nOrdinal, _OLEDB_TYPE(data), data)
  210.  
  211. #define COLUMN_ENTRY_LENGTH(nOrdinal, data, length) \
  212.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), offsetbuf(length), 0)
  213.  
  214. #define COLUMN_ENTRY_STATUS(nOrdinal, data, status) \
  215.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), 0, offsetbuf(status))
  216.  
  217. #define COLUMN_ENTRY_LENGTH_STATUS(nOrdinal, data, length, status) \
  218.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), offsetbuf(length), offsetbuf(status))
  219.  
  220.  
  221. // Follow macros are used if precision and scale need to be specified
  222. #define COLUMN_ENTRY_PS(nOrdinal, nPrecision, nScale, data) \
  223.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), 0, 0)
  224.  
  225. #define COLUMN_ENTRY_PS_LENGTH(nOrdinal, nPrecision, nScale, data, length) \
  226.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), offsetbuf(length), 0)
  227.  
  228. #define COLUMN_ENTRY_PS_STATUS(nOrdinal, nPrecision, nScale, data, status) \
  229.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), 0, offsetbuf(status))
  230.  
  231. #define COLUMN_ENTRY_PS_LENGTH_STATUS(nOrdinal, nPrecision, nScale, data, length, status) \
  232.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), offsetbuf(length), offsetbuf(status))
  233.  
  234.  
  235. #define BOOKMARK_ENTRY(variable) \
  236.     COLUMN_ENTRY_TYPE_SIZE(0, DBTYPE_BYTES, _SIZE_TYPE(variable##.m_rgBuffer), variable##.m_rgBuffer)
  237.  
  238. #define _BLOB_ENTRY_CODE(nOrdinal, IID, flags, dataOffset, statusOffset) \
  239.     if (pBuffer != NULL) \
  240.     { \
  241.         CAccessorBase::FreeType(DBTYPE_IUNKNOWN, pBuffer + dataOffset); \
  242.     } \
  243.     else if (pBinding != NULL) \
  244.     { \
  245.         DBOBJECT* pObject = NULL; \
  246.         ATLTRY(pObject = new DBOBJECT); \
  247.         if (pObject == NULL) \
  248.             return E_OUTOFMEMORY; \
  249.         pObject->dwFlags = flags; \
  250.         pObject->iid     = IID; \
  251.         CAccessorBase::Bind(pBinding, nOrdinal, DBTYPE_IUNKNOWN, sizeof(IUnknown*), 0, 0, eParamIO, \
  252.             dataOffset, 0, statusOffset, pObject); \
  253.         pBinding++; \
  254.     } \
  255.     nColumns++;
  256.  
  257. #define BLOB_ENTRY(nOrdinal, IID, flags, data) \
  258.     _BLOB_ENTRY_CODE(nOrdinal, IID, flags, offsetbuf(data), 0);
  259.  
  260. #define BLOB_ENTRY_STATUS(nOrdinal, IID, flags, data, status) \
  261.     _BLOB_ENTRY_CODE(nOrdinal, IID, flags, offsetbuf(data), offsetbuf(status));
  262.  
  263. #define BEGIN_PARAM_MAP(x) \
  264.     public: \
  265.     typedef x _classtype; \
  266.     typedef x _ParamClass; \
  267.     static bool HasParameters() { return true; } \
  268.     static HRESULT _GetParamEntries(ULONG* pColumns, DBBINDING *pBinding, BYTE* pBuffer = NULL) \
  269.     { \
  270.         ATLASSERT(pColumns != NULL); \
  271.         DBPARAMIO eParamIO = DBPARAMIO_INPUT; \
  272.         int nColumns = 0; \
  273.         pBuffer;
  274.  
  275. #define END_PARAM_MAP() \
  276.         *pColumns = nColumns; \
  277.         return S_OK; \
  278.     }
  279.  
  280. #define SET_PARAM_TYPE(type) \
  281.     eParamIO = type;
  282.  
  283. #define DEFINE_COMMAND(x, szCommand) \
  284.     typedef x _CommandClass; \
  285.     static HRESULT GetDefaultCommand(LPCTSTR* ppszCommand) \
  286.     { \
  287.         *ppszCommand = szCommand; \
  288.         return S_OK; \
  289.     }
  290.  
  291.  
  292. ///////////////////////////////////////////////////////////////////////////
  293. // class CDBErrorInfo
  294.  
  295. class CDBErrorInfo
  296. {
  297. public:
  298.     // Use to get the number of error record when you want to explicitly check that
  299.     // the passed interface set the error information
  300.     HRESULT GetErrorRecords(IUnknown* pUnk, const IID& iid, ULONG* pcRecords)
  301.     {
  302.         CComPtr<ISupportErrorInfo> spSupportErrorInfo;
  303. #ifdef __BORLANDC__
  304.         HRESULT hr = pUnk->QueryInterface((void **) &spSupportErrorInfo);
  305. #else
  306.         HRESULT hr = pUnk->QueryInterface(&spSupportErrorInfo);
  307. #endif
  308.         if (FAILED(hr))
  309.             return hr;
  310.  
  311.         hr = spSupportErrorInfo->InterfaceSupportsErrorInfo(iid);
  312.         if (FAILED(hr))
  313.             return hr;
  314.  
  315.         return GetErrorRecords(pcRecords);
  316.     }
  317.     // Use to get the number of error records
  318.     HRESULT GetErrorRecords(ULONG* pcRecords)
  319.     {
  320.         ATLASSERT(pcRecords != NULL);
  321.         HRESULT hr;
  322.         m_spErrorInfo.Release();
  323.         m_spErrorRecords.Release();
  324.         hr = ::GetErrorInfo(0, &m_spErrorInfo);
  325.         if (hr == S_FALSE)
  326.             return E_FAIL;
  327.  
  328.         hr = m_spErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&m_spErrorRecords);
  329.         if (FAILED(hr))
  330.         {
  331.             // Well we got the IErrorInfo so we'll just treat that as
  332.             // the one record
  333.             *pcRecords = 1;
  334.             return S_OK;
  335.         }
  336.  
  337.         return m_spErrorRecords->GetRecordCount(pcRecords);
  338.     }
  339.     // Get the error information for the passed record number. GetErrorRecords must
  340.     // be called before this function is called.
  341.     HRESULT GetAllErrorInfo(ULONG ulRecordNum, LCID lcid, BSTR* pbstrDescription,
  342.         BSTR* pbstrSource = NULL, GUID* pguid = NULL, DWORD* pdwHelpContext = NULL,
  343.         BSTR* pbstrHelpFile = NULL) const
  344.     {
  345.         CComPtr<IErrorInfo> spErrorInfo;
  346.  
  347.         // If we have the IErrorRecords interface pointer then use it, otherwise
  348.         // we'll just default to the IErrorInfo we have already retrieved in the call
  349.         // to GetErrorRecords
  350.         if (m_spErrorRecords != NULL)
  351.         {
  352.             HRESULT hr = m_spErrorRecords->GetErrorInfo(ulRecordNum, lcid, &spErrorInfo);
  353.             if (FAILED(hr))
  354.                 return hr;
  355.         }
  356.         else
  357.         {
  358.             ATLASSERT(m_spErrorInfo != NULL);
  359.             spErrorInfo = m_spErrorInfo;
  360.         }
  361.  
  362.         if (pbstrDescription != NULL)
  363.             spErrorInfo->GetDescription(pbstrDescription);
  364.  
  365.         if (pguid != NULL)
  366.             spErrorInfo->GetGUID(pguid);
  367.  
  368.         if (pdwHelpContext != NULL)
  369.             spErrorInfo->GetHelpContext(pdwHelpContext);
  370.  
  371.         if (pbstrHelpFile != NULL)
  372.             spErrorInfo->GetHelpFile(pbstrHelpFile);
  373.  
  374.         if (pbstrSource != NULL)
  375.             spErrorInfo->GetSource(pbstrSource);
  376.  
  377.         return S_OK;
  378.     }
  379.     // Get the error information for the passed record number
  380.     HRESULT GetBasicErrorInfo(ULONG ulRecordNum, ERRORINFO* pErrorInfo) const
  381.     {
  382.         return m_spErrorRecords->GetBasicErrorInfo(ulRecordNum, pErrorInfo);
  383.     }
  384.     // Get the custom error object for the passed record number
  385.     HRESULT GetCustomErrorObject(ULONG ulRecordNum, REFIID riid, IUnknown** ppObject) const
  386.     {
  387.         return m_spErrorRecords->GetCustomErrorObject(ulRecordNum, riid, ppObject);
  388.     }
  389.     // Get the IErrorInfo interface for the passed record number
  390.     HRESULT GetErrorInfo(ULONG ulRecordNum, LCID lcid, IErrorInfo** ppErrorInfo) const
  391.     {
  392.         return m_spErrorRecords->GetErrorInfo(ulRecordNum, lcid, ppErrorInfo);
  393.     }
  394.     // Get the error parameters for the passed record number
  395.     HRESULT GetErrorParameters(ULONG ulRecordNum, DISPPARAMS* pdispparams) const
  396.     {
  397.         return m_spErrorRecords->GetErrorParameters(ulRecordNum, pdispparams);
  398.     }
  399.  
  400. // Implementation
  401.     CComPtr<IErrorInfo>     m_spErrorInfo;
  402.     CComPtr<IErrorRecords>  m_spErrorRecords;
  403. };
  404.  
  405. #ifdef _DEBUG
  406. inline void AtlTraceErrorRecords(HRESULT hrErr = S_OK)
  407. {
  408.     CDBErrorInfo ErrorInfo;
  409.     ULONG        cRecords;
  410.     HRESULT      hr;
  411.     ULONG        i;
  412.     CComBSTR     bstrDesc, bstrHelpFile, bstrSource;
  413.     GUID         guid;
  414.     DWORD        dwHelpContext;
  415.     WCHAR        wszGuid[40];
  416.     USES_CONVERSION;
  417.  
  418.     // If the user passed in an HRESULT then trace it
  419.     if (hrErr != S_OK)
  420.         ATLTRACE2(atlTraceDBClient, 0, _T("OLE DB Error Record dump for hr = 0x%x\n"), hrErr);
  421.  
  422.     LCID lcLocale = GetSystemDefaultLCID();
  423.  
  424.     hr = ErrorInfo.GetErrorRecords(&cRecords);
  425.     if (FAILED(hr) && ErrorInfo.m_spErrorInfo == NULL)
  426.     {
  427.         ATLTRACE2(atlTraceDBClient, 0, _T("No OLE DB Error Information found: hr = 0x%x\n"), hr);
  428.     }
  429.     else
  430.     {
  431.         for (i = 0; i < cRecords; i++)
  432.         {
  433.             hr = ErrorInfo.GetAllErrorInfo(i, lcLocale, &bstrDesc, &bstrSource, &guid,
  434.                                         &dwHelpContext, &bstrHelpFile);
  435.             if (FAILED(hr))
  436.             {
  437.                 ATLTRACE2(atlTraceDBClient, 0,
  438.                     _T("OLE DB Error Record dump retrieval failed: hr = 0x%x\n"), hr);
  439.                 return;
  440.             }
  441.             StringFromGUID2(guid, wszGuid, sizeof(wszGuid) / sizeof(WCHAR));
  442.             ATLTRACE2(atlTraceDBClient, 0,
  443.                 _T("Row #: %4d Source: \"%s\" Description: \"%s\" Help File: \"%s\" Help Context: %4d GUID: %s\n"),
  444.                 i, OLE2T(bstrSource), OLE2T(bstrDesc), OLE2T(bstrHelpFile), dwHelpContext, OLE2T(wszGuid));
  445.             bstrSource.Empty();
  446.             bstrDesc.Empty();
  447.             bstrHelpFile.Empty();
  448.         }
  449.         ATLTRACE2(atlTraceDBClient, 0, _T("OLE DB Error Record dump end\n"));
  450.     }
  451. }
  452. #else
  453. inline void AtlTraceErrorRecords(HRESULT hrErr = S_OK)  { hrErr; }
  454. #endif
  455.  
  456.  
  457. ///////////////////////////////////////////////////////////////////////////
  458. // class CDBPropSet
  459.  
  460. class CDBPropSet : public tagDBPROPSET
  461. {
  462. public:
  463.     CDBPropSet()
  464.     {
  465.         rgProperties    = NULL;
  466.         cProperties     = 0;
  467.     }
  468.     CDBPropSet(const GUID& guid)
  469.     {
  470.         rgProperties    = NULL;
  471.         cProperties     = 0;
  472.         guidPropertySet = guid;
  473.     }
  474.     CDBPropSet(const CDBPropSet& propset)
  475.     {
  476.         InternalCopy(propset);
  477.     }
  478.     ~CDBPropSet()
  479.     {
  480.         for (ULONG i = 0; i < cProperties; i++)
  481.             VariantClear(&rgProperties[i].vValue);
  482.  
  483.         CoTaskMemFree(rgProperties);
  484.     }
  485.     CDBPropSet& operator=(CDBPropSet& propset)
  486.     {
  487.         this->~CDBPropSet();
  488.         InternalCopy(propset);
  489.         return *this;
  490.     }
  491.     // Set the GUID of the property set this class represents.
  492.     // Use if you didn't pass the GUID to the constructor.
  493.     void SetGUID(const GUID& guid)
  494.     {
  495.         guidPropertySet = guid;
  496.     }
  497.     // Add the passed property to the property set
  498.     bool AddProperty(DWORD dwPropertyID, const VARIANT& var)
  499.     {
  500.         HRESULT hr;
  501.         if (!Add())
  502.             return false;
  503.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  504.         hr = ::VariantCopy(&(rgProperties[cProperties].vValue), const_cast<VARIANT*>(&var));
  505.         if (FAILED(hr))
  506.             return false;
  507.         cProperties++;
  508.         return true;
  509.     }
  510.     // Add the passed property to the property set
  511.     bool AddProperty(DWORD dwPropertyID, LPCSTR szValue)
  512.     {
  513.         USES_CONVERSION;
  514.         if (!Add())
  515.             return false;
  516.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  517.         rgProperties[cProperties].vValue.vt      = VT_BSTR;
  518.         rgProperties[cProperties].vValue.bstrVal = SysAllocString(A2COLE(szValue));
  519.         if (rgProperties[cProperties].vValue.bstrVal == NULL)
  520.             return false;
  521.         cProperties++;
  522.         return true;
  523.     }
  524.     // Add the passed property to the property set
  525.     bool AddProperty(DWORD dwPropertyID, LPCWSTR szValue)
  526.     {
  527.         USES_CONVERSION;
  528.         if (!Add())
  529.             return false;
  530.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  531.         rgProperties[cProperties].vValue.vt      = VT_BSTR;
  532.         rgProperties[cProperties].vValue.bstrVal = SysAllocString(W2COLE(szValue));
  533.         if (rgProperties[cProperties].vValue.bstrVal == NULL)
  534.             return false;
  535.         cProperties++;
  536.         return true;
  537.     }
  538.     // Add the passed property to the property set
  539.     bool AddProperty(DWORD dwPropertyID, bool bValue)
  540.     {
  541.         if (!Add())
  542.             return false;
  543.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  544.         rgProperties[cProperties].vValue.vt      = VT_BOOL;
  545. #pragma warning(disable: 4310) // cast truncates constant value
  546.         rgProperties[cProperties].vValue.boolVal = (bValue) ? VARIANT_TRUE : VARIANT_FALSE;
  547. #pragma warning(default: 4310)
  548.         cProperties++;
  549.         return true;
  550.     }
  551.     // Add the passed property to the property set
  552.     bool AddProperty(DWORD dwPropertyID, BYTE bValue)
  553.     {
  554.         if (!Add())
  555.             return false;
  556.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  557.         rgProperties[cProperties].vValue.vt     = VT_UI1;
  558.         rgProperties[cProperties].vValue.bVal   = bValue;
  559.         cProperties++;
  560.         return true;
  561.     }
  562.     // Add the passed property to the property set
  563.     bool AddProperty(DWORD dwPropertyID, short nValue)
  564.     {
  565.         if (!Add())
  566.             return false;
  567.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  568.         rgProperties[cProperties].vValue.vt     = VT_I2;
  569.         rgProperties[cProperties].vValue.iVal   = nValue;
  570.         cProperties++;
  571.         return true;
  572.     }
  573.     // Add the passed property to the property set
  574.     bool AddProperty(DWORD dwPropertyID, long nValue)
  575.     {
  576.         if (!Add())
  577.             return false;
  578.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  579.         rgProperties[cProperties].vValue.vt     = VT_I4;
  580.         rgProperties[cProperties].vValue.lVal   = nValue;
  581.         cProperties++;
  582.         return true;
  583.     }
  584.     // Add the passed property to the property set
  585.     bool AddProperty(DWORD dwPropertyID, float fltValue)
  586.     {
  587.         if (!Add())
  588.             return false;
  589.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  590.         rgProperties[cProperties].vValue.vt     = VT_R4;
  591.         rgProperties[cProperties].vValue.fltVal = fltValue;
  592.         cProperties++;
  593.         return true;
  594.     }
  595.     // Add the passed property to the property set
  596.     bool AddProperty(DWORD dwPropertyID, double dblValue)
  597.     {
  598.         if (!Add())
  599.             return false;
  600.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  601.         rgProperties[cProperties].vValue.vt     = VT_R8;
  602.         rgProperties[cProperties].vValue.dblVal = dblValue;
  603.         cProperties++;
  604.         return true;
  605.     }
  606.     // Add the passed property to the property set
  607.     bool AddProperty(DWORD dwPropertyID, CY cyValue)
  608.     {
  609.         if (!Add())
  610.             return false;
  611.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  612.         rgProperties[cProperties].vValue.vt     = VT_CY;
  613.         rgProperties[cProperties].vValue.cyVal  = cyValue;
  614.         cProperties++;
  615.         return true;
  616.     }
  617. // Implementation
  618.     // Create memory to add a new property
  619.     bool Add()
  620.     {
  621.         rgProperties = (DBPROP*)CoTaskMemRealloc(rgProperties, (cProperties + 1) * sizeof(DBPROP));
  622.         if (rgProperties != NULL)
  623.         {
  624.             rgProperties[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
  625.             rgProperties[cProperties].colid     = DB_NULLID;
  626.             rgProperties[cProperties].vValue.vt = VT_EMPTY;
  627.             return true;
  628.         }
  629.         else
  630.             return false;
  631.     }
  632.     // Copies in the passed value now it this value been cleared
  633.     void InternalCopy(const CDBPropSet& propset)
  634.     {
  635.         cProperties     = propset.cProperties;
  636.         guidPropertySet = propset.guidPropertySet;
  637.         rgProperties    = (DBPROP*)CoTaskMemAlloc(cProperties * sizeof(DBPROP));
  638.         if (rgProperties != NULL)
  639.         {
  640.             for (ULONG i = 0; i < cProperties; i++)
  641.             {
  642.                 rgProperties[i].dwPropertyID = propset.rgProperties[i].dwPropertyID;
  643.                 rgProperties[i].dwOptions    = DBPROPOPTIONS_REQUIRED;
  644.                 rgProperties[i].colid        = DB_NULLID;
  645.                 rgProperties[i].vValue.vt    = VT_EMPTY;
  646.                 VariantCopy(&rgProperties[i].vValue, &propset.rgProperties[i].vValue);
  647.             }
  648.         }
  649.         else
  650.         {
  651.             // The memory allocation failed so set the count
  652.             // of properties to zero
  653.             cProperties = 0;
  654.         }
  655.     }
  656. };
  657.  
  658.  
  659. ///////////////////////////////////////////////////////////////////////////
  660. // class CDBPropIDSet
  661.  
  662. class CDBPropIDSet : public tagDBPROPIDSET
  663. {
  664. // Constructors and Destructors
  665. public:
  666.     CDBPropIDSet()
  667.     {
  668.         rgPropertyIDs   = NULL;
  669.         cPropertyIDs    = 0;
  670.     }
  671.     CDBPropIDSet(const GUID& guid)
  672.     {
  673.         rgPropertyIDs   = NULL;
  674.         cPropertyIDs    = 0;
  675.         guidPropertySet = guid;
  676.     }
  677.     CDBPropIDSet(const CDBPropIDSet& propidset)
  678.     {
  679.         InternalCopy(propidset);
  680.     }
  681.     ~CDBPropIDSet()
  682.     {
  683.         if (rgPropertyIDs != NULL)
  684.             free(rgPropertyIDs);
  685.     }
  686.     CDBPropIDSet& operator=(CDBPropIDSet& propset)
  687.     {
  688.         this->~CDBPropIDSet();
  689.         InternalCopy(propset);
  690.         return *this;
  691.     }
  692.     // Set the GUID of the property ID set
  693.     void SetGUID(const GUID& guid)
  694.     {
  695.         guidPropertySet = guid;
  696.     }
  697.     // Add a property ID to the set
  698.     bool AddPropertyID(DBPROPID propid)
  699.     {
  700.         if (!Add())
  701.             return false;
  702.         rgPropertyIDs[cPropertyIDs] = propid;
  703.         cPropertyIDs++;
  704.         return true;
  705.     }
  706. // Implementation
  707.     bool Add()
  708.     {
  709.         rgPropertyIDs = (DBPROPID*)realloc(rgPropertyIDs, (cPropertyIDs + 1) * sizeof(DBPROPID));
  710.         return (rgPropertyIDs != NULL) ? true : false;
  711.     }
  712.     void InternalCopy(const CDBPropIDSet& propidset)
  713.     {
  714.         cPropertyIDs    = propidset.cPropertyIDs;
  715.         guidPropertySet = propidset.guidPropertySet;
  716.         rgPropertyIDs   = (DBPROPID*)malloc(cPropertyIDs * sizeof(DBPROPID));
  717.         if (rgPropertyIDs != NULL)
  718.         {
  719.             for (ULONG i = 0; i < cPropertyIDs; i++)
  720.                 rgPropertyIDs[i] = propidset.rgPropertyIDs[i];
  721.         }
  722.         else
  723.         {
  724.             // The memory allocation failed so set the count
  725.             // of properties to zero
  726.             cPropertyIDs = 0;
  727.         }
  728.     }
  729. };
  730.  
  731.  
  732. ///////////////////////////////////////////////////////////////////////////
  733. // class CBookmarkBase
  734.  
  735. class ATL_NO_VTABLE CBookmarkBase
  736. {
  737. public:
  738.     virtual ULONG GetSize() const = 0;
  739.     virtual BYTE* GetBuffer() const = 0;
  740. };
  741.  
  742.  
  743. ///////////////////////////////////////////////////////////////////////////
  744. // class CBookmark
  745.  
  746. template <ULONG nSize = 0>
  747. class CBookmark : public CBookmarkBase
  748. {
  749. public:
  750.     virtual ULONG   GetSize() const { return nSize; }
  751.     virtual BYTE*   GetBuffer() const { return (BYTE*)m_rgBuffer; }
  752.  
  753. // Implementation
  754.     BYTE m_rgBuffer[nSize];
  755. };
  756.  
  757.  
  758. // Size of 0 means that the memory for the bookmark will be allocated
  759. // at run time.
  760. template <>
  761. class CBookmark<0> : public CBookmarkBase
  762. {
  763. public:
  764.     CBookmark()
  765.     {
  766.         m_nSize = 0;
  767.         m_pBuffer = NULL;
  768.     }
  769.     CBookmark(ULONG nSize)
  770.     {
  771.         m_pBuffer = NULL;
  772.         ATLTRY(m_pBuffer = new BYTE[nSize]);
  773.         m_nSize = (m_pBuffer == NULL) ? 0 : nSize;
  774.     }
  775.     ~CBookmark()
  776.     {
  777.         delete [] m_pBuffer;
  778.     }
  779.     CBookmark& operator=(const CBookmark& bookmark)
  780.     {
  781.         SetBookmark(bookmark.GetSize(), bookmark.GetBuffer());
  782.         return *this;
  783.     }
  784.     virtual ULONG GetSize() const { return m_nSize; }
  785.     virtual BYTE* GetBuffer() const { return m_pBuffer; }
  786.     // Sets the bookmark to the passed value
  787.     HRESULT SetBookmark(ULONG nSize, BYTE* pBuffer)
  788.     {
  789.         ATLASSERT(pBuffer != NULL);
  790.         delete [] m_pBuffer;
  791.         m_pBuffer = NULL;
  792.         ATLTRY(m_pBuffer = new BYTE[nSize]);
  793.         if (m_pBuffer != NULL)
  794.         {
  795.             memcpy(m_pBuffer, pBuffer, nSize);
  796.             m_nSize = nSize;
  797.             return S_OK;
  798.         }
  799.         else
  800.         {
  801.             m_nSize = 0;
  802.             return E_OUTOFMEMORY;
  803.         }
  804.     }
  805.     ULONG   m_nSize;
  806.     BYTE*   m_pBuffer;
  807. };
  808.  
  809.  
  810. ///////////////////////////////////////////////////////////////////////////
  811. // class CAccessorBase
  812.  
  813. class CAccessorBase
  814. {
  815. public:
  816.     CAccessorBase()
  817.     {
  818.         m_pAccessorInfo  = NULL;
  819.         m_nAccessors     = 0;
  820.         m_pBuffer        = NULL;
  821.     }
  822.     void Close()
  823.     {
  824.         // If Close is called then ReleaseAccessors must have been
  825.         // called first
  826.         ATLASSERT(m_nAccessors == 0);
  827.         ATLASSERT(m_pAccessorInfo == NULL);
  828.     }
  829.     // Get the number of accessors that have been created
  830.     ULONG GetNumAccessors() const { return m_nAccessors; }
  831.     // Get the handle of the passed accessor (offset from 0)
  832.     HACCESSOR GetHAccessor(ULONG nAccessor) const
  833.     {
  834.         ATLASSERT(nAccessor<m_nAccessors);
  835.         return m_pAccessorInfo[nAccessor].hAccessor;
  836.     };
  837.     // Called during Close to release the accessor information
  838.     HRESULT ReleaseAccessors(IUnknown* pUnk)
  839.     {
  840.         ATLASSERT(pUnk != NULL);
  841.         HRESULT hr = S_OK;
  842.         if (m_nAccessors > 0)
  843.         {
  844.             CComPtr<IAccessor> spAccessor;
  845.             hr = pUnk->QueryInterface(IID_IAccessor, (void**)&spAccessor);
  846.             if (SUCCEEDED(hr))
  847.             {
  848.                 ATLASSERT(m_pAccessorInfo != NULL);
  849.                 for (ULONG i = 0; i < m_nAccessors; i++)
  850.                     spAccessor->ReleaseAccessor(m_pAccessorInfo[i].hAccessor, NULL);
  851.             }
  852.             m_nAccessors = 0;
  853.             delete [] m_pAccessorInfo;
  854.             m_pAccessorInfo = NULL;
  855.         }
  856.         return hr;
  857.     }
  858.     // Returns true or false depending upon whether data should be
  859.     // automatically retrieved for the passed accessor.
  860.     bool IsAutoAccessor(ULONG nAccessor) const
  861.     {
  862.         ATLASSERT(nAccessor < m_nAccessors);
  863.         ATLASSERT(m_pAccessorInfo != NULL);
  864.         return m_pAccessorInfo[nAccessor].bAutoAccessor;
  865.     }
  866.  
  867. // Implementation
  868.     // Used by the rowset class to find out where to place the data
  869.     BYTE* GetBuffer() const
  870.     {
  871.         return m_pBuffer;
  872.     }
  873.     // Set the buffer that is used to retrieve the data
  874.     void SetBuffer(BYTE* pBuffer)
  875.     {
  876.         m_pBuffer = pBuffer;
  877.     }
  878.  
  879.     // Allocate internal memory for the passed number of accessors
  880.     HRESULT AllocateAccessorMemory(int nAccessors)
  881.     {
  882.         // Can't be called twice without calling ReleaseAccessors first
  883.         ATLASSERT(m_pAccessorInfo == NULL);
  884.         m_nAccessors    = nAccessors;
  885.         m_pAccessorInfo = NULL;
  886.         ATLTRY(m_pAccessorInfo = new _ATL_ACCESSOR_INFO[nAccessors]);
  887.         if (m_pAccessorInfo == NULL)
  888.             return E_OUTOFMEMORY;
  889.         else
  890.             return S_OK;
  891.     }
  892.     // BindParameters will be overriden if parameters are used
  893.     HRESULT BindParameters(HACCESSOR*, ICommand*, void**) { return S_OK; }
  894.  
  895.     // Create an accessor for the passed binding information. The created accessor is
  896.     // returned through the pHAccessor parameter.
  897.     static HRESULT BindEntries(DBBINDING* pBindings, int nColumns, HACCESSOR* pHAccessor,
  898.         ULONG nSize, IAccessor* pAccessor)
  899.     {
  900.         ATLASSERT(pBindings  != NULL);
  901.         ATLASSERT(pHAccessor != NULL);
  902.         ATLASSERT(pAccessor  != NULL);
  903.         HRESULT hr;
  904.         int i;
  905.         DWORD dwAccessorFlags = (pBindings->eParamIO == DBPARAMIO_NOTPARAM) ?
  906.             DBACCESSOR_ROWDATA : DBACCESSOR_PARAMETERDATA;
  907.  
  908. #ifdef _DEBUG
  909.         // In debug builds we will retrieve the status flags and trace out
  910.         // any errors that may occur.
  911.         DBBINDSTATUS* pStatus = NULL;
  912.         ATLTRY(pStatus = new DBBINDSTATUS[nColumns]);
  913.         hr = pAccessor->CreateAccessor(dwAccessorFlags, nColumns,
  914.             pBindings, nSize, pHAccessor, pStatus);
  915.         if (FAILED(hr) && pStatus != NULL)
  916.         {
  917.             for (i=0; i<nColumns; i++)
  918.             {
  919.                 if (pStatus[i] != DBBINDSTATUS_OK)
  920.                     ATLTRACE2(atlTraceDBClient, 0, _T("Binding entry %d failed. Status: %d\n"), i, pStatus[i]);
  921.             }
  922.         }
  923.         delete [] pStatus;
  924. #else
  925.         hr = pAccessor->CreateAccessor(dwAccessorFlags, nColumns,
  926.             pBindings, nSize, pHAccessor, NULL);
  927. #endif
  928.         for (i=0; i<nColumns; i++)
  929.             delete pBindings[i].pObject;
  930.  
  931.         return hr;
  932.     }
  933.     // Set up the binding structure pointed to by pBindings based upon
  934.     // the other passed parameters.
  935.     static void Bind(DBBINDING* pBinding, ULONG nOrdinal, DBTYPE wType,
  936.         ULONG nLength, BYTE nPrecision, BYTE nScale, DBPARAMIO eParamIO,
  937.         ULONG nDataOffset, ULONG nLengthOffset = NULL, ULONG nStatusOffset = NULL,
  938.         DBOBJECT* pdbobject = NULL)
  939.     {
  940.         ATLASSERT(pBinding != NULL);
  941.  
  942.         // If we are getting a pointer to the data then let the provider
  943.         // own the memory
  944.         if (wType & DBTYPE_BYREF)
  945.             pBinding->dwMemOwner = DBMEMOWNER_PROVIDEROWNED;
  946.         else
  947.             pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  948.  
  949.         pBinding->pObject   = pdbobject;
  950.  
  951.         pBinding->eParamIO      = eParamIO;
  952.         pBinding->iOrdinal      = nOrdinal;
  953.         pBinding->wType         = wType;
  954.         pBinding->bPrecision    = nPrecision;
  955.         pBinding->bScale        = nScale;
  956.         pBinding->dwFlags       = 0;
  957.  
  958.         pBinding->obValue       = nDataOffset;
  959.         pBinding->obLength      = 0;
  960.         pBinding->obStatus      = 0;
  961.         pBinding->pTypeInfo     = NULL;
  962.         pBinding->pBindExt      = NULL;
  963.         pBinding->cbMaxLen      = nLength;
  964.  
  965.         pBinding->dwPart = DBPART_VALUE;
  966.         if (nLengthOffset != NULL)
  967.         {
  968.             pBinding->dwPart |= DBPART_LENGTH;
  969.             pBinding->obLength = nLengthOffset;
  970.         }
  971.         if (nStatusOffset != NULL)
  972.         {
  973.             pBinding->dwPart |= DBPART_STATUS;
  974.             pBinding->obStatus = nStatusOffset;
  975.         }
  976.     }
  977.  
  978.     // Free memory if appropriate
  979.     static inline void FreeType(DBTYPE wType, BYTE* pValue, IRowset* pRowset = NULL)
  980.     {
  981.         switch (wType)
  982.         {
  983.             case DBTYPE_BSTR:
  984.                 SysFreeString(*((BSTR*)pValue));
  985.             break;
  986.             case DBTYPE_VARIANT:
  987.                 VariantClear((VARIANT*)pValue);
  988.             break;
  989.             case DBTYPE_IUNKNOWN:
  990.             case DBTYPE_IDISPATCH:
  991.                 (*(IUnknown**)pValue)->Release();
  992.             break;
  993.             case DBTYPE_ARRAY:
  994.                 SafeArrayDestroy((SAFEARRAY*)pValue);
  995.             break;
  996.  
  997.             case DBTYPE_HCHAPTER:
  998.                 CComQIPtr<IChapteredRowset> spChapteredRowset = pRowset;
  999.                 if (spChapteredRowset != NULL)
  1000.                     spChapteredRowset->ReleaseChapter(*(HCHAPTER*)pValue, NULL);
  1001.             break;
  1002.         }
  1003.         if ((wType & DBTYPE_VECTOR) && ~(wType & DBTYPE_BYREF))
  1004.             CoTaskMemFree(((DBVECTOR*)pValue)->ptr);
  1005.     }
  1006.  
  1007.     _ATL_ACCESSOR_INFO* m_pAccessorInfo;
  1008.     ULONG               m_nAccessors;
  1009.     BYTE*               m_pBuffer;
  1010. };
  1011.  
  1012. ///////////////////////////////////////////////////////////////////////////
  1013. // class CRowset
  1014.  
  1015. class CRowset
  1016. {
  1017. // Constructors and Destructors
  1018. public:
  1019.     CRowset()
  1020.     {
  1021.         m_pAccessor = NULL;
  1022.         m_hRow      = NULL;
  1023.     }
  1024.     CRowset(IRowset* pRowset)
  1025.     {
  1026.         m_spRowset  = pRowset;
  1027.         CRowset();
  1028.     }
  1029.     ~CRowset()
  1030.     {
  1031.         Close();
  1032.     }
  1033.     // Release any retrieved row handles and then release the rowset
  1034.     void Close()
  1035.     {
  1036.         if (m_spRowset != NULL)
  1037.         {
  1038.             ReleaseRows();
  1039.             m_spRowset.Release();
  1040.             m_spRowsetChange.Release();
  1041.         }
  1042.     }
  1043.     // Addref the current row
  1044.     HRESULT AddRefRows()
  1045.     {
  1046.         ATLASSERT(m_spRowset != NULL);
  1047.         return m_spRowset->AddRefRows(1, &m_hRow, NULL, NULL);
  1048.     }
  1049.     // Release the current row
  1050.     HRESULT ReleaseRows()
  1051.     {
  1052.         ATLASSERT(m_spRowset != NULL);
  1053.         HRESULT hr = S_OK;
  1054.  
  1055.         if (m_hRow != NULL)
  1056.         {
  1057.             hr = m_spRowset->ReleaseRows(1, &m_hRow, NULL, NULL, NULL);
  1058.             m_hRow = NULL;
  1059.         }
  1060.         return hr;
  1061.     }
  1062.     // Compare two bookmarks with each other
  1063.     HRESULT Compare(const CBookmarkBase& bookmark1, const CBookmarkBase& bookmark2,
  1064.         DBCOMPARE* pComparison) const
  1065.     {
  1066.         ATLASSERT(m_spRowset != NULL);
  1067.         CComPtr<IRowsetLocate> spLocate;
  1068.         HRESULT hr = m_spRowset.QueryInterface(&spLocate);
  1069.         if (FAILED(hr))
  1070.             return hr;
  1071.  
  1072.         return spLocate->Compare(NULL, bookmark1.GetSize(), bookmark1.GetBuffer(),
  1073.             bookmark2.GetSize(), bookmark2.GetBuffer(), pComparison);
  1074.     }
  1075.     // Compare the passed hRow with the current row
  1076.     HRESULT IsSameRow(HROW hRow) const
  1077.     {
  1078.         ATLASSERT(m_spRowset != NULL);
  1079.         CComPtr<IRowsetIdentity> spRowsetIdentity;
  1080.         HRESULT hr = m_spRowset.QueryInterface(&spRowsetIdentity);
  1081.         if (FAILED(hr))
  1082.             return hr;
  1083.  
  1084.         return spRowsetIdentity->IsSameRow(m_hRow, hRow);
  1085.     }
  1086.     // Move to the previous record
  1087.     HRESULT MovePrev()
  1088.     {
  1089.         return MoveNext(-2, true);
  1090.     }
  1091.     // Move to the next record
  1092.     HRESULT MoveNext()
  1093.     {
  1094.         return MoveNext(0, true);
  1095.     }
  1096.     // Move lSkip records forward or backward
  1097.     HRESULT MoveNext(LONG lSkip, bool bForward)
  1098.     {
  1099.         HRESULT hr;
  1100.         ULONG ulRowsFetched = 0;
  1101.  
  1102.         // Check the data was opened successfully and the accessor
  1103.         // has been set.
  1104.         ATLASSERT(m_spRowset != NULL);
  1105.         ATLASSERT(m_pAccessor != NULL);
  1106.  
  1107.         // Release a row if one is already around
  1108.         ReleaseRows();
  1109.  
  1110.         // Get the row handle
  1111.         HROW* phRow = &m_hRow;
  1112.         hr = m_spRowset->GetNextRows(NULL, lSkip, (bForward) ? 1 : -1, &ulRowsFetched, &phRow);
  1113.         if (hr != S_OK)
  1114.             return hr;
  1115.  
  1116.         // Get the data
  1117.         hr = GetData();
  1118.         if (FAILED(hr))
  1119.         {
  1120.             ATLTRACE2(atlTraceDBClient, 0, _T("GetData failed - HRESULT = 0x%X\n"),hr);
  1121.             ReleaseRows();
  1122.         }
  1123.         return hr;
  1124.     }
  1125.     // Move to the first record
  1126.     HRESULT MoveFirst()
  1127.     {
  1128.         HRESULT hr;
  1129.  
  1130.         // Check the data was opened successfully and the accessor
  1131.         // has been set.
  1132.         ATLASSERT(m_spRowset != NULL);
  1133.         ATLASSERT(m_pAccessor != NULL);
  1134.  
  1135.         // Release a row if one is already around
  1136.         ReleaseRows();
  1137.  
  1138.         hr = m_spRowset->RestartPosition(NULL);
  1139.         if (FAILED(hr))
  1140.             return hr;
  1141.  
  1142.         // Get the data
  1143.         return MoveNext();
  1144.     }
  1145.     // Move to the last record
  1146.     HRESULT MoveLast()
  1147.     {
  1148.         // Check the data was opened successfully and the accessor
  1149.         // has been set.
  1150.         ATLASSERT(m_spRowset != NULL);
  1151.         ATLASSERT(m_pAccessor != NULL);
  1152.  
  1153.         // Release a row if one is already around
  1154.         ReleaseRows();
  1155.  
  1156.         HRESULT hr;
  1157.         ULONG ulRowsFetched = 0;
  1158.         HROW* phRow = &m_hRow;
  1159.         // Restart the rowset position and then move backwards
  1160.         m_spRowset->RestartPosition(NULL);
  1161.         hr = m_spRowset->GetNextRows(NULL, -1, 1, &ulRowsFetched, &phRow);
  1162.         if (hr != S_OK)
  1163.             return hr;
  1164.  
  1165.         // Get the data
  1166.         hr = GetData();
  1167.         if (FAILED(hr))
  1168.         {
  1169.             ATLTRACE2(atlTraceDBClient, 0, _T("GetData from MoveLast failed - HRESULT = 0x%X\n"),hr);
  1170.             ReleaseRows();
  1171.         }
  1172.  
  1173.         return S_OK;
  1174.     }
  1175.     // Move to the passed bookmark
  1176.     HRESULT MoveToBookmark(const CBookmarkBase& bookmark, LONG lSkip = 0)
  1177.     {
  1178.         // Check the data was opened successfully and the accessor
  1179.         // has been set.
  1180.         ATLASSERT(m_spRowset != NULL);
  1181.         ATLASSERT(m_pAccessor != NULL);
  1182.  
  1183.         CComPtr<IRowsetLocate> spLocate;
  1184.         HRESULT hr = m_spRowset.QueryInterface(&spLocate);
  1185.         if (FAILED(hr))
  1186.             return hr;
  1187.  
  1188.         // Release a row if one is already around
  1189.         ReleaseRows();
  1190.  
  1191.         ULONG ulRowsFetched = 0;
  1192.         HROW* phRow = &m_hRow;
  1193.         hr = spLocate->GetRowsAt(NULL, NULL, bookmark.GetSize(), bookmark.GetBuffer(),
  1194.             lSkip, 1, &ulRowsFetched, &phRow);
  1195.         // Note we're not using SUCCEEDED here, because we could get DB_S_ENDOFROWSET
  1196.         if (hr != S_OK)
  1197.             return hr;
  1198.  
  1199.         // Get the data
  1200.         hr = GetData();
  1201.         if (FAILED(hr))
  1202.         {
  1203.             ATLTRACE2(atlTraceDBClient, 0, _T("GetData from Bookmark failed - HRESULT = 0x%X\n"),hr);
  1204.             ReleaseRows();
  1205.         }
  1206.  
  1207.         return S_OK;
  1208.     }
  1209.     // Get the data for the current record
  1210.     HRESULT GetData()
  1211.     {
  1212.         HRESULT hr = S_OK;
  1213.         ATLASSERT(m_pAccessor != NULL);
  1214.  
  1215.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1216.         for (ULONG i=0; i<nAccessors; i++)
  1217.         {
  1218.             if (m_pAccessor->IsAutoAccessor(i))
  1219.             {
  1220.                 hr = GetData(i);
  1221.                 if (FAILED(hr))
  1222.                     return hr;
  1223.             }
  1224.         }
  1225.         return hr;
  1226.     }
  1227.     // Get the data for the passed accessor. Use for a non-auto accessor
  1228.     HRESULT GetData(int nAccessor)
  1229.     {
  1230.         ATLASSERT(m_spRowset != NULL);
  1231.         ATLASSERT(m_pAccessor != NULL);
  1232.         ATLASSERT(m_hRow != NULL);
  1233.  
  1234.         // Note that we are using the specified buffer if it has been set,
  1235.         // otherwise we use the accessor for the data.
  1236.         return m_spRowset->GetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor), m_pAccessor->GetBuffer());
  1237.     }
  1238.     // Get the data for the passed accessor. Use for a non-auto accessor
  1239.     HRESULT GetDataHere(int nAccessor, void* pBuffer)
  1240.     {
  1241.         ATLASSERT(m_spRowset != NULL);
  1242.         ATLASSERT(m_pAccessor != NULL);
  1243.         ATLASSERT(m_hRow != NULL);
  1244.  
  1245.         // Note that we are using the specified buffer if it has been set,
  1246.         // otherwise we use the accessor for the data.
  1247.         return m_spRowset->GetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor), pBuffer);
  1248.     }
  1249.     HRESULT GetDataHere(void* pBuffer)
  1250.     {
  1251.         HRESULT hr = S_OK;
  1252.  
  1253.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1254.         for (ULONG i=0; i<nAccessors; i++)
  1255.         {
  1256.             hr = GetDataHere(i, pBuffer);
  1257.             if (FAILED(hr))
  1258.                 return hr;
  1259.         }
  1260.         return hr;
  1261.     }
  1262.  
  1263.     // Insert the current record
  1264.     HRESULT Insert(int nAccessor = 0, bool bGetHRow = false)
  1265.     {
  1266.         ATLASSERT(m_pAccessor != NULL);
  1267.         HRESULT hr;
  1268.         if (m_spRowsetChange != NULL)
  1269.         {
  1270.             HROW* pHRow;
  1271.             if (bGetHRow)
  1272.             {
  1273.                 ReleaseRows();
  1274.                 pHRow = &m_hRow;
  1275.             }
  1276.             else
  1277.                 pHRow = NULL;
  1278.  
  1279.             hr = m_spRowsetChange->InsertRow(NULL, m_pAccessor->GetHAccessor(nAccessor),
  1280.                     m_pAccessor->GetBuffer(), pHRow);
  1281.  
  1282.         }
  1283.         else
  1284.             hr = E_NOINTERFACE;
  1285.  
  1286.         return hr;
  1287.     }
  1288.     // Delete the current record
  1289.     HRESULT Delete() const
  1290.     {
  1291.         ATLASSERT(m_pAccessor != NULL);
  1292.         HRESULT hr;
  1293.         if (m_spRowsetChange != NULL)
  1294.             hr = m_spRowsetChange->DeleteRows(NULL, 1, &m_hRow, NULL);
  1295.         else
  1296.             hr = E_NOINTERFACE;
  1297.  
  1298.         return hr;
  1299.     }
  1300.     // Update the current record
  1301.     HRESULT SetData() const
  1302.     {
  1303.         ATLASSERT(m_pAccessor != NULL);
  1304.         HRESULT hr = S_OK;
  1305.  
  1306.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1307.         for (ULONG i=0; i<nAccessors; i++)
  1308.         {
  1309.             hr = SetData(i);
  1310.             if (FAILED(hr))
  1311.                 return hr;
  1312.         }
  1313.         return hr;
  1314.     }
  1315.     // Update the current record with the data in the passed accessor
  1316.     HRESULT SetData(int nAccessor) const
  1317.     {
  1318.         ATLASSERT(m_pAccessor != NULL);
  1319.         HRESULT hr;
  1320.         if (m_spRowsetChange != NULL)
  1321.         {
  1322.             hr = m_spRowsetChange->SetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor),
  1323.                 m_pAccessor->GetBuffer());
  1324.         }
  1325.         else
  1326.             hr = E_NOINTERFACE;
  1327.  
  1328.         return hr;
  1329.     }
  1330.  
  1331.     // Get the data most recently fetched from or transmitted to the data source.
  1332.     // Does not get values based on pending changes.
  1333.     HRESULT GetOriginalData()
  1334.     {
  1335.         ATLASSERT(m_spRowset != NULL);
  1336.         ATLASSERT(m_pAccessor != NULL);
  1337.  
  1338.         HRESULT hr = S_OK;
  1339.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1340.         hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1341.         if (FAILED(hr))
  1342.             return hr;
  1343.  
  1344.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1345.         for (ULONG i = 0; i < nAccessors; i++)
  1346.         {
  1347.             hr = spRowsetUpdate->GetOriginalData(m_hRow, m_pAccessor->GetHAccessor(i), m_pAccessor->GetBuffer());
  1348.             if (FAILED(hr))
  1349.                 return hr;
  1350.         }
  1351.         return hr;
  1352.     }
  1353.     // Get the status of the current row
  1354.     HRESULT GetRowStatus(DBPENDINGSTATUS* pStatus) const
  1355.     {
  1356.         ATLASSERT(m_spRowset != NULL);
  1357.         ATLASSERT(pStatus != NULL);
  1358.  
  1359.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1360.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1361.         if (FAILED(hr))
  1362.             return hr;
  1363.  
  1364.         return spRowsetUpdate->GetRowStatus(NULL, 1, &m_hRow, pStatus);
  1365.     }
  1366.     // Undo any changes made to the current row since it was last fetched or Update
  1367.     // was called for it
  1368.     HRESULT Undo(ULONG* pcRows = NULL, HROW* phRow = NULL, DBROWSTATUS* pStatus = NULL)
  1369.     {
  1370.         ATLASSERT(m_spRowset != NULL);
  1371.  
  1372.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1373.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1374.         if (FAILED(hr))
  1375.             return hr;
  1376.  
  1377.         HROW*           prgRows;
  1378.         DBROWSTATUS*    pRowStatus;
  1379.         if (phRow != NULL)
  1380.             hr = spRowsetUpdate->Undo(NULL, 1, &m_hRow, pcRows, &prgRows, &pRowStatus);
  1381.         else
  1382.             hr = spRowsetUpdate->Undo(NULL, 1, &m_hRow, pcRows, NULL, &pRowStatus);
  1383.         if (FAILED(hr))
  1384.             return hr;
  1385.  
  1386.         if (phRow != NULL)
  1387.         {
  1388.             *phRow = *prgRows;
  1389.             CoTaskMemFree(prgRows);
  1390.         }
  1391.         if (pStatus != NULL)
  1392.             *pStatus = *pRowStatus;
  1393.  
  1394.         CoTaskMemFree(pRowStatus);
  1395.         return hr;
  1396.     }
  1397.     // Transmits any pending changes made to a row since it was last fetched or Update was
  1398.     // called for it. Also see SetData.
  1399.     HRESULT Update(ULONG* pcRows = NULL, HROW* phRow = NULL, DBROWSTATUS* pStatus = NULL)
  1400.     {
  1401.         ATLASSERT(m_spRowset != NULL);
  1402.  
  1403.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1404.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1405.         if (FAILED(hr))
  1406.             return hr;
  1407.  
  1408.         HROW*           prgRows;
  1409.         DBROWSTATUS*    pRowStatus;
  1410.         if (phRow != NULL)
  1411.             hr = spRowsetUpdate->Update(NULL, 1, &m_hRow, pcRows, &prgRows, &pRowStatus);
  1412.         else
  1413.             hr = spRowsetUpdate->Update(NULL, 1, &m_hRow, pcRows, NULL, &pRowStatus);
  1414.         if (FAILED(hr))
  1415.             return hr;
  1416.  
  1417.         if (phRow != NULL)
  1418.         {
  1419.             *phRow = *prgRows;
  1420.             CoTaskMemFree(prgRows);
  1421.         }
  1422.         if (pStatus != NULL)
  1423.             *pStatus = *pRowStatus;
  1424.  
  1425.         CoTaskMemFree(pRowStatus);
  1426.         return hr;
  1427.     }
  1428.  
  1429.     // Get the approximate position of the row corresponding to the passed bookmark
  1430.     HRESULT GetApproximatePosition(const CBookmarkBase* pBookmark, ULONG* pPosition, ULONG* pcRows)
  1431.     {
  1432.         ATLASSERT(m_spRowset != NULL);
  1433.  
  1434.         CComPtr<IRowsetScroll> spRowsetScroll;
  1435.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
  1436.         if (SUCCEEDED(hr))
  1437.         {
  1438.             if (pBookmark != NULL)
  1439.                 hr = spRowsetScroll->GetApproximatePosition(NULL, pBookmark->GetSize(), pBookmark->GetBuffer(),
  1440.                         pPosition, pcRows);
  1441.             else
  1442.                 hr = spRowsetScroll->GetApproximatePosition(NULL, 0, NULL, pPosition, pcRows);
  1443.  
  1444.         }
  1445.         return hr;
  1446.  
  1447.     }
  1448.     // Move to a fractional position in the rowset
  1449.     HRESULT MoveToRatio(ULONG nNumerator, ULONG nDenominator, bool bForward = true)
  1450.     {
  1451.         ATLASSERT(m_spRowset != NULL);
  1452.         ULONG   nRowsFetched;
  1453.  
  1454.         CComPtr<IRowsetScroll> spRowsetScroll;
  1455.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
  1456.         if (FAILED(hr))
  1457.             return hr;
  1458.  
  1459.         ReleaseRows();
  1460.         HROW* phRow = &m_hRow;
  1461.         hr = spRowsetScroll->GetRowsAtRatio(NULL, NULL, nNumerator, nDenominator, (bForward) ? 1 : -1,
  1462.             &nRowsFetched, &phRow);
  1463.         // Note we're not using SUCCEEDED here, because we could get DB_S_ENDOFROWSET
  1464.         if (hr == S_OK)
  1465.             hr = GetData();
  1466.  
  1467.         return hr;
  1468.     }
  1469.  
  1470. // Implementation
  1471.     static const IID& GetIID()
  1472.     {
  1473.         return IID_IRowset;
  1474.     }
  1475.     IRowset* GetInterface() const
  1476.     {
  1477.         return m_spRowset;
  1478.     }
  1479.     IRowset** GetInterfacePtr()
  1480.     {
  1481.         return &m_spRowset;
  1482.     }
  1483.     void SetupOptionalRowsetInterfaces()
  1484.     {
  1485.         // Cache IRowsetChange if available
  1486.         if (m_spRowset != NULL)
  1487.             m_spRowset->QueryInterface(&m_spRowsetChange);
  1488.     }
  1489.     HRESULT BindFinished() const { return S_OK; }
  1490.     void    SetAccessor(CAccessorBase* pAccessor)
  1491.     {
  1492.         m_pAccessor = pAccessor;
  1493.     }
  1494.  
  1495.     CComPtr<IRowset>        m_spRowset;
  1496.     CComPtr<IRowsetChange>  m_spRowsetChange;
  1497.     CAccessorBase*          m_pAccessor;
  1498.     HROW                    m_hRow;
  1499. };
  1500.  
  1501.  
  1502. ///////////////////////////////////////////////////////////////////////////
  1503. // class CBulkRowset
  1504.  
  1505. class CBulkRowset : public CRowset
  1506. {
  1507. public:
  1508.     CBulkRowset()
  1509.     {
  1510.         // Default the number of rows to bulk fetch to 10
  1511.         m_nRows = 10;
  1512.         m_hr    = S_OK;
  1513.         m_phRow = NULL;
  1514.     }
  1515.     CBulkRowset::~CBulkRowset()
  1516.     {
  1517.         Close();
  1518.  
  1519.         delete [] m_phRow;
  1520.     }
  1521.     // Set the number of row handles that will be retrieved in each
  1522.     // bulk row fetch. The default is 10 and this function must be called
  1523.     // before Open if you wish to change it.
  1524.     void SetRows(ULONG nRows)
  1525.     {
  1526.         // This function must be called before the memory is allocated
  1527.         // during binding
  1528.         ATLASSERT(m_phRow == NULL);
  1529.         m_nRows = nRows;
  1530.     }
  1531.     // AddRef all the currently retrieved row handles
  1532.     HRESULT AddRefRows()
  1533.     {
  1534.         ATLASSERT(m_spRowset != NULL);
  1535.         return m_spRowset->AddRefRows(m_nCurrentRows, m_phRow, NULL, NULL);
  1536.     }
  1537.     // Release all the currently retrieved row handles
  1538.     HRESULT ReleaseRows()
  1539.     {
  1540.         ATLASSERT(m_spRowset != NULL);
  1541.         // We're going to Release the rows so reset the current row position
  1542.         m_nCurrentRow = 0;
  1543.         m_hRow        = NULL;
  1544.         return m_spRowset->ReleaseRows(m_nCurrentRows, m_phRow, NULL, NULL, NULL);
  1545.     }
  1546.     // Move to the first record
  1547.     HRESULT MoveFirst()
  1548.     {
  1549.         ATLASSERT(m_spRowset != NULL);
  1550.         ReleaseRows();
  1551.  
  1552.         // Cause MoveNext to perform a new bulk fetch
  1553.         m_nCurrentRow  = m_nRows;
  1554.  
  1555.         HRESULT hr = m_spRowset->RestartPosition(NULL);
  1556.         if (FAILED(hr))
  1557.             return hr;
  1558.  
  1559.         // Get the data
  1560.         return MoveNext();
  1561.     }
  1562.     // Move to the next record
  1563.     HRESULT MoveNext()
  1564.     {
  1565.         ATLASSERT(m_spRowset != NULL);
  1566.         ATLASSERT(m_phRow    != NULL);
  1567.  
  1568.         // Move to the next record in the buffer
  1569.         m_nCurrentRow++;
  1570.  
  1571.         // Have we reached the end of the buffer?
  1572.         if (m_nCurrentRow >= m_nCurrentRows)
  1573.         {
  1574.             // If we've reached the end of the buffer and we had a non S_OK HRESULT from
  1575.             // the last call to GetNextRows then return that HRESULT now.
  1576.             if (m_hr != S_OK)
  1577.                 return m_hr;
  1578.  
  1579.             // We've finished with these rows so we need some more
  1580.             // First release any HROWs that we have
  1581.             ReleaseRows();
  1582.  
  1583.             m_hr = m_spRowset->GetNextRows(NULL, 0, m_nRows, &m_nCurrentRows, &m_phRow);
  1584.             // If we have an error HRESULT or we haven't retrieved any rows then return
  1585.             // the HRESULT now.
  1586.             if (FAILED(m_hr) || m_nCurrentRows == 0)
  1587.                 return m_hr;
  1588.         }
  1589.  
  1590.         // Get the data for the current row
  1591.         m_hRow = m_phRow[m_nCurrentRow];
  1592.         return GetData();
  1593.     }
  1594.     // Move to the previous record
  1595.     HRESULT MovePrev()
  1596.     {
  1597.         ATLASSERT(m_spRowset != NULL);
  1598.         ATLASSERT(m_phRow    != NULL);
  1599.  
  1600.         // Check if we're at the start of the block
  1601.         if (m_nCurrentRow == 0)
  1602.         {
  1603.             ReleaseRows();
  1604.  
  1605.             // Go back the amount of rows in the block - 1 and fetch forward
  1606.             m_hr = m_spRowset->GetNextRows(NULL, -(LONG)m_nRows-1, m_nRows, &m_nCurrentRows, &m_phRow);
  1607.  
  1608.             // Set the current record to the end of the new block
  1609.             m_nCurrentRow = m_nCurrentRows - 1;
  1610.  
  1611.             // If we have an error HRESULT or we haven't retrieved any rows then return
  1612.             // the HRESULT now.
  1613.             if (FAILED(m_hr) || m_nCurrentRows == 0)
  1614.                 return m_hr;
  1615.         }
  1616.         else
  1617.         {
  1618.             // Move back a row in the block
  1619.             m_nCurrentRow--;
  1620.         }
  1621.  
  1622.         // Get the data for the current row
  1623.         m_hRow = m_phRow[m_nCurrentRow];
  1624.         return GetData();
  1625.     }
  1626.     // Move to the last record
  1627.     HRESULT MoveLast()
  1628.     {
  1629.         ReleaseRows();
  1630.         return CRowset::MoveLast();
  1631.     }
  1632.     // Move to the passed bookmark
  1633.     HRESULT MoveToBookmark(const CBookmarkBase& bookmark, LONG lSkip = 0)
  1634.     {
  1635.         ATLASSERT(m_spRowset != NULL);
  1636.         CComPtr<IRowsetLocate> spLocate;
  1637.         HRESULT hr = m_spRowset->QueryInterface(&spLocate);
  1638.         if (FAILED(hr))
  1639.             return hr;
  1640.  
  1641.         ReleaseRows();
  1642.         m_hr = spLocate->GetRowsAt(NULL, NULL, bookmark.GetSize(), bookmark.GetBuffer(),
  1643.             lSkip, m_nRows, &m_nCurrentRows, &m_phRow);
  1644.         if (m_hr != S_OK || m_nCurrentRows == 0)
  1645.             return m_hr;
  1646.  
  1647.         // Get the data
  1648.         m_hRow = m_phRow[m_nCurrentRow];
  1649.         return GetData();
  1650.     }
  1651.     // Move to a fractional position in the rowset
  1652.     HRESULT MoveToRatio(ULONG nNumerator, ULONG nDenominator)
  1653.     {
  1654.         ATLASSERT(m_spRowset != NULL);
  1655.  
  1656.         CComPtr<IRowsetScroll> spRowsetScroll;
  1657.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
  1658.         if (FAILED(hr))
  1659.             return hr;
  1660.  
  1661.         ReleaseRows();
  1662.         m_hr = spRowsetScroll->GetRowsAtRatio(NULL, NULL, nNumerator, nDenominator, m_nRows, &m_nCurrentRows, &m_phRow);
  1663.         if (m_hr != S_OK || m_nCurrentRows == 0)
  1664.             return m_hr;
  1665.  
  1666.         // Get the data
  1667.         m_hRow = m_phRow[m_nCurrentRow];
  1668.         return GetData();
  1669.     }
  1670.     // Insert the current record
  1671.     HRESULT Insert(int nAccessor = 0, bool bGetHRow = false)
  1672.     {
  1673.         ReleaseRows();
  1674.         return CRowset::Insert(nAccessor, bGetHRow);
  1675.     }
  1676.  
  1677. // Implementation
  1678.     HRESULT BindFinished()
  1679.     {
  1680.         // No rows in the buffer yet
  1681.         m_nCurrentRows = 0;
  1682.         // Cause MoveNext to automatically perform a new bulk fetch the first time
  1683.         m_nCurrentRow  = m_nRows;
  1684.  
  1685.         m_phRow = NULL;
  1686.         ATLTRY(m_phRow = new HROW[m_nRows]);
  1687.         if (m_phRow == NULL)
  1688.             return E_OUTOFMEMORY;
  1689.  
  1690.         return S_OK;
  1691.     }
  1692.  
  1693.     HRESULT m_hr;           // HRESULT to return from MoveNext at end of buffer
  1694.     HROW*   m_phRow;        // Pointer to array of HROWs for each row in buffer
  1695.     ULONG   m_nRows;        // Number of rows that will fit in the buffer
  1696.     ULONG   m_nCurrentRows; // Number of rows currently in the buffer
  1697.     ULONG   m_nCurrentRow;
  1698. };
  1699.  
  1700. ///////////////////////////////////////////////////////////////////////////
  1701. // class CArrayRowset
  1702. //
  1703. // Allows you to access a rowset with an array syntax
  1704.  
  1705. template <class T, class TRowset = CRowset>
  1706. class CArrayRowset :
  1707.     public CVirtualBuffer<T>,
  1708.     public TRowset
  1709. {
  1710. public:
  1711.     CArrayRowset(int nMax = 100000) : CVirtualBuffer<T>(nMax)
  1712.     {
  1713.         m_nRowsRead = 0;
  1714.     }
  1715.     T& operator[](int nRow)
  1716.     {
  1717.         ATLASSERT(nRow >= 0);
  1718.         HRESULT hr;
  1719.         T* m_pCurrent = m_pBase + m_nRowsRead;
  1720.  
  1721.         // Retrieve the row if we haven't retrieved it already
  1722.         while ((ULONG)nRow >= m_nRowsRead)
  1723.         {
  1724.             m_pAccessor->SetBuffer((BYTE*)m_pCurrent);
  1725.             __try
  1726.             {
  1727.                 // Get the row
  1728.                 hr = MoveNext();
  1729.                 if (hr != S_OK)
  1730.                 {
  1731.                     *((char*)0) = 0; // Force exception
  1732.                 }
  1733.             }
  1734.             __except(Except(GetExceptionInformation()))
  1735.             {
  1736.             }
  1737.             m_nRowsRead++;
  1738.             m_pCurrent++;
  1739.         }
  1740.  
  1741.         return *(m_pBase + nRow);
  1742.     }
  1743.  
  1744.     HRESULT Snapshot()
  1745.     {
  1746.         ATLASSERT(m_nRowsRead == 0);
  1747.         ATLASSERT(m_spRowset != NULL);
  1748.         HRESULT hr = MoveFirst();
  1749.         if (FAILED(hr))
  1750.             return hr;
  1751.         do
  1752.         {
  1753.             Write(*(T*)m_pAccessor->GetBuffer());
  1754.             m_nRowsRead++;
  1755.             hr = MoveNext();
  1756.         } while (SUCCEEDED(hr) &&  hr != DB_S_ENDOFROWSET);
  1757.  
  1758.         return (hr == DB_S_ENDOFROWSET) ? S_OK : hr;
  1759.     }
  1760.  
  1761.  
  1762. // Implementation
  1763.     ULONG   m_nRowsRead;
  1764. };
  1765.  
  1766. // Used when you don't need any parameters or output columns
  1767. class CNoAccessor
  1768. {
  1769. public:
  1770.     // We don't need any typedef's here as the default
  1771.     // global typedef is not to have any parameters and
  1772.     // output columns.
  1773.     HRESULT BindColumns(IUnknown*) { return S_OK; }
  1774.     HRESULT BindParameters(HACCESSOR*, ICommand*, void**) { return S_OK; }
  1775.     void    Close() { }
  1776.     HRESULT ReleaseAccessors(IUnknown*) { return S_OK; }
  1777. };
  1778.  
  1779. // Used when a rowset will not be returned from the command
  1780. class CNoRowset
  1781. {
  1782. public:
  1783.     HRESULT             BindFinished() { return S_OK; }
  1784.     void                Close() { }
  1785.     static const IID&   GetIID() { return IID_NULL; }
  1786.     IRowset*            GetInterface() const { return NULL; }
  1787.     IRowset**           GetInterfacePtr() { return NULL; }
  1788.     void                SetAccessor(void*) { }
  1789.     void                SetupOptionalRowsetInterfaces() { }
  1790. };
  1791.  
  1792. ///////////////////////////////////////////////////////////////////////////
  1793. // class CAccessor
  1794.  
  1795. // T is the class that contains the data that will be accessed.
  1796. template <class T>
  1797. class CAccessor :
  1798.     public T,
  1799.     public CAccessorBase
  1800. {
  1801. public:
  1802. // Implementation
  1803.     // Free's any columns in the current record that need to be freed.
  1804.     // E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
  1805.     void FreeRecordMemory(IRowset* /* pRowset */)
  1806.     {
  1807.         ULONG nColumns;
  1808.         ULONG i;
  1809.  
  1810.         for (i = 0; i < GetNumAccessors(); i++)
  1811.         {
  1812.             // Passing in m_pBuffer tells the column entry maps to free the
  1813.             // memory for the types if appropriate
  1814.             _GetBindEntries(&nColumns, NULL, i, NULL, m_pBuffer);
  1815.         }
  1816.     }
  1817.     HRESULT BindColumns(IUnknown* pUnk)
  1818.     {
  1819.         HRESULT hr;
  1820.         ULONG   nAccessors;
  1821.         ULONG   nSize;
  1822.         nAccessors = _OutputColumnsClass::_GetNumAccessors();
  1823.  
  1824.         SetBuffer((BYTE*)this);
  1825.  
  1826.         nSize = sizeof(T);
  1827.         hr = BindAccessors(nAccessors, nSize, pUnk);
  1828.         return hr;
  1829.     }
  1830.     HRESULT BindAccessors(ULONG nAccessors, ULONG nSize, IUnknown* pUnk)
  1831.     {
  1832.         ATLASSERT(pUnk != NULL);
  1833.         HRESULT hr;
  1834.  
  1835.         CComPtr<IAccessor> spAccessor;
  1836.         hr = pUnk->QueryInterface(&spAccessor);
  1837.         if (SUCCEEDED(hr))
  1838.         {
  1839.             // Allocate the accessor memory if we haven't done so yet
  1840.             if (m_pAccessorInfo == NULL)
  1841.             {
  1842.                 hr = AllocateAccessorMemory(nAccessors);
  1843.                 if (FAILED(hr))
  1844.                     return hr;
  1845.             }
  1846.  
  1847.             for (ULONG i=0; i<nAccessors && SUCCEEDED(hr); i++)
  1848.                 hr = BindAccessor(spAccessor, i, nSize);
  1849.         }
  1850.  
  1851.         return hr;
  1852.     }
  1853.  
  1854.     HRESULT BindAccessor(IAccessor* pAccessor, ULONG nAccessor, ULONG nSize)
  1855.     {
  1856.         DBBINDING*  pBindings = NULL;
  1857.         ULONG       nColumns;
  1858.         bool        bAuto;
  1859.         HRESULT     hr;
  1860.  
  1861.         // First time just get the number of entries by passing in &nColumns
  1862.         _OutputColumnsClass::_GetBindEntries(&nColumns, NULL, nAccessor, NULL);
  1863.  
  1864.         // Now allocate the binding structures
  1865.         ATLTRY(pBindings = new DBBINDING[nColumns]);
  1866.         if (pBindings == NULL)
  1867.             return E_OUTOFMEMORY;
  1868.  
  1869.         // Now get the bind entries
  1870.         hr = _OutputColumnsClass::_GetBindEntries(&nColumns, pBindings, nAccessor, &bAuto);
  1871.         if (FAILED(hr))
  1872.             return hr;
  1873.  
  1874.         m_pAccessorInfo[nAccessor].bAutoAccessor = bAuto;
  1875.         hr = BindEntries(pBindings, nColumns, &m_pAccessorInfo[nAccessor].hAccessor, nSize, pAccessor);
  1876.         delete [] pBindings;
  1877.         return hr;
  1878.     }
  1879.  
  1880.     HRESULT BindParameters(HACCESSOR* pHAccessor, ICommand* pCommand, void** ppParameterBuffer)
  1881.     {
  1882.         HRESULT hr = S_OK;
  1883.         // In the static accessor case, the parameter buffer will be T
  1884.         *ppParameterBuffer = this;
  1885.  
  1886.         // Only bind the parameters if we haven't already done it
  1887.         if (*pHAccessor == NULL)
  1888.         {
  1889.             ULONG   nColumns;
  1890.             _ParamClass::_GetParamEntries(&nColumns, NULL);
  1891.  
  1892.             DBBINDING* pBinding = NULL;
  1893.             ATLTRY(pBinding = new DBBINDING[nColumns]);
  1894.             if (pBinding == NULL)
  1895.                 return E_OUTOFMEMORY;
  1896.  
  1897.             hr = _ParamClass::_GetParamEntries(&nColumns, pBinding);
  1898.             if (SUCCEEDED(hr))
  1899.             {
  1900.                 // Get the IAccessor from the passed IUnknown
  1901.                 CComPtr<IAccessor> spAccessor;
  1902.                 hr = pCommand->QueryInterface(&spAccessor);
  1903.                 if (SUCCEEDED(hr))
  1904.                 {
  1905.                     hr = BindEntries(pBinding, nColumns, pHAccessor, sizeof(T),
  1906.                         spAccessor);
  1907.                 }
  1908.             }
  1909.             delete [] pBinding;
  1910.         }
  1911.         return hr;
  1912.     }
  1913. };
  1914.  
  1915.  
  1916. ///////////////////////////////////////////////////////////////////////////
  1917. // CDynamicAccessor
  1918.  
  1919. class CDynamicAccessor :
  1920.     public CAccessorBase
  1921. {
  1922. public:
  1923.     CDynamicAccessor()
  1924.     {
  1925.         m_nColumns        = 0;
  1926.         m_pColumnInfo     = NULL;
  1927.         m_pStringsBuffer  = NULL;
  1928.     };
  1929.     ~CDynamicAccessor()
  1930.     {
  1931.         Close();
  1932.     }
  1933.     void Close()
  1934.     {
  1935.         if (m_pColumnInfo != NULL)
  1936.         {
  1937.             CoTaskMemFree(m_pColumnInfo);
  1938.             m_pColumnInfo = NULL;
  1939.         }
  1940.  
  1941.         // Free the memory for the string buffer returned by IColumnsInfo::GetColumnInfo,
  1942.         // if necessary
  1943.         if (m_pStringsBuffer != NULL)
  1944.         {
  1945.             CoTaskMemFree(m_pStringsBuffer);
  1946.             m_pStringsBuffer = NULL;
  1947.         }
  1948.  
  1949.         delete [] m_pBuffer;
  1950.         m_pBuffer = NULL;
  1951.         m_nColumns = 0;
  1952.  
  1953.         CAccessorBase::Close();
  1954.     }
  1955.     bool GetColumnType(ULONG nColumn, DBTYPE* pType) const
  1956.     {
  1957.         if (TranslateColumnNo(nColumn))
  1958.         {
  1959.             *pType = m_pColumnInfo[nColumn].wType;
  1960.             return true;
  1961.         }
  1962.         else
  1963.             return false;
  1964.     }
  1965.     bool GetColumnFlags(ULONG nColumn, DBCOLUMNFLAGS* pFlags) const
  1966.     {
  1967.         if (TranslateColumnNo(nColumn))
  1968.         {
  1969.             *pFlags = m_pColumnInfo[nColumn].dwFlags;
  1970.             return true;
  1971.         }
  1972.         else
  1973.             return false;
  1974.     }
  1975.     bool GetOrdinal(TCHAR* pColumnName, ULONG* pOrdinal) const
  1976.     {
  1977.         ATLASSERT(pColumnName != NULL);
  1978.         ULONG nColumn;
  1979.         if (GetInternalColumnNo(pColumnName, &nColumn))
  1980.         {
  1981.             *pOrdinal = m_pColumnInfo[nColumn].iOrdinal;
  1982.             return true;
  1983.         }
  1984.         else
  1985.             return false;
  1986.     }
  1987.  
  1988.     void* GetValue(ULONG nColumn) const
  1989.     {
  1990.         if (TranslateColumnNo(nColumn))
  1991.             return _GetDataPtr(nColumn);
  1992.         else
  1993.             return NULL;
  1994.     }
  1995.  
  1996.     void* GetValue(TCHAR* pColumnName) const
  1997.     {
  1998.         ATLASSERT(pColumnName != NULL);
  1999.         ULONG nColumn;
  2000.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2001.             return _GetDataPtr(nColumn);
  2002.         else
  2003.             return NULL;    // Not Found
  2004.     }
  2005.  
  2006.     template <class ctype>
  2007.     void _GetValue(ULONG nColumn, ctype* pData) const
  2008.     {
  2009.         ATLASSERT(pData != NULL);
  2010.         ATLASSERT(m_pColumnInfo[nColumn].ulColumnSize == sizeof(ctype));
  2011.         ctype* pBuffer = (ctype*)_GetDataPtr(nColumn);
  2012.         *pData = *pBuffer;
  2013.     }
  2014.     template <class ctype>
  2015.     void _SetValue(ULONG nColumn, const ctype& data)
  2016.     {
  2017.         ATLASSERT(m_pColumnInfo[nColumn].ulColumnSize == sizeof(ctype));
  2018.         ctype* pBuffer = (ctype*)_GetDataPtr(nColumn);
  2019.         *pBuffer = (ctype)data;
  2020.     }
  2021.     template <class ctype>
  2022.     bool GetValue(ULONG nColumn, ctype* pData) const
  2023.     {
  2024.         if (TranslateColumnNo(nColumn))
  2025.         {
  2026.             _GetValue(nColumn, pData);
  2027.             return true;
  2028.         }
  2029.         return false;
  2030.     }
  2031.     template <class ctype>
  2032.     bool SetValue(ULONG nColumn, const ctype& data)
  2033.     {
  2034.         if (TranslateColumnNo(nColumn))
  2035.         {
  2036.             _SetValue(nColumn, data);
  2037.             return true;
  2038.         }
  2039.         return false;
  2040.     }
  2041.     template <class ctype>
  2042.     bool GetValue(TCHAR *pColumnName, ctype* pData) const
  2043.     {
  2044.         ATLASSERT(pColumnName != NULL);
  2045.         ULONG nColumn;
  2046.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2047.         {
  2048.             _GetValue(nColumn, pData);
  2049.             return true;
  2050.         }
  2051.         return false;
  2052.     }
  2053.     template <class ctype>
  2054.     bool SetValue(TCHAR *pColumnName, const ctype& data)
  2055.     {
  2056.         ATLASSERT(pColumnName != NULL);
  2057.         ULONG nColumn;
  2058.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2059.         {
  2060.             _SetValue(nColumn, data);
  2061.             return true;
  2062.         }
  2063.         return false;
  2064.     }
  2065.     bool GetLength(ULONG nColumn, ULONG* pLength) const
  2066.     {
  2067.         ATLASSERT(pLength != NULL);
  2068.         if (TranslateColumnNo(nColumn))
  2069.         {
  2070.             *pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
  2071.             return true;
  2072.         }
  2073.         else
  2074.             return false;
  2075.     }
  2076.     bool SetLength(ULONG nColumn, ULONG nLength)
  2077.     {
  2078.         if (TranslateColumnNo(nColumn))
  2079.         {
  2080.             *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
  2081.             return true;
  2082.         }
  2083.         else
  2084.             return false;
  2085.     }
  2086.     bool GetLength(TCHAR* pColumnName, ULONG* pLength) const
  2087.     {
  2088.         ATLASSERT(pColumnName != NULL);
  2089.         ATLASSERT(pLength != NULL);
  2090.         ULONG nColumn;
  2091.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2092.         {
  2093.             *pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
  2094.             return true;
  2095.         }
  2096.         else
  2097.             return false;
  2098.     }
  2099.     bool SetLength(TCHAR* pColumnName, ULONG nLength)
  2100.     {
  2101.         ATLASSERT(pColumnName != NULL);
  2102.         ULONG nColumn;
  2103.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2104.         {
  2105.             *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
  2106.             return true;
  2107.         }
  2108.         else
  2109.             return false;
  2110.     }
  2111.     bool GetStatus(ULONG nColumn, DBSTATUS* pStatus) const
  2112.     {
  2113.         ATLASSERT(pStatus != NULL);
  2114.         if (TranslateColumnNo(nColumn))
  2115.         {
  2116.             *pStatus = *(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG)));
  2117.             return true;
  2118.         }
  2119.         else
  2120.             return false;
  2121.     }
  2122.     bool SetStatus(ULONG nColumn, DBSTATUS status)
  2123.     {
  2124.         if (TranslateColumnNo(nColumn))
  2125.         {
  2126.             *(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG))) = status;
  2127.             return true;
  2128.         }
  2129.         else
  2130.             return false;
  2131.     }
  2132.     bool GetStatus(TCHAR* pColumnName, DBSTATUS* pStatus) const
  2133.     {
  2134.         ATLASSERT(pColumnName != NULL);
  2135.         ATLASSERT(pStatus != NULL);
  2136.         ULONG nColumn;
  2137.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2138.         {
  2139.             *pStatus = *(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG));
  2140.             return true;
  2141.         }
  2142.         else
  2143.             return false;
  2144.     }
  2145.     bool SetStatus(TCHAR* pColumnName, DBSTATUS status)
  2146.     {
  2147.         ATLASSERT(pColumnName != NULL);
  2148.         ULONG nColumn;
  2149.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2150.         {
  2151.             *(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG)) = status;
  2152.             return true;
  2153.         }
  2154.         else
  2155.             return false;
  2156.     }
  2157.  
  2158.     // Returns true if a bookmark is available
  2159.     HRESULT GetBookmark(CBookmark<>* pBookmark) const
  2160.     {
  2161.         HRESULT hr;
  2162.         if (m_pColumnInfo->iOrdinal == 0)
  2163.             hr = pBookmark->SetBookmark(m_pColumnInfo->ulColumnSize, (BYTE*)_GetDataPtr(0));
  2164.         else
  2165.             hr = E_FAIL;
  2166.         return hr;
  2167.     }
  2168.  
  2169.     ULONG GetColumnCount() const
  2170.     {
  2171.         return m_nColumns;
  2172.     }
  2173.  
  2174.     LPOLESTR GetColumnName(ULONG nColumn) const
  2175.     {
  2176.         if (TranslateColumnNo(nColumn))
  2177.             return m_pColumnInfo[nColumn].pwszName;
  2178.         else
  2179.             return NULL;
  2180.     }
  2181.  
  2182.     HRESULT GetColumnInfo(IRowset* pRowset, ULONG* pColumns, DBCOLUMNINFO** ppColumnInfo)
  2183.     {
  2184.         CComPtr<IColumnsInfo> spColumnsInfo;
  2185.         HRESULT hr = pRowset->QueryInterface(&spColumnsInfo);
  2186.         if (SUCCEEDED(hr))
  2187.             hr = spColumnsInfo->GetColumnInfo(pColumns, ppColumnInfo, &m_pStringsBuffer);
  2188.  
  2189.         return hr;
  2190.     }
  2191.  
  2192.     HRESULT AddBindEntry(const DBCOLUMNINFO& info)
  2193.     {
  2194.         m_pColumnInfo = (DBCOLUMNINFO*)CoTaskMemRealloc(m_pColumnInfo, (m_nColumns + 1) * sizeof(DBCOLUMNINFO));
  2195.         if (m_pColumnInfo == NULL)
  2196.             return E_OUTOFMEMORY;
  2197.  
  2198.         m_pColumnInfo[m_nColumns] = info;
  2199.         m_nColumns++;
  2200.  
  2201.         return S_OK;
  2202.     }
  2203.  
  2204. // Implementation
  2205.     // Free's any columns in the current record that need to be freed.
  2206.     // E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
  2207.     void FreeRecordMemory(IRowset* pRowset)
  2208.     {
  2209.         ULONG i;
  2210.  
  2211.         for (i = 0; i < m_nColumns; i++)
  2212.             CAccessorBase::FreeType(m_pColumnInfo[i].wType, (BYTE*)_GetDataPtr(i), pRowset);
  2213.     }
  2214.     void* _GetDataPtr(ULONG nColumn) const
  2215.     {
  2216.         return m_pBuffer + (ULONG)m_pColumnInfo[nColumn].pTypeInfo;
  2217.     }
  2218.     bool GetInternalColumnNo(TCHAR* pColumnName, ULONG* pColumn) const
  2219.     {
  2220.         ATLASSERT(pColumnName != NULL);
  2221.         ATLASSERT(pColumn != NULL);
  2222.         USES_CONVERSION;
  2223.         ULONG       i;
  2224.         ULONG       nSize = (lstrlen(pColumnName) + 1) * sizeof(OLECHAR);
  2225.         OLECHAR*    pOleColumnName = T2OLE(pColumnName);
  2226.  
  2227.         // Search through the columns trying to find a match
  2228.         for (i = 0; i < m_nColumns; i++)
  2229.         {
  2230.             if (m_pColumnInfo[i].pwszName != NULL &&
  2231.                 memcmp(m_pColumnInfo[i].pwszName, pOleColumnName, nSize) == 0)
  2232.                 break;
  2233.         }
  2234.         if (i < m_nColumns)
  2235.         {
  2236.             *pColumn = i;
  2237.             return true;
  2238.         }
  2239.         else
  2240.             return false;   // Not Found
  2241.     }
  2242.     HRESULT BindColumns(IUnknown* pUnk)
  2243.     {
  2244.         ATLASSERT(pUnk != NULL);
  2245.         CComPtr<IAccessor> spAccessor;
  2246.         HRESULT hr = pUnk->QueryInterface(&spAccessor);
  2247.         if (FAILED(hr))
  2248.             return hr;
  2249.  
  2250.         ULONG   i;
  2251.         ULONG   nOffset = 0, nLengthOffset, nStatusOffset;
  2252.  
  2253.         // If the user hasn't specifed the column information to bind by calling AddBindEntry then
  2254.         // we get it ourselves
  2255.         if (m_pColumnInfo == NULL)
  2256.         {
  2257.             CComPtr<IColumnsInfo> spColumnsInfo;
  2258.             hr = pUnk->QueryInterface(&spColumnsInfo);
  2259.             if (FAILED(hr))
  2260.                 return hr;
  2261.  
  2262.             hr = spColumnsInfo->GetColumnInfo(&m_nColumns, &m_pColumnInfo, &m_pStringsBuffer);
  2263.             if (FAILED(hr))
  2264.                 return hr;
  2265.  
  2266.             m_bOverride = false;
  2267.         }
  2268.         else
  2269.             m_bOverride = true;
  2270.  
  2271.         DBBINDING* pBinding = NULL;
  2272.         ATLTRY(pBinding= new DBBINDING[m_nColumns]);
  2273.         if (pBinding == NULL)
  2274.             return E_OUTOFMEMORY;
  2275.  
  2276.         DBBINDING* pCurrent = pBinding;
  2277.         DBOBJECT*  pObject;
  2278.         for (i = 0; i < m_nColumns; i++)
  2279.         {
  2280.             // If it's a BLOB or the column size is large enough for us to treat it as
  2281.             // a BLOB then we also need to set up the DBOBJECT structure.
  2282.             if (m_pColumnInfo[i].ulColumnSize > 1024 || m_pColumnInfo[i].wType == DBTYPE_IUNKNOWN)
  2283.             {
  2284.                 pObject = NULL;
  2285.                 ATLTRY(pObject = new DBOBJECT);
  2286.                 if (pObject == NULL)
  2287.                     return E_OUTOFMEMORY;
  2288.                 pObject->dwFlags = STGM_READ;
  2289.                 pObject->iid     = IID_ISequentialStream;
  2290.                 m_pColumnInfo[i].wType      = DBTYPE_IUNKNOWN;
  2291.                 m_pColumnInfo[i].ulColumnSize   = sizeof(IUnknown*);
  2292.             }
  2293.             else
  2294.                 pObject = NULL;
  2295.  
  2296.             // If column is of type STR or WSTR increase length by 1
  2297.             // to accommodate the NULL terminator.
  2298.             if (m_pColumnInfo[i].wType == DBTYPE_STR ||
  2299.                 m_pColumnInfo[i].wType == DBTYPE_WSTR)
  2300.                     m_pColumnInfo[i].ulColumnSize += 1;
  2301.  
  2302.             nLengthOffset = AddOffset(nOffset, m_pColumnInfo[i].ulColumnSize);
  2303.             nStatusOffset = AddOffset(nLengthOffset, sizeof(ULONG));
  2304.             Bind(pCurrent, m_pColumnInfo[i].iOrdinal, m_pColumnInfo[i].wType,
  2305.                 m_pColumnInfo[i].ulColumnSize, m_pColumnInfo[i].bPrecision, m_pColumnInfo[i].bScale,
  2306.                 DBPARAMIO_NOTPARAM, nOffset,
  2307.                 nLengthOffset, nStatusOffset, pObject);
  2308.             pCurrent++;
  2309.  
  2310.             // Note that, as we're not using this for anything else, we're using the
  2311.             // pTypeInfo element to store the offset to our data.
  2312.             m_pColumnInfo[i].pTypeInfo = (ITypeInfo*)nOffset;
  2313.  
  2314.             nOffset = AddOffset(nStatusOffset, sizeof(DBSTATUS));
  2315.         }
  2316.         // Allocate the accessor memory if we haven't done so yet
  2317.         if (m_pAccessorInfo == NULL)
  2318.         {
  2319.             hr = AllocateAccessorMemory(1); // We only have one accessor
  2320.             if (FAILED(hr))
  2321.             {
  2322.                 delete [] pBinding;
  2323.                 return hr;
  2324.             }
  2325.             m_pAccessorInfo->bAutoAccessor = TRUE;
  2326.         }
  2327.  
  2328.         // Allocate enough memory for the data buffer and tell the rowset
  2329.         // Note that the rowset will free the memory in its destructor.
  2330.         m_pBuffer = NULL;
  2331.         ATLTRY(m_pBuffer = new BYTE[nOffset]);
  2332.         if (m_pBuffer == NULL)
  2333.         {
  2334.             delete [] pBinding;
  2335.             return E_OUTOFMEMORY;
  2336.         }
  2337.         hr = BindEntries(pBinding, m_nColumns, &m_pAccessorInfo->hAccessor,
  2338.                 nOffset, spAccessor);
  2339.         delete [] pBinding;
  2340.  
  2341.         return hr;
  2342.     }
  2343.  
  2344.     static ULONG AddOffset(ULONG nCurrent, ULONG nAdd)
  2345.     {
  2346.         struct foobar
  2347.         {
  2348.             char    foo;
  2349.             long    bar;
  2350.         };
  2351.         ULONG nAlign = offsetof(foobar, bar);
  2352.  
  2353.         return nCurrent + nAdd + (nAdd % nAlign);;
  2354.     }
  2355.  
  2356.     // Translate the column number to the index into the column info array
  2357.     bool TranslateColumnNo(ULONG& nColumn) const
  2358.     {
  2359.         ATLASSERT(m_pColumnInfo != NULL);
  2360.         // If the user has overriden the binding then we need to search
  2361.         // through the column info for the ordinal number
  2362.         if (m_bOverride)
  2363.         {
  2364.             for (ULONG i = 0; i < m_nColumns; i++)
  2365.             {
  2366.                 if (m_pColumnInfo[i].iOrdinal == nColumn)
  2367.                 {
  2368.                     nColumn = i;
  2369.                     return true;
  2370.                 }
  2371.             }
  2372.             return false;
  2373.         }
  2374.         else
  2375.         {
  2376.             // Note that m_pColumnInfo->iOrdinal will be zero if have bound
  2377.             // a bookmark as the first entry, otherwise it will be 1.
  2378.             // If the column is out of range then return false
  2379.             if (nColumn > (m_nColumns - 1 + m_pColumnInfo->iOrdinal))
  2380.                 return false;
  2381.  
  2382.             // otherwise translate the column to an index into our internal
  2383.             // binding entries array
  2384.             nColumn -= m_pColumnInfo->iOrdinal;
  2385.             return true;
  2386.         }
  2387.     }
  2388.     typedef CDynamicAccessor _OutputColumnsClass;
  2389.     static bool HasOutputColumns() { return true; }
  2390.  
  2391.     ULONG               m_nColumns;
  2392.     DBCOLUMNINFO*       m_pColumnInfo;
  2393.     OLECHAR*            m_pStringsBuffer;
  2394.     bool                m_bOverride;
  2395. };
  2396.  
  2397.  
  2398. ///////////////////////////////////////////////////////////////////////////
  2399. // class CDynamicParameterAccessor
  2400.  
  2401. class CDynamicParameterAccessor : public CDynamicAccessor
  2402. {
  2403. // Constructors and Destructors
  2404. public:
  2405.     typedef CDynamicParameterAccessor _ParamClass;
  2406.     CDynamicParameterAccessor()
  2407.     {
  2408.         m_pParameterEntry       = NULL;
  2409.         m_pParameterBuffer      = NULL;
  2410.         m_ppParamName           = NULL;
  2411.         m_nParameterBufferSize  = 0;
  2412.         m_nParams               = 0;
  2413.     };
  2414.  
  2415.     ~CDynamicParameterAccessor()
  2416.     {
  2417.         delete [] m_pParameterEntry;
  2418.         if (m_ppParamName != NULL)
  2419.         {
  2420.             if (*m_ppParamName != NULL)
  2421.                 CoTaskMemFree(*m_ppParamName);
  2422.             delete [] m_ppParamName;
  2423.         }
  2424.         delete m_pParameterBuffer;
  2425.     };
  2426.     // nParam is the parameter number (offset from 1)
  2427.     bool GetParamType(ULONG nParam, DBTYPE* pType) const
  2428.     {
  2429.         ATLASSERT(pType != NULL);
  2430.         if (nParam == 0 || nParam > m_nParams)
  2431.             return false;
  2432.  
  2433.         *pType = m_pParameterEntry[nParam-1].wType;
  2434.         return true;
  2435.     }
  2436.     template <class ctype>
  2437.     bool GetParam(ULONG nParam, ctype* pData) const
  2438.     {
  2439.         ATLASSERT(pData != NULL);
  2440.         ctype* pBuffer = (ctype*)GetParam(nParam);
  2441.         if (pBuffer == NULL)
  2442.             return false;
  2443.         *pData = *pBuffer;
  2444.         return true;
  2445.  
  2446.     }
  2447.     template <class ctype>
  2448.     bool SetParam(ULONG nParam, ctype* pData)
  2449.     {
  2450.         ATLASSERT(pData != NULL);
  2451.         ctype* pBuffer = (ctype*)GetParam(nParam);
  2452.         if (pBuffer == NULL)
  2453.             return false;
  2454.         *pBuffer = *pData;
  2455.         return true;
  2456.  
  2457.     }
  2458.     template <class ctype>
  2459.     bool GetParam(TCHAR* pParamName, ctype* pData) const
  2460.     {
  2461.         ATLASSERT(pData != NULL);
  2462.         ctype* pBuffer = (ctype*)GetParam(pParamName);
  2463.         if (pBuffer == NULL)
  2464.             return false;
  2465.         *pData = *pBuffer;
  2466.         return true;
  2467.  
  2468.     }
  2469.     template <class ctype>
  2470.     bool SetParam(TCHAR* pParamName, ctype* pData)
  2471.     {
  2472.         ATLASSERT(pData != NULL);
  2473.         ctype* pBuffer = (ctype*)GetParam(pParamName);
  2474.         if (pBuffer == NULL)
  2475.             return false;
  2476.         *pBuffer = *pData;
  2477.         return true;
  2478.  
  2479.     }
  2480.     void* GetParam(ULONG nParam) const
  2481.     {
  2482.         if (nParam == 0 || nParam > m_nParams)
  2483.             return NULL;
  2484.         else
  2485.             return m_pParameterBuffer + m_pParameterEntry[nParam-1].obValue;
  2486.     }
  2487.     void* GetParam(TCHAR* pParamName) const
  2488.     {
  2489.         USES_CONVERSION;
  2490.         ULONG    i;
  2491.         ULONG       nSize = (lstrlen(pParamName) + 1) * sizeof(OLECHAR);
  2492.         OLECHAR*    pOleParamName = T2OLE(pParamName);
  2493.  
  2494.         for (i=0; i<m_nParams; i++)
  2495.         {
  2496.             if (memcmp(m_ppParamName[i], pOleParamName, nSize) == 0)
  2497.                 break;
  2498.         }
  2499.         if (i < m_nParams)
  2500.             return (m_pParameterBuffer + m_pParameterEntry[i].obValue);
  2501.         else
  2502.             return NULL;    // Not Found
  2503.     }
  2504.     // Get the number of parameters
  2505.     ULONG GetParamCount() const
  2506.     {
  2507.         return m_nParams;
  2508.     }
  2509.     // Get the parameter name for the passed parameter number
  2510.     LPOLESTR GetParamName(ULONG ulParam) const
  2511.     {
  2512.         ATLASSERT((ulParam >= 0) && (ulParam<m_nParams));
  2513.         return m_ppParamName[ulParam];
  2514.     }
  2515.  
  2516. // Implementation
  2517.     HRESULT BindParameters(HACCESSOR* pHAccessor, ICommand* pCommand,
  2518.                 void** ppParameterBuffer)
  2519.     {
  2520.         // If we have already bound the parameters then just return
  2521.         // the pointer to the parameter buffer
  2522.         if (*pHAccessor != NULL)
  2523.         {
  2524.             *ppParameterBuffer = m_pParameterBuffer;
  2525.             return S_OK;
  2526.         }
  2527.  
  2528.         CComPtr<IAccessor> spAccessor;
  2529.         HRESULT hr = pCommand->QueryInterface(&spAccessor);
  2530.         if (FAILED(hr))
  2531.             return hr;
  2532.  
  2533.         // Try to bind parameters if available
  2534.         CComPtr<ICommandWithParameters> spCommandParameters;
  2535.         hr = pCommand->QueryInterface(&spCommandParameters);
  2536.         if (FAILED(hr))
  2537.             return hr;
  2538.  
  2539.         ULONG           ulParams     = 0;
  2540.         DBPARAMINFO*    pParamInfo   = NULL;
  2541.         LPOLESTR        pNamesBuffer = NULL;
  2542.  
  2543.         // Get Parameter Information
  2544.         hr = spCommandParameters->GetParameterInfo(&ulParams, &pParamInfo,
  2545.                 &pNamesBuffer);
  2546.         if (FAILED(hr))
  2547.             return hr;
  2548.  
  2549.         // Create the parameter information for binding
  2550.         hr = AllocateParameterInfo(ulParams);
  2551.         if (FAILED(hr))
  2552.         {
  2553.             CoTaskMemFree(pParamInfo);
  2554.             CoTaskMemFree(pNamesBuffer);
  2555.             return hr;
  2556.         }
  2557.  
  2558.         ULONG nOffset = 0;
  2559.         DBBINDING* pCurrent = m_pParameterEntry;
  2560.         for (ULONG l=0; l<ulParams; l++)
  2561.         {
  2562.             m_pParameterEntry[l].eParamIO = 0;
  2563.             if (pParamInfo[l].dwFlags & DBPARAMFLAGS_ISINPUT)
  2564.                 m_pParameterEntry[l].eParamIO |= DBPARAMIO_INPUT;
  2565.  
  2566.             if (pParamInfo[l].dwFlags & DBPARAMFLAGS_ISOUTPUT)
  2567.                 m_pParameterEntry[l].eParamIO |= DBPARAMIO_OUTPUT;
  2568.  
  2569.             Bind(pCurrent, pParamInfo[l].iOrdinal, pParamInfo[l].wType,
  2570.                 pParamInfo[l].ulParamSize, pParamInfo[l].bPrecision, pParamInfo[l].bScale,
  2571.                 m_pParameterEntry[l].eParamIO, nOffset);
  2572.             pCurrent++;
  2573.  
  2574.             m_ppParamName[l] = pNamesBuffer;
  2575.             if (pNamesBuffer && *pNamesBuffer)
  2576.             {
  2577.                 // Search for the NULL termination character
  2578.                 while (*pNamesBuffer++)
  2579.                     ;
  2580.             }
  2581.             nOffset += pParamInfo[l].ulParamSize;
  2582.  
  2583.         }
  2584.  
  2585.         // Allocate memory for the new buffer
  2586.         m_pParameterBuffer = NULL;
  2587.         ATLTRY(m_pParameterBuffer = new BYTE[nOffset]);
  2588.         if (m_pParameterBuffer == NULL)
  2589.         {
  2590.             // Note that pNamesBuffer will be freed in the destructor
  2591.             // by freeing *m_ppParamName
  2592.             CoTaskMemFree(pParamInfo);
  2593.             return E_OUTOFMEMORY;
  2594.         }
  2595.         *ppParameterBuffer = m_pParameterBuffer;
  2596.         m_nParameterBufferSize = nOffset;
  2597.         m_nParams = ulParams;
  2598.         BindEntries(m_pParameterEntry, ulParams, pHAccessor, nOffset, spAccessor);
  2599.  
  2600.         CoTaskMemFree(pParamInfo);
  2601.  
  2602.         return S_OK;
  2603.     }
  2604.     bool HasParameters() const
  2605.     {
  2606.         return true;
  2607.     }
  2608.     HRESULT AllocateParameterInfo(int nParamEntries)
  2609.     {
  2610.         // Allocate memory for the bind structures
  2611.         m_pParameterEntry = NULL;
  2612.         ATLTRY(m_pParameterEntry = new DBBINDING[nParamEntries]);
  2613.         if (m_pParameterEntry == NULL)
  2614.             return E_OUTOFMEMORY;
  2615.  
  2616.         // Allocate memory to store the field names
  2617.         m_ppParamName = NULL;
  2618.         ATLTRY(m_ppParamName = new OLECHAR*[nParamEntries]);
  2619.         if (m_ppParamName == NULL)
  2620.             return E_OUTOFMEMORY;
  2621.         return S_OK;
  2622.     }
  2623.  
  2624. // Data Members
  2625.     // Number of parameters
  2626.     ULONG               m_nParams;
  2627.     // A pointer to the entry structures for each parameter
  2628.     DBBINDING*          m_pParameterEntry;
  2629.     // String names for the parameters
  2630.     OLECHAR**           m_ppParamName;
  2631.     // The size of the buffer where the parameters are stored
  2632.     ULONG               m_nParameterBufferSize;
  2633.     // A pointer to the buffer where the parameters are stored
  2634.     BYTE*               m_pParameterBuffer;
  2635. };
  2636.  
  2637.  
  2638. ///////////////////////////////////////////////////////////////////////////
  2639. // class CManualAccessor
  2640.  
  2641. class CManualAccessor :
  2642.     public CAccessorBase
  2643. {
  2644. public:
  2645.     CManualAccessor()
  2646.     {
  2647.         // By default we don't have any parameters unless CreateParameterAccessor is called
  2648.         m_pEntry          = NULL;
  2649.         m_nParameters     = 0;
  2650.         m_pParameterEntry = NULL;
  2651.         m_nColumns        = 0;
  2652.     }
  2653.     ~CManualAccessor()
  2654.     {
  2655.         delete [] m_pEntry;
  2656.         delete [] m_pParameterEntry;
  2657.     }
  2658.     HRESULT CreateAccessor(int nBindEntries, void* pBuffer, ULONG nBufferSize)
  2659.     {
  2660.         m_pBuffer     = (BYTE*)pBuffer;
  2661.         m_nBufferSize = nBufferSize;
  2662.         m_nColumns    = nBindEntries;
  2663.         m_nEntry      = 0;
  2664.  
  2665.         // If they've previously created some entries then free them
  2666.         delete [] m_pEntry;
  2667.         m_pEntry = NULL;
  2668.  
  2669.         // Allocate memory for the bind structures
  2670.         ATLTRY(m_pEntry = new DBBINDING[nBindEntries]);
  2671.         if (m_pEntry == NULL)
  2672.             return E_OUTOFMEMORY;
  2673.         else
  2674.             return S_OK;
  2675.     }
  2676.     HRESULT CreateParameterAccessor(int nBindEntries, void* pBuffer, ULONG nBufferSize)
  2677.     {
  2678.         m_pParameterBuffer     = (BYTE*)pBuffer;
  2679.         m_nParameterBufferSize = nBufferSize;
  2680.         m_nParameters          = nBindEntries;
  2681.         m_nCurrentParameter    = 0;
  2682.  
  2683.         // Allocate memory for the bind structures
  2684.         m_pParameterEntry = NULL;
  2685.         ATLTRY(m_pParameterEntry  = new DBBINDING[nBindEntries]);
  2686.         if (m_pParameterEntry == NULL)
  2687.             return E_OUTOFMEMORY;
  2688.         else
  2689.             return S_OK;
  2690.     }
  2691.     void AddBindEntry(ULONG nOrdinal, DBTYPE wType, ULONG nColumnSize,
  2692.             void* pData, void* pLength = NULL, void* pStatus = NULL)
  2693.     {
  2694.         ATLASSERT(m_nEntry < m_nColumns);
  2695.         ULONG   nLengthOffset, nStatusOffset;
  2696.  
  2697.         if (pStatus != NULL)
  2698.             nStatusOffset = (BYTE*)pStatus - m_pBuffer;
  2699.         else
  2700.             nStatusOffset = 0;
  2701.  
  2702.         if (pLength != NULL)
  2703.             nLengthOffset = (BYTE*)pLength - m_pBuffer;
  2704.         else
  2705.             nLengthOffset = 0;
  2706.  
  2707.         Bind(m_pEntry+m_nEntry, nOrdinal, wType, nColumnSize, 0, 0, DBPARAMIO_NOTPARAM,
  2708.             (BYTE*)pData - m_pBuffer, nLengthOffset, nStatusOffset);
  2709.  
  2710.         m_nEntry++;
  2711.     }
  2712.     void AddParameterEntry(ULONG nOrdinal, DBTYPE wType, ULONG nColumnSize,
  2713.             void* pData, void* pLength = NULL, void* pStatus = NULL,
  2714.             DBPARAMIO eParamIO = DBPARAMIO_INPUT)
  2715.     {
  2716.         ATLASSERT(m_nCurrentParameter < m_nParameters);
  2717.         ULONG   nLengthOffset, nStatusOffset;
  2718.  
  2719.         if (pStatus != NULL)
  2720.             nStatusOffset = (BYTE*)pStatus - m_pParameterBuffer;
  2721.         else
  2722.             nStatusOffset = 0;
  2723.  
  2724.         if (pLength != NULL)
  2725.             nLengthOffset = (BYTE*)pLength - m_pBuffer;
  2726.         else
  2727.             nLengthOffset = 0;
  2728.  
  2729.         Bind(m_pParameterEntry + m_nCurrentParameter, nOrdinal, wType, nColumnSize, 0, 0,
  2730.             eParamIO, (BYTE*)pData - m_pParameterBuffer, nLengthOffset, nStatusOffset);
  2731.  
  2732.         m_nCurrentParameter++;
  2733.     }
  2734.  
  2735. // Implementation
  2736.     // Free's any columns in the current record that need to be freed.
  2737.     // E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
  2738.     void FreeRecordMemory(IRowset* pRowset)
  2739.     {
  2740.         ULONG i;
  2741.  
  2742.         for (i = 0; i < m_nColumns; i++)
  2743.             CAccessorBase::FreeType(m_pEntry[i].wType, m_pBuffer + m_pEntry[i].obValue, pRowset);
  2744.     }
  2745.     HRESULT BindColumns(IUnknown* pUnk)
  2746.     {
  2747.         ATLASSERT(pUnk != NULL);
  2748.         CComPtr<IAccessor> spAccessor;
  2749.         HRESULT hr = pUnk->QueryInterface(&spAccessor);
  2750.         if (FAILED(hr))
  2751.             return hr;
  2752.  
  2753.         // Allocate the accessor memory if we haven't done so yet
  2754.         if (m_pAccessorInfo == NULL)
  2755.         {
  2756.             hr = AllocateAccessorMemory(1); // We only have one accessor
  2757.             if (FAILED(hr))
  2758.                 return hr;
  2759.             m_pAccessorInfo->bAutoAccessor = TRUE;
  2760.         }
  2761.  
  2762.         return BindEntries(m_pEntry, m_nColumns, &m_pAccessorInfo->hAccessor, m_nBufferSize, spAccessor);
  2763.     }
  2764.  
  2765.     HRESULT BindParameters(HACCESSOR* pHAccessor, ICommand* pCommand, void** ppParameterBuffer)
  2766.     {
  2767.         HRESULT hr;
  2768.         *ppParameterBuffer = m_pParameterBuffer;
  2769.  
  2770.         // Only bind the parameter if we haven't done so yet
  2771.         if (*pHAccessor == NULL)
  2772.         {
  2773.             // Get the IAccessor from the passed IUnknown
  2774.             CComPtr<IAccessor> spAccessor;
  2775.             hr = pCommand->QueryInterface(&spAccessor);
  2776.             if (SUCCEEDED(hr))
  2777.             {
  2778.                 hr = BindEntries(m_pParameterEntry, m_nParameters, pHAccessor,
  2779.                         m_nParameterBufferSize, spAccessor);
  2780.             }
  2781.         }
  2782.         else
  2783.             hr = S_OK;
  2784.  
  2785.         return hr;
  2786.     }
  2787.     typedef CManualAccessor _ParamClass;
  2788.     bool HasParameters() { return (m_nParameters > 0); }
  2789.     typedef CManualAccessor _OutputColumnsClass;
  2790.     static bool HasOutputColumns() { return true; }
  2791.     ULONG GetColumnCount() const
  2792.     {
  2793.         return m_nColumns;
  2794.     }
  2795.  
  2796.     // The binding structure for the output columns
  2797.     DBBINDING*          m_pEntry;
  2798.     // The number of output columns
  2799.     ULONG               m_nColumns;
  2800.     // The number of the current entry for the output columns
  2801.     ULONG               m_nEntry;
  2802.     // The size of the data buffer for the output columns
  2803.     ULONG               m_nBufferSize;
  2804.     // The number of parameters columns
  2805.     ULONG               m_nParameters;
  2806.     // The number of the parameter column to bind next
  2807.     ULONG               m_nCurrentParameter;
  2808.     // A pointer to the entry structures for each parameter
  2809.     DBBINDING*          m_pParameterEntry;
  2810.     // The size of the buffer where the parameters are stored
  2811.     ULONG               m_nParameterBufferSize;
  2812.     // A pointer to the buffer where the parameters are stored
  2813.     BYTE*               m_pParameterBuffer;
  2814. };
  2815.  
  2816.  
  2817. ///////////////////////////////////////////////////////////////////////////
  2818. // CAccessorRowset
  2819.  
  2820. template <class TAccessor = CNoAccessor, class TRowset = CRowset>
  2821. class CAccessorRowset :
  2822.     public TAccessor,
  2823.     public TRowset
  2824. {
  2825. public:
  2826.     CAccessorRowset()
  2827.     {
  2828.         // Give the rowset a pointer to the accessor
  2829.         SetAccessor(this);
  2830.     }
  2831.     ~CAccessorRowset()
  2832.     {
  2833.         Close();
  2834.     }
  2835.     // Used to get the column information from the opened rowset. The user is responsible
  2836.     // for freeing the returned column information and string buffer.
  2837.     HRESULT GetColumnInfo(ULONG* pulColumns,
  2838.         DBCOLUMNINFO** ppColumnInfo, LPOLESTR* ppStrings) const
  2839.     {
  2840.         ATLASSERT(GetInterface() != NULL);
  2841.         if (ppColumnInfo == NULL || pulColumns == NULL || ppStrings == NULL)
  2842.             return E_POINTER;
  2843.  
  2844.         CComPtr<IColumnsInfo> spColumns;
  2845.         HRESULT hr = GetInterface()->QueryInterface(&spColumns);
  2846.         if (SUCCEEDED(hr))
  2847.             hr = spColumns->GetColumnInfo(pulColumns, ppColumnInfo, ppStrings);
  2848.  
  2849.         return hr;
  2850.     }
  2851.     // Used to get the column information when overriding the bindings using CDynamicAccessor
  2852.     // The user should CoTaskMemFree the column information pointer that is returned.
  2853.     HRESULT GetColumnInfo(ULONG* pColumns, DBCOLUMNINFO** ppColumnInfo)
  2854.     {
  2855.         // If you get a compilation here, then you are most likely calling this function
  2856.         // from a class that is not using CDynamicAccessor.
  2857.         ATLASSERT(GetInterface() != NULL);
  2858.         return TAccessor::GetColumnInfo(GetInterface(), pColumns, ppColumnInfo);
  2859.     }
  2860.     // Call to bind the output columns
  2861.     HRESULT Bind()
  2862.     {
  2863.         // Bind should only be called when we've successfully opened the rowset
  2864.         ATLASSERT(GetInterface() != NULL);
  2865.         HRESULT hr = TAccessor::BindColumns(GetInterface());
  2866.         if (SUCCEEDED(hr))
  2867.             hr = BindFinished();
  2868.         return hr;
  2869.     }
  2870.     // Close the opened rowset and release the created accessors for the output columns
  2871.     void Close()
  2872.     {
  2873.         if (GetInterface() != NULL)
  2874.         {
  2875.             ReleaseAccessors(GetInterface());
  2876.             TAccessor::Close();
  2877.             TRowset::Close();
  2878.         }
  2879.     }
  2880.     // Free's any columns in the current record that need to be freed.
  2881.     // E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
  2882.     void FreeRecordMemory()
  2883.     {
  2884.         TAccessor::FreeRecordMemory(m_spRowset);
  2885.     }
  2886. };
  2887.  
  2888.  
  2889. ///////////////////////////////////////////////////////////////////////////
  2890. // class CEnumeratorAccessor
  2891.  
  2892. class CEnumeratorAccessor
  2893. {
  2894. public:
  2895.     WCHAR           m_szName[129];
  2896.     WCHAR           m_szParseName[129];
  2897.     WCHAR           m_szDescription[129];
  2898.     USHORT          m_nType;
  2899.     VARIANT_BOOL    m_bIsParent;
  2900.  
  2901. // Binding Maps
  2902. BEGIN_COLUMN_MAP(CEnumeratorAccessor)
  2903.     COLUMN_ENTRY(1, m_szName)
  2904.     COLUMN_ENTRY(2, m_szParseName)
  2905.     COLUMN_ENTRY(3, m_szDescription)
  2906.     COLUMN_ENTRY(4, m_nType)
  2907.     COLUMN_ENTRY(5, m_bIsParent)
  2908. END_COLUMN_MAP()
  2909. };
  2910.  
  2911.  
  2912. ///////////////////////////////////////////////////////////////////////////
  2913. // class CEnumerator
  2914.  
  2915. class CEnumerator : public CAccessorRowset<CAccessor<CEnumeratorAccessor> >
  2916. {
  2917. public:
  2918.     HRESULT Open(LPMONIKER pMoniker)
  2919.     {
  2920.         if (pMoniker == NULL)
  2921.             return E_FAIL;
  2922.  
  2923.         // Bind the moniker for the sources rowset
  2924.         if (FAILED(BindMoniker(pMoniker, 0, IID_ISourcesRowset,
  2925.                     (void**)&m_spSourcesRowset)))
  2926.             return E_FAIL;
  2927.  
  2928.         // Enumerate the data sources
  2929.         if (FAILED(m_spSourcesRowset->GetSourcesRowset(NULL, IID_IRowset, 0,
  2930.             NULL, (IUnknown**)&m_spRowset)))
  2931.             return E_FAIL;
  2932.  
  2933.         return Bind();
  2934.     }
  2935.     HRESULT Open(const CEnumerator& enumerator)
  2936.     {
  2937.         HRESULT hr;
  2938.         CComPtr<IMoniker> spMoniker;
  2939.  
  2940.         hr = enumerator.GetMoniker(&spMoniker);
  2941.         if (FAILED(hr))
  2942.             return hr;
  2943.  
  2944.         return Open(spMoniker);
  2945.     }
  2946.     HRESULT Open(const CLSID* pClsid = &CLSID_OLEDB_ENUMERATOR)
  2947.     {
  2948.         if (pClsid == NULL)
  2949.             return E_FAIL;
  2950.  
  2951.         HRESULT hr;
  2952.         // Create the enumerator
  2953.         hr = CoCreateInstance(*pClsid, NULL, CLSCTX_INPROC_SERVER,
  2954.                 IID_ISourcesRowset, (LPVOID*)&m_spSourcesRowset);
  2955.         if (FAILED(hr))
  2956.             return hr;
  2957.  
  2958.         // Get the rowset so we can enumerate the data sources
  2959.         hr = m_spSourcesRowset->GetSourcesRowset(NULL, IID_IRowset, 0,
  2960.             NULL, (IUnknown**)&m_spRowset);
  2961.         if (FAILED(hr))
  2962.             return hr;
  2963.  
  2964.         return Bind();
  2965.     }
  2966.  
  2967.     HRESULT GetMoniker(LPMONIKER* ppMoniker) const
  2968.     {
  2969.         CComPtr<IParseDisplayName> spParse;
  2970.         HRESULT hr;
  2971.         ULONG   chEaten;
  2972.  
  2973.         if (ppMoniker == NULL)
  2974.             return E_POINTER;
  2975.  
  2976.         if (m_spSourcesRowset == NULL)
  2977.             return E_FAIL;
  2978.  
  2979.         hr = m_spSourcesRowset->QueryInterface(IID_IParseDisplayName, (void**)&spParse);
  2980.         if (FAILED(hr))
  2981.             return hr;
  2982.  
  2983.         hr = spParse->ParseDisplayName(NULL, (LPOLESTR)m_szParseName,
  2984.                 &chEaten, ppMoniker);
  2985.         return hr;
  2986.     }
  2987.  
  2988.     HRESULT GetMoniker(LPMONIKER* ppMoniker, LPCTSTR lpszDisplayName) const
  2989.     {
  2990.         USES_CONVERSION;
  2991.         CComPtr<IParseDisplayName> spParse;
  2992.         HRESULT hr;
  2993.         ULONG   chEaten;
  2994.  
  2995.         if (ppMoniker == NULL || lpszDisplayName == NULL)
  2996.             return E_POINTER;
  2997.  
  2998.         if (m_spSourcesRowset == NULL)
  2999.             return E_FAIL;
  3000.  
  3001.         hr = m_spSourcesRowset->QueryInterface(IID_IParseDisplayName, (void**)&spParse);
  3002.         if (FAILED(hr))
  3003.             return hr;
  3004.  
  3005.         hr = spParse->ParseDisplayName(NULL, (LPOLESTR)T2COLE(lpszDisplayName),
  3006.                 &chEaten, ppMoniker);
  3007.         return hr;
  3008.     }
  3009.  
  3010.     bool Find(TCHAR* szSearchName)
  3011.     {
  3012.         USES_CONVERSION;
  3013.         // Loop through the providers looking for the passed name
  3014.         while (MoveNext()==S_OK && lstrcmp(W2T(m_szName), szSearchName))
  3015.             ATLTRACE2(atlTraceDBClient, 0, _T("%s, %s, %d\n"), W2T(m_szName), W2T(m_szParseName), m_nType);
  3016.         if (lstrcmp(W2T(m_szName), szSearchName))
  3017.             return false;
  3018.         else
  3019.             return true;
  3020.     }
  3021.  
  3022.     CComPtr<ISourcesRowset> m_spSourcesRowset;
  3023. };
  3024.  
  3025.  
  3026. ///////////////////////////////////////////////////////////////////////////
  3027. // CDataSource
  3028.  
  3029. class CDataSource
  3030. {
  3031. public:
  3032.     HRESULT Open(const CLSID& clsid, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  3033.     {
  3034.         HRESULT hr;
  3035.  
  3036.         m_spInit.Release();
  3037.         hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize,
  3038.                 (void**)&m_spInit);
  3039.         if (FAILED(hr))
  3040.             return hr;
  3041.  
  3042.         // Initialize the provider
  3043.         return OpenWithProperties(pPropSet, nPropertySets);
  3044.     }
  3045.     HRESULT Open(const CLSID& clsid, LPCTSTR pName, LPCTSTR pUserName = NULL,
  3046.         LPCTSTR pPassword = NULL, long nInitMode = 0)
  3047.     {
  3048.         HRESULT   hr;
  3049.  
  3050.         m_spInit.Release();
  3051.         hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize,
  3052.                 (void**)&m_spInit);
  3053.         if (FAILED(hr))
  3054.             return hr;
  3055.  
  3056.         return OpenWithNameUserPassword(pName, pUserName, pPassword, nInitMode);
  3057.     }
  3058.     HRESULT Open(LPCTSTR szProgID, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  3059.     {
  3060.         USES_CONVERSION;
  3061.         HRESULT hr;
  3062.         CLSID   clsid;
  3063.  
  3064.         hr = CLSIDFromProgID(T2COLE(szProgID), &clsid);
  3065.         if (FAILED(hr))
  3066.             return hr;
  3067.  
  3068.         return Open(clsid, pPropSet, nPropertySets);
  3069.     }
  3070.     HRESULT Open(LPCTSTR szProgID, LPCTSTR pName, LPCTSTR pUserName = NULL,
  3071.         LPCTSTR pPassword = NULL, long nInitMode = 0)
  3072.     {
  3073.         USES_CONVERSION;
  3074.         HRESULT hr;
  3075.         CLSID   clsid;
  3076.  
  3077.         hr = CLSIDFromProgID(T2COLE(szProgID), &clsid);
  3078.         if (FAILED(hr))
  3079.             return hr;
  3080.  
  3081.         return Open(clsid, pName, pUserName, pPassword, nInitMode);
  3082.     }
  3083.     HRESULT Open(const CEnumerator& enumerator, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  3084.     {
  3085.         CComPtr<IMoniker> spMoniker;
  3086.         HRESULT   hr;
  3087.  
  3088.         hr = enumerator.GetMoniker(&spMoniker);
  3089.         if (FAILED(hr))
  3090.             return hr;
  3091.  
  3092.         m_spInit.Release();
  3093.         //  Now bind the moniker
  3094.         hr = BindMoniker(spMoniker, 0, IID_IDBInitialize, (void**)&m_spInit);
  3095.         if (FAILED(hr))
  3096.             return hr;
  3097.  
  3098.         return OpenWithProperties(pPropSet, nPropertySets);
  3099.     }
  3100.     HRESULT Open(const CEnumerator& enumerator, LPCTSTR pName, LPCTSTR pUserName = NULL,
  3101.         LPCTSTR pPassword = NULL, long nInitMode = 0)
  3102.     {
  3103.         CComPtr<IMoniker> spMoniker;
  3104.         HRESULT   hr;
  3105.  
  3106.         hr = enumerator.GetMoniker(&spMoniker);
  3107.         if (FAILED(hr))
  3108.             return hr;
  3109.  
  3110.         m_spInit.Release();
  3111.         //  Now bind the moniker
  3112.         hr = BindMoniker(spMoniker, 0, IID_IDBInitialize, (void**)&m_spInit);
  3113.         if (FAILED(hr))
  3114.             return hr;
  3115.  
  3116.         return OpenWithNameUserPassword(pName, pUserName, pPassword, nInitMode);
  3117.     }
  3118.     // Invoke the data links dialog and open the selected database
  3119.     HRESULT Open(HWND hWnd = GetActiveWindow(), DBPROMPTOPTIONS dwPromptOptions = DBPROMPTOPTIONS_WIZARDSHEET)
  3120.     {
  3121.         CComPtr<IDBPromptInitialize> spDBInit;
  3122.  
  3123.         HRESULT hr = CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER,
  3124.             IID_IDBPromptInitialize, (void**) &spDBInit);
  3125.         if (FAILED(hr))
  3126.             return hr;
  3127.  
  3128.         CComPtr<IDBProperties> spIDBProperties;
  3129.         hr = spDBInit->PromptDataSource(NULL, hWnd, dwPromptOptions, 0, NULL, NULL,
  3130.             IID_IDBProperties, (IUnknown**)&spIDBProperties);
  3131.  
  3132.         if (hr == S_OK)
  3133.         {
  3134.             hr = spIDBProperties->QueryInterface(&m_spInit);
  3135.             if (SUCCEEDED(hr))
  3136.                 hr = m_spInit->Initialize();
  3137.         }
  3138.         else if (hr == S_FALSE)
  3139.             hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_CANCELLED);  // The user clicked cancel
  3140.  
  3141.         return hr;
  3142.     }
  3143.     // Opens a data source using the service components
  3144.     HRESULT OpenWithServiceComponents(const CLSID& clsid, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  3145.     {
  3146.         CComPtr<IDataInitialize> spDataInit;
  3147.         HRESULT hr;
  3148.     
  3149.         hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER, 
  3150.             IID_IDataInitialize, (void**)&spDataInit);
  3151.         if (FAILED(hr))
  3152.             return hr;
  3153.  
  3154.         m_spInit.Release();
  3155.         hr = spDataInit->CreateDBInstance(clsid, NULL, CLSCTX_INPROC_SERVER, NULL, 
  3156.             IID_IDBInitialize, (IUnknown**)&m_spInit);
  3157.         if (FAILED(hr))
  3158.             return hr;
  3159.  
  3160.         // Initialize the provider
  3161.         return OpenWithProperties(pPropSet, nPropertySets);
  3162.     }
  3163.     // Opens a data source using the service components
  3164.     HRESULT OpenWithServiceComponents(LPCTSTR szProgID, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  3165.     {
  3166.         USES_CONVERSION;
  3167.         HRESULT hr;
  3168.         CLSID   clsid;
  3169.  
  3170.         hr = CLSIDFromProgID(T2COLE(szProgID), &clsid);
  3171.         if (FAILED(hr))
  3172.             return hr;
  3173.  
  3174.         return OpenWithServiceComponents(clsid, pPropSet, nPropertySets);
  3175.     }
  3176.     // Bring up the "Organize Dialog" which allows the user to select a previously created data link
  3177.     // file (.UDL file). The selected file will be used to open the datbase.
  3178.     HRESULT OpenWithPromptFileName(HWND hWnd = GetActiveWindow(), DBPROMPTOPTIONS dwPromptOptions = DBPROMPTOPTIONS_NONE,
  3179.         LPCOLESTR szInitialDirectory = NULL)
  3180.     {
  3181.         USES_CONVERSION;
  3182.         CComPtr<IDBPromptInitialize> spDBInit;
  3183.  
  3184.         HRESULT hr = CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER,
  3185.             IID_IDBPromptInitialize, (void**) &spDBInit);
  3186.         if (FAILED(hr))
  3187.             return hr;
  3188.  
  3189.         CComPtr<IDBProperties> spIDBProperties;
  3190.         LPOLESTR szSelected;
  3191.  
  3192.         hr = spDBInit->PromptFileName(hWnd, dwPromptOptions, szInitialDirectory, L"*.udl", &szSelected);
  3193.  
  3194.         if (hr == S_OK)
  3195.             hr = OpenFromFileName(szSelected);
  3196.         else if (hr == S_FALSE)
  3197.             hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_CANCELLED);  // The user clicked cancel
  3198.  
  3199.         return hr;
  3200.     }
  3201.     // Open the datasource specified by the passed filename, typically a .UDL file
  3202.     HRESULT OpenFromFileName(LPCOLESTR szFileName)
  3203.     {
  3204.         CComPtr<IDataInitialize> spDataInit;
  3205.         LPOLESTR                 szInitString;
  3206.  
  3207.         HRESULT hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
  3208.             IID_IDataInitialize, (void**)&spDataInit);
  3209.         if (FAILED(hr))
  3210.             return hr;
  3211.  
  3212.         hr = spDataInit->LoadStringFromStorage(szFileName, &szInitString);
  3213.         if (FAILED(hr))
  3214.             return hr;
  3215.  
  3216.         return OpenFromInitializationString(szInitString);
  3217.     }
  3218.     // Open the datasource specified by the passed initialization string
  3219.     HRESULT OpenFromInitializationString(LPCOLESTR szInitializationString)
  3220.     {
  3221.         CComPtr<IDataInitialize> spDataInit;
  3222.  
  3223.         HRESULT hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
  3224.             IID_IDataInitialize, (void**)&spDataInit);
  3225.         if (FAILED(hr))
  3226.             return hr;
  3227.  
  3228.         CComPtr<IDBProperties> spIDBProperties;
  3229.         hr = spDataInit->GetDataSource(NULL, CLSCTX_INPROC_SERVER, szInitializationString,
  3230.             IID_IDBInitialize, (IUnknown**)&m_spInit);
  3231.         if (FAILED(hr))
  3232.             return hr;
  3233.  
  3234.         return m_spInit->Initialize();
  3235.     }
  3236.     // Get the initialization string from the currently open data source. The returned string
  3237.     // must be CoTaskMemFree'd when finished with.
  3238.     HRESULT GetInitializationString(BSTR* pInitializationString, bool bIncludePassword=false)
  3239.     {
  3240.         // If the datasource isn't open then we're not going to get an init string
  3241.         _ASSERTE(m_spInit != NULL);
  3242.         CComPtr<IDataInitialize> spDataInit;
  3243.         LPOLESTR    szInitString;
  3244.  
  3245.         HRESULT hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
  3246.             IID_IDataInitialize, (void**)&spDataInit);
  3247.         if (FAILED(hr))
  3248.             return hr;
  3249.  
  3250.         hr = spDataInit->GetInitializationString(m_spInit, bIncludePassword, &szInitString);
  3251.  
  3252.         if (SUCCEEDED(hr))
  3253.             *pInitializationString = ::SysAllocString(szInitString);
  3254.  
  3255.         return hr;
  3256.     }
  3257.     HRESULT GetProperties(ULONG ulPropIDSets, const DBPROPIDSET* pPropIDSet,
  3258.                 ULONG* pulPropertySets, DBPROPSET** ppPropsets) const
  3259.     {
  3260.         CComPtr<IDBProperties> spProperties;
  3261.  
  3262.         // Check that we are connected
  3263.         ATLASSERT(m_spInit != NULL);
  3264.  
  3265.         HRESULT hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3266.         if (FAILED(hr))
  3267.             return hr;
  3268.  
  3269.         hr = spProperties->GetProperties(ulPropIDSets, pPropIDSet, pulPropertySets,
  3270.                 ppPropsets);
  3271.         return hr;
  3272.     }
  3273.  
  3274.     HRESULT GetProperty(const GUID& guid, DBPROPID propid, VARIANT* pVariant) const
  3275.     {
  3276.         ATLASSERT(pVariant != NULL);
  3277.         CComPtr<IDBProperties> spProperties;
  3278.  
  3279.         // Check that we are connected
  3280.         ATLASSERT(m_spInit != NULL);
  3281.  
  3282.         HRESULT hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3283.         if (FAILED(hr))
  3284.             return hr;
  3285.  
  3286.         CDBPropIDSet set(guid);
  3287.         set.AddPropertyID(propid);
  3288.         DBPROPSET* pPropSet = NULL;
  3289.         ULONG ulPropSet = 0;
  3290.         hr = spProperties->GetProperties(1, &set, &ulPropSet, &pPropSet);
  3291.         if (FAILED(hr))
  3292.             return hr;
  3293.  
  3294.         ATLASSERT(ulPropSet == 1);
  3295.         VariantCopy(pVariant, &pPropSet->rgProperties[0].vValue);
  3296.         CoTaskMemFree(pPropSet->rgProperties);
  3297.         CoTaskMemFree(pPropSet);
  3298.  
  3299.         return S_OK;
  3300.     }
  3301.     void Close()
  3302.     {
  3303.         m_spInit.Release();
  3304.     }
  3305.  
  3306. // Implementation
  3307.     HRESULT OpenFromIDBProperties(IDBProperties* pIDBProperties)
  3308.     {
  3309.         CComPtr<IPersist> spPersist;
  3310.         CLSID   clsid;
  3311.         HRESULT hr;
  3312.  
  3313.         hr = pIDBProperties->QueryInterface(IID_IPersist, (void**)&spPersist);
  3314.         if (FAILED(hr))
  3315.             return hr;
  3316.  
  3317.         spPersist->GetClassID(&clsid);
  3318.  
  3319.         ULONG       ulPropSets=0;
  3320.         CDBPropSet* pPropSets=NULL;
  3321.         pIDBProperties->GetProperties(0, NULL, &ulPropSets, (DBPROPSET**)&pPropSets);
  3322.  
  3323.         hr = Open(clsid, &pPropSets[0], ulPropSets);
  3324.  
  3325.         for (ULONG i=0; i < ulPropSets; i++)
  3326.             (pPropSets+i)->~CDBPropSet();
  3327.         CoTaskMemFree(pPropSets);
  3328.  
  3329.         return hr;
  3330.     }
  3331.     HRESULT OpenWithNameUserPassword(LPCTSTR pName, LPCTSTR pUserName, LPCTSTR pPassword, long nInitMode = 0)
  3332.     {
  3333.         ATLASSERT(m_spInit != NULL);
  3334.         CComPtr<IDBProperties>  spProperties;
  3335.         HRESULT                 hr;
  3336.  
  3337.         hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3338.         if (FAILED(hr))
  3339.             return hr;
  3340.  
  3341.         // Set connection properties
  3342.         CDBPropSet propSet(DBPROPSET_DBINIT);
  3343.  
  3344.         // Add Datbase name, User name and Password
  3345.         if (pName != NULL)
  3346.             propSet.AddProperty(DBPROP_INIT_DATASOURCE, pName);
  3347.  
  3348.         if (pUserName != NULL)
  3349.             propSet.AddProperty(DBPROP_AUTH_USERID, pUserName);
  3350.  
  3351.         if (pPassword != NULL)
  3352.             propSet.AddProperty(DBPROP_AUTH_PASSWORD, pPassword);
  3353.  
  3354.         if (nInitMode)
  3355.             propSet.AddProperty(DBPROP_INIT_MODE, nInitMode);
  3356.  
  3357.         hr = spProperties->SetProperties(1, &propSet);
  3358.         if (FAILED(hr))
  3359.             return hr;
  3360.  
  3361.         // Initialize the provider
  3362.         return m_spInit->Initialize();
  3363.     }
  3364.     HRESULT OpenWithProperties(DBPROPSET* pPropSet, ULONG nPropertySets=1)
  3365.     {
  3366.         ATLASSERT(m_spInit != NULL);
  3367.  
  3368.         // Set the properties if there are some to set
  3369.         if (pPropSet != NULL)
  3370.         {
  3371.             CComPtr<IDBProperties>  spProperties;
  3372.             HRESULT                 hr;
  3373.  
  3374.             hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3375.             if (FAILED(hr))
  3376.                 return hr;
  3377.  
  3378.             hr = spProperties->SetProperties(nPropertySets, pPropSet);
  3379.             if (FAILED(hr))
  3380.                 return hr;
  3381.         }
  3382.  
  3383.         // Initialize the provider
  3384.         return m_spInit->Initialize();
  3385.     }
  3386.  
  3387.     CComPtr<IDBInitialize>  m_spInit;
  3388. };
  3389.  
  3390.  
  3391. ///////////////////////////////////////////////////////////////////////////
  3392. // class CSession
  3393.  
  3394. class CSession
  3395. {
  3396. public:
  3397.     // Create a session on the passed datasource
  3398.     HRESULT Open(const CDataSource& ds)
  3399.     {
  3400.         CComPtr<IDBCreateSession> spSession;
  3401.  
  3402.         // Check we have connected to the database
  3403.         ATLASSERT(ds.m_spInit != NULL);
  3404.  
  3405.         HRESULT hr = ds.m_spInit->QueryInterface(IID_IDBCreateSession, (void**)&spSession);
  3406.         if (FAILED(hr))
  3407.             return hr;
  3408.  
  3409.         hr = spSession->CreateSession(NULL, IID_IOpenRowset, (IUnknown**)&m_spOpenRowset);
  3410.         return hr;
  3411.     }
  3412.     // Close the session
  3413.     void Close()
  3414.     {
  3415.         m_spOpenRowset.Release();
  3416.     }
  3417.     // Start a transaction
  3418.     HRESULT StartTransaction(ISOLEVEL isoLevel = ISOLATIONLEVEL_READCOMMITTED, ULONG isoFlags = 0,
  3419.         ITransactionOptions* pOtherOptions = NULL, ULONG* pulTransactionLevel = NULL) const
  3420.     {
  3421.         ATLASSERT(m_spOpenRowset != NULL);
  3422.         CComPtr<ITransactionLocal> spTransactionLocal;
  3423.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransactionLocal);
  3424.  
  3425.         if (SUCCEEDED(hr))
  3426.             hr = spTransactionLocal->StartTransaction(isoLevel, isoFlags, pOtherOptions, pulTransactionLevel);
  3427.  
  3428.         return hr;
  3429.     }
  3430.     // Abort the current transaction
  3431.     HRESULT Abort(BOID* pboidReason = NULL, BOOL bRetaining = FALSE, BOOL bAsync = FALSE) const
  3432.     {
  3433.         ATLASSERT(m_spOpenRowset != NULL);
  3434.         CComPtr<ITransaction> spTransaction;
  3435.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransaction);
  3436.  
  3437.         if (SUCCEEDED(hr))
  3438.             hr = spTransaction->Abort(pboidReason, bRetaining, bAsync);
  3439.  
  3440.         return hr;
  3441.     }
  3442.     // Commit the current transaction
  3443.     HRESULT Commit(BOOL bRetaining = FALSE, DWORD grfTC = XACTTC_SYNC, DWORD grfRM = 0) const
  3444.     {
  3445.         ATLASSERT(m_spOpenRowset != NULL);
  3446.         CComPtr<ITransaction> spTransaction;
  3447.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransaction);
  3448.  
  3449.         if (SUCCEEDED(hr))
  3450.             hr = spTransaction->Commit(bRetaining, grfTC, grfRM);
  3451.  
  3452.         return hr;
  3453.     }
  3454.     // Get information for the current transaction
  3455.     HRESULT GetTransactionInfo(XACTTRANSINFO* pInfo) const
  3456.     {
  3457.         ATLASSERT(m_spOpenRowset != NULL);
  3458.         CComPtr<ITransaction> spTransaction;
  3459.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransaction);
  3460.  
  3461.         if (SUCCEEDED(hr))
  3462.             hr = spTransaction->GetTransactionInfo(pInfo);
  3463.  
  3464.         return hr;
  3465.     }
  3466. // Implementation
  3467.     CComPtr<IOpenRowset> m_spOpenRowset;
  3468. };
  3469.  
  3470.  
  3471. ///////////////////////////////////////////////////////////////////////////
  3472. // CTable
  3473.  
  3474. template <class TAccessor = CNoAccessor, class TRowset = CRowset>
  3475. class CTable :
  3476.     public CAccessorRowset<TAccessor, TRowset>
  3477. {
  3478. public:
  3479.     // Open a rowset on the passed name
  3480.     HRESULT Open(const CSession& session, LPCTSTR szTableName, DBPROPSET* pPropSet = NULL)
  3481.     {
  3482.         USES_CONVERSION;
  3483.         DBID    idTable;
  3484.  
  3485.         idTable.eKind           = DBKIND_NAME;
  3486.         idTable.uName.pwszName  = (LPOLESTR)T2COLE(szTableName);
  3487.  
  3488.         return Open(session, idTable, pPropSet);
  3489.     }
  3490.     // Open the a rowset on the passed DBID
  3491.     HRESULT Open(const CSession& session, DBID& dbid, DBPROPSET* pPropSet = NULL)
  3492.     {
  3493.         // Check the session is valid
  3494.         ATLASSERT(session.m_spOpenRowset != NULL);
  3495.         HRESULT hr;
  3496.  
  3497.         hr = session.m_spOpenRowset->OpenRowset(NULL, &dbid, NULL, GetIID(),
  3498.             (pPropSet) ? 1 : 0, pPropSet, (IUnknown**)GetInterfacePtr());
  3499.         if (SUCCEEDED(hr))
  3500.         {
  3501.             SetupOptionalRowsetInterfaces();
  3502.  
  3503.             // If we have output columns then bind
  3504.             if (_OutputColumnsClass::HasOutputColumns())
  3505.                 hr = Bind();
  3506.         }
  3507.  
  3508.         return hr;
  3509.     }
  3510. };
  3511.  
  3512. #if (OLEDBVER < 0x0150)
  3513. #define DBGUID_DEFAULT DBGUID_DBSQL
  3514. #endif
  3515.  
  3516.  
  3517. ///////////////////////////////////////////////////////////////////////////
  3518. // CCommandBase
  3519.  
  3520. class CCommandBase
  3521. {
  3522. public:
  3523.     CCommandBase()
  3524.     {
  3525.         m_hParameterAccessor = NULL;
  3526.     }
  3527.  
  3528.     ~CCommandBase()
  3529.     {
  3530.         ReleaseCommand();
  3531.     }
  3532.     // Create the command
  3533.     HRESULT CreateCommand(const CSession& session)
  3534.     {
  3535.         // Before creating the command, release the old one if necessary.
  3536.         ReleaseCommand();
  3537.  
  3538.         // Check the session is valid
  3539.         ATLASSERT(session.m_spOpenRowset != NULL);
  3540.  
  3541.         CComPtr<IDBCreateCommand> spCreateCommand;
  3542.  
  3543.         HRESULT hr = session.m_spOpenRowset->QueryInterface(IID_IDBCreateCommand, (void**)&spCreateCommand);
  3544.         if (FAILED(hr))
  3545.             return hr;
  3546.  
  3547.         return spCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&m_spCommand);
  3548.     }
  3549.     // Prepare the command
  3550.     HRESULT Prepare(ULONG cExpectedRuns = 0)
  3551.     {
  3552.         CComPtr<ICommandPrepare> spCommandPrepare;
  3553.         HRESULT hr = m_spCommand->QueryInterface(&spCommandPrepare);
  3554.         if (SUCCEEDED(hr))
  3555.             hr = spCommandPrepare->Prepare(cExpectedRuns);
  3556.  
  3557.         return hr;
  3558.     }
  3559.     // Unprepare the command
  3560.     HRESULT Unprepare()
  3561.     {
  3562.         CComPtr<ICommandPrepare> spCommandPrepare;
  3563.         HRESULT hr = m_spCommand->QueryInterface(&spCommandPrepare);
  3564.         if (SUCCEEDED(hr))
  3565.             hr = spCommandPrepare->Unprepare();
  3566.  
  3567.         return hr;
  3568.     }
  3569.     // Create the command and set the command text
  3570.     HRESULT Create(const CSession& session, LPCTSTR szCommand,
  3571.         REFGUID guidCommand = DBGUID_DEFAULT)
  3572.     {
  3573.         USES_CONVERSION;
  3574.         HRESULT hr;
  3575.  
  3576.         hr = CreateCommand(session);
  3577.         if (SUCCEEDED(hr))
  3578.         {
  3579.             CComPtr<ICommandText> spCommandText;
  3580.             hr = m_spCommand->QueryInterface(&spCommandText);
  3581.             if (SUCCEEDED(hr))
  3582.                 hr = spCommandText->SetCommandText(guidCommand, T2COLE(szCommand));
  3583.         }
  3584.         return hr;
  3585.     }
  3586.     // Release the command
  3587.     void ReleaseCommand()
  3588.     {
  3589.         // Release the parameter accessor if necessary, before releasing the command
  3590.         if (m_hParameterAccessor != NULL)
  3591.         {
  3592.             CComPtr<IAccessor> spAccessor;
  3593.             HRESULT hr = m_spCommand->QueryInterface(&spAccessor);
  3594.             if (SUCCEEDED(hr))
  3595.             {
  3596.                 spAccessor->ReleaseAccessor(m_hParameterAccessor, NULL); \
  3597.                 m_hParameterAccessor = NULL;
  3598.             }
  3599.         }
  3600.         m_spCommand.Release();
  3601.     }
  3602.     // Get the parameter information from the command
  3603.     HRESULT GetParameterInfo(ULONG* pParams, DBPARAMINFO** ppParamInfo,
  3604.                 OLECHAR** ppNamesBuffer)
  3605.     {
  3606.         CComPtr<ICommandWithParameters> spCommandParameters;
  3607.         HRESULT hr = m_spCommand->QueryInterface(&spCommandParameters);
  3608.         if (SUCCEEDED(hr))
  3609.         {
  3610.             // Get the parameter information
  3611.             hr = spCommandParameters->GetParameterInfo(pParams, ppParamInfo,
  3612.                     ppNamesBuffer);
  3613.         }
  3614.         return hr;
  3615.     }
  3616.     // Set the parameter information for the command
  3617.     HRESULT SetParameterInfo(ULONG ulParams, const ULONG* pOrdinals,
  3618.                 const DBPARAMBINDINFO* pParamInfo)
  3619.     {
  3620.         CComPtr<ICommandWithParameters> spCommandParameters;
  3621.         HRESULT hr = m_spCommand->QueryInterface(&spCommandParameters);
  3622.         if (SUCCEEDED(hr))
  3623.         {
  3624.             // Set the parameter information
  3625.             hr = spCommandParameters->SetParameterInfo(ulParams, pOrdinals,
  3626.                 pParamInfo);
  3627.         }
  3628.         return hr;
  3629.     }
  3630.  
  3631.     CComPtr<ICommand>   m_spCommand;
  3632.     HACCESSOR           m_hParameterAccessor;
  3633. };
  3634.  
  3635. // Used to turn on multiple result set support in CCommand
  3636. class CMultipleResults
  3637. {
  3638. public:
  3639.     bool UseMultipleResults() { return true; }
  3640.     IMultipleResults** GetMultiplePtrAddress() { return &m_spMultipleResults.p; }
  3641.     IMultipleResults* GetMultiplePtr() { return m_spMultipleResults; }
  3642.  
  3643.     CComPtr<IMultipleResults> m_spMultipleResults;
  3644. };
  3645.  
  3646. // Used to turn off multiple result set support in CCommand
  3647. class CNoMultipleResults
  3648. {
  3649. public:
  3650.     bool UseMultipleResults() { return false; }
  3651.     IMultipleResults** GetMultiplePtrAddress() { return NULL; }
  3652.     IMultipleResults* GetMultiplePtr() { return NULL; }
  3653. };
  3654.  
  3655.  
  3656. ///////////////////////////////////////////////////////////////////////////
  3657. // CCommand
  3658.  
  3659. template <class TAccessor = CNoAccessor, class TRowset = CRowset, class TMultiple = CNoMultipleResults>
  3660. class CCommand :
  3661.     public CAccessorRowset<TAccessor, TRowset>,
  3662.     public CCommandBase,
  3663.     public TMultiple
  3664. {
  3665. public:
  3666.     // Create a command on the session and execute it
  3667.     HRESULT Open(const CSession& session, LPCTSTR szCommand = NULL,
  3668.         DBPROPSET *pPropSet = NULL, LONG* pRowsAffected = NULL,
  3669.         REFGUID guidCommand = DBGUID_DEFAULT, bool bBind = true)
  3670.     {
  3671.         HRESULT hr;
  3672.         if (szCommand == NULL)
  3673.         {
  3674.             hr = _CommandClass::GetDefaultCommand(&szCommand);
  3675.             if (FAILED(hr))
  3676.                 return hr;
  3677.         }
  3678.         hr = Create(session, szCommand, guidCommand);
  3679.         if (FAILED(hr))
  3680.             return hr;
  3681.  
  3682.         return Open(pPropSet, pRowsAffected, bBind);
  3683.     }
  3684.     // Used if you have previously created the command
  3685.     HRESULT Open(DBPROPSET *pPropSet = NULL, LONG* pRowsAffected = NULL, bool bBind = true)
  3686.     {
  3687.         HRESULT     hr;
  3688.         DBPARAMS    params;
  3689.         DBPARAMS    *pParams;
  3690.  
  3691.         // Bind the parameters if we have some
  3692.         if (_ParamClass::HasParameters())
  3693.         {
  3694.             // Bind the parameters in the accessor if they haven't already been bound
  3695.             hr = BindParameters(&m_hParameterAccessor, m_spCommand, ¶ms.pData);
  3696.             if (FAILED(hr))
  3697.                 return hr;
  3698.  
  3699.             // Setup the DBPARAMS structure
  3700.             params.cParamSets = 1;
  3701.             params.hAccessor = m_hParameterAccessor;
  3702.             pParams = ¶ms;
  3703.         }
  3704.         else
  3705.             pParams = NULL;
  3706.  
  3707.         hr = Execute(GetInterfacePtr(), pParams, pPropSet, pRowsAffected);
  3708.         if (FAILED(hr))
  3709.             return hr;
  3710.  
  3711.         // Only bind if we have been asked to and we have output columns
  3712.         if (bBind && _OutputColumnsClass::HasOutputColumns())
  3713.             return Bind();
  3714.         else
  3715.             return hr;
  3716.     }
  3717.     // Get the next rowset when using multiple result sets
  3718.     HRESULT GetNextResult(LONG* pulRowsAffected, bool bBind = true)
  3719.     {
  3720.         // This function should only be called if CMultipleResults is being
  3721.         // used as the third template parameter
  3722.         ATLASSERT(GetMultiplePtrAddress() != NULL);
  3723.  
  3724.         // If user calls GetNextResult but the interface is not available
  3725.         // return E_FAIL.
  3726.         if (GetMultiplePtr() == NULL)
  3727.             return E_FAIL;
  3728.  
  3729.         // Close the existing rowset in preparation for opening the next one
  3730.         Close();
  3731.  
  3732.         HRESULT hr = GetMultiplePtr()->GetResult(NULL, 0, IID_IRowset,
  3733.             pulRowsAffected, (IUnknown**)GetInterfacePtr());
  3734.         if (FAILED(hr))
  3735.             return hr;
  3736.  
  3737.         if (bBind && GetInterface() != NULL)
  3738.             return Bind();
  3739.         else
  3740.             return hr;
  3741.     }
  3742.  
  3743. // Implementation
  3744.     HRESULT Execute(IRowset** ppRowset, DBPARAMS* pParams, DBPROPSET *pPropSet, LONG* pRowsAffected)
  3745.     {
  3746.         HRESULT hr;
  3747.  
  3748.         // Specify the properties if we have some
  3749.         if (pPropSet)
  3750.         {
  3751.             CComPtr<ICommandProperties> spCommandProperties;
  3752.             hr = m_spCommand->QueryInterface(&spCommandProperties);
  3753.             if (FAILED(hr))
  3754.                 return hr;
  3755.  
  3756.             hr = spCommandProperties->SetProperties(1, pPropSet);
  3757.             if (FAILED(hr))
  3758.                 return hr;
  3759.         }
  3760.  
  3761.         // If the user want the rows affected then return it back, otherwise
  3762.         // just point to our local variable here.
  3763.         LONG nAffected, *pAffected;
  3764.         if (pRowsAffected)
  3765.             pAffected = pRowsAffected;
  3766.         else
  3767.             pAffected = &nAffected;
  3768.  
  3769.         if (UseMultipleResults())
  3770.         {
  3771.             hr = m_spCommand->Execute(NULL, IID_IMultipleResults, pParams,
  3772.                 pAffected, (IUnknown**)GetMultiplePtrAddress());
  3773.  
  3774.             if (SUCCEEDED(hr))
  3775.             {
  3776.                 hr = GetNextResult(pAffected, false);
  3777.             }
  3778.             else
  3779.             {
  3780.                 // If we can't get IMultipleResults then just try to get IRowset
  3781.                 hr = m_spCommand->Execute(NULL, IID_IRowset, pParams, pAffected,
  3782.                     (IUnknown**)GetInterfacePtr());
  3783.             }
  3784.         }
  3785.         else
  3786.         {
  3787.             hr = m_spCommand->Execute(NULL, GetIID(), pParams, pAffected,
  3788.                 (IUnknown**)ppRowset);
  3789.             if (SUCCEEDED(hr))
  3790.                 SetupOptionalRowsetInterfaces();
  3791.         }
  3792.         return hr;
  3793.     }
  3794. };
  3795.  
  3796.  
  3797. // This class can be used to implement the IRowsetNotify interface.
  3798. // It is supplied so that if you only want to implement one of the
  3799. // notifications you don't have to supply empty functions for the
  3800. // other methods.
  3801. class ATL_NO_VTABLE IRowsetNotifyImpl : public IRowsetNotify
  3802. {
  3803. public:
  3804.     STDMETHOD(OnFieldChange)(
  3805.             /* [in] */ IRowset* /* pRowset */,
  3806.             /* [in] */ HROW /* hRow */,
  3807.             /* [in] */ ULONG /* cColumns */,
  3808.             /* [size_is][in] */ ULONG /* rgColumns*/ [] ,
  3809.             /* [in] */ DBREASON /* eReason */,
  3810.             /* [in] */ DBEVENTPHASE /* ePhase */,
  3811.             /* [in] */ BOOL /* fCantDeny */)
  3812.     {
  3813.         ATLTRACENOTIMPL(_T("IRowsetNotifyImpl::OnFieldChange"));
  3814.     }
  3815.     STDMETHOD(OnRowChange)(
  3816.             /* [in] */ IRowset* /* pRowset */,
  3817.             /* [in] */ ULONG /* cRows */,
  3818.             /* [size_is][in] */ const HROW /* rghRows*/ [] ,
  3819.             /* [in] */ DBREASON /* eReason */,
  3820.             /* [in] */ DBEVENTPHASE /* ePhase */,
  3821.             /* [in] */ BOOL /* fCantDeny */)
  3822.     {
  3823.         ATLTRACENOTIMPL(_T("IRowsetNotifyImpl::OnRowChange"));
  3824.     }
  3825.     STDMETHOD(OnRowsetChange)(
  3826.         /* [in] */ IRowset* /* pRowset */,
  3827.         /* [in] */ DBREASON /* eReason */,
  3828.         /* [in] */ DBEVENTPHASE /* ePhase */,
  3829.         /* [in] */ BOOL /* fCantDeny*/)
  3830.     {
  3831.         ATLTRACENOTIMPL(_T("IRowsetNotifyImpl::OnRowsetChange"));
  3832.     }
  3833. };
  3834.  
  3835. }; //namespace ATL
  3836.  
  3837. #endif // __ATLDBCLI_H_
  3838.