1420 lines
40 KiB
C++
1420 lines
40 KiB
C++
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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
|