//----------------------------------------------------------------------------- // Microsoft OLE DB RowsetViewer // Copyright (C) 1994 - 1999 By Microsoft Corporation. // // @doc // // @module CROWSET.CPP // //----------------------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////////// // Includes // ////////////////////////////////////////////////////////////////////////////// #include "Headers.h" ///////////////////////////////////////////////////////////////// // CDataAccess::CDataAccess // ///////////////////////////////////////////////////////////////// CDataAccess::CDataAccess(SOURCE eObjectType, CMainWindow* pCMainWindow, CMDIChild* pCMDIChild) : CAsynchBase(eObjectType, pCMainWindow, pCMDIChild) { //eBaseClass m_eBaseClass = BASE_CLASS(m_eBaseClass | eCDataAccess); //Rowset m_pIAccessor = NULL; //Rowset Interface m_pIColumnsInfo = NULL; //Rowset Interface m_pIConvertType = NULL; //Rowset Interface m_pIColumnsRowset = NULL; //Rowset interface //Extra Interfaces //Accessor m_hAccessor = NULL; m_cbRowSize = 0; m_pData = NULL; //Bookmark Accessor m_hBmkAccessor = NULL; m_bSchemaRowset = FALSE; } ///////////////////////////////////////////////////////////////// // CDataAccess::~CDataAccess // ///////////////////////////////////////////////////////////////// CDataAccess::~CDataAccess() { } ///////////////////////////////////////////////////////////////// // IUnknown** CDataAccess::GetInterfaceAddress // ///////////////////////////////////////////////////////////////// IUnknown** CDataAccess::GetInterfaceAddress(REFIID riid) { HANDLE_GETINTERFACE(IAccessor); HANDLE_GETINTERFACE(IColumnsInfo); HANDLE_GETINTERFACE(IConvertType); HANDLE_GETINTERFACE(IColumnsRowset); //Otherwise delegate return CAsynchBase::GetInterfaceAddress(riid); } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::AutoQI // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::AutoQI(DWORD dwCreateOpts) { //Delegate First so we have base interfaces CAsynchBase::AutoQI(dwCreateOpts); //[MANDATORY] Obtain [mandatory] interfaces if(dwCreateOpts & CREATE_QI_MANDATORY) { OBTAIN_INTERFACE(IColumnsInfo); OBTAIN_INTERFACE(IAccessor); OBTAIN_INTERFACE(IConvertType); } //Auto QI if(dwCreateOpts & CREATE_QI_OPTIONAL) { OBTAIN_INTERFACE(IColumnsRowset); } return S_OK; } ///////////////////////////////////////////////////////////////// // CDataAccess::AutoRelease // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::AutoRelease() { //Data m_bSchemaRowset = FALSE; //ColInfo m_ColumnInfo.RemoveAll(); //Accessor m_Bindings.RemoveAll(); ReleaseAccessor(&m_hAccessor); SAFE_FREE(m_pData); ReleaseAccessor(&m_hBmkAccessor); //Rowset RELEASE_INTERFACE(IAccessor); RELEASE_INTERFACE(IColumnsInfo); RELEASE_INTERFACE(IConvertType); RELEASE_INTERFACE(IColumnsRowset); //Delegate return CAsynchBase::AutoRelease(); } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::GetColInfo // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::GetColInfo() { HRESULT hr = E_FAIL; DBORDINAL cColumnInfo = 0; DBCOLUMNINFO* rgColumnInfo = NULL; WCHAR* pwszStringBuffer = NULL; DBORDINAL cHiddenColumns = 0; //Release an Previous Info m_ColumnInfo.RemoveAll(); //Delegate TESTC(hr = GetColInfo(&cColumnInfo, &rgColumnInfo, &pwszStringBuffer, &cHiddenColumns)); //Attach m_ColumnInfo.Attach(cColumnInfo, rgColumnInfo, pwszStringBuffer, cHiddenColumns); CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::GetColInfo // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::GetColInfo(DBORDINAL* pcColumns, DBCOLUMNINFO** prgColumnInfo, WCHAR** ppStringBuffer, DBORDINAL* pcHiddenColumns) { HRESULT hr = E_FAIL; if(m_pIColumnsInfo) { //Get column information from the command object via IColumnsInfo::GetColumnInfo XTEST(hr = m_pIColumnsInfo->GetColumnInfo(pcColumns, prgColumnInfo, ppStringBuffer)); TESTC(TRACE_METHOD(hr, L"IColumnsInfo::GetColumnInfo(&%Iu, &0x%p, &0x%p)", pcColumns ? *pcColumns : 0, prgColumnInfo ? *prgColumnInfo : NULL, ppStringBuffer ? *ppStringBuffer : NULL)); //Obtain any Hidden Columns (if requested by the user) if(GetOptions()->m_dwRowsetOpts & ROWSET_HIDDENCOLUMNS) { //DBPROP_HIDDENCOLUMNS if(pcColumns && pcHiddenColumns) { CComPtr spRowsetInfo; if(SUCCEEDED(m_pIColumnsInfo->QueryInterface(IID_IRowsetInfo, (void**)&spRowsetInfo))) { if(SUCCEEDED(GetProperty(IID_IRowsetInfo, spRowsetInfo, DBPROP_HIDDENCOLUMNS, DBPROPSET_ROWSET, DBTYPE_I4, pcHiddenColumns))) *pcColumns += *pcHiddenColumns; } } } } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // CDataAccess::GetColumnImage // ///////////////////////////////////////////////////////////////// INT CDataAccess::GetColumnImage(const DBCOLUMNINFO* pColInfo, DBSTATUS dbStatus) { //NOTE: Errors take precidence in the following function over readonly or chapter icons... //Otherwise determine image from Status switch(dbStatus) { case DBSTATUS_S_OK: case DBSTATUS_S_ISNULL: case DBSTATUS_S_DEFAULT: case DBSTATUS_S_IGNORE: if(pColInfo) { //Hidden Column? if(m_ColumnInfo.IsHidden(pColInfo->iOrdinal)) return IMAGE_DELETE; //Chapter Column (takes precidence) if(pColInfo->dwFlags & DBCOLUMNFLAGS_ISCHAPTER) return IMAGE_CHAPTER; //ReadOnly Column if(!(pColInfo->dwFlags & DBCOLUMNFLAGS_WRITE || pColInfo->dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN)) return IMAGE_LOCK; } return IMAGE_NONE; case DBSTATUS_S_TRUNCATED: return IMAGE_QUESTION; default: return IMAGE_ERROR; } return IMAGE_NORMAL; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::ValidateAccessor // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::ValidateAccessor(HACCESSOR hAccessor, ULONG ulRefCount) { //Warn user about NULL Accessor handle... if(hAccessor == DB_NULL_HACCESSOR || ulRefCount == 0) { if(IDNO == wMessageBox ( GetFocus(), MB_TASKMODAL | MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON1, wsz_ERROR, L"hAccessor == 0x%p\n" L"%s\n\n" L"This may crash your Provider...\n" L"Do you wish to continue anyway?", hAccessor, hAccessor == DB_NULL_HROW ? L"Invalid Accessor Handle" : L"Released Accessor Handle" ) ) return E_FAIL; } return S_OK; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::CreateAccessors // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::CreateAccessors(BINDCOLS eBindCols) { HRESULT hr = S_OK; DBCOUNTITEM cBindings = 0; DBBINDING* rgBindings = NULL; //Release Previous Accessors ReleaseAccessor(&m_hAccessor); m_Bindings.RemoveAll(); SAFE_FREE(m_pData); ReleaseAccessor(&m_hBmkAccessor); //Delegate TESTC(hr = SetupBindings(eBindCols, &cBindings, &rgBindings, &m_cbRowSize)); TESTC(hr = CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, m_cbRowSize, &m_hAccessor)); //Allocate pData SAFE_ALLOC(m_pData, BYTE, m_cbRowSize); CLEANUP: m_Bindings.Attach(cBindings, rgBindings); return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::SetupBindings // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::SetupBindings(BINDCOLS eBindCols, DBCOUNTITEM* pcBindings, DBBINDING** prgBindings, DBLENGTH* pcRowSize) { HRESULT hr = S_OK; DBLENGTH dwOffset = 0; DBCOUNTITEM i,cBindings = 0; DBBINDING* rgBindings = NULL; //Only capable of the Following Converions (for Display) DWORD dwMaxLength = GetOptions()->m_dwMaxLength; DWORD dwAccessorOpts = GetOptions()->m_dwAccessorOpts; //Alloc the space to hold the Bindings SAFE_ALLOC(rgBindings, DBBINDING, m_ColumnInfo.GetCount()); cBindings = 0; for(i=0; iiOrdinal = pColInfo->iOrdinal; pBinding->obStatus = dwOffset; dwOffset += ROUNDUP(sizeof(DBSTATUS)); pBinding->obLength = dwOffset; dwOffset += ROUNDUP(sizeof(DBLENGTH)); pBinding->obValue = dwOffset; pBinding->pTypeInfo = NULL; pBinding->pBindExt = NULL; pBinding->dwPart = dwAccessorOpts & (DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS); pBinding->dwMemOwner= (dwAccessorOpts & ACCESSOR_OWNED_PROVIDER) ? DBMEMOWNER_PROVIDEROWNED : DBMEMOWNER_CLIENTOWNED; pBinding->eParamIO = DBPARAMIO_NOTPARAM; pBinding->dwFlags = 0; pBinding->bPrecision= pColInfo->bPrecision; pBinding->bScale = pColInfo->bScale; pBinding->pObject = NULL; pBinding->wType = GetOptions()->GetBindingType(pColInfo->wType); pBinding->cbMaxLen = GetMaxDisplaySize(pBinding->wType, pColInfo->wType, pColInfo->ulColumnSize, dwMaxLength); //BLOB or IUnknown Bindings if(pBinding->wType == DBTYPE_IUNKNOWN || pColInfo->wType == DBTYPE_IUNKNOWN || (pColInfo->dwFlags & DBCOLUMNFLAGS_ISLONG && (dwAccessorOpts & (ACCESSOR_BLOB_ISEQSTREAM|ACCESSOR_BLOB_ILOCKBYTES|ACCESSOR_BLOB_ISTORAGE|ACCESSOR_BLOB_ISTREAM)))) { //For ColumnsRowset pBinding->wType = DBTYPE_IUNKNOWN; pBinding->cbMaxLen = sizeof(IUnknown*); //Create pObject SAFE_ALLOC(pBinding->pObject, DBOBJECT, 1); pBinding->pObject->dwFlags = STGM_READ; pBinding->pObject->iid = IID_IUnknown; //Setup pObject->iid if(dwAccessorOpts & ACCESSOR_BLOB_ISEQSTREAM) pBinding->pObject->iid = IID_ISequentialStream; else if(dwAccessorOpts & ACCESSOR_BLOB_ISTREAM) pBinding->pObject->iid = IID_IStream; else if(dwAccessorOpts & ACCESSOR_BLOB_ILOCKBYTES) pBinding->pObject->iid = IID_ILockBytes; else if(dwAccessorOpts & ACCESSOR_BLOB_ISTORAGE) pBinding->pObject->iid = IID_IStorage; } //Special Handling for other non-OLE DB defined convertable types to WSTR //NOTE: The spec requires all supported types to be converted to //WSTR, but this only applies where the OLE DB conversion is defined. //Some are not defined so we need to bind nativly. switch(pColInfo->wType) { case DBTYPE_IUNKNOWN: case DBTYPE_IDISPATCH: pBinding->wType = pColInfo->wType; pBinding->cbMaxLen = sizeof(IUnknown*); break; case DBTYPE_HCHAPTER: pBinding->wType = pColInfo->wType; pBinding->cbMaxLen = sizeof(HCHAPTER); break; default: //DBTYPE_VECTOR if(pColInfo->wType & DBTYPE_VECTOR) { pBinding->wType = pColInfo->wType; pBinding->cbMaxLen = sizeof(DBVECTOR); } //DBTYPE_ARRAY if(pColInfo->wType & DBTYPE_ARRAY) { pBinding->wType = pColInfo->wType; pBinding->cbMaxLen = sizeof(SAFEARRAY*); } break; }; //Offset dwOffset += pBinding->cbMaxLen; dwOffset = ROUNDUP( dwOffset ); switch(eBindCols) { case BIND_ALLCOLS: cBindings++; break; case BIND_ALLCOLSEXPECTBOOKMARK: if(pColInfo->iOrdinal != 0) cBindings++; break; case BIND_UPDATEABLECOLS: if(pColInfo->dwFlags & DBCOLUMNFLAGS_WRITE || pColInfo->dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN) cBindings++; break; default: ASSERT(!"Unhandled Type!"); break; } } //Size for pData if(pcRowSize) *pcRowSize = dwOffset; CLEANUP: //Accessors *pcBindings = cBindings; *prgBindings = rgBindings; return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::CreateAccessor // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::CreateAccessor(DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING* rgBindings, DBLENGTH cRowSize, HACCESSOR* phAccessor) { ASSERT(phAccessor); HRESULT hr = E_FAIL; DBBINDSTATUS* rgStatus = NULL; if(m_pIAccessor) { //Alloc the space to hold the status SAFE_ALLOC(rgStatus, DBBINDSTATUS, cBindings); //Create the accessor *phAccessor = NULL; XTEST(hr = m_pIAccessor->CreateAccessor(dwAccessorFlags, cBindings, rgBindings, cRowSize, phAccessor, rgStatus)); TRACE_METHOD(hr, L"IAccessor::CreateAccessor(0x%08x, %Iu, 0x%p, %Iu, &0x%p, 0x%p)", dwAccessorFlags, cBindings, rgBindings, cRowSize, *phAccessor, rgStatus); //Display Errors TESTC(hr = DisplayAccessorErrors(hr, cBindings, rgBindings, rgStatus)); } CLEANUP: SAFE_FREE(rgStatus); return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::AddRefAccessor // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::AddRefAccessor(HACCESSOR hAccessor) { HRESULT hr = S_OK; ULONG ulRefCount = 0; if(m_pIAccessor) { //IAccessor::AddRefAccessor XTEST(hr = m_pIAccessor->AddRefAccessor(hAccessor, &ulRefCount)); TESTC(TRACE_METHOD(hr, L"IAccessor::AddRefAccessor(0x%p, &%d)", hAccessor, ulRefCount)); } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::ReleaseAccessor // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::ReleaseAccessor(HACCESSOR* phAccessor, BOOL fReleaseAlways) { ASSERT(phAccessor); HRESULT hr = S_OK; ULONG ulRefCount = 0; if(m_pIAccessor && (*phAccessor || fReleaseAlways)) { XTEST(hr = m_pIAccessor->ReleaseAccessor(*phAccessor, &ulRefCount)); TESTC(TRACE_METHOD(hr, L"IAccessor::ReleaseAccessor(0x%p, &%d)", *phAccessor, ulRefCount)); } CLEANUP: //Only NULL the handle if the RefCount is 0 if(ulRefCount == 0) *phAccessor = NULL; return hr; } //////////////////////////////////////////////////////////////// // CDataAccess::GetColumnData // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::GetColumnData(const DBBINDING* pBinding, void* pData, DBSTATUS* pdbStatus, DBLENGTH* pdbLength, DBTYPE* pwSubType, WCHAR* pwszValue, ULONG ulMaxSize, DWORD dwConvFlags, DBTYPE wBackendType) { ASSERT(pBinding); ASSERT(pwszValue); HRESULT hr = S_OK; DBTYPE wSubType = DBTYPE_EMPTY; //STATUS DBSTATUS dbStatus = STATUS_IS_BOUND(*pBinding) ? BINDING_STATUS(*pBinding, pData) : DBSTATUS_E_BADSTATUS; //LENGTH is the Actual (Untruncated) length of the string //We we need to make sure all operations are off of cbMaxLen and not LENGTH bound //NOTE: If the length is not bound, we set the length to our cbMaxLength NOT 0. This is because //many datatypes can still be returned, and our conversion routines are based upon the length... DBLENGTH dbLength = LENGTH_IS_BOUND(*pBinding) ? BINDING_LENGTH(*pBinding, pData) : pBinding->cbMaxLen; //VALUE pwszValue[0] = wEOL; void* pValue = VALUE_IS_BOUND(*pBinding) ? &BINDING_VALUE(*pBinding, pData) : NULL; //cbMaxLen //NOTE: The maximum size we can possibly read is either the specified cbMaxLen or our //data buffer we are retriving into for display... switch(dbStatus) { case DBSTATUS_S_ISNULL: case DBSTATUS_S_DEFAULT: case DBSTATUS_S_IGNORE: hr = S_OK; goto CLEANUP; case DBSTATUS_S_TRUNCATED: case DBSTATUS_S_OK: break; default: hr = E_FAIL; goto CLEANUP; }; if(pValue) { //Handle any special conversions ourselves... switch(DBTYPE_SRC_DST(pBinding->wType, DBTYPE_WSTR)) { //DBTYPE_IUNKNOWN => DBTYPE_WSTR case DBTYPE_SRC_DST(DBTYPE_IUNKNOWN, DBTYPE_WSTR): case DBTYPE_SRC_DST(DBTYPE_IUNKNOWN | DBTYPE_BYREF, DBTYPE_WSTR): { //Handle BYREF as well. (simple enough) if(pBinding->wType & DBTYPE_BYREF) pValue = *(void**)pValue; //IUnknown is not a required conversion to string (WSTR) IUnknown* pIUnknown = *(IUnknown**)pValue; //Determine the type of object. (default) REFIID iid = pBinding->pObject ? pBinding->pObject->iid : IID_IUnknown; //Create a temporary stream wrapper... CStream cStream(m_pCMainWindow); cStream.CreateObject(this, iid, pIUnknown); cStream.m_wType = wBackendType; //Read the Stream Object (numerous different stream objects) if(FAILED(hr = cStream.ReadString(iid, 0/*ulOffset*/, (ulMaxSize*sizeof(WCHAR))-sizeof(WCHAR), ulMaxSize, pwszValue))) { //I have no clue what type of object this is, or was unable to read the //stream for some reason, so just display the error StringFormat(pwszValue, ulMaxSize, L"%S", GetErrorName(hr)); } //The refcount of the Stream now is 1. //Our FreeBindingData frees outofline data... break; } default: { //Otherwise delegate... //NOTE: Make sure we indicate to DataConvert that the input buffer is only //as large as the specified length, but is bound by our allocated buffer size, //so it doesn't try and read beyond valid memory... XTESTC_(hr = DataConvert ( dbStatus, dbLength, pBinding->cbMaxLen, pBinding->wType, pValue, pBinding->bPrecision, pBinding->bScale, DBTYPE_WSTR, //Status. //Only update our status if returned successfully from the provider. dbStatus == DBSTATUS_S_OK ? &dbStatus : NULL, NULL, pwszValue, ulMaxSize*sizeof(WCHAR), dwConvFlags ),S_OK); //Indicate a sub type... if(pBinding->wType == DBTYPE_VARIANT) { VARIANT* pVariant = (VARIANT*)pValue; wSubType = pVariant->vt; } break; } }; } //PostProcessing if(SUCCEEDED(hr)) { //Hexadecimal if(dwConvFlags & CONV_HEX) { switch(wBackendType) { case DBTYPE_I1: case DBTYPE_UI1: case DBTYPE_I2: case DBTYPE_UI2: case DBTYPE_I4: case DBTYPE_UI4: PostProcessString(pwszValue, ulMaxSize, dwConvFlags); break; default: break; }; } //Schema Rowsets: if(m_bSchemaRowset && (dwConvFlags & CONV_TYPENAME)) { //Special Handling for Known Schema Rowsets, //map the "DBTYPE" column from UI2 to a textual name of the Type... if( (pBinding->iOrdinal == 2 && m_guidSource == DBSCHEMA_PROVIDER_TYPES) || (pBinding->iOrdinal == 12 && m_guidSource == DBSCHEMA_COLUMNS) || (pBinding->iOrdinal == 8 && m_guidSource == DBSCHEMA_TABLES_INFO) || (pBinding->iOrdinal == 10 && m_guidSource == DBSCHEMA_PROCEDURE_COLUMNS) ) { //TODO: Why the double conversion? ASSERT(wBackendType == DBTYPE_UI2); //The SchemaRowset column just contains an string(integer) of the type. //(ie: DBTYPE_WSTR = "130". So we need to convert the string to an int, then to //the actual assoicated mapped type name (for ease of viewing without having to //have the header handy when viewing schemas). DBTYPE wSchemaType; if(ConvertToLONG(pwszValue, (LONG*)&wSchemaType, 0/*Min*/, LONG_MAX, 0/*Base*/)) { //NOTE: on failure we have no choice to leave the column as the original //returned integer. StringCopy(pwszValue, GetDBTypeName(wSchemaType), ulMaxSize); } } } } CLEANUP: //Output Params if(pdbStatus) *pdbStatus = dbStatus; if(pdbLength) *pdbLength = dbLength; if(pwSubType) *pwSubType = wSubType; return hr; } //////////////////////////////////////////////////////////////// // CDataAccess::SetColumnData // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::SetColumnData(const DBBINDING* pBinding, void* pData, DBSTATUS dbStatus, DBLENGTH dbLength, WCHAR* pwszValue, DWORD dwConvFlags, DBTYPE wBackendType) { //NOTE: We ASSERT on pwszValue since DataConvert and even our conversion reqturns will fails //for NULL source. If you really have a NULL string it should be since as an empty //string for conversion purposes and DataConvert will set length = 0... ASSERT(pwszValue); ASSERT(pBinding); HRESULT hr = S_OK; void* pValue = VALUE_IS_BOUND(*pBinding) ? &BINDING_VALUE(*pBinding, pData) : NULL; DBSTATUS dbDstStatus = dbStatus; DBLENGTH cbDstLength = 0; //STATUS switch(dbStatus) { case DBSTATUS_S_OK: case DBSTATUS_S_TRUNCATED: break; default: //Any other Status, IGNORE, ISNULL, DEFAULT, ERROR, were done, nothing to convert... goto CLEANUP; } if(pValue) { //Handle any special conversions ourselves... switch(DBTYPE_SRC_DST(pBinding->wType, DBTYPE_WSTR)) { //DBTYPE_IUNKNOWN => DBTYPE_WSTR case DBTYPE_SRC_DST(DBTYPE_IUNKNOWN, DBTYPE_WSTR): case DBTYPE_SRC_DST(DBTYPE_IUNKNOWN | DBTYPE_BYREF, DBTYPE_WSTR): { //Handle BYREF as well. (simple enough) if(pBinding->wType & DBTYPE_BYREF) { SAFE_ALLOC(*(void**)pValue, IUnknown*, 1); pValue = *(void**)pValue; } //Create the storage object (buffer) to use for SetData. CStorageBuffer* pCStorageBuffer = new CStorageBuffer; //m_cRef=1 //Create a temporary stream wrapper... CStream cStream(m_pCMainWindow); cStream.CreateObject(this, IID_IStream, (IStream*)pCStorageBuffer); cStream.m_wType = wBackendType; //Now, Set the user entered data into our storage object. //NOTE: We set the Length = total bytes in stream, since some providers //requires this ahead of time. And we reset the stream so the current seek //is at the head for when the provider begins to read from the stream on SetData cStream.WriteString(IID_ISequentialStream, 0, pwszValue ? wcslen(pwszValue)*sizeof(WCHAR) : 0, pwszValue, &cbDstLength); pCStorageBuffer->Seek(0); //Passing the object pointer should be suffiencent enough, as the provider //will have to QI of the object anyway to determine if it even supports //the interface, but since many incorrectly do not we will explictly //QI here so the majority of the providers don't crash IUnknown* pIUnknown = NULL; XTESTC_(pCStorageBuffer->QueryInterface(pBinding->pObject ? pBinding->pObject->iid : IID_IUnknown, (void**)&pIUnknown), S_OK); *(IUnknown**)pValue = pIUnknown; //The refcount of the Stream now is 2. //The provider releases one, and our FreeBindingData fress outofline data... break; } default: { //Otherwise delegate... XTESTC_(hr = DataConvert ( dbStatus, dbLength * sizeof(WCHAR), dbLength * sizeof(WCHAR) + sizeof(WCHAR), DBTYPE_WSTR, pwszValue, pBinding->bPrecision, pBinding->bScale, pBinding->wType, &dbDstStatus, &cbDstLength, pValue, pBinding->cbMaxLen, dwConvFlags ),S_OK); break; } }; } CLEANUP: if(STATUS_IS_BOUND(*pBinding)) BINDING_STATUS(*pBinding, pData) = dbDstStatus; if(LENGTH_IS_BOUND(*pBinding)) BINDING_LENGTH(*pBinding, pData) = cbDstLength; return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CDataAccess::GetColumnsRowset // ///////////////////////////////////////////////////////////////// HRESULT CDataAccess::GetColumnsRowset(CAggregate* pCAggregate, bool fOptColumns, REFIID riid, ULONG cPropSets, DBPROPSET* rgPropSets, IUnknown** ppIUnknown) { DBORDINAL i,cOptColumns = 0; DBID* rgOptColumns = 0; HRESULT hr = S_OK; if(!m_pIColumnsRowset) return E_FAIL; //GetAvailableColumns //We just use cRestrictions passed in as the flag //to indicate weither to use Optional Columns if(fOptColumns) { XTEST(hr = m_pIColumnsRowset->GetAvailableColumns(&cOptColumns, &rgOptColumns)); TESTC(TRACE_METHOD(hr, L"IColumnsRowset::GetAvailableColumns(&%Iu, &0x%p)", cOptColumns, rgOptColumns)); } //GetColumnsRowset XTEST_(hr = m_pIColumnsRowset->GetColumnsRowset( pCAggregate, // punkOuter cOptColumns, // cOptionalColumns rgOptColumns, // rgOptionalColumns riid, // rowset interface cPropSets, // # of properties rgPropSets, // properties ppIUnknown),S_OK); // rowset pointer TRACE_METHOD(hr, L"IColumnsRowset::GetColumnsRowset(0x%p, %Iu, 0x%p, %s, %d, 0x%p, &0x%p)", pCAggregate, cOptColumns, rgOptColumns, GetInterfaceName(riid), cPropSets, rgPropSets, ppIUnknown ? *ppIUnknown : NULL); //Display Errors (if occurred) TESTC(hr = DisplayPropErrors(hr, cPropSets, rgPropSets)); //Handle Aggregation if(pCAggregate) TESTC(hr = pCAggregate->HandleAggregation(riid, ppIUnknown)); CLEANUP: //FreeDBIDs for(i=0; im_dwAccessorOpts & ACCESSOR_BIND_BOOKMARK ? BIND_ALLCOLS : BIND_ALLCOLSEXPECTBOOKMARK); if(m_pCMDIChild) { //Reset Cursor m_pCMDIChild->m_pCDataGrid->m_fLastFetchForward = FALSE; m_pCMDIChild->m_pCDataGrid->m_lCurPos = 0; //First Clear the existing Window... TESTC(hr = m_pCMDIChild->m_pCDataGrid->ClearAll()); } //Obtain Properties // if(FAILED(GetProperty(IID_IRowsetInfo, m_pIRowsetInfo, DBPROP_REMOVEDELETED, DBPROPSET_ROWSET, DBTYPE_BOOL, &m_fRemoveDeleted))) // m_fRemoveDeleted = FALSE; //Create ColumnInfo TESTC(hr = GetColInfo()); //Create Accessors and Setup bindings TESTC(hr = CreateAccessors(eBindCols)); if(m_pCMDIChild) { //Refresh the Columns and Rows TESTC(hr = m_pCMDIChild->m_pCDataGrid->RefreshData()); } //Display the object... TESTC(hr = CDataAccess::DisplayObject()); CLEANUP: if(m_pCMDIChild) m_pCMDIChild->UpdateControls(); return hr; } ///////////////////////////////////////////////////////////////////////////// // CRowset::GetObjectDesc // ///////////////////////////////////////////////////////////////////////////// WCHAR* CRowset::GetObjectDesc() { if(!m_strObjectDesc && m_guidSource != GUID_NULL) { //Schema Rowset if(m_bSchemaRowset) { //Lookup the "Friendly Name" of the Schema Rowset... m_strObjectDesc.CopyFrom(GetSchemaName(m_guidSource)); } else if(m_guidSource == IID_IColumnsRowset) { //What interface created this... m_strObjectDesc.CopyFrom(GetInterfaceName(m_guidSource)); } } return m_strObjectDesc; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::ValidateRow // ///////////////////////////////////////////////////////////////// HRESULT CRowset::ValidateRow(HROW hRow, ULONG ulRefCount) { //Warn user about NULL Rowhandles... if(hRow == DB_NULL_HROW || ulRefCount == 0) { if(IDNO == wMessageBox ( GetFocus(), MB_TASKMODAL | MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON1, wsz_ERROR, L"hRow == 0x%p\n" L"%s\n\n" L"This may crash your Provider...\n" L"Do you wish to continue anyway?", hRow, hRow == DB_NULL_HROW ? L"Invalid Row Handle" : L"Released Row Handle" ) ) return E_FAIL; } return S_OK; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::GetData // ///////////////////////////////////////////////////////////////// HRESULT CRowset::GetData(HROW hRow, HACCESSOR hAccessor, void* pData, DBPROPID dwSourceID) { //Since GetData is the only OLE DB method that is allowed to crash //if given invalid argumenets (for effiecientcy - provider speicific //weither it validates arguments), we should check the params and at the //very least warn the user... HRESULT hr = E_FAIL; //Defaults if(hAccessor == NULL) hAccessor = m_hAccessor; if(pData == NULL) pData = m_pData; //Check Arguments... TESTC(hr = ValidateRow(hRow)); TESTC(hr = ValidateAccessor(hAccessor)); //GetData switch(dwSourceID) { //IRowset::GetData case DBPROP_IRowset: if(m_pIRowset) { XTEST(hr = m_pIRowset->GetData(hRow, hAccessor, pData)); TESTC(TRACE_METHOD(hr, L"IRowset::GetData(0x%p, 0x%p, 0x%p)", hRow, hAccessor, pData)); } break; //IRowsetLocate::GetData case DBPROP_IRowsetLocate: if(m_pIRowsetLocate) { XTEST(hr = m_pIRowsetLocate->GetData(hRow, hAccessor, pData)); TESTC(TRACE_METHOD(hr, L"IRowsetLocate::GetData(0x%p, 0x%p, 0x%p)", hRow, hAccessor, pData)); } break; //IRowsetScroll::GetData case DBPROP_IRowsetScroll: if(m_pIRowsetScroll) { XTEST(hr = m_pIRowsetScroll->GetData(hRow, hAccessor, pData)); TESTC(TRACE_METHOD(hr, L"IRowsetScroll::GetData(0x%p, 0x%p, 0x%p)", hRow, hAccessor, pData)); } break; //IRowsetUpdate::GetOriginalData case DBPROP_IRowsetUpdate: if(m_pIRowsetUpdate) { XTEST(hr = m_pIRowsetUpdate->GetOriginalData(hRow, hAccessor, pData)); TESTC(TRACE_METHOD(hr, L"IRowsetUpdate::GetOriginalData(0x%p, 0x%p, 0x%p)", hRow, hAccessor, pData)); } break; //IRowsetResynch::GetVisibleData case DBPROP_IRowsetResynch: if(m_pIRowsetResynch) { XTEST(hr = m_pIRowsetResynch->GetVisibleData(hRow, hAccessor, pData)); TESTC(TRACE_METHOD(hr, L"IRowsetResynch::GetVisibleData(0x%p, 0x%p, 0x%p)", hRow, hAccessor, pData)); } break; //IRowsetRefresh::GetLastVisibleData case DBPROP_IRowsetRefresh: if(m_pIRowsetRefresh) { XTEST(hr = m_pIRowsetRefresh->GetLastVisibleData(hRow, hAccessor, pData)); TESTC(TRACE_METHOD(hr, L"IRowsetRefresh::GetLastVisibleData(0x%p, 0x%p, 0x%p)", hRow, hAccessor, pData)); } break; default: ASSERT(!"Unhandled Source!"); break; }; CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::GetBookmark // ///////////////////////////////////////////////////////////////// HRESULT CRowset::GetBookmark(HROW hRow, DBBKMARK* pcbBookmark, BYTE** ppBookmark) { ASSERT(pcbBookmark); ASSERT(ppBookmark); HRESULT hr = S_OK; *pcbBookmark = 0; *ppBookmark = NULL; DBSTATUS dbStatus = 0; BYTE* pData = NULL; //Make sure we have a bookmark... if(!m_ColumnInfo.GetCount() || !m_ColumnInfo.GetElements() || m_ColumnInfo[0].iOrdinal!=0) return E_FAIL; //Obtain the maximum bookmark size... DBLENGTH ulMaxBmkSize = ROUNDUP(GetMaxDisplaySize(DBTYPE_BYTES, m_ColumnInfo[0].wType, m_ColumnInfo[0].ulColumnSize, MAX_COL_SIZE)); //Deferred Accessor Creation if(m_hBmkAccessor==NULL) { //Static bindings //Design the bindigs so the value is first and status length last. This way we can return //the bookmark memory, the rest of the code can access the bookmark directly, and we can free //all the memory as well starting with the bookmark address... const DBCOUNTITEM cBindings = 1; DBBINDING rgBindings[cBindings] = { 0, 0, //VALUE ulMaxBmkSize, //LENGTH ulMaxBmkSize + sizeof(DBLENGTH), //STATUS NULL, NULL, NULL, DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM, ulMaxBmkSize + sizeof(DBLENGTH) + sizeof(DBSTATUS), 0, DBTYPE_BYTES, 0, 0, }; //Now create the accessor... TESTC(hr = CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, 0, &m_hBmkAccessor)); } //Allocate enough space for the bookmark, (including status and length of course) SAFE_ALLOC(pData, BYTE, ulMaxBmkSize + sizeof(DBLENGTH) + sizeof(DBSTATUS)); //GetData to obtain Bookmark value (delegate) TESTC(hr = GetData(hRow, m_hBmkAccessor, pData)); //Length dbStatus = *(DBSTATUS*)(pData + ulMaxBmkSize + sizeof(DBLENGTH)); if(dbStatus == DBSTATUS_S_OK) { *pcbBookmark = *(ULONG*)(pData + ulMaxBmkSize); //LENGTH *ppBookmark = pData; //VALUE } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::GetChapter // ///////////////////////////////////////////////////////////////// HRESULT CRowset::GetChapter(HROW hRow, DBORDINAL iOrdinal, HCHAPTER* phChapter) { ASSERT(hRow); ASSERT(phChapter); HRESULT hr = S_OK; HACCESSOR hAccessor = NULL; //Initialize in case of ISNULL data... *phChapter = NULL; //Bind only the Chapter column... const static DBCOUNTITEM cBindings = 1; DBBINDING rgBindings[cBindings] = { iOrdinal, 0, //pData = phChapter + 0 offset = phChapter 0, 0, NULL, NULL, NULL, DBPART_VALUE, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM, sizeof(HCHAPTER), 0, DBTYPE_HCHAPTER, 0, 0, }; //Create the Accessor... TESTC(hr = CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, 0, &hAccessor)); //GetData to obtain Bookmark value (delegate) TESTC(hr = GetData(hRow, hAccessor, phChapter)); CLEANUP: ReleaseAccessor(&hAccessor); return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::RestartPosition // ///////////////////////////////////////////////////////////////// HRESULT CRowset::RestartPosition() { HRESULT hr = E_FAIL; //IRowset::RestartPosition if(m_pIRowset) { XTEST(hr = m_pIRowset->RestartPosition(m_hChapter)); TESTC(TRACE_METHOD(hr, L"IRowset::RestartPosition(0x%p)", m_hChapter)); } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::GetNextRows // ///////////////////////////////////////////////////////////////// HRESULT CRowset::GetNextRows(DBROWOFFSET lOffset, DBROWCOUNT cRows, DBCOUNTITEM* pcRowsObtained, HROW** prghRows) { HRESULT hr = E_FAIL; DBCOUNTITEM cRowsObtained = 0; HROW* rghRows = NULL; if(prghRows) rghRows = *prghRows; if(m_pIRowset) { //GetNextRows XTEST(hr = m_pIRowset->GetNextRows( m_hChapter, // hChapter lOffset, // lOffset cRows, // cRows &cRowsObtained, // cRowsObtained &rghRows)); // rghRows TESTC(TRACE_METHOD(hr, L"IRowset::GetNextRows(0x%p, %Id, %Id, &%Iu, &0x%p)", m_hChapter, lOffset, cRows, cRowsObtained, rghRows)); } CLEANUP: if(pcRowsObtained) *pcRowsObtained = cRowsObtained; if(prghRows) *prghRows = rghRows; else SAFE_FREE(rghRows); return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::AddRefRows // ///////////////////////////////////////////////////////////////// HRESULT CRowset::AddRefRows(DBROWCOUNT cRows, HROW* rghRows, ULONG* rgRefCounts) { HRESULT hr = E_FAIL; //Alloc Array for Status DBROWSTATUS* rgRowStatus = NULL; SAFE_ALLOC(rgRowStatus, DBROWSTATUS, cRows); if(m_pIRowset) { //AddRefRows XTEST(hr = m_pIRowset->AddRefRows(cRows, rghRows, rgRefCounts, rgRowStatus)); DisplayRowErrors(hr, cRows, rghRows, rgRowStatus); TESTC(TRACE_METHOD(hr, L"IRowset::AddRefRows(%lu, 0x%p, 0x%p, 0x%p)", cRows, rghRows, rgRefCounts, rgRowStatus)); } CLEANUP: SAFE_FREE(rgRowStatus); return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::ReleaseRows // ///////////////////////////////////////////////////////////////// HRESULT CRowset::ReleaseRows(DBROWCOUNT cRows, HROW* rghRows, ULONG* rgRefCounts) { HRESULT hr = E_FAIL; DBROWSTATUS* rgRowStatus = NULL; if(m_pIRowset) { //Alloc Array for Status SAFE_ALLOC(rgRowStatus, DBROWSTATUS, cRows); //ReleaseRows XTEST(hr = m_pIRowset->ReleaseRows(cRows, rghRows, NULL, rgRefCounts, rgRowStatus)); DisplayRowErrors(hr, cRows, rghRows, rgRowStatus); TESTC(TRACE_METHOD(hr, L"IRowset::ReleaseRows(%lu, 0x%p, NULL, 0x%p, 0x%p)", cRows, rghRows, rgRefCounts, rgRowStatus)); } CLEANUP: SAFE_FREE(rgRowStatus); return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CRowset::GetRowFromHROW // ///////////////////////////////////////////////////////////////// HRESULT CRowset::GetRowFromHROW(CAggregate* pCAggregate, HROW hRow, REFIID riid, IUnknown** ppIUnknown) { HRESULT hr = E_FAIL; if(m_pIGetRow) { //IGetRow::GetRowFromHROW XTEST(hr = m_pIGetRow->GetRowFromHROW(pCAggregate, hRow, riid, ppIUnknown)); TESTC(TRACE_METHOD(hr, L"IGetRow::GetRowFromHROW(0x%p, 0x%p, %s, &0x%p)", pCAggregate, hRow, GetInterfaceName(riid), ppIUnknown ? *ppIUnknown : NULL)); //Handle Aggregation if(pCAggregate) TESTC(hr = pCAggregate->HandleAggregation(riid, ppIUnknown)); } CLEANUP: return hr; }