|
|
//+-----------------------------------------------------------------------
//
// Add/Remove Programs Data Source Object
//
//------------------------------------------------------------------------
#include "priv.h"
// Do not build this file if on Win9X or NT4
#ifndef DOWNLEVEL_PLATFORM
#include "datasrc.h"
#include "dump.h"
#include "util.h"
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
// constructor
CDataSrc::CDataSrc() { TraceMsg(TF_OBJLIFE, "(Mtx) creating"); TraceAddRef(CDataSrc, _cRef); ASSERT(NULL == _parpevt); ASSERT(NULL == _pmtxarray); ASSERT(NULL == _psam); ASSERT(FALSE == _fAppsEnumed); ASSERT(FALSE == _fInEnumOp); _loadstate = LS_NOTSTARTED; }
// destructor
CDataSrc::~CDataSrc() { TraceMsg(TF_OBJLIFE, "(Mtx) destroying");
ATOMICRELEASE(_pmtxarray); ATOMICRELEASE(_parpevt); ATOMICRELEASE(_psam); }
/*--------------------------------------------------------------------
Purpose: IUnknown::QueryInterface */ STDMETHODIMP CDataSrc::QueryInterface(REFIID riid, LPVOID * ppvObj) { static const QITAB qit[] = { QITABENT(CDataSrc, IARPSimpleProvider), QITABENT(CDataSrc, OLEDBSimpleProvider), QITABENT(CDataSrc, ISequentialStream), QITABENT(CDataSrc, IWorkerEvent), { 0 }, };
HRESULT hres = QISearch(this, (LPCQITAB)qit, riid, ppvObj); if (FAILED(hres)) hres = CWorkerThread::QueryInterface(riid, ppvObj);
return hres; }
/*--------------------------------------------------------------------
Purpose: IARPWorker::KillWT Kills the worker thread that enumerates apps */ STDMETHODIMP CDataSrc::KillWT() { // Primary thread wants to kill us, this means we are about to be released
// also kill the mtxarray thread here, because that kill has to be on the main thread, too.
// And we can't depend on CDataSrc descrutor to do it (because that final release could be called on the
// back groud thread)
_KillMtxWorkerThread();
return CWorkerThread::KillWT(); }
/*-------------------------------------------------------------------------
Purpose: IWorkerEvent::FireOnDataReady
Called by worker thread when some data is ready. */ STDMETHODIMP CDataSrc::FireOnDataReady( DBROWCOUNT iRow ) { // OSP listener expects row to be 1-based
_parpevt->RowChanged(iRow + 1); return S_OK; }
/*-------------------------------------------------------------------------
Purpose: IWorkerEvent::FireOnFinished
Called by worker thread when it is complete. */ STDMETHODIMP CDataSrc::FireOnFinished(void) { _loadstate = LS_DONE; return S_OK; }
/*-------------------------------------------------------------------------
Purpose: IWorkerEvent::FireOnDatasetChanged
Called by worker thread when it is complete. */ STDMETHODIMP CDataSrc::FireOnDatasetChanged(void) { if (_parpevt) _parpevt->DataSetChanged(); return S_OK; }
// CDataSrc::_CalcRows
// Calculate the number of rows in the OSP
DBROWCOUNT CDataSrc::_CalcRows(void) { DBROWCOUNT lRet = 0; if (_pmtxarray) _pmtxarray->GetItemCount(&lRet); return lRet; }
// CDataSrc::_CalcCols
// Calculate the number of columns in the OSP
DB_LORDINAL CDataSrc::_CalcCols(void) { DB_LORDINAL lRet = 0; if (_pmtxarray) _pmtxarray->GetFieldCount(&lRet); return lRet; }
inline BOOL CDataSrc::_IsValidDataRow(DBROWCOUNT iRow) { // Rows are 1-based. The 0th row refers to label information.
// -1 means wildcard.
// The 0th row is NOT a valid data row.
return (iRow > 0 && iRow <= _cRows); }
inline BOOL CDataSrc::_IsValidRow(DBROWCOUNT iRow) { // Rows are 1-based. The 0th row refers to label information.
// -1 means wildcard.
return (iRow >= 0 && iRow <= _cRows); }
inline BOOL CDataSrc::_IsValidCol(DB_LORDINAL iCol) { // Columns are 1-based. The 0th column refers to header information.
// -1 means wildcard.
return (iCol >= 1 && iCol <= _cCols); }
inline BOOL CDataSrc::_IsValidCell(DBROWCOUNT iRow, DB_LORDINAL iCol) { return _IsValidRow(iRow) && _IsValidCol(iCol); }
/*-------------------------------------------------------------------------
Purpose: Returns the appdata object of the given row (1-based). Returns NULL if there is none. */ IAppData * CDataSrc::_GetAppData(DBROWCOUNT iRow) { IAppData * pappdata = NULL;
if (_pmtxarray) { ASSERT(0 < iRow && iRow <= _cRows); _pmtxarray->GetAppData(iRow-1, &pappdata); } return pappdata; }
// Structure used to transfer matrix object thru ISequentialStream()
typedef struct tagARPDSODATA { LOAD_STATE loadstate; DB_LORDINAL cCols; // count of columns
DBROWCOUNT cRows; // count of rows
DWORD dwEnum; // items to enumerate (ENUM_*)
IMtxArray * pmtxarray; // data is stored here
BSTR bstrSort; // sort string
} ARPDSODATA;
/*-------------------------------------------------------------------------
Purpose: ISequentialStream::Read
Return the matrix object of this datasource object. IARPSimpleProvider::TransferData uses this method. */ STDMETHODIMP CDataSrc::Read(void * pvData, ULONG cbData, ULONG * pcbRead) { HRESULT hres = E_INVALIDARG; ASSERT(IS_VALID_WRITE_BUFFER(pvData, BYTE, cbData)); ASSERT(NULL == pcbRead || IS_VALID_WRITE_PTR(pcbRead, ULONG)); if (pvData) { ARPDSODATA * pdsodata = (ARPDSODATA *)pvData;
if (pcbRead) *pcbRead = 0;
if (sizeof(*pdsodata) <= cbData) { pdsodata->loadstate = _loadstate; pdsodata->cCols = _cCols; pdsodata->cRows = _cRows; pdsodata->dwEnum = _dwEnum;
pdsodata->pmtxarray = _pmtxarray; if (_pmtxarray) _pmtxarray->AddRef(); pdsodata->bstrSort = _cbstrSort.Copy();
if (pcbRead) *pcbRead = sizeof(*pdsodata); } hres = S_OK; } return hres; }
/*-------------------------------------------------------------------------
Purpose: ISequentialStream::Write
Set the matrix object of this datasource object. IARPSimpleProvider::TransferData uses this method. */ STDMETHODIMP CDataSrc::Write(void const * pvData, ULONG cbData, ULONG * pcbWritten) { HRESULT hres = E_INVALIDARG; ASSERT(IS_VALID_READ_BUFFER(pvData, BYTE, cbData)); ASSERT(NULL == pcbWritten || IS_VALID_WRITE_PTR(pcbWritten, ULONG)); if (pvData) { ARPDSODATA * pdsodata = (ARPDSODATA *)pvData;
if (pcbWritten) *pcbWritten = 0;
if (sizeof(*pdsodata) <= cbData) { _loadstate = pdsodata->loadstate; _cCols = pdsodata->cCols; _cRows = pdsodata->cRows; _dwEnum = pdsodata->dwEnum;
// We won't addref this, since the supplier should have done that.
_pmtxarray = pdsodata->pmtxarray;
_cbstrSort.Empty(); _cbstrSort.Attach(pdsodata->bstrSort);
if (pcbWritten) *pcbWritten = sizeof(*pdsodata); } hres = S_OK; } return hres; }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::Initialize
Must be called before enumerating items. */ STDMETHODIMP CDataSrc::Initialize(IShellAppManager * psam, IARPEvent * parpevt, DWORD dwEnum) { ASSERT(psam); ASSERT(IS_VALID_CODE_PTR(parpevt, CEventBroker));
ATOMICRELEASE(_psam); ATOMICRELEASE(_parpevt);
_psam = psam; _psam->AddRef(); _parpevt = parpevt; _parpevt->AddRef();
_dwEnum = dwEnum; return S_OK; }
HRESULT CDataSrc::_EnumAppItems(DWORD dwEnum, LPCWSTR pszCategory) { HRESULT hres = E_INVALIDARG; IInstalledApp* pAppIns; CAppData* pcad;
ASSERT(NULL == pszCategory || IS_VALID_STRING_PTRW(pszCategory, -1)); switch (dwEnum) { case ENUM_INSTALLED: IEnumInstalledApps* pEnumIns;
// Now that we have the object, start enumerating the items
hres = THR(_psam->EnumInstalledApps(&pEnumIns)); if (SUCCEEDED(hres)) { // Loop through all the apps on the machine, building our table
while (S_OK == pEnumIns->Next(&pAppIns)) { // If we've been asked to bail, do so
if (IsKilled()) { pAppIns->Release(); break; } APPINFODATA ai = {0}; // Get the 'fast' app info from the app manager object
ai.cbSize = sizeof(ai); ai.dwMask = AIM_DISPLAYNAME | AIM_VERSION | AIM_PUBLISHER | AIM_PRODUCTID | AIM_REGISTEREDOWNER | AIM_REGISTEREDCOMPANY | AIM_SUPPORTURL | AIM_SUPPORTTELEPHONE | AIM_HELPLINK | AIM_INSTALLLOCATION | AIM_INSTALLDATE | AIM_COMMENTS | AIM_IMAGE | AIM_READMEURL | AIM_CONTACT | AIM_UPDATEINFOURL; if (SUCCEEDED(pAppIns->GetAppInfo(&ai)) && lstrlen(ai.pszDisplayName) > 0) { SLOWAPPINFO sai = {0}; pAppIns->GetCachedSlowAppInfo(&sai); // Now save all this information away
pcad = new CAppData(pAppIns, &ai, &sai); if (pcad) { _pmtxarray->AddItem(pcad, NULL); pcad->Release(); } else { // Something failed
pAppIns->Release(); ClearAppInfoData(&ai); } } // NOTE: we do NOT release the pointer (pAppIns) here,
// its lifetime is passed to the CAppData object
} pEnumIns->Release(); hres = S_OK; } break;
case ENUM_PUBLISHED: IEnumPublishedApps * pepa; // Salt 'n...
// Convert an empty string to a null string if we need to
if (pszCategory && 0 == *pszCategory) pszCategory = NULL; // Enumerate published apps
hres = THR(_psam->EnumPublishedApps(pszCategory, &pepa)); if (SUCCEEDED(hres)) { IPublishedApp * ppa;
while (S_OK == pepa->Next(&ppa)) { // If we've been asked to bail, do so
if (IsKilled()) { ppa->Release(); break; } APPINFODATA ai = {0}; // Get the 'fast' app info from the app manager object
ai.cbSize = sizeof(ai); ai.dwMask = AIM_DISPLAYNAME | AIM_VERSION | AIM_PUBLISHER | AIM_PRODUCTID | AIM_REGISTEREDOWNER | AIM_REGISTEREDCOMPANY | AIM_SUPPORTURL | AIM_SUPPORTTELEPHONE | AIM_HELPLINK | AIM_INSTALLLOCATION | AIM_INSTALLDATE | AIM_COMMENTS | AIM_IMAGE; if (SUCCEEDED(ppa->GetAppInfo(&ai)) && lstrlen(ai.pszDisplayName) > 0) { PUBAPPINFO pai = {0}; pai.cbSize = sizeof(pai); pai.dwMask = PAI_SOURCE | PAI_ASSIGNEDTIME | PAI_PUBLISHEDTIME | PAI_EXPIRETIME | PAI_SCHEDULEDTIME; ppa->GetPublishedAppInfo(&pai); // Now save all this information away
pcad = new CAppData(ppa, &ai, &pai); if (pcad) { _pmtxarray->AddItem(pcad, NULL); pcad->Release(); } else { // Something failed
ppa->Release(); ClearAppInfoData(&ai); ClearPubAppInfo(&pai); } } // NOTE: we do NOT release the pointer (ppa) here,
// its lifetime is passed to the CAppData object
} pepa->Release(); hres = S_OK; } break;
case ENUM_OCSETUP: // Create an object that enums the OCSetup items
COCSetupEnum * pocse; pocse = new COCSetupEnum; if ( pocse && pocse->EnumOCSetupItems() ) { COCSetupApp * pocsa;
while ( pocse->Next(&pocsa) ) { // If we've been asked to bail, do so
if (IsKilled()) { delete pocsa; break; } // REVIEW: Is it worth it to use an APPINFODATA structure? COcSetupApp
// doesn't need this structure but I think it buys us sorting once inside
// the CAppData array as well as a free implementation of the get_DisplayName
// property which can be accessed via script. The data sorting might be
// important but it might also be worth it to special case that ability.
APPINFODATA ai = {0}; ai.cbSize = sizeof(ai); ai.dwMask = AIM_DISPLAYNAME;
if ( pocsa->GetAppInfo(&ai) && (lstrlen(ai.pszDisplayName) > 0) ) { // Now save all this information away
pcad = new CAppData(pocsa, &ai); if (pcad) { _pmtxarray->AddItem(pcad, NULL); pcad->Release(); } else { // Something failed
delete pocsa; ClearAppInfoData(&ai); } } // NOTE: we do NOT release the pointer (pocsa) here,
// its lifetime is passed to the CAppData object
} } hres = S_OK; break;
case ENUM_CATEGORIES: SHELLAPPCATEGORYLIST sacl = {0};
// Get the list of categories
hres = _psam->GetPublishedAppCategories(&sacl); if (SUCCEEDED(hres)) { SHELLAPPCATEGORY * psac = sacl.pCategory;
// If we've been asked to bail, do so
if (IsKilled()) { ReleaseShellCategory(psac); break; } UINT i;
for (i = 0; i < sacl.cCategories; i++, psac++) { // Now save all this information away
pcad = new CAppData(psac); if (pcad) { _pmtxarray->AddItem(pcad, NULL); pcad->Release(); } else { // Something failed
ReleaseShellCategory(psac); } }
// NOTE: we do NOT release the pointer (sacl) here,
// its lifetime is passed to the CAppData object
} break; }
return hres; }
/*-------------------------------------------------------------------------
Purpose: CDataSrc::_ThreadStartProc() The thread proc for the background thread that enumerates applications */ DWORD CDataSrc::_ThreadStartProc() { TraceMsg(TF_TASKS, "[%x] Starting enumerator thread", _dwThreadId);
// Enumerate the applications, this function does the real work
_EnumAppItems(_dwEnum, _cbstrCategory);
// Claim to the world that we are done
_fAppsEnumed = TRUE; _fInEnumOp = FALSE;
// Tell Trident that dataset has changed
PostWorkerMessage(WORKERWIN_FIRE_DATASETCHANGED, 0, 0);
// Call our base class and do clean up.
return CWorkerThread::_ThreadStartProc(); }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::Recalculate
Recalculate the number of rows and columns and apply the sorting criteria for installed apps, load it's slowappinfo. */ STDMETHODIMP CDataSrc::Recalculate(void) { HRESULT hres = E_PENDING; if (_fAppsEnumed) { // Calculate the columns used and cache that away.
_cCols = _CalcCols(); _cRows = _CalcRows(); // Presort the items according to the existing sort criteria
_ApplySortCriteria(FALSE); if (0 < _cRows) _parpevt->RowsAvailable(0, _cRows); _parpevt->LoadCompleted();
// We only get slow info for the installed apps
if (ENUM_INSTALLED == _dwEnum) { _loadstate = LS_LOADING_SLOWINFO;
// Create and kick off the worker thread
IWorkerEvent * pwrkevt; IARPWorker * pmtxworker;
QueryInterface(IID_IWorkerEvent, (LPVOID *)&pwrkevt); ASSERT(pwrkevt); // this should never fail
hres = _pmtxarray->QueryInterface(IID_IARPWorker, (LPVOID *)&pmtxworker); if (SUCCEEDED(hres)) { // Tell the worker thread to notify us
pmtxworker->SetListenerWT(pwrkevt); hres = pmtxworker->StartWT(THREAD_PRIORITY_BELOW_NORMAL);
pmtxworker->Release(); } pwrkevt->Release(); } else _loadstate = LS_DONE;
hres = S_OK; } else { //
// ISSUE-2000/09/01-BrianAu Watch this message.
// This used to be an assert. Based on comments from the Trident devs
// and from what I can glean from this code, the assert is unnecessary.
// When the enumeration is complete we fire a 'dataMemberChanged' event which
// results in Recalculate being called again. Since the _fAppsEnumed
// flag is set only after enumeration is complete, any prior calls to
// this function are harmless.
//
TraceMsg(TF_ALWAYS, "This function should only be called when app enumeration is done"); } return hres; }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::EnumerateItemsAsync
Enumerate the app items asynchronously. This call returns when all the items have been enumerated. The caller should call Initialize first. */ STDMETHODIMP CDataSrc::EnumerateItemsAsync(void) { HRESULT hres = S_OK;
ASSERT(_parpevt); // Caller should have called Initialize() first
ASSERT(_psam);
if (!_fInEnumOp) { _fInEnumOp = TRUE; // Make sure the slow info worker thread isn't already running. Stop it if it is.
_KillMtxWorkerThread();
// If we already have a list, nuke it
ATOMICRELEASE(_pmtxarray);
hres = THR(CMtxArray_CreateInstance(IID_IMtxArray, (LPVOID *)&_pmtxarray)); if (SUCCEEDED(hres)) { _pmtxarray->Initialize(_dwEnum);
// Start enumerating items
SetListenerWT(this);
// Can't AddRef and worker thread
hres = THR(StartWT(THREAD_PRIORITY_NORMAL)); } else // Let people try again.
_fInEnumOp = FALSE; } else { // This function should only be called before any enumeration started
ASSERTMSG(FALSE, "This function should only be called before any enumeration started"); hres = E_PENDING; } return hres; }
/*-------------------------------------------------------------------------
Purpose: Sorts the data */ HRESULT CDataSrc::_ApplySortCriteria(BOOL bFireDataSetChanged) { HRESULT hres = E_FAIL;
if (_pmtxarray) { _pmtxarray->SetSortCriteria(_cbstrSort); hres = _pmtxarray->SortItems(); if (SUCCEEDED(hres)) { // Mark the duplicated name entries for published apps
if ((ENUM_PUBLISHED == _dwEnum) && !StrCmpW(_cbstrSort, L"displayname")) _pmtxarray->MarkDupEntries(); // Tell the databinding agent that our dataset changed
if (bFireDataSetChanged) _parpevt->DataSetChanged(); } }
return hres; }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::SetSortCriteria Set the sort criterion for the datasource.
Returns S_OK if the sort criteria is different, S_FALSE if it is no different.
bstrSortExpr Name of column to sort by ("" = no sorting) */ STDMETHODIMP CDataSrc::SetSortCriteria(BSTR bstrSortExpr) { HRESULT hres = S_FALSE;
// Is this a new sort criteria?
if (NULL == (LPWSTR)_cbstrSort || 0 != StrCmpIW(bstrSortExpr, _cbstrSort)) { // Yes
_cbstrSort = bstrSortExpr; hres = S_OK; _fSortDirty = TRUE; }
return hres; }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::SetFilter Set the filter criterion for the datasource. Right now this only works for published apps, via a category.
Returns S_OK if the filter criteria is different, S_FALSE if it is no different.
bstrSortExpr Name of column to sort by ("" = no sorting) */ STDMETHODIMP CDataSrc::SetFilter(BSTR bstrFilter) { HRESULT hres = S_FALSE;
// Is this a new filter criteria?
if (NULL == (LPWSTR)_cbstrCategory || 0 != StrCmpIW(bstrFilter, _cbstrCategory)) { // Yes
_cbstrCategory = bstrFilter; hres = S_OK; }
return hres; }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::Sort
Initiates a sort operation if any of the changes invalidates the existing criteria. */ STDMETHODIMP CDataSrc::Sort(void) { HRESULT hres = S_OK;
if (_fSortDirty) { // Is the datasource started?
if (LS_NOTSTARTED != _loadstate) { // Yes; we can apply the sort now
hres = _ApplySortCriteria(TRUE); if (SUCCEEDED(hres)) _fSortDirty = FALSE; } }
return hres; }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::TransferData
Transfer the contents of given datasource object to this datasource. This is useful for operations that change the dataset in-place, like sorting. */ STDMETHODIMP CDataSrc::TransferData(IARPSimpleProvider * parposp) { HRESULT hres; ISequentialStream * pstream; ASSERT(parposp);
hres = parposp->QueryInterface(IID_ISequentialStream, (LPVOID *)&pstream); if (SUCCEEDED(hres)) { IARPWorker * pmtxworker; ARPDSODATA dsodata; ULONG cb;
// Transfer the state and data from that datasource to this
// datasource.
pstream->Read(&dsodata, sizeof(dsodata), &cb); Write(&dsodata, cb, NULL);
if (_pmtxarray) { hres = _pmtxarray->QueryInterface(IID_IARPWorker, (LPVOID *)&pmtxworker); if (SUCCEEDED(hres)) { // Tell the worker thread that this is the new datasource
// object to receive events
IWorkerEvent * pwrkevt; QueryInterface(IID_IWorkerEvent, (LPVOID *)&pwrkevt); ASSERT(pwrkevt); // this should never fail
pmtxworker->SetListenerWT(pwrkevt); pmtxworker->Release();
pwrkevt->Release(); }
_fAppsEnumed = TRUE; } pstream->Release(); } return hres; }
/*-------------------------------------------------------------------------
Purpose: IARPSimpleProvider::DoCommand
Commit a specific action on the record. Unlike standard ADO commands that affect a recordset, these commands are intended to be specific to managing the apps themselves (like installing or uninstalling).
NOTE: this method is called indirectly via script. */ STDMETHODIMP CDataSrc::DoCommand(HWND hwndParent, APPCMD appcmd, DBROWCOUNT iRow) { HRESULT hres = S_OK; IAppData * pappdata = _GetAppData(iRow); if (pappdata) { if (_IsValidDataRow(iRow)) { hres = pappdata->DoCommand(hwndParent, appcmd);
// Was the app succesfully uninstalled/changed/whatever?
if (S_OK == hres) { // Yes
DBROWCOUNT lDeleted; switch (appcmd) { case APPCMD_UNINSTALL: // Fire the event to the databinding agent
deleteRows(iRow, 1, &lDeleted); break;
case APPCMD_UPGRADE: case APPCMD_REPAIR: case APPCMD_MODIFY: case APPCMD_INSTALL: // Fire the event
_parpevt->RowChanged(iRow); break; } } } pappdata->Release(); } return hres; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::getRowCount
Return the number of rows in the table. */ STDMETHODIMP CDataSrc::getRowCount(DBROWCOUNT *pcRows) { ASSERT(IS_VALID_WRITE_PTR(pcRows, DBROWCOUNT));
*pcRows = _cRows; TraceMsg(TF_DSO, "(Mtx) getRowCount returning %d", _cRows); return S_OK; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::getColumnCount
Return the number of columns in the table. */ STDMETHODIMP CDataSrc::getColumnCount(DB_LORDINAL *pcCols) { ASSERT(IS_VALID_WRITE_PTR(pcCols, DB_LORDINAL));
*pcCols = _cCols;
TraceMsg(TF_DSO, "(Mtx) getColumnCount returning %d", _cCols);
return S_OK; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::getRWStatus
Gets the read/write status of a cell, row, column or the entire array.
This implementation cannot set the read/write status on any cell, so all data cells are presumed to have the default access and all column heading cells are presumed to be read-only. Therefore, we don't keep track of this info in the individual cells.
E_INVALIDARG - returned if indices are out of bounds */ STDMETHODIMP CDataSrc::getRWStatus(DBROWCOUNT iRow, DB_LORDINAL iCol, OSPRW *prwStatus) { HRESULT hres = E_INVALIDARG;
if ((_IsValidRow(iRow) || -1 == iRow) && (_IsValidCol(iCol) || -1 == iCol)) { if (iRow == -1) { *prwStatus = OSPRW_MIXED; } else if (iRow == 0) *prwStatus = OSPRW_READONLY; else *prwStatus = OSPRW_DEFAULT; hres = S_OK; }
if (FAILED(hres)) TraceMsg(TF_WARNING, "(Mtx) getRWStatus(%d, %d) failed %s", iRow, iCol, Dbg_GetHRESULT(hres)); else TraceMsg(TF_DSO, "(Mtx) getRWStatus(%d, %d) returning %s", iRow, iCol, Dbg_GetOSPRW(*prwStatus)); return hres; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::getVariant
Retrieves a variant value for a cell. */ STDMETHODIMP CDataSrc::getVariant(DBROWCOUNT iRow, DB_LORDINAL iCol, OSPFORMAT format, VARIANT * pvar) { HRESULT hres = E_INVALIDARG;
TraceMsg(TF_DSO, "(Mtx) getVariant(%d, %d)", iRow, iCol); ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT)); if (_IsValidCell(iRow, iCol)) { VARIANT var;
// Massage col to be 0-based
iCol--; // Are they asking for the field name?
if (0 == iRow) { // Yes; get the field name
if (_pmtxarray) hres = _pmtxarray->GetFieldName(iCol, &var); else hres = E_FAIL; } else { // No; get the field value
IAppData * pappdata = _GetAppData(iRow); if (pappdata) { hres = pappdata->GetVariant(iCol, &var); pappdata->Release(); } else hres = E_FAIL; } if (SUCCEEDED(hres)) { if (OSPFORMAT_RAW == format) { // Copy the raw variant value
*pvar = var; } else if (OSPFORMAT_FORMATTED == format || OSPFORMAT_HTML == format) { // Consumer wants it in text format
if (VT_BSTR == var.vt || VT_EMPTY == var.vt) { // Already done
*pvar = var; } else if (VT_UI4 == var.vt) { // Coerce
VarBstrFromUI4( var.lVal, 0, 0, &(pvar->bstrVal)); if (pvar->bstrVal != NULL) { pvar->vt = VT_BSTR; } else hres = E_OUTOFMEMORY; } else hres = E_NOTIMPL; } else hres = E_INVALIDARG;
if (FAILED(hres)) { VariantClear(&var); pvar->vt = VT_BSTR; pvar->bstrVal = SysAllocString(L"#Error"); } } }
if (FAILED(hres)) TraceMsg(TF_WARNING, "(Mtx) getVariant failed %s", Dbg_GetHRESULT(hres)); return hres; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::setVariant
Set a cell's variant value from a given variant. The given variant type is coerced into the columns underlying type. */ STDMETHODIMP CDataSrc::setVariant(DBROWCOUNT iRow, DB_LORDINAL iCol, OSPFORMAT format, VARIANT var) { HRESULT hres = E_INVALIDARG;
TraceMsg(TF_DSO, "(Mtx) setVariant(%d, %d)", iRow, iCol);
#ifdef NYI
if (_IsValidCol(iCol)) { // Massage col to be 0-based
iCol--; // Is the data agent trying to change an existing cell?
if (_IsValidDataRow(iRow)) { // Yes
IAppData * pappdata = _GetAppData(iRow); if (pappdata) { hres = pappdata->SetVariant(iCol, &var); pappdata->Release(); } else hres = E_FAIL; } else { // No; it wants to add a new row
} } #else
hres = E_NOTIMPL; #endif
if (FAILED(hres)) TraceMsg(TF_WARNING, "(Mtx) setVariant failed %s", Dbg_GetHRESULT(hres)); return hres; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::getLocale
Returns to the consumer the locale of the data we are providing. App management data is in the locale of the system, so return an empty bstr. */ STDMETHODIMP CDataSrc::getLocale(BSTR *pbstrLocale) { TraceMsg(TF_DSO, "(Mtx) getLocale"); *pbstrLocale = SysAllocString(L""); return *pbstrLocale ? S_OK : E_OUTOFMEMORY; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::deleteRows
Used to delete rows from the array. Bounds are checked to make sure that the rows can all be deleted. Label rows cannot be deleted.
E_INVALIDARG - returned if any rows to be deleted are out of bounds */ STDMETHODIMP CDataSrc::deleteRows(DBROWCOUNT iRow, DBROWCOUNT cRows, DBROWCOUNT *pcRowsDeleted) { HRESULT hres = E_INVALIDARG;
TraceMsg(TF_DSO, "(Mtx) deleteRows(%d, %d)", iRow, cRows); *pcRowsDeleted = 0;
if (_IsValidDataRow(iRow) && cRows >= 0 && _IsValidDataRow(iRow + cRows - 1)) { _parpevt->AboutToDeleteRows(iRow, cRows);
*pcRowsDeleted = cRows; if (cRows > 0) { // Delete the rows from the array
_pmtxarray->DeleteItems(iRow - 1, cRows); _cRows = _CalcRows();
// Notify the event-handler of the deletion
_parpevt->DeletedRows(iRow, cRows); } hres = S_OK; }
if (FAILED(hres)) TraceMsg(TF_WARNING, "(Mtx) deleteRows failed %s", Dbg_GetHRESULT(hres)); return hres; }
//+-----------------------------------------------------------------------
//
// Member: InsertRows()
//
// Synopsis: Allows for the insertion of new rows. This can either be
// used to insert new rows between existing rows, or to
// append new rows to the end of the table. Thus, to
// insert new rows at the end of the table, a user would
// specify the initial row as 1 greater than the current
// row dimension.
// Note that iRow is checked to ensure that it is within the
// proper bounds (1..<current # of rows>+1).
// User cannot delete column heading row.
//
// Arguments: iRow rows will be inserted *before* row 'iRow'
// cRows how many rows to insert
// pcRowsInserted actual number of rows inserted (OUT)
//
// Returns: S_OK upon success, i.e. all rows could be inserted.
// E_INVALIDARG if row is out of allowed bounds.
// It is possible that fewer than the requested rows were
// inserted. In this case, E_OUTOFMEMORY would be returned,
// and the actual number of rows inserted would be set.
//
//------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::insertRows
*/ STDMETHODIMP CDataSrc::insertRows(DBROWCOUNT iRow, DBROWCOUNT cRows, DBROWCOUNT *pcRowsInserted) { HRESULT hres = E_NOTIMPL;
TraceMsg(TF_DSO, "(Mtx) insertRows(%d, %d)", iRow, cRows); if (FAILED(hres)) TraceMsg(TF_WARNING, "(Mtx) insertRows failed %s", Dbg_GetHRESULT(hres)); return hres; }
//+-----------------------------------------------------------------------
//
// Member: Find()
//
// Synopsis: Searches for a row matching the specified criteria
//
// Arguments: iRowStart The starting row for the search
// iCol The column being tested
// vTest The value against which cells in the
// test column are tested
// findFlags Flags indicating whether to search up/down
// and whether comparisons are case sensitive.
// compType The comparison operator for matching (find a
// cell =, >=, <=, >, <, <> the test value)
// piRowFound The row with a matching cell [OUT]
//
// Returns: S_OK upon success, i.e. a row was found (piRowFound set).
// E_FAIL upon failure, i.e. a row was not found.
// E_INVALIDARG if starting row 'iRowStart' or test column 'iCol'
// are out of bounds.
// DISP_E_TYPEMISMATCH if the test value's type does not match
// the test column's type.
//
//------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::find
*/ STDMETHODIMP CDataSrc::find(DBROWCOUNT iRowStart, DB_LORDINAL iCol, VARIANT vTest, OSPFIND findFlags, OSPCOMP compType, DBROWCOUNT *piRowFound) { HRESULT hres = E_NOTIMPL;
TraceMsg(TF_DSO, "(Mtx) find(%d, %d)", iRowStart, iCol); if (FAILED(hres)) TraceMsg(TF_WARNING, "(Mtx) find failed %s", Dbg_GetHRESULT(hres)); return hres; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::addOLEDBSimpleProviderListener
Sets or clears a reference to the OSP listener. */ STDMETHODIMP CDataSrc::addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener *pospl) { HRESULT hres;
TraceMsg(TF_DSO, "(Mtx) addOLEDBSimpleProviderListener <%s>", Dbg_GetLS(_loadstate)); if (_parpevt == NULL) hres = E_FAIL; else { _parpevt->SetOSPListener(pospl); // If the event sink has been added, and we're already loaded,
// then fire transferComplete, because we probably couldn't before.
if (LS_NOTSTARTED < _loadstate) _parpevt->LoadCompleted();
hres = S_OK; }
return hres; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::removeOLEDBSimpleProviderListener
*/ STDMETHODIMP CDataSrc::removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener * pospl) { if (_parpevt && S_OK == _parpevt->IsOSPListener(pospl)) { TraceMsg(TF_DSO, "(Mtx) removeOLEDBSimpleProviderListener"); _parpevt->SetOSPListener(NULL); } return S_OK; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::getEstimatedRows
Returns an estimated number of rows in the matrix. Return -1 if unknown. */ STDMETHODIMP CDataSrc::getEstimatedRows(DBROWCOUNT *pcRows) { if (LS_NOTSTARTED == _loadstate) *pcRows = -1; else *pcRows = _cRows;
TraceMsg(TF_DSO, "(Mtx) getEstimatedRows returning %d <%s>", *pcRows, Dbg_GetLS(_loadstate)); return S_OK; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::isAsync
*/ STDMETHODIMP CDataSrc::isAsync(BOOL *pbAsync) { // This OSP always behaves as if it is async. Specifically, we always fire
// TransferComplete, even if we have to buffer the notification until our
// addOLEDBSimplerProviderListener is actually called.
*pbAsync = TRUE; return S_OK; }
/*----------------------------------------------------------
Purpose: OLEDBSimpleProvider::stopTransfer
The data download has been cancelled. */ STDMETHODIMP CDataSrc::stopTransfer() { TraceMsg(TF_DSO, "(Mtx) stopTransfer <%s>", Dbg_GetLS(_loadstate)); // Force the load state into UNINITIALISED or LOADED ...
//
switch (_loadstate) { case LS_NOTSTARTED: case LS_DONE: // No need to do anything, because we either haven't started
// or are already finished.
break;
case LS_LOADING_SLOWINFO: // Stop the worker thread.
_KillMtxWorkerThread();
// Say we're done
_loadstate = LS_DONE; TraceMsg(TF_DSO, "(Mtx) Setting state to <%s>", Dbg_GetLS(_loadstate)); // Fire an abort event
_parpevt->LoadAborted(); break; }
return S_OK; }
/*-------------------------------------------------------------------------
Purpose: Helper method to kill the worker thread */ HRESULT CDataSrc::_KillMtxWorkerThread(void) { HRESULT hres = S_OK;
if (_pmtxarray) { IARPWorker * pmtxworker; hres = _pmtxarray->QueryInterface(IID_IARPWorker, (LPVOID *)&pmtxworker); if (SUCCEEDED(hres)) { hres = pmtxworker->KillWT(); pmtxworker->Release(); } } return hres; }
/*----------------------------------------------------------
Purpose: Create-instance function for CDataSrc
*/ HRESULT CDataSrc_CreateInstance(REFIID riid, LPVOID * ppvObj) { HRESULT hres = E_OUTOFMEMORY;
*ppvObj = NULL; CDataSrc * pObj = new CDataSrc(); if (pObj) { hres = pObj->QueryInterface(riid, ppvObj); pObj->Release(); }
return hres; }
#endif //DOWNLEVEL_PLATFORM
|