2203 lines
42 KiB
C++
2203 lines
42 KiB
C++
// object.cpp : Implementation of CObject
|
|
#include "stdafx.h"
|
|
#include "object.h"
|
|
#include "Property.h"
|
|
#include "Service.h"
|
|
#include "Program.h"
|
|
#include "ScheduleEntry.h"
|
|
|
|
#if 0
|
|
#undef TIMING
|
|
#define TIMING 1
|
|
#include "timing.h"
|
|
#endif
|
|
|
|
class _bstr_t2 : public _bstr_t
|
|
{
|
|
public:
|
|
_bstr_t2(IID iid)
|
|
{
|
|
OLECHAR sz[40];
|
|
|
|
StringFromGUID2(iid, sz, sizeof(sz)/sizeof(OLECHAR));
|
|
|
|
_bstr_t::operator =(sz);
|
|
}
|
|
};
|
|
|
|
HRESULT CObject::get_RelatedObjectID(boolean fInverse, long id, long idRel, long *pidRet)
|
|
{
|
|
HRESULT hr;
|
|
ADODB::_RecordsetPtr prs;
|
|
const TCHAR *szID = !fInverse ? _T("idObj1") : _T("idObj2");
|
|
const TCHAR *szIDRet = !fInverse ? _T("idObj2") : _T("idObj1");
|
|
|
|
if (!fInverse)
|
|
{
|
|
hr = m_pdb->get_RelsByID1Rel(&prs);
|
|
szID = _T("idObj1");
|
|
szIDRet = _T("idObj2");
|
|
}
|
|
else
|
|
{
|
|
hr = m_pdb->get_RelsByID2Rel(&prs);
|
|
szID = _T("idObj2");
|
|
szIDRet = _T("idObj1");
|
|
}
|
|
|
|
*pidRet = 0;
|
|
|
|
if (m_pdb->FSQLServer())
|
|
{
|
|
TCHAR szFind[32];
|
|
|
|
prs->MoveFirst();
|
|
wsprintf(szFind, _T("%s = %d"), szID, id);
|
|
hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
|
|
}
|
|
else
|
|
{
|
|
_variant_t var(id);
|
|
hr = prs->Seek(var, ADODB::adSeekFirstEQ);
|
|
}
|
|
if (FAILED(hr))
|
|
return S_FALSE;
|
|
|
|
while (!prs->EndOfFile)
|
|
{
|
|
long idCur = prs->Fields->Item[szID]->Value;
|
|
if (idCur != id)
|
|
return S_FALSE;
|
|
|
|
long idRelCur = prs->Fields->Item["idRel"]->Value;
|
|
if (idRelCur == idRel)
|
|
{
|
|
*pidRet = prs->Fields->Item[szIDRet]->Value;
|
|
return S_OK;
|
|
}
|
|
prs->MoveNext();
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT AddRelationship(ADODB::_RecordsetPtr prs, long idObj1, long idRel, long iOrder, long idObj2)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = prs->AddNew();
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
prs->Fields->Item["idObj1"]->Value = idObj1;
|
|
prs->Fields->Item["idRel"]->Value = idRel;
|
|
prs->Fields->Item["order"]->Value = iOrder;
|
|
prs->Fields->Item["idObj2"]->Value = idObj2;
|
|
hr = prs->Update();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CObjects::AddRelationshipAt(long idObj1, long idRel, long iItem, long idObj2)
|
|
{
|
|
HRESULT hr;
|
|
const long iOrderInc = 10;
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
hr = m_pdb->get_RelsByID1Rel(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (m_pdb->FSQLServer())
|
|
{
|
|
TCHAR szFind[32];
|
|
|
|
prs->MoveFirst();
|
|
wsprintf(szFind, _T("idObj1 = %d"), idObj1);
|
|
hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
|
|
}
|
|
else
|
|
{
|
|
_variant_t var(idObj1);
|
|
hr = prs->Seek(var, ADODB::adSeekFirstEQ);
|
|
}
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
long iOrder = iOrderInc;
|
|
long i;
|
|
// Scan until we reach the requested item (or end)
|
|
for (i = 0; (i <= iItem) || (iItem == -1); i++)
|
|
{
|
|
if (prs->EndOfFile)
|
|
break;
|
|
|
|
long idCur = prs->Fields->Item["idObj1"]->Value;
|
|
if (idCur != idObj1)
|
|
break;
|
|
|
|
long idRelCur = prs->Fields->Item["idRel"]->Value;
|
|
if (idRelCur != idRel)
|
|
break;
|
|
|
|
prs->MoveNext();
|
|
}
|
|
|
|
// If there were any items, back up, and use the "order" value for the that one.
|
|
if (i > 0)
|
|
{
|
|
prs->MovePrevious();
|
|
iOrder = prs->Fields->Item["order"]->Value;
|
|
|
|
// Now open up space by pushing up the items which follow.
|
|
if (iItem == -1)
|
|
iOrder += iOrderInc;
|
|
else
|
|
{
|
|
long iOrderCur = iOrder + 2*iOrderInc;
|
|
long cItems = 1;
|
|
|
|
prs->MoveNext();
|
|
|
|
// Scan forward looking for a big enough gap.
|
|
while (!prs->EndOfFile)
|
|
{
|
|
long idCur = prs->Fields->Item["idObj1"]->Value;
|
|
if (idCur == idObj1)
|
|
{
|
|
long idRelCur = prs->Fields->Item["idRel"]->Value;
|
|
if (idRelCur == idRel)
|
|
{
|
|
iOrderCur = prs->Fields->Item["order"]->Value;
|
|
if (iOrderCur > iOrder + cItems*2)
|
|
break;
|
|
cItems++;
|
|
prs->MoveNext();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We've reached the end... just space them all iOrderInc apart.
|
|
iOrderCur = iOrder + (cItems + 1)*iOrderInc;
|
|
break;
|
|
}
|
|
|
|
long dSpace = (iOrderCur - iOrder)/(cItems + 1);
|
|
|
|
// Then scan backward spreading the gap evenly between the items.
|
|
for (long c = 0; c < cItems; c++)
|
|
{
|
|
if (prs->EndOfFile)
|
|
{
|
|
// MovePrevious doen't seem to work from EndOfFile...
|
|
// Use MoveLast instead
|
|
prs->MoveLast();
|
|
#ifdef _DEBUG
|
|
long iOrderOld = prs->Fields->Item["order"]->Value;
|
|
#endif
|
|
}
|
|
else
|
|
prs->MovePrevious();
|
|
iOrderCur -= dSpace;
|
|
#ifdef _DEBUG
|
|
long iOrderOld = prs->Fields->Item["order"]->Value;
|
|
TCHAR sz[256];
|
|
wsprintf(sz, _T("AddRelationshipAt : %d --> %d\n"), iOrderOld, iOrderCur);
|
|
OutputDebugString(sz);
|
|
#endif
|
|
|
|
prs->Fields->Item["order"]->Value = iOrderCur;
|
|
prs->Update();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// The slot at iOrder has been opened up, so the new relation can now be added.
|
|
|
|
return AddRelationship(prs, idObj1, idRel, iOrder, idObj2);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CObjectType
|
|
|
|
STDMETHODIMP CObjectType::get_ID(long *pid)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOut<long>(pid, m_id);
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
HRESULT CObjectType::GetDB(CGuideDB **ppdb)
|
|
{
|
|
*ppdb = m_pdb;
|
|
if (*ppdb == NULL)
|
|
return E_FAIL;
|
|
|
|
(*ppdb)->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CObjectType::get_IID(BSTR *pbstrIID)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOut(pbstrIID);
|
|
|
|
OLECHAR sz[40];
|
|
|
|
StringFromGUID2(m_clsid, sz, sizeof(sz)/sizeof(OLECHAR));
|
|
|
|
_bstr_t bstr(sz);
|
|
|
|
*pbstrIID = bstr.copy();
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
HRESULT CObjectType::CreateInstance(long id, IUnknown **ppunk)
|
|
{
|
|
HRESULT hr;
|
|
|
|
CComPtr<CObject> pobj = NewComObjectCachedLRU(CObject);
|
|
|
|
if (pobj == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = pobj->Init(id, this);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
*ppunk = pobj.Detach();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CObjectType::get_NewCollection(IObjects * *ppobjs)
|
|
{
|
|
HRESULT hr;
|
|
|
|
CComPtr<CObjects> pobjs = NewComObjectCachedLRU(CObjects);
|
|
if (pobjs == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
m_pdb->CacheCollection(pobjs);
|
|
|
|
pobjs->put_DB(m_pdb);
|
|
pobjs->put_ObjectType(this);
|
|
|
|
if (!m_fKnowCollectionCLSID)
|
|
{
|
|
OLECHAR szCLSID[128];
|
|
|
|
wcscpy(szCLSID, L"CLSID\\");
|
|
long cch = wcslen(szCLSID);
|
|
|
|
StringFromGUID2(m_clsid, szCLSID + cch, sizeof(szCLSID)/sizeof(OLECHAR) - cch);
|
|
|
|
HKEY hkey;
|
|
long lErr;
|
|
lErr = RegOpenKeyEx(HKEY_CLASSES_ROOT, szCLSID, 0, KEY_READ, &hkey);
|
|
if (lErr == ERROR_SUCCESS)
|
|
{
|
|
OLECHAR szCollectionCLSID[128];
|
|
DWORD cb = sizeof(szCollectionCLSID);
|
|
DWORD regtype;
|
|
lErr = RegQueryValueEx(hkey, _T("CollectionCLSID"), 0,
|
|
®type, (LPBYTE) szCollectionCLSID, &cb);
|
|
RegCloseKey(hkey);
|
|
if ((lErr == ERROR_SUCCESS) && (regtype == REG_SZ))
|
|
{
|
|
hr = CLSIDFromString(szCollectionCLSID, &m_clsidCollection);
|
|
if (SUCCEEDED(hr))
|
|
m_fKnowCollectionCLSID = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_fKnowCollectionCLSID)
|
|
{
|
|
pobjs->Init(m_clsidCollection);
|
|
}
|
|
|
|
*ppobjs = pobjs.Detach();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CObjectType::get_New(IUnknown **ppobj)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IUnknown>(ppobj, NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
hr = m_pdb->get_ObjsRS(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Create a new record.
|
|
hr = prs->AddNew();
|
|
|
|
prs->Fields->Item["idType"]->Value = m_id;
|
|
|
|
hr = prs->Update();
|
|
|
|
long id = prs->Fields->Item["id"]->Value;
|
|
|
|
return m_pdb->CacheObject(id, this, ppobj);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CObjectTypes
|
|
|
|
HRESULT CObjectTypes::Init(CGuideDB *pdb)
|
|
{
|
|
m_pdb = pdb;
|
|
|
|
if (pdb == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// Special case the "Object" object type
|
|
CObjectType *pobjtype;
|
|
pobjtype = Cache(0, _bstr_t2(__uuidof(IUnknown)));
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
m_pdb->get_ObjTypesRS(&prs);
|
|
|
|
prs->MoveFirst();
|
|
while (!prs->EndOfFile)
|
|
{
|
|
// Read in all the records
|
|
|
|
long id = prs->Fields->Item["id"]->Value;
|
|
bstr_t bstrCLSID = prs->Fields->Item["clsid"]->Value;
|
|
|
|
pobjtype = Cache(id, bstrCLSID);
|
|
prs->MoveNext();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CObjectTypes::get_ItemWithCLSID(BSTR bstrCLSID, CObjectType **ppobjtype)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateIn(bstrCLSID);
|
|
ValidateOutPtr<CObjectType>(ppobjtype, NULL);
|
|
|
|
CLSID clsid;
|
|
|
|
HRESULT hr = CLSIDFromString(bstrCLSID, &clsid);
|
|
if (hr == CO_E_CLASSSTRING)
|
|
return hr;
|
|
|
|
t_map::iterator it = m_map.find(clsid);
|
|
if (it == m_map.end())
|
|
return E_INVALIDARG;
|
|
|
|
*ppobjtype = (*it).second;
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
CObjectType *CObjectTypes::Cache(long id, BSTR bstrCLSID)
|
|
{
|
|
CObjectType *pobjtype = NULL;
|
|
CLSID clsid;
|
|
|
|
HRESULT hr = CLSIDFromString(bstrCLSID, &clsid);
|
|
|
|
if (hr == CO_E_CLASSSTRING)
|
|
return NULL;
|
|
|
|
t_map::iterator it = m_map.find(clsid);
|
|
if (it != m_map.end())
|
|
{
|
|
pobjtype = (*it).second;
|
|
}
|
|
else
|
|
{
|
|
pobjtype = new CObjectType;
|
|
|
|
if (pobjtype == NULL)
|
|
return NULL;
|
|
|
|
pobjtype->Init(m_pdb, id, clsid);
|
|
|
|
m_map[clsid] = pobjtype;
|
|
m_pdb->put_ObjectType(id, pobjtype);
|
|
}
|
|
|
|
return pobjtype;
|
|
}
|
|
|
|
STDMETHODIMP CObjectTypes::get_AddNew(BSTR bstrCLSID, CObjectType **ppobjtype)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateIn(bstrCLSID);
|
|
ValidateOutPtr<CObjectType>(ppobjtype, NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
hr = get_ItemWithCLSID(bstrCLSID, ppobjtype);
|
|
if (SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
hr = m_pdb->get_ObjTypesRS(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
// Create a new record.
|
|
hr = prs->AddNew();
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
prs->Fields->Item["clsid"]->Value = bstrCLSID;
|
|
|
|
hr = prs->Update();
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
long id = prs->Fields->Item["id"]->Value;
|
|
|
|
*ppobjtype = Cache(id, bstrCLSID);
|
|
|
|
if (*ppobjtype == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
#if 0
|
|
STDMETHODIMP CObjectTypes::get_AddNew(CLSID clsid, CObjectType **ppobjtype)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<CObjectType>(ppobjtype, NULL);
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CObject
|
|
|
|
STDMETHODIMP CObject::Init(long id, CObjectType *pobjtype)
|
|
{
|
|
HRESULT hr;
|
|
if (m_id != 0)
|
|
return E_INVALIDARG;
|
|
|
|
m_id = id;
|
|
m_pobjtype = pobjtype;
|
|
_ASSERTE(m_pobjtype != NULL);
|
|
m_pdb = m_pobjtype->m_pdb;
|
|
|
|
if (m_pobjtype->m_id != 0)
|
|
{
|
|
hr = m_punk.CoCreateInstance(pobjtype->m_clsid, GetControllingUnknown());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CObject::Save(IUnknown *punk)
|
|
{
|
|
HRESULT hr;
|
|
if (punk == NULL)
|
|
punk = this;
|
|
|
|
CComQIPtr<IPersistPropertyBag> ppersistpropbag(punk);
|
|
if (ppersistpropbag != NULL)
|
|
{
|
|
hr = CreatePropBag(ppersistpropbag);
|
|
|
|
hr = ppersistpropbag->Save(m_ppropbag, TRUE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
CComQIPtr<IPersistStream> ppersiststream(punk);
|
|
if (ppersiststream != NULL)
|
|
{
|
|
CComPtr<IStream> pstream;
|
|
hr = CreateStreamOnHGlobal(NULL, TRUE, &pstream);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Write a tag to indicate IPersistStream was used.
|
|
char ch = Format_IPersistStream;
|
|
ULONG cb = sizeof(ch);
|
|
hr = pstream->Write(&ch, cb, &cb);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Now have the object save it's bits.
|
|
hr = ppersiststream->Save(pstream, TRUE);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Create a SafeArray from the bits.
|
|
HANDLE hdata;
|
|
hr = GetHGlobalFromStream(pstream, &hdata);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
long cbData = GlobalSize(hdata);
|
|
SAFEARRAY *parray = SafeArrayCreateVector(VT_UI1, 0, cbData);
|
|
if (parray == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
BYTE *pbDst;
|
|
hr = SafeArrayAccessData(parray, (void **) &pbDst);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
BYTE *pbSrc = (BYTE *) GlobalLock(hdata);
|
|
|
|
memcpy(pbDst, pbSrc, cbData);
|
|
|
|
GlobalUnlock(hdata);
|
|
SafeArrayUnaccessData(parray);
|
|
|
|
// Put the SafeArray in a variant
|
|
_variant_t varT;
|
|
varT.vt = VT_ARRAY | VT_UI1;
|
|
varT.parray = parray;
|
|
|
|
// Write the bits to the database
|
|
ADODB::_RecordsetPtr prs;
|
|
hr = m_pdb->get_ObjsByID(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
_variant_t var = m_id;
|
|
prs->Seek(var, ADODB::adSeekFirstEQ);
|
|
if (prs->EndOfFile)
|
|
return E_FAIL;
|
|
|
|
hr = prs->Fields->Item["oValue"]->AppendChunk(varT);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CObject::Load()
|
|
{
|
|
HRESULT hr;
|
|
CComQIPtr<IPersistPropertyBag> ppersistpropbag(this);
|
|
if (ppersistpropbag != NULL)
|
|
{
|
|
hr = CreatePropBag(ppersistpropbag);
|
|
|
|
hr = ppersistpropbag->Load(m_ppropbag, NULL);
|
|
}
|
|
else
|
|
{
|
|
CComQIPtr<IPersistStream> ppersiststream(this);
|
|
if (ppersiststream != NULL)
|
|
{
|
|
ADODB::_RecordsetPtr prs;
|
|
hr = m_pdb->get_ObjsByID(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
_variant_t var = m_id;
|
|
prs->Seek(var, ADODB::adSeekFirstEQ);
|
|
if (prs->EndOfFile)
|
|
return E_FAIL;
|
|
|
|
long cb;
|
|
ADODB::FieldPtr pfield;
|
|
pfield.Attach(prs->Fields->Item["oValue"]);
|
|
hr = pfield->get_ActualSize(&cb);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
_variant_t varData;
|
|
hr = pfield->GetChunk(cb, &varData);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if ((varData.vt & VT_ARRAY) == 0)
|
|
return E_FAIL;
|
|
|
|
BYTE *pbSrc;
|
|
hr = SafeArrayAccessData(varData.parray, (void **) &pbSrc);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
HANDLE hdata;
|
|
BOOL fFree = FALSE;
|
|
hdata = GlobalHandle(pbSrc);
|
|
if (hdata == NULL)
|
|
{
|
|
hdata = GlobalAlloc(GHND, cb);
|
|
if (hdata == NULL)
|
|
{
|
|
SafeArrayUnaccessData(varData.parray);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
BYTE *pbDst = (BYTE *) GlobalLock(hdata);
|
|
|
|
memcpy(pbDst, pbSrc, cb);
|
|
fFree = TRUE;
|
|
}
|
|
else
|
|
{
|
|
BYTE *pbTest = (BYTE *) GlobalLock(hdata);
|
|
int i = 0;
|
|
if (pbTest != pbSrc)
|
|
i++;
|
|
GlobalUnlock(hdata);
|
|
}
|
|
|
|
{
|
|
CComPtr<IStream> pstream;
|
|
hr = CreateStreamOnHGlobal(hdata, fFree, &pstream);
|
|
if (FAILED(hr))
|
|
{
|
|
if (fFree)
|
|
GlobalFree(hdata);
|
|
return hr;
|
|
}
|
|
|
|
char ch;
|
|
ULONG cbT = sizeof(ch);
|
|
hr = pstream->Read(&ch, cbT, &cbT);
|
|
switch (ch)
|
|
{
|
|
case Format_IPersistStream:
|
|
hr = ppersiststream->Load(pstream);
|
|
break;
|
|
|
|
default:
|
|
hr = STG_E_DOCFILECORRUPT;
|
|
}
|
|
}
|
|
SafeArrayUnaccessData(varData.parray);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CObject::CreatePropBag(IPersist *ppersist)
|
|
{
|
|
HRESULT hr;
|
|
if (m_ppropbag == NULL)
|
|
{
|
|
CComPtr<IMetaProperties> pprops;
|
|
hr = get_MetaProperties(&pprops);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CLSID clsid;
|
|
hr = ppersist->GetClassID(&clsid);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
OLECHAR sz[40];
|
|
|
|
StringFromGUID2(clsid, sz, sizeof(sz)/sizeof(OLECHAR));
|
|
|
|
_bstr_t bstrCLSID(sz);
|
|
CComPtr<IMetaPropertySet> ppropset;
|
|
hr = m_pdb->get_MetaPropertySet(bstrCLSID, &ppropset);
|
|
CComPtr<IMetaPropertyTypes> pproptypes;
|
|
hr = ppropset->get_MetaPropertyTypes(&pproptypes);
|
|
m_ppropbag = NewComObject(CObjectPropertyBag);
|
|
m_ppropbag->Init(pprops, pproptypes);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CObject::get_Type(CObjectType **ppobjtype)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<CObjectType>(ppobjtype, NULL);
|
|
|
|
_ASSERTE(m_pobjtype != NULL);
|
|
*ppobjtype = m_pobjtype;
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObject::get_ID(long *pid)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOut<long>(pid, m_id);
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObject::get_MetaProperties(IMetaProperties **ppprops)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IMetaProperties>(ppprops, NULL);
|
|
HRESULT hr;
|
|
|
|
if (m_pprops == NULL)
|
|
{
|
|
hr = m_pdb->get_MetaPropertiesOf((IUnknown *) this, &m_pprops);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
if (m_pprops == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return m_pprops.QueryInterface(ppprops);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObject::get_ItemInverseRelatedBy(long idRel, IUnknown **ppobj1)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IUnknown>(ppobj1, NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
long idObj1;
|
|
hr = get_RelatedObjectID(TRUE, m_id, idRel, &idObj1);
|
|
if (hr == S_FALSE || FAILED(hr))
|
|
return hr;
|
|
|
|
return m_pdb->CacheObject(idObj1, (long) 0, ppobj1);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
HRESULT CObject::get_ItemsRelatedBy(CObjectType *pobjtype,
|
|
long idRel, boolean fInverse, IObjects **ppobjs)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = pobjtype->get_NewCollection(ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComQIPtr<CObjects> pobjs(*ppobjs);
|
|
|
|
hr = pobjs->InitRelation(this, idRel, fInverse);
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CObject::get_ItemsInverseRelatedBy(CObjectType *pobjtype, long idRel, IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<CObjectType>(pobjtype);
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
return get_ItemsRelatedBy(pobjtype, idRel, TRUE, ppobjs);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObject::get_ItemRelatedBy(long idRel, IUnknown **ppobj2)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IUnknown>(ppobj2, NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
long idObj2;
|
|
hr = get_RelatedObjectID(FALSE, m_id, idRel, &idObj2);
|
|
if (hr == S_FALSE || FAILED(hr))
|
|
return hr;
|
|
|
|
return m_pdb->CacheObject(idObj2, (long) 0, ppobj2);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObject::put_ItemRelatedBy(long idRel, IUnknown *pobj2)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<IUnknown>(pobj2);
|
|
|
|
HRESULT hr;
|
|
long idObj2;
|
|
|
|
m_pdb->get_IdOf(pobj2, &idObj2);
|
|
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
|
|
hr = m_pdb->get_RelsByID1Rel(&prs);
|
|
|
|
if (m_pdb->FSQLServer())
|
|
{
|
|
TCHAR szFind[32];
|
|
|
|
prs->MoveFirst();
|
|
wsprintf(szFind, _T("idObj1 = %d"), m_id);
|
|
hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
|
|
}
|
|
else
|
|
{
|
|
_variant_t var(m_id);
|
|
hr = prs->Seek(var, ADODB::adSeekFirstEQ);
|
|
}
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
while (!prs->EndOfFile)
|
|
{
|
|
long idCur = prs->Fields->Item["idObj1"]->Value;
|
|
if (idCur != m_id)
|
|
break;
|
|
|
|
long idRelCur = prs->Fields->Item["idRel"]->Value;
|
|
if (idRelCur == idRel)
|
|
{
|
|
prs->Fields->Item["idObj2"]->Value = idObj2;
|
|
return S_OK;
|
|
}
|
|
prs->MoveNext();
|
|
}
|
|
|
|
return AddRelationship(prs, m_id, idRel, 0, idObj2);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObject::get_ItemsRelatedBy(CObjectType *pobjtype, long idRel, IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<CObjectType>(pobjtype);
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
return get_ItemsRelatedBy(pobjtype, idRel, FALSE, ppobjs);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
HRESULT CObjects::Clone(IObjects **ppobjs)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = m_pobjtype->get_NewCollection(ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComQIPtr<CObjects> pobjs(*ppobjs);
|
|
|
|
pobjs->Copy(this);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CObjects
|
|
|
|
// Sample Query:
|
|
//
|
|
// SELECT DISTINCT Objects.id
|
|
// FROM ((((
|
|
// Objects
|
|
// INNER JOIN ObjectRelationships
|
|
// ON Objects.id = ObjectRelationships.idObj2)
|
|
// INNER JOIN Properties AS Properties_0
|
|
// ON Objects.id = Properties_0.idObj)
|
|
// INNER JOIN Properties AS Properties_1
|
|
// ON Objects.id = Properties_1.idObj)
|
|
// INNER JOIN Properties AS Properties_2
|
|
// ON Objects.id = Properties_2.idObj)
|
|
// WHERE
|
|
// (((
|
|
// Objects.idType = 1)
|
|
// AND (ObjectRelationships.idObj1 = 10))
|
|
// AND
|
|
// (
|
|
// ((Properties_0.idPropType=15) AND (Properties_0.sValue="Arthur"))
|
|
// OR
|
|
// ((Properties_2.idPropType=15) AND (Properties_2.sValue="Jeopardy!"))
|
|
// )
|
|
// );
|
|
|
|
HRESULT CObjects::GetQuery(QueryType qtype, _bstr_t *pbstr)
|
|
{
|
|
TCHAR sz[8*1024];
|
|
HRESULT hr;
|
|
_bstr_t bstrCmd;
|
|
_bstr_t bstrFrom;
|
|
_bstr_t bstrOrder;
|
|
_bstr_t bstrWhere;
|
|
long cWhere = 0;
|
|
|
|
switch (qtype)
|
|
{
|
|
case Select:
|
|
bstrCmd = _T("SELECT DISTINCT Objects.id, Objects.idType ");
|
|
break;
|
|
|
|
case Delete:
|
|
bstrCmd = _T("DELETE Objects.* ");
|
|
break;
|
|
}
|
|
|
|
bstrFrom = "Objects";
|
|
|
|
_ASSERTE(m_pobjtype != NULL);
|
|
|
|
long idType = 0;
|
|
|
|
hr = m_pobjtype->get_ID(&idType);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (idType != 0)
|
|
{
|
|
wsprintf(sz, _T("Objects.idType = %d"), idType);
|
|
bstrWhere = sz;
|
|
cWhere++;
|
|
}
|
|
|
|
if (m_fOnlyUnreferenced)
|
|
{
|
|
if (cWhere > 0)
|
|
bstrWhere = bstrWhere + " AND ";
|
|
|
|
bstrWhere = bstrWhere
|
|
+ "(Objects.id NOT IN"
|
|
+ " (SELECT DISTINCT ObjectRelationships.idObj2 FROM ObjectRelationships))"
|
|
+ " AND (Objects.id NOT IN"
|
|
+ " (SELECT DISTINCT Properties.lValue FROM Properties WHERE Properties.ValueType = 13))";
|
|
cWhere++;
|
|
}
|
|
|
|
if ((qtype == Select) && (m_idPropTypeKey != 0))
|
|
{
|
|
switch (m_vtKey)
|
|
{
|
|
default:
|
|
return E_UNEXPECTED;
|
|
|
|
case VT_I2:
|
|
case VT_I4:
|
|
bstrCmd = bstrCmd + ", Props_Key.lValue";
|
|
bstrOrder = _T(" ORDER BY Props_Key.lValue");
|
|
break;
|
|
|
|
case VT_R4:
|
|
case VT_R8:
|
|
case VT_DATE:
|
|
bstrCmd = bstrCmd + ", Props_Key.fValue";
|
|
bstrOrder = _T(" ORDER BY Props_Key.fValue");
|
|
break;
|
|
|
|
case VT_BSTR_BLOB:
|
|
case VT_BSTR:
|
|
bstrCmd = bstrCmd + ", left(Props_Key.sValue,255) AS sValue";
|
|
bstrOrder = _T(" ORDER BY left(Props_Key.sValue,255)");
|
|
break;
|
|
}
|
|
|
|
wsprintf(sz, _T("(%s INNER JOIN Properties AS Props_Key")
|
|
_T(" ON Objects.id = Props_Key.idObj)"),
|
|
(const TCHAR *) bstrFrom);
|
|
bstrFrom = sz;
|
|
if (cWhere++ > 0)
|
|
bstrWhere = bstrWhere + " AND ";
|
|
wsprintf(sz, _T("(Props_Key.idPropType = %d) AND (Props_Key.ValueType = %d)"),
|
|
m_idPropTypeKey, m_vtKey);
|
|
bstrWhere = bstrWhere + sz;
|
|
if (m_idProviderKey != 0)
|
|
{
|
|
wsprintf(sz, _T("AND (Props_Key.idProvider = %d)"), m_idProviderKey);
|
|
bstrWhere = bstrWhere + sz;
|
|
}
|
|
if (m_idLangKey != 0)
|
|
{
|
|
wsprintf(sz, _T("AND (Props_Key.idLanguage = %d)"), m_idLangKey);
|
|
bstrWhere = bstrWhere + sz;
|
|
}
|
|
|
|
}
|
|
|
|
if (m_pobjRelated != NULL)
|
|
{
|
|
const TCHAR *szRelatedField;
|
|
const TCHAR *szResultField;
|
|
|
|
if (!m_fInverse)
|
|
{
|
|
szRelatedField = _T("idObj1");
|
|
szResultField = _T("idObj2");
|
|
if (qtype == Select)
|
|
{
|
|
bstrCmd = bstrCmd + _T(", ObjectRelationships.[order]");
|
|
bstrOrder = _T(" ORDER BY ObjectRelationships.[order]");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
szRelatedField = _T("idObj2");
|
|
szResultField = _T("idObj1");
|
|
}
|
|
|
|
wsprintf(sz, _T("(%s INNER JOIN ObjectRelationships")
|
|
_T(" ON Objects.id = ObjectRelationships.%s)"),
|
|
(const TCHAR *) bstrFrom, szResultField);
|
|
|
|
bstrFrom = sz;
|
|
|
|
long id;
|
|
|
|
m_pdb->get_IdOf(m_pobjRelated, &id);
|
|
|
|
if (cWhere++ == 0)
|
|
{
|
|
wsprintf(sz, _T("(ObjectRelationships.idRel = %d)")
|
|
_T(" AND (ObjectRelationships.%s = %d)"),
|
|
m_idRel, szRelatedField, id);
|
|
}
|
|
else
|
|
{
|
|
wsprintf(sz, _T("(%s) AND ")
|
|
_T("(ObjectRelationships.idRel = %d)")
|
|
_T(" AND (ObjectRelationships.%s = %d)"),
|
|
(const TCHAR *) bstrWhere,
|
|
m_idRel, szRelatedField, id);
|
|
}
|
|
bstrWhere = sz;
|
|
}
|
|
|
|
if (m_ppropcond != NULL)
|
|
{
|
|
long cProps = 0;
|
|
CComQIPtr<CMetaPropertyCondition> ppropcond(m_ppropcond);
|
|
_bstr_t bstrWhereClause;
|
|
|
|
hr = ppropcond->get_QueryClause(cProps, &bstrWhereClause);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (cProps > 0)
|
|
{
|
|
if (cWhere > 0)
|
|
{
|
|
wsprintf(sz, _T("(%s) AND (%s)"),
|
|
(const TCHAR *) bstrWhere,
|
|
(const TCHAR *) bstrWhereClause);
|
|
|
|
bstrWhere = sz;
|
|
}
|
|
else
|
|
{
|
|
bstrWhere += bstrWhereClause;
|
|
}
|
|
cWhere++;
|
|
|
|
for (int i = 0; i < cProps; i++)
|
|
{
|
|
wsprintf(sz, _T("(%s INNER JOIN Properties AS Props_%d")
|
|
_T(" ON Objects.id = Props_%d.idObj)"),
|
|
(const TCHAR *) bstrFrom, i, i);
|
|
bstrFrom = sz;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!!bstrWhere)
|
|
{
|
|
bstrWhere = _bstr_t(" WHERE ") + bstrWhere;
|
|
}
|
|
|
|
*pbstr = bstrCmd + _T(" FROM ") + bstrFrom
|
|
+ bstrWhere + bstrOrder + _T(";");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CObjects::GetRS(ADODB::_Recordset **pprs)
|
|
{
|
|
HRESULT hr;
|
|
#if 1
|
|
BOOL fQuery = TRUE;
|
|
#else
|
|
BOOL fQuery = ((m_ppropcond != NULL) || (m_pobjRelated != NULL));
|
|
#endif
|
|
|
|
if (m_prs == NULL)
|
|
{
|
|
if (fQuery)
|
|
{
|
|
_bstr_t bstrQuery;
|
|
|
|
hr = GetQuery(Select, &bstrQuery);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = m_pdb->NewQuery(bstrQuery, &m_prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pdb->get_ObjsByType(&m_prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (m_prs == NULL)
|
|
return E_FAIL;
|
|
|
|
(*pprs = m_prs)->AddRef();
|
|
|
|
return fQuery ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_Count(long *plCount)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOut<long>(plCount, 0);
|
|
DeclarePerfTimer("get_Count");
|
|
|
|
HRESULT hr;
|
|
|
|
if (m_cItems == -1)
|
|
{
|
|
long idType;
|
|
long cItems = 0;
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
|
|
hr = GetRS(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
bool fFast = (hr == S_OK);
|
|
#if 0
|
|
if (fFast)
|
|
{
|
|
PerfTimerReset();
|
|
hr = prs->MoveLast();
|
|
hr = prs->get_RecordCount(&cItems);
|
|
if (SUCCEEDED(hr) && (cItems >= 0))
|
|
{
|
|
PerfTimerDump("Succeeded get_RecordCount");
|
|
goto ReturnIt;
|
|
}
|
|
cItems = 0;
|
|
PerfTimerDump("Failed get_RecordCount");
|
|
}
|
|
#endif
|
|
|
|
PerfTimerReset();
|
|
hr = m_pobjtype->get_ID(&idType);
|
|
if (FAILED(hr))
|
|
return E_FAIL;
|
|
|
|
if (!fFast && idType != 0)
|
|
{
|
|
if (m_pdb->FSQLServer())
|
|
{
|
|
TCHAR szFind[32];
|
|
|
|
prs->MoveFirst();
|
|
wsprintf(szFind, _T("idType = %d"), idType);
|
|
hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
|
|
}
|
|
else
|
|
{
|
|
_variant_t var(idType);
|
|
prs->Seek(var, ADODB::adSeekAfterEQ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = prs->MoveFirst();
|
|
if (hr == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, ADODB::adErrNoCurrentRecord))
|
|
goto ReturnIt;
|
|
}
|
|
|
|
// UNDONE: This is not the most efficient, but it's what works for now.
|
|
while (!prs->EndOfFile)
|
|
{
|
|
if (idType != 0)
|
|
{
|
|
long idType2 = prs->Fields->Item["idType"]->Value;
|
|
if (idType != idType2)
|
|
break;
|
|
}
|
|
cItems++;
|
|
prs->MoveNext();
|
|
}
|
|
PerfTimerDump("Count done");
|
|
ReturnIt:
|
|
m_cItems = cItems;
|
|
}
|
|
|
|
*plCount = m_cItems;
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_Item(VARIANT varIndex, IUnknown **ppobj)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IUnknown>(ppobj, NULL);
|
|
|
|
HRESULT hr;
|
|
_variant_t var(varIndex);
|
|
|
|
try
|
|
{
|
|
var.ChangeType(VT_I4);
|
|
}
|
|
catch (_com_error)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
long i = var.lVal;
|
|
|
|
if (i < 0)
|
|
return E_INVALIDARG;
|
|
|
|
long idType;
|
|
|
|
hr = m_pobjtype->get_ID(&idType);
|
|
if (FAILED(hr))
|
|
return E_FAIL;
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
|
|
hr = GetRS(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
prs->MoveFirst();
|
|
}
|
|
else
|
|
{
|
|
if (m_pdb->FSQLServer())
|
|
{
|
|
TCHAR szFind[32];
|
|
|
|
wsprintf(szFind, _T("idType = %d"), idType);
|
|
hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
|
|
}
|
|
else
|
|
{
|
|
var = idType;
|
|
prs->Seek(var, ADODB::adSeekAfterEQ);
|
|
}
|
|
}
|
|
|
|
if (prs->EndOfFile)
|
|
return E_INVALIDARG;
|
|
|
|
if (i > 0)
|
|
prs->Move(i);
|
|
|
|
if (prs->EndOfFile)
|
|
return E_INVALIDARG;
|
|
|
|
long idType2 = prs->Fields->Item["idType"]->Value;
|
|
if ((idType != 0) && (idType != idType2))
|
|
return E_INVALIDARG;
|
|
|
|
long idObj = prs->Fields->Item["id"]->Value;
|
|
|
|
if (idType == 0)
|
|
return m_pdb->CacheObject(idObj, idType2, ppobj);
|
|
|
|
return m_pdb->CacheObject(idObj, m_pobjtype, ppobj);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemWithID(long id, IUnknown **ppobj)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IUnknown>(ppobj, NULL);
|
|
HRESULT hr;
|
|
|
|
hr = m_pdb->get_Object(id, ppobj);
|
|
if (SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
long idType;
|
|
|
|
hr = m_pobjtype->get_ID(&idType);
|
|
if (FAILED(hr))
|
|
return E_FAIL;
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
|
|
hr = m_pdb->get_ObjsByID(&prs);
|
|
|
|
if (m_pdb->FSQLServer())
|
|
{
|
|
TCHAR szFind[32];
|
|
|
|
prs->MoveFirst();
|
|
wsprintf(szFind, _T("idObj = %d"), id);
|
|
hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
|
|
}
|
|
else
|
|
{
|
|
_variant_t var = id;
|
|
prs->Seek(var, ADODB::adSeekFirstEQ);
|
|
}
|
|
|
|
if (prs->EndOfFile)
|
|
return E_INVALIDARG;
|
|
|
|
if (idType != 0)
|
|
{
|
|
long idType2 = prs->Fields->Item["idType"]->Value;
|
|
if (idType != idType2)
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return m_pdb->CacheObject(id, m_pobjtype, ppobj);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemWithKey(VARIANT varKey, IUnknown **ppobj)
|
|
{
|
|
ENTER_API
|
|
{
|
|
HRESULT hr;
|
|
_bstr_t bstrKey;
|
|
|
|
try
|
|
{
|
|
bstrKey = varKey;
|
|
}
|
|
catch (_com_error)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ADODB::_RecordsetPtr prs;
|
|
|
|
hr = GetRS(&prs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
_bstr_t bstrFind;
|
|
|
|
switch (varKey.vt)
|
|
{
|
|
default:
|
|
return E_INVALIDARG;
|
|
|
|
case VT_I2:
|
|
case VT_I4:
|
|
bstrFind = _T("lValue = ") + bstrKey;
|
|
break;
|
|
|
|
case VT_R4:
|
|
case VT_R8:
|
|
case VT_DATE:
|
|
bstrFind = _T("fValue = ") + bstrKey;
|
|
break;
|
|
|
|
case VT_BSTR_BLOB:
|
|
case VT_BSTR:
|
|
// UNDONE: Handle embedded quotes in bstrKey
|
|
bstrFind = _T("sValue = '") + bstrKey + _T("'");
|
|
break;
|
|
}
|
|
|
|
DeclarePerfTimer("get_ItemWithKey");
|
|
PerfTimerReset();
|
|
hr = prs->MoveFirst();
|
|
hr = prs->Find(bstrFind, 0, ADODB::adSearchForward);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
PerfTimerDump("MoveFirst + Find");
|
|
|
|
if (prs->EndOfFile)
|
|
return E_INVALIDARG;
|
|
|
|
long idType = prs->Fields->Item["idType"]->Value;
|
|
|
|
long idObj = prs->Fields->Item["id"]->Value;
|
|
|
|
return m_pdb->CacheObject(idObj, idType, ppobj);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemsWithType(BSTR bstrCLSID, IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
CObjectType *pobjtype;
|
|
|
|
m_pdb->get_ObjectType(bstrCLSID, &pobjtype);
|
|
|
|
return get_ItemsWithType(pobjtype, ppobjs);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
HRESULT CObjects::get_ItemsWithType(CObjectType *pobjtype, IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<CObjectType>(pobjtype);
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
// Objects can't have two types... if this collection is for objects of
|
|
// a specific type, then it contains no objects of the requested type.
|
|
_ASSERTE(m_pobjtype != NULL);
|
|
|
|
long idType;
|
|
m_pobjtype->get_ID(&idType);
|
|
if (idType != 0)
|
|
return E_INVALIDARG;
|
|
|
|
// If there's no related object clause nor a property condition then
|
|
// we're after the base collection of objects of the specified type.
|
|
// That's cached in by m_pdb.
|
|
if ((m_pobjRelated == NULL) && (m_ppropcond == NULL))
|
|
return m_pdb->get_ObjectsWithType(pobjtype, ppobjs);
|
|
|
|
HRESULT hr = pobjtype->get_NewCollection(ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComQIPtr<CObjects> pobjs(*ppobjs);
|
|
|
|
pobjs->Copy(this);
|
|
|
|
return S_OK;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemsWithMetaProperty(IMetaProperty *pprop, IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<IMetaProperty>(pprop);
|
|
ValidateOutPtr<IObjects>(ppobjs);
|
|
|
|
HRESULT hr;
|
|
CComPtr<IMetaPropertyCondition> pcond;
|
|
|
|
hr = pprop->get_Cond(_bstr_t("="), &pcond);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = get_ItemsWithMetaPropertyCond(pcond, ppobjs);
|
|
|
|
return hr;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::UnreferencedItems(IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
HRESULT hr;
|
|
|
|
hr = Clone(ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComQIPtr<CObjects> pobjs(*ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = pobjs->put_OnlyUnreferenced(TRUE);
|
|
|
|
return hr;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemsWithMetaPropertyCond(IMetaPropertyCondition *ppropcond, IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<IMetaPropertyCondition>(ppropcond);
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
HRESULT hr;
|
|
CComPtr<IMetaPropertyCondition> ppropcond2;
|
|
|
|
if (m_ppropcond != NULL)
|
|
{
|
|
hr = m_ppropcond->get_And(ppropcond, &ppropcond2);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
ppropcond = ppropcond2;
|
|
}
|
|
|
|
hr = Clone(ppobjs);
|
|
|
|
CComQIPtr<CObjects> pobjs(*ppobjs);
|
|
|
|
pobjs->put_MetaPropertyCond(ppropcond);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemsByKey(IMetaPropertyType *pproptype,
|
|
IGuideDataProvider *pprovider, long idLang, long vt, IObjects * *ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<IMetaPropertyType>(pproptype);
|
|
ValidateInPtr_NULL_OK<IGuideDataProvider>(pprovider);
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
switch (vt)
|
|
{
|
|
default:
|
|
return E_INVALIDARG;
|
|
|
|
case VT_I2:
|
|
case VT_I4:
|
|
case VT_R4:
|
|
case VT_R8:
|
|
case VT_DATE:
|
|
case VT_BSTR_BLOB:
|
|
case VT_BSTR:
|
|
break;
|
|
}
|
|
|
|
HRESULT hr = Clone(ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComQIPtr<CObjects> pobjs(*ppobjs);
|
|
|
|
pobjs->put_Key(pproptype, pprovider, idLang, vt);
|
|
|
|
return hr;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::Reset()
|
|
{
|
|
m_cItems = -1; // UNDONE: Is "m_cItems++;" sufficient?
|
|
if (m_prs != NULL)
|
|
m_prs.Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_AddNew(IUnknown **ppobj)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IUnknown>(ppobj, NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
// Force the collection to be requeried so the new item shows up.
|
|
Reset();
|
|
|
|
if ((m_pobjRelated == NULL) && (m_ppropcond == NULL))
|
|
{
|
|
// This collection is the base collection for this type.
|
|
|
|
hr = m_pobjtype->get_New(ppobj);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
//UNDONE: Too many AddRef()s?
|
|
if (*ppobj != NULL)
|
|
(*ppobj)->AddRef();
|
|
|
|
// Only fire the ItemAdded() event when adding to the base collection.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
long id;
|
|
long idType;
|
|
|
|
m_pdb->get_IdOf(*ppobj, &id);
|
|
m_pobjtype->get_ID(&idType);
|
|
m_pdb->Broadcast_ItemAdded(id, idType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = get_AddNewAt(-1, ppobj);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_AddNewAt(long index, IUnknown **ppobj)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IUnknown>(ppobj, NULL);
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
// Can't add items if there is no object type
|
|
_ASSERTE(m_pobjtype != NULL);
|
|
|
|
// Force the collection to be requeried so the new item shows up.
|
|
Reset();
|
|
|
|
if ((m_pobjRelated == NULL) && (m_ppropcond == NULL) && (index != -1))
|
|
{
|
|
hr = get_AddNew(ppobj);
|
|
}
|
|
else if (m_pobjRelated != NULL)
|
|
{
|
|
// This is a subset of the base collection of items of this type.
|
|
// It contains only the items of the specified type which are
|
|
// related to m_pobjRelated by the relationship m_idRel.
|
|
// We must first create a new item in the base collection
|
|
// and then add it to the subset.
|
|
|
|
CComPtr<IObjects> pobjs;
|
|
hr = m_pdb->get_ObjectsWithType(m_pobjtype, &pobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = pobjs->get_AddNew(ppobj);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = AddAt(*ppobj, index);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::AddAt(IUnknown *pobj, long index)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<IUnknown>(pobj);
|
|
|
|
HRESULT hr;
|
|
|
|
// Can't add an item at a particular index if this is not an ordered collection.
|
|
// Order is only expressed for related objects and not for inverse related objects.
|
|
if (m_pobjRelated == NULL || m_fInverse)
|
|
return E_INVALIDARG;
|
|
|
|
m_cItems = -1; // UNDONE: Is "m_cItems++;" sufficient?
|
|
if (m_prs != NULL)
|
|
m_prs.Release();
|
|
|
|
long idObjRelated;
|
|
|
|
m_pdb->get_IdOf(m_pobjRelated, &idObjRelated);
|
|
|
|
long idObj;
|
|
m_pdb->get_IdOf(pobj, &idObj);
|
|
|
|
hr = AddRelationshipAt(idObjRelated, m_idRel, index, idObj);
|
|
|
|
return hr;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::Remove(VARIANT varIndex)
|
|
{
|
|
ENTER_API
|
|
{
|
|
HRESULT hr;
|
|
CComPtr<IUnknown> pobj;
|
|
|
|
switch (varIndex.vt)
|
|
{
|
|
case VT_UNKNOWN:
|
|
case VT_DISPATCH:
|
|
{
|
|
hr = varIndex.punkVal->QueryInterface(__uuidof(IUnknown), (void **) &pobj);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
hr = get_Item(varIndex, &pobj);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return (m_pobjRelated != NULL) ? RemoveFromRelationship(pobj) : Remove(pobj);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
HRESULT CObjects::Remove(IUnknown *pobjRemove)
|
|
{
|
|
HRESULT hr;
|
|
CComQIPtr<CObject> pobj(pobjRemove);
|
|
long idObj;
|
|
CObjectType *pobjtype;
|
|
|
|
pobj->get_Type(&pobjtype);
|
|
|
|
long idtype;
|
|
m_pobjtype->get_ID(&idtype);
|
|
|
|
if ((idtype != NULL) && (pobjtype != m_pobjtype))
|
|
return E_INVALIDARG;
|
|
|
|
pobj->get_ID(&idObj);
|
|
|
|
m_pdb->UncacheObject(idObj);
|
|
|
|
// Delete all the properties
|
|
TCHAR sz[1024];
|
|
|
|
wsprintf(sz, _T("DELETE * FROM Properties WHERE idObj=%d;"), idObj);
|
|
|
|
hr = m_pdb->Execute(_bstr_t(sz));
|
|
|
|
// Remove any properties pointing to the deleted object
|
|
wsprintf(sz,
|
|
_T("DELETE * FROM Properties WHERE (ValueType = 13) AND (lValue = %d);"), idObj);
|
|
|
|
hr = m_pdb->Execute(_bstr_t(sz));
|
|
|
|
// Remove the object from all relationships
|
|
|
|
wsprintf(sz, _T("DELETE * FROM ObjectRelationships")
|
|
_T(" WHERE (idObj1=%d) OR (idObj2=%d);"), idObj, idObj);
|
|
|
|
hr = m_pdb->Execute(_bstr_t(sz));
|
|
|
|
// Remove the object.
|
|
|
|
wsprintf(sz, _T("DELETE * FROM Objects WHERE id=%d;"), idObj);
|
|
|
|
hr = m_pdb->Execute(_bstr_t(sz));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
long idType = 0;
|
|
pobjtype->get_ID(&idType);
|
|
m_pdb->Broadcast_ItemRemoved(idObj, idType);
|
|
}
|
|
|
|
Reset();
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CObjects::RemoveAll()
|
|
{
|
|
ENTER_API
|
|
{
|
|
HRESULT hr;
|
|
_bstr_t bstrQuery;
|
|
boolean fTransacted = TRUE;
|
|
|
|
hr = GetQuery(Delete, &bstrQuery);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = m_pdb->BeginTrans();
|
|
if (FAILED(hr))
|
|
fTransacted = FALSE;
|
|
|
|
hr = m_pdb->Execute(bstrQuery);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
// Delete all the dangling properties
|
|
hr = m_pdb->Execute( _bstr_t("DELETE * FROM Properties"
|
|
" WHERE idObj NOT IN"
|
|
" (SELECT Objects.id FROM Objects);") );
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
// Remove the object from all relationships
|
|
hr = m_pdb->Execute( _bstr_t("DELETE * FROM ObjectRelationships"
|
|
" WHERE idObj1 NOT IN"
|
|
" (SELECT Objects.id FROM Objects);") );
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
if (!m_fOnlyUnreferenced)
|
|
{
|
|
// If this collection only contains unreferenced objects then
|
|
// there is no need to execute the following.
|
|
// Otherwise...
|
|
|
|
// Remove dangling object references.
|
|
hr = m_pdb->Execute( _bstr_t("DELETE * FROM ObjectRelationships"
|
|
" WHERE idObj2 NOT IN"
|
|
" (SELECT Objects.id FROM Objects);") );
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
// Remove any properties pointing to deleted objects
|
|
hr = m_pdb->Execute( _bstr_t("DELETE * FROM Properties"
|
|
" WHERE (ValueType = 13) AND (lValue NOT IN"
|
|
" (SELECT Objects.id FROM Objects));") );
|
|
}
|
|
|
|
Cleanup:
|
|
if (fTransacted)
|
|
{
|
|
if (FAILED(hr))
|
|
m_pdb->RollbackTrans();
|
|
else
|
|
hr = m_pdb->CommitTrans();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Reset();
|
|
m_pdb->PurgeCachedObjects();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
HRESULT CObjects::RemoveFromRelationship(IUnknown *pobjRemove)
|
|
{
|
|
long idObj1;
|
|
long idObj2;
|
|
|
|
m_pdb->get_IdOf(m_pobjRelated, &idObj1);
|
|
m_pdb->get_IdOf(pobjRemove, &idObj2);
|
|
|
|
if (m_fInverse)
|
|
swap(idObj1, idObj2);
|
|
|
|
TCHAR sz[1024];
|
|
|
|
wsprintf(sz, _T("DELETE *")
|
|
_T(" FROM ObjectRelationships")
|
|
_T(" WHERE ((ObjectRelationships.idObj1=%d)")
|
|
_T(" AND (ObjectRelationships.idRel=%d)")
|
|
_T(" AND (ObjectRelationships.idObj2=%d));"),
|
|
idObj1, m_idRel, idObj2);
|
|
|
|
m_cItems = -1; // UNDONE: Is "m_cItems++;" sufficient?
|
|
if (m_prs != NULL)
|
|
m_prs.Release();
|
|
|
|
return m_pdb->Execute(_bstr_t(sz));
|
|
}
|
|
|
|
|
|
HRESULT CObjects::get_ItemsRelatedToBy(IUnknown *pobj, IMetaPropertyType *pproptype,
|
|
boolean fInverse, IObjects **ppobjs)
|
|
{
|
|
// Can't handle multiple relations yet.
|
|
if (m_pobjRelated != NULL)
|
|
return E_INVALIDARG;
|
|
|
|
CComQIPtr<CMetaPropertyType> pproptypeT(pproptype);
|
|
|
|
if (pproptypeT == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
long idRel = pproptypeT->GetID();
|
|
|
|
HRESULT hr;
|
|
|
|
hr = Clone(ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComQIPtr<CObjects> pobjs(*ppobjs);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = pobjs->InitRelation(pobj, idRel, fInverse);
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemsRelatedToBy(IUnknown *pobj, IMetaPropertyType *pproptype,
|
|
IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<IUnknown>(pobj);
|
|
ValidateInPtr<IMetaPropertyType>(pproptype);
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
return get_ItemsRelatedToBy(pobj, pproptype, FALSE, ppobjs);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemsInverseRelatedToBy(IUnknown *pobj, IMetaPropertyType *pproptype,
|
|
IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateInPtr<IUnknown>(pobj);
|
|
ValidateInPtr<IMetaPropertyType>(pproptype);
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
return get_ItemsRelatedToBy(pobj, pproptype, TRUE, ppobjs);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjects::get_ItemsInTimeRange(DATE dt1, DATE dt2, IObjects **ppobjs)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOutPtr<IObjects>(ppobjs, NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
// Ensure that dt1 is less than or equal to dt2
|
|
if (dt1 > dt2)
|
|
swap(dt1, dt2);
|
|
|
|
_variant_t varT1(dt1, VT_DATE);
|
|
_variant_t varT2(dt2, VT_DATE);
|
|
|
|
CComPtr<IMetaPropertyType> pproptypeStartTime = m_pdb->StartMetaPropertyType();
|
|
CComPtr<IMetaPropertyType> pproptypeEndTime = m_pdb->EndMetaPropertyType();
|
|
|
|
CComPtr<IMetaPropertyCondition> ppropcond1;
|
|
CComPtr<IMetaPropertyCondition> ppropcond2;
|
|
_bstr_t bstrLT(_T("<"));
|
|
_bstr_t bstrGT(_T(">"));
|
|
_bstr_t bstrLE(_T("<="));
|
|
|
|
if (dt1 < dt2)
|
|
{
|
|
// Look for StartTime > dt2 & EndTime < dt1
|
|
hr = pproptypeStartTime->get_Cond(bstrLT, 0, varT2, &ppropcond1);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
hr = pproptypeEndTime->get_Cond(bstrGT, 0, varT1, &ppropcond2);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
// Look for StartTime <= dt1 & EndTime > dt1
|
|
hr = pproptypeStartTime->get_Cond(bstrLE, 0, varT1, &ppropcond1);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
hr = pproptypeEndTime->get_Cond(bstrGT, 0, varT1, &ppropcond2);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IMetaPropertyCondition> ppropcond;
|
|
hr = ppropcond1->get_And(ppropcond2, &ppropcond);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
return get_ItemsWithMetaPropertyCond(ppropcond, ppobjs);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
IMetaProperty * CObjectPropertyBag::GetProp(_bstr_t bstrPropName, boolean fCreate)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (m_pproptypes == NULL)
|
|
return NULL;
|
|
|
|
CComPtr<IMetaPropertyType> pproptype;
|
|
|
|
_variant_t varNil;
|
|
hr = m_pproptypes->get_AddNew(0, bstrPropName, &pproptype);
|
|
|
|
CComPtr<IMetaProperty> pprop;
|
|
hr = m_pprops->get_ItemWithTypeProviderLang(pproptype, NULL, 0, &pprop);
|
|
if (FAILED(hr))
|
|
{
|
|
if (!fCreate)
|
|
return NULL;
|
|
|
|
CComQIPtr<CMetaProperties> ppropsT(m_pprops);
|
|
hr = ppropsT->get_AddNew(pproptype, NULL, NULL, varNil, &pprop);
|
|
if (FAILED(hr))
|
|
return NULL;
|
|
}
|
|
|
|
return pprop.Detach();
|
|
}
|
|
|
|
STDMETHODIMP CObjectPropertyBag::Read(LPCOLESTR pszPropName, VARIANT *pvar, IErrorLog *pErrorLog)
|
|
{
|
|
ENTER_API
|
|
{
|
|
ValidateOut(pvar);
|
|
|
|
CComPtr<IMetaProperty> pprop;
|
|
|
|
pprop.Attach(GetProp(_bstr_t(pszPropName), FALSE));
|
|
if (pprop == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
return pprop->get_Value(pvar);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
|
|
STDMETHODIMP CObjectPropertyBag::Write(LPCOLESTR pszPropName, VARIANT *pvar)
|
|
{
|
|
ENTER_API
|
|
{
|
|
CComPtr<IMetaProperty> pprop;
|
|
|
|
pprop.Attach(GetProp(_bstr_t(pszPropName), TRUE));
|
|
if (pprop == NULL)
|
|
return E_FAIL;
|
|
|
|
CComQIPtr<CMetaProperty> ppropT(pprop);
|
|
|
|
return ppropT->PutValue(*pvar);
|
|
}
|
|
LEAVE_API
|
|
}
|
|
void CObjects::Dump()
|
|
{
|
|
#ifdef _DEBUG
|
|
TCHAR sz[256];
|
|
wsprintf(sz, _T("CObjects::Dump(%x)\n"), (long) this);
|
|
OutputDebugString(sz);
|
|
|
|
long idType;
|
|
m_pobjtype->get_ID(&idType);
|
|
wsprintf(sz, _T(" type == %d\n"), idType);
|
|
OutputDebugString(sz);
|
|
|
|
wsprintf(sz, _T(" m_cItems == %d\n"), m_cItems);
|
|
OutputDebugString(sz);
|
|
|
|
if (m_idPropTypeKey != 0)
|
|
{
|
|
wsprintf(sz, _T(" key == %d:%d:%d:%d\n"), m_idPropTypeKey,
|
|
m_idProviderKey, m_idLangKey, m_vtKey);
|
|
OutputDebugString(sz);
|
|
}
|
|
|
|
if (m_prs != NULL)
|
|
{
|
|
wsprintf(sz, _T(" m_prs == %x\n"), m_prs);
|
|
OutputDebugString(sz);
|
|
}
|
|
|
|
if (m_pobjRelated != NULL)
|
|
{
|
|
long id;
|
|
|
|
m_pdb->get_IdOf(m_pobjRelated,&id);
|
|
wsprintf(sz, _T(" %s to %d by %d\n"),
|
|
m_fInverse ? _T("Inverse Related") : _T("Related"),
|
|
id, m_idRel);
|
|
OutputDebugString(sz);
|
|
}
|
|
if (m_fOnlyUnreferenced)
|
|
{
|
|
OutputDebugString(_T(" Only Unreferenced\n"));
|
|
}
|
|
|
|
_bstr_t bstr;
|
|
|
|
GetQuery(Select, &bstr);
|
|
OutputDebugString(_T(" Query: "));
|
|
OutputDebugString(bstr);
|
|
OutputDebugString(_T("\n"));
|
|
#endif
|
|
}
|
|
|
|
#if SUPPORT_PROPBAG2
|
|
// IPropertyBag2 interface
|
|
STDMETHODIMP CObjectPropertyBag::CountProperties(long *plCount)
|
|
{
|
|
*plCount = 0;
|
|
return S_OK;
|
|
}
|
|
STDMETHODIMP CObjectPropertyBag::GetPropertyInfo(ULONG iProp, ULONG cProps, PROPBAG2 *ppropbag2, ULONG *pcProps)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CObjectPropertyBag::LoadObject(LPCOLESTR pstrName, DWORD dwHint, IUnknown *punk, IErrorLog *perrlog)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CObjectPropertyBag::Read(ULONG cProps, PROPBAG2 *ppropbag2, IErrorLog *perrlog, VARIANT *rgvar, HRESULT *phr)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CObjectPropertyBag::Write(ULONG cProps, PROPBAG2 *ppropbag2, VARIANT *rgvar)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
#endif
|