Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2202 lines
42 KiB

  1. // object.cpp : Implementation of CObject
  2. #include "stdafx.h"
  3. #include "object.h"
  4. #include "Property.h"
  5. #include "Service.h"
  6. #include "Program.h"
  7. #include "ScheduleEntry.h"
  8. #if 0
  9. #undef TIMING
  10. #define TIMING 1
  11. #include "timing.h"
  12. #endif
  13. class _bstr_t2 : public _bstr_t
  14. {
  15. public:
  16. _bstr_t2(IID iid)
  17. {
  18. OLECHAR sz[40];
  19. StringFromGUID2(iid, sz, sizeof(sz)/sizeof(OLECHAR));
  20. _bstr_t::operator =(sz);
  21. }
  22. };
  23. HRESULT CObject::get_RelatedObjectID(boolean fInverse, long id, long idRel, long *pidRet)
  24. {
  25. HRESULT hr;
  26. ADODB::_RecordsetPtr prs;
  27. const TCHAR *szID = !fInverse ? _T("idObj1") : _T("idObj2");
  28. const TCHAR *szIDRet = !fInverse ? _T("idObj2") : _T("idObj1");
  29. if (!fInverse)
  30. {
  31. hr = m_pdb->get_RelsByID1Rel(&prs);
  32. szID = _T("idObj1");
  33. szIDRet = _T("idObj2");
  34. }
  35. else
  36. {
  37. hr = m_pdb->get_RelsByID2Rel(&prs);
  38. szID = _T("idObj2");
  39. szIDRet = _T("idObj1");
  40. }
  41. *pidRet = 0;
  42. if (m_pdb->FSQLServer())
  43. {
  44. TCHAR szFind[32];
  45. prs->MoveFirst();
  46. wsprintf(szFind, _T("%s = %d"), szID, id);
  47. hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
  48. }
  49. else
  50. {
  51. _variant_t var(id);
  52. hr = prs->Seek(var, ADODB::adSeekFirstEQ);
  53. }
  54. if (FAILED(hr))
  55. return S_FALSE;
  56. while (!prs->EndOfFile)
  57. {
  58. long idCur = prs->Fields->Item[szID]->Value;
  59. if (idCur != id)
  60. return S_FALSE;
  61. long idRelCur = prs->Fields->Item["idRel"]->Value;
  62. if (idRelCur == idRel)
  63. {
  64. *pidRet = prs->Fields->Item[szIDRet]->Value;
  65. return S_OK;
  66. }
  67. prs->MoveNext();
  68. }
  69. return S_FALSE;
  70. }
  71. HRESULT AddRelationship(ADODB::_RecordsetPtr prs, long idObj1, long idRel, long iOrder, long idObj2)
  72. {
  73. HRESULT hr;
  74. hr = prs->AddNew();
  75. if (FAILED(hr))
  76. return hr;
  77. prs->Fields->Item["idObj1"]->Value = idObj1;
  78. prs->Fields->Item["idRel"]->Value = idRel;
  79. prs->Fields->Item["order"]->Value = iOrder;
  80. prs->Fields->Item["idObj2"]->Value = idObj2;
  81. hr = prs->Update();
  82. return hr;
  83. }
  84. HRESULT CObjects::AddRelationshipAt(long idObj1, long idRel, long iItem, long idObj2)
  85. {
  86. HRESULT hr;
  87. const long iOrderInc = 10;
  88. ADODB::_RecordsetPtr prs;
  89. hr = m_pdb->get_RelsByID1Rel(&prs);
  90. if (FAILED(hr))
  91. return hr;
  92. if (m_pdb->FSQLServer())
  93. {
  94. TCHAR szFind[32];
  95. prs->MoveFirst();
  96. wsprintf(szFind, _T("idObj1 = %d"), idObj1);
  97. hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
  98. }
  99. else
  100. {
  101. _variant_t var(idObj1);
  102. hr = prs->Seek(var, ADODB::adSeekFirstEQ);
  103. }
  104. if (FAILED(hr))
  105. return hr;
  106. long iOrder = iOrderInc;
  107. long i;
  108. // Scan until we reach the requested item (or end)
  109. for (i = 0; (i <= iItem) || (iItem == -1); i++)
  110. {
  111. if (prs->EndOfFile)
  112. break;
  113. long idCur = prs->Fields->Item["idObj1"]->Value;
  114. if (idCur != idObj1)
  115. break;
  116. long idRelCur = prs->Fields->Item["idRel"]->Value;
  117. if (idRelCur != idRel)
  118. break;
  119. prs->MoveNext();
  120. }
  121. // If there were any items, back up, and use the "order" value for the that one.
  122. if (i > 0)
  123. {
  124. prs->MovePrevious();
  125. iOrder = prs->Fields->Item["order"]->Value;
  126. // Now open up space by pushing up the items which follow.
  127. if (iItem == -1)
  128. iOrder += iOrderInc;
  129. else
  130. {
  131. long iOrderCur = iOrder + 2*iOrderInc;
  132. long cItems = 1;
  133. prs->MoveNext();
  134. // Scan forward looking for a big enough gap.
  135. while (!prs->EndOfFile)
  136. {
  137. long idCur = prs->Fields->Item["idObj1"]->Value;
  138. if (idCur == idObj1)
  139. {
  140. long idRelCur = prs->Fields->Item["idRel"]->Value;
  141. if (idRelCur == idRel)
  142. {
  143. iOrderCur = prs->Fields->Item["order"]->Value;
  144. if (iOrderCur > iOrder + cItems*2)
  145. break;
  146. cItems++;
  147. prs->MoveNext();
  148. continue;
  149. }
  150. }
  151. // We've reached the end... just space them all iOrderInc apart.
  152. iOrderCur = iOrder + (cItems + 1)*iOrderInc;
  153. break;
  154. }
  155. long dSpace = (iOrderCur - iOrder)/(cItems + 1);
  156. // Then scan backward spreading the gap evenly between the items.
  157. for (long c = 0; c < cItems; c++)
  158. {
  159. if (prs->EndOfFile)
  160. {
  161. // MovePrevious doen't seem to work from EndOfFile...
  162. // Use MoveLast instead
  163. prs->MoveLast();
  164. #ifdef _DEBUG
  165. long iOrderOld = prs->Fields->Item["order"]->Value;
  166. #endif
  167. }
  168. else
  169. prs->MovePrevious();
  170. iOrderCur -= dSpace;
  171. #ifdef _DEBUG
  172. long iOrderOld = prs->Fields->Item["order"]->Value;
  173. TCHAR sz[256];
  174. wsprintf(sz, _T("AddRelationshipAt : %d --> %d\n"), iOrderOld, iOrderCur);
  175. OutputDebugString(sz);
  176. #endif
  177. prs->Fields->Item["order"]->Value = iOrderCur;
  178. prs->Update();
  179. }
  180. }
  181. }
  182. // The slot at iOrder has been opened up, so the new relation can now be added.
  183. return AddRelationship(prs, idObj1, idRel, iOrder, idObj2);
  184. }
  185. /////////////////////////////////////////////////////////////////////////////
  186. // CObjectType
  187. STDMETHODIMP CObjectType::get_ID(long *pid)
  188. {
  189. ENTER_API
  190. {
  191. ValidateOut<long>(pid, m_id);
  192. return S_OK;
  193. }
  194. LEAVE_API
  195. }
  196. HRESULT CObjectType::GetDB(CGuideDB **ppdb)
  197. {
  198. *ppdb = m_pdb;
  199. if (*ppdb == NULL)
  200. return E_FAIL;
  201. (*ppdb)->AddRef();
  202. return S_OK;
  203. }
  204. STDMETHODIMP CObjectType::get_IID(BSTR *pbstrIID)
  205. {
  206. ENTER_API
  207. {
  208. ValidateOut(pbstrIID);
  209. OLECHAR sz[40];
  210. StringFromGUID2(m_clsid, sz, sizeof(sz)/sizeof(OLECHAR));
  211. _bstr_t bstr(sz);
  212. *pbstrIID = bstr.copy();
  213. return S_OK;
  214. }
  215. LEAVE_API
  216. }
  217. HRESULT CObjectType::CreateInstance(long id, IUnknown **ppunk)
  218. {
  219. HRESULT hr;
  220. CComPtr<CObject> pobj = NewComObjectCachedLRU(CObject);
  221. if (pobj == NULL)
  222. return E_OUTOFMEMORY;
  223. hr = pobj->Init(id, this);
  224. if (FAILED(hr))
  225. return hr;
  226. *ppunk = pobj.Detach();
  227. return S_OK;
  228. }
  229. HRESULT CObjectType::get_NewCollection(IObjects * *ppobjs)
  230. {
  231. HRESULT hr;
  232. CComPtr<CObjects> pobjs = NewComObjectCachedLRU(CObjects);
  233. if (pobjs == NULL)
  234. return E_OUTOFMEMORY;
  235. m_pdb->CacheCollection(pobjs);
  236. pobjs->put_DB(m_pdb);
  237. pobjs->put_ObjectType(this);
  238. if (!m_fKnowCollectionCLSID)
  239. {
  240. OLECHAR szCLSID[128];
  241. wcscpy(szCLSID, L"CLSID\\");
  242. long cch = wcslen(szCLSID);
  243. StringFromGUID2(m_clsid, szCLSID + cch, sizeof(szCLSID)/sizeof(OLECHAR) - cch);
  244. HKEY hkey;
  245. long lErr;
  246. lErr = RegOpenKeyEx(HKEY_CLASSES_ROOT, szCLSID, 0, KEY_READ, &hkey);
  247. if (lErr == ERROR_SUCCESS)
  248. {
  249. OLECHAR szCollectionCLSID[128];
  250. DWORD cb = sizeof(szCollectionCLSID);
  251. DWORD regtype;
  252. lErr = RegQueryValueEx(hkey, _T("CollectionCLSID"), 0,
  253. &regtype, (LPBYTE) szCollectionCLSID, &cb);
  254. RegCloseKey(hkey);
  255. if ((lErr == ERROR_SUCCESS) && (regtype == REG_SZ))
  256. {
  257. hr = CLSIDFromString(szCollectionCLSID, &m_clsidCollection);
  258. if (SUCCEEDED(hr))
  259. m_fKnowCollectionCLSID = TRUE;
  260. }
  261. }
  262. }
  263. if (m_fKnowCollectionCLSID)
  264. {
  265. pobjs->Init(m_clsidCollection);
  266. }
  267. *ppobjs = pobjs.Detach();
  268. return S_OK;
  269. }
  270. STDMETHODIMP CObjectType::get_New(IUnknown **ppobj)
  271. {
  272. ENTER_API
  273. {
  274. ValidateOutPtr<IUnknown>(ppobj, NULL);
  275. HRESULT hr;
  276. ADODB::_RecordsetPtr prs;
  277. hr = m_pdb->get_ObjsRS(&prs);
  278. if (FAILED(hr))
  279. return hr;
  280. // Create a new record.
  281. hr = prs->AddNew();
  282. prs->Fields->Item["idType"]->Value = m_id;
  283. hr = prs->Update();
  284. long id = prs->Fields->Item["id"]->Value;
  285. return m_pdb->CacheObject(id, this, ppobj);
  286. }
  287. LEAVE_API
  288. }
  289. /////////////////////////////////////////////////////////////////////////////
  290. // CObjectTypes
  291. HRESULT CObjectTypes::Init(CGuideDB *pdb)
  292. {
  293. m_pdb = pdb;
  294. if (pdb == NULL)
  295. return E_INVALIDARG;
  296. // Special case the "Object" object type
  297. CObjectType *pobjtype;
  298. pobjtype = Cache(0, _bstr_t2(__uuidof(IUnknown)));
  299. ADODB::_RecordsetPtr prs;
  300. m_pdb->get_ObjTypesRS(&prs);
  301. prs->MoveFirst();
  302. while (!prs->EndOfFile)
  303. {
  304. // Read in all the records
  305. long id = prs->Fields->Item["id"]->Value;
  306. bstr_t bstrCLSID = prs->Fields->Item["clsid"]->Value;
  307. pobjtype = Cache(id, bstrCLSID);
  308. prs->MoveNext();
  309. }
  310. return S_OK;
  311. }
  312. STDMETHODIMP CObjectTypes::get_ItemWithCLSID(BSTR bstrCLSID, CObjectType **ppobjtype)
  313. {
  314. ENTER_API
  315. {
  316. ValidateIn(bstrCLSID);
  317. ValidateOutPtr<CObjectType>(ppobjtype, NULL);
  318. CLSID clsid;
  319. HRESULT hr = CLSIDFromString(bstrCLSID, &clsid);
  320. if (hr == CO_E_CLASSSTRING)
  321. return hr;
  322. t_map::iterator it = m_map.find(clsid);
  323. if (it == m_map.end())
  324. return E_INVALIDARG;
  325. *ppobjtype = (*it).second;
  326. return S_OK;
  327. }
  328. LEAVE_API
  329. }
  330. CObjectType *CObjectTypes::Cache(long id, BSTR bstrCLSID)
  331. {
  332. CObjectType *pobjtype = NULL;
  333. CLSID clsid;
  334. HRESULT hr = CLSIDFromString(bstrCLSID, &clsid);
  335. if (hr == CO_E_CLASSSTRING)
  336. return NULL;
  337. t_map::iterator it = m_map.find(clsid);
  338. if (it != m_map.end())
  339. {
  340. pobjtype = (*it).second;
  341. }
  342. else
  343. {
  344. pobjtype = new CObjectType;
  345. if (pobjtype == NULL)
  346. return NULL;
  347. pobjtype->Init(m_pdb, id, clsid);
  348. m_map[clsid] = pobjtype;
  349. m_pdb->put_ObjectType(id, pobjtype);
  350. }
  351. return pobjtype;
  352. }
  353. STDMETHODIMP CObjectTypes::get_AddNew(BSTR bstrCLSID, CObjectType **ppobjtype)
  354. {
  355. ENTER_API
  356. {
  357. ValidateIn(bstrCLSID);
  358. ValidateOutPtr<CObjectType>(ppobjtype, NULL);
  359. HRESULT hr;
  360. hr = get_ItemWithCLSID(bstrCLSID, ppobjtype);
  361. if (SUCCEEDED(hr))
  362. return hr;
  363. ADODB::_RecordsetPtr prs;
  364. hr = m_pdb->get_ObjTypesRS(&prs);
  365. if (FAILED(hr))
  366. return hr;
  367. // Create a new record.
  368. hr = prs->AddNew();
  369. if (FAILED(hr))
  370. return hr;
  371. prs->Fields->Item["clsid"]->Value = bstrCLSID;
  372. hr = prs->Update();
  373. if (FAILED(hr))
  374. return hr;
  375. long id = prs->Fields->Item["id"]->Value;
  376. *ppobjtype = Cache(id, bstrCLSID);
  377. if (*ppobjtype == NULL)
  378. return E_OUTOFMEMORY;
  379. return S_OK;
  380. }
  381. LEAVE_API
  382. }
  383. #if 0
  384. STDMETHODIMP CObjectTypes::get_AddNew(CLSID clsid, CObjectType **ppobjtype)
  385. {
  386. ENTER_API
  387. {
  388. ValidateOutPtr<CObjectType>(ppobjtype, NULL);
  389. return S_OK;
  390. }
  391. LEAVE_API
  392. }
  393. #endif
  394. /////////////////////////////////////////////////////////////////////////////
  395. // CObject
  396. STDMETHODIMP CObject::Init(long id, CObjectType *pobjtype)
  397. {
  398. HRESULT hr;
  399. if (m_id != 0)
  400. return E_INVALIDARG;
  401. m_id = id;
  402. m_pobjtype = pobjtype;
  403. _ASSERTE(m_pobjtype != NULL);
  404. m_pdb = m_pobjtype->m_pdb;
  405. if (m_pobjtype->m_id != 0)
  406. {
  407. hr = m_punk.CoCreateInstance(pobjtype->m_clsid, GetControllingUnknown());
  408. if (FAILED(hr))
  409. return hr;
  410. }
  411. return S_OK;
  412. }
  413. STDMETHODIMP CObject::Save(IUnknown *punk)
  414. {
  415. HRESULT hr;
  416. if (punk == NULL)
  417. punk = this;
  418. CComQIPtr<IPersistPropertyBag> ppersistpropbag(punk);
  419. if (ppersistpropbag != NULL)
  420. {
  421. hr = CreatePropBag(ppersistpropbag);
  422. hr = ppersistpropbag->Save(m_ppropbag, TRUE, FALSE);
  423. }
  424. else
  425. {
  426. CComQIPtr<IPersistStream> ppersiststream(punk);
  427. if (ppersiststream != NULL)
  428. {
  429. CComPtr<IStream> pstream;
  430. hr = CreateStreamOnHGlobal(NULL, TRUE, &pstream);
  431. if (FAILED(hr))
  432. return hr;
  433. // Write a tag to indicate IPersistStream was used.
  434. char ch = Format_IPersistStream;
  435. ULONG cb = sizeof(ch);
  436. hr = pstream->Write(&ch, cb, &cb);
  437. if (FAILED(hr))
  438. return hr;
  439. // Now have the object save it's bits.
  440. hr = ppersiststream->Save(pstream, TRUE);
  441. if (FAILED(hr))
  442. return hr;
  443. // Create a SafeArray from the bits.
  444. HANDLE hdata;
  445. hr = GetHGlobalFromStream(pstream, &hdata);
  446. if (FAILED(hr))
  447. return hr;
  448. long cbData = GlobalSize(hdata);
  449. SAFEARRAY *parray = SafeArrayCreateVector(VT_UI1, 0, cbData);
  450. if (parray == NULL)
  451. return E_OUTOFMEMORY;
  452. BYTE *pbDst;
  453. hr = SafeArrayAccessData(parray, (void **) &pbDst);
  454. if (FAILED(hr))
  455. return hr;
  456. BYTE *pbSrc = (BYTE *) GlobalLock(hdata);
  457. memcpy(pbDst, pbSrc, cbData);
  458. GlobalUnlock(hdata);
  459. SafeArrayUnaccessData(parray);
  460. // Put the SafeArray in a variant
  461. _variant_t varT;
  462. varT.vt = VT_ARRAY | VT_UI1;
  463. varT.parray = parray;
  464. // Write the bits to the database
  465. ADODB::_RecordsetPtr prs;
  466. hr = m_pdb->get_ObjsByID(&prs);
  467. if (FAILED(hr))
  468. return hr;
  469. _variant_t var = m_id;
  470. prs->Seek(var, ADODB::adSeekFirstEQ);
  471. if (prs->EndOfFile)
  472. return E_FAIL;
  473. hr = prs->Fields->Item["oValue"]->AppendChunk(varT);
  474. if (FAILED(hr))
  475. return hr;
  476. }
  477. else
  478. {
  479. return E_INVALIDARG;
  480. }
  481. }
  482. return hr;
  483. }
  484. STDMETHODIMP CObject::Load()
  485. {
  486. HRESULT hr;
  487. CComQIPtr<IPersistPropertyBag> ppersistpropbag(this);
  488. if (ppersistpropbag != NULL)
  489. {
  490. hr = CreatePropBag(ppersistpropbag);
  491. hr = ppersistpropbag->Load(m_ppropbag, NULL);
  492. }
  493. else
  494. {
  495. CComQIPtr<IPersistStream> ppersiststream(this);
  496. if (ppersiststream != NULL)
  497. {
  498. ADODB::_RecordsetPtr prs;
  499. hr = m_pdb->get_ObjsByID(&prs);
  500. if (FAILED(hr))
  501. return hr;
  502. _variant_t var = m_id;
  503. prs->Seek(var, ADODB::adSeekFirstEQ);
  504. if (prs->EndOfFile)
  505. return E_FAIL;
  506. long cb;
  507. ADODB::FieldPtr pfield;
  508. pfield.Attach(prs->Fields->Item["oValue"]);
  509. hr = pfield->get_ActualSize(&cb);
  510. if (FAILED(hr))
  511. return hr;
  512. _variant_t varData;
  513. hr = pfield->GetChunk(cb, &varData);
  514. if (FAILED(hr))
  515. return hr;
  516. if ((varData.vt & VT_ARRAY) == 0)
  517. return E_FAIL;
  518. BYTE *pbSrc;
  519. hr = SafeArrayAccessData(varData.parray, (void **) &pbSrc);
  520. if (FAILED(hr))
  521. return hr;
  522. HANDLE hdata;
  523. BOOL fFree = FALSE;
  524. hdata = GlobalHandle(pbSrc);
  525. if (hdata == NULL)
  526. {
  527. hdata = GlobalAlloc(GHND, cb);
  528. if (hdata == NULL)
  529. {
  530. SafeArrayUnaccessData(varData.parray);
  531. return E_OUTOFMEMORY;
  532. }
  533. BYTE *pbDst = (BYTE *) GlobalLock(hdata);
  534. memcpy(pbDst, pbSrc, cb);
  535. fFree = TRUE;
  536. }
  537. else
  538. {
  539. BYTE *pbTest = (BYTE *) GlobalLock(hdata);
  540. int i = 0;
  541. if (pbTest != pbSrc)
  542. i++;
  543. GlobalUnlock(hdata);
  544. }
  545. {
  546. CComPtr<IStream> pstream;
  547. hr = CreateStreamOnHGlobal(hdata, fFree, &pstream);
  548. if (FAILED(hr))
  549. {
  550. if (fFree)
  551. GlobalFree(hdata);
  552. return hr;
  553. }
  554. char ch;
  555. ULONG cbT = sizeof(ch);
  556. hr = pstream->Read(&ch, cbT, &cbT);
  557. switch (ch)
  558. {
  559. case Format_IPersistStream:
  560. hr = ppersiststream->Load(pstream);
  561. break;
  562. default:
  563. hr = STG_E_DOCFILECORRUPT;
  564. }
  565. }
  566. SafeArrayUnaccessData(varData.parray);
  567. }
  568. }
  569. return S_OK;
  570. }
  571. HRESULT CObject::CreatePropBag(IPersist *ppersist)
  572. {
  573. HRESULT hr;
  574. if (m_ppropbag == NULL)
  575. {
  576. CComPtr<IMetaProperties> pprops;
  577. hr = get_MetaProperties(&pprops);
  578. if (FAILED(hr))
  579. return hr;
  580. CLSID clsid;
  581. hr = ppersist->GetClassID(&clsid);
  582. if (FAILED(hr))
  583. return hr;
  584. OLECHAR sz[40];
  585. StringFromGUID2(clsid, sz, sizeof(sz)/sizeof(OLECHAR));
  586. _bstr_t bstrCLSID(sz);
  587. CComPtr<IMetaPropertySet> ppropset;
  588. hr = m_pdb->get_MetaPropertySet(bstrCLSID, &ppropset);
  589. CComPtr<IMetaPropertyTypes> pproptypes;
  590. hr = ppropset->get_MetaPropertyTypes(&pproptypes);
  591. m_ppropbag = NewComObject(CObjectPropertyBag);
  592. m_ppropbag->Init(pprops, pproptypes);
  593. }
  594. return S_OK;
  595. }
  596. STDMETHODIMP CObject::get_Type(CObjectType **ppobjtype)
  597. {
  598. ENTER_API
  599. {
  600. ValidateOutPtr<CObjectType>(ppobjtype, NULL);
  601. _ASSERTE(m_pobjtype != NULL);
  602. *ppobjtype = m_pobjtype;
  603. return S_OK;
  604. }
  605. LEAVE_API
  606. }
  607. STDMETHODIMP CObject::get_ID(long *pid)
  608. {
  609. ENTER_API
  610. {
  611. ValidateOut<long>(pid, m_id);
  612. return S_OK;
  613. }
  614. LEAVE_API
  615. }
  616. STDMETHODIMP CObject::get_MetaProperties(IMetaProperties **ppprops)
  617. {
  618. ENTER_API
  619. {
  620. ValidateOutPtr<IMetaProperties>(ppprops, NULL);
  621. HRESULT hr;
  622. if (m_pprops == NULL)
  623. {
  624. hr = m_pdb->get_MetaPropertiesOf((IUnknown *) this, &m_pprops);
  625. if (FAILED(hr))
  626. return hr;
  627. }
  628. if (m_pprops == NULL)
  629. return E_OUTOFMEMORY;
  630. return m_pprops.QueryInterface(ppprops);
  631. }
  632. LEAVE_API
  633. }
  634. STDMETHODIMP CObject::get_ItemInverseRelatedBy(long idRel, IUnknown **ppobj1)
  635. {
  636. ENTER_API
  637. {
  638. ValidateOutPtr<IUnknown>(ppobj1, NULL);
  639. HRESULT hr;
  640. long idObj1;
  641. hr = get_RelatedObjectID(TRUE, m_id, idRel, &idObj1);
  642. if (hr == S_FALSE || FAILED(hr))
  643. return hr;
  644. return m_pdb->CacheObject(idObj1, (long) 0, ppobj1);
  645. }
  646. LEAVE_API
  647. }
  648. HRESULT CObject::get_ItemsRelatedBy(CObjectType *pobjtype,
  649. long idRel, boolean fInverse, IObjects **ppobjs)
  650. {
  651. HRESULT hr;
  652. hr = pobjtype->get_NewCollection(ppobjs);
  653. if (FAILED(hr))
  654. return hr;
  655. CComQIPtr<CObjects> pobjs(*ppobjs);
  656. hr = pobjs->InitRelation(this, idRel, fInverse);
  657. return hr;
  658. }
  659. STDMETHODIMP CObject::get_ItemsInverseRelatedBy(CObjectType *pobjtype, long idRel, IObjects **ppobjs)
  660. {
  661. ENTER_API
  662. {
  663. ValidateInPtr<CObjectType>(pobjtype);
  664. ValidateOutPtr<IObjects>(ppobjs, NULL);
  665. return get_ItemsRelatedBy(pobjtype, idRel, TRUE, ppobjs);
  666. }
  667. LEAVE_API
  668. }
  669. STDMETHODIMP CObject::get_ItemRelatedBy(long idRel, IUnknown **ppobj2)
  670. {
  671. ENTER_API
  672. {
  673. ValidateOutPtr<IUnknown>(ppobj2, NULL);
  674. HRESULT hr;
  675. long idObj2;
  676. hr = get_RelatedObjectID(FALSE, m_id, idRel, &idObj2);
  677. if (hr == S_FALSE || FAILED(hr))
  678. return hr;
  679. return m_pdb->CacheObject(idObj2, (long) 0, ppobj2);
  680. }
  681. LEAVE_API
  682. }
  683. STDMETHODIMP CObject::put_ItemRelatedBy(long idRel, IUnknown *pobj2)
  684. {
  685. ENTER_API
  686. {
  687. ValidateInPtr<IUnknown>(pobj2);
  688. HRESULT hr;
  689. long idObj2;
  690. m_pdb->get_IdOf(pobj2, &idObj2);
  691. ADODB::_RecordsetPtr prs;
  692. hr = m_pdb->get_RelsByID1Rel(&prs);
  693. if (m_pdb->FSQLServer())
  694. {
  695. TCHAR szFind[32];
  696. prs->MoveFirst();
  697. wsprintf(szFind, _T("idObj1 = %d"), m_id);
  698. hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
  699. }
  700. else
  701. {
  702. _variant_t var(m_id);
  703. hr = prs->Seek(var, ADODB::adSeekFirstEQ);
  704. }
  705. if (FAILED(hr))
  706. return hr;
  707. while (!prs->EndOfFile)
  708. {
  709. long idCur = prs->Fields->Item["idObj1"]->Value;
  710. if (idCur != m_id)
  711. break;
  712. long idRelCur = prs->Fields->Item["idRel"]->Value;
  713. if (idRelCur == idRel)
  714. {
  715. prs->Fields->Item["idObj2"]->Value = idObj2;
  716. return S_OK;
  717. }
  718. prs->MoveNext();
  719. }
  720. return AddRelationship(prs, m_id, idRel, 0, idObj2);
  721. }
  722. LEAVE_API
  723. }
  724. STDMETHODIMP CObject::get_ItemsRelatedBy(CObjectType *pobjtype, long idRel, IObjects **ppobjs)
  725. {
  726. ENTER_API
  727. {
  728. ValidateInPtr<CObjectType>(pobjtype);
  729. ValidateOutPtr<IObjects>(ppobjs, NULL);
  730. return get_ItemsRelatedBy(pobjtype, idRel, FALSE, ppobjs);
  731. }
  732. LEAVE_API
  733. }
  734. HRESULT CObjects::Clone(IObjects **ppobjs)
  735. {
  736. HRESULT hr;
  737. hr = m_pobjtype->get_NewCollection(ppobjs);
  738. if (FAILED(hr))
  739. return hr;
  740. CComQIPtr<CObjects> pobjs(*ppobjs);
  741. pobjs->Copy(this);
  742. return hr;
  743. }
  744. /////////////////////////////////////////////////////////////////////////////
  745. // CObjects
  746. // Sample Query:
  747. //
  748. // SELECT DISTINCT Objects.id
  749. // FROM ((((
  750. // Objects
  751. // INNER JOIN ObjectRelationships
  752. // ON Objects.id = ObjectRelationships.idObj2)
  753. // INNER JOIN Properties AS Properties_0
  754. // ON Objects.id = Properties_0.idObj)
  755. // INNER JOIN Properties AS Properties_1
  756. // ON Objects.id = Properties_1.idObj)
  757. // INNER JOIN Properties AS Properties_2
  758. // ON Objects.id = Properties_2.idObj)
  759. // WHERE
  760. // (((
  761. // Objects.idType = 1)
  762. // AND (ObjectRelationships.idObj1 = 10))
  763. // AND
  764. // (
  765. // ((Properties_0.idPropType=15) AND (Properties_0.sValue="Arthur"))
  766. // OR
  767. // ((Properties_2.idPropType=15) AND (Properties_2.sValue="Jeopardy!"))
  768. // )
  769. // );
  770. HRESULT CObjects::GetQuery(QueryType qtype, _bstr_t *pbstr)
  771. {
  772. TCHAR sz[8*1024];
  773. HRESULT hr;
  774. _bstr_t bstrCmd;
  775. _bstr_t bstrFrom;
  776. _bstr_t bstrOrder;
  777. _bstr_t bstrWhere;
  778. long cWhere = 0;
  779. switch (qtype)
  780. {
  781. case Select:
  782. bstrCmd = _T("SELECT DISTINCT Objects.id, Objects.idType ");
  783. break;
  784. case Delete:
  785. bstrCmd = _T("DELETE Objects.* ");
  786. break;
  787. }
  788. bstrFrom = "Objects";
  789. _ASSERTE(m_pobjtype != NULL);
  790. long idType = 0;
  791. hr = m_pobjtype->get_ID(&idType);
  792. if (FAILED(hr))
  793. return hr;
  794. if (idType != 0)
  795. {
  796. wsprintf(sz, _T("Objects.idType = %d"), idType);
  797. bstrWhere = sz;
  798. cWhere++;
  799. }
  800. if (m_fOnlyUnreferenced)
  801. {
  802. if (cWhere > 0)
  803. bstrWhere = bstrWhere + " AND ";
  804. bstrWhere = bstrWhere
  805. + "(Objects.id NOT IN"
  806. + " (SELECT DISTINCT ObjectRelationships.idObj2 FROM ObjectRelationships))"
  807. + " AND (Objects.id NOT IN"
  808. + " (SELECT DISTINCT Properties.lValue FROM Properties WHERE Properties.ValueType = 13))";
  809. cWhere++;
  810. }
  811. if ((qtype == Select) && (m_idPropTypeKey != 0))
  812. {
  813. switch (m_vtKey)
  814. {
  815. default:
  816. return E_UNEXPECTED;
  817. case VT_I2:
  818. case VT_I4:
  819. bstrCmd = bstrCmd + ", Props_Key.lValue";
  820. bstrOrder = _T(" ORDER BY Props_Key.lValue");
  821. break;
  822. case VT_R4:
  823. case VT_R8:
  824. case VT_DATE:
  825. bstrCmd = bstrCmd + ", Props_Key.fValue";
  826. bstrOrder = _T(" ORDER BY Props_Key.fValue");
  827. break;
  828. case VT_BSTR_BLOB:
  829. case VT_BSTR:
  830. bstrCmd = bstrCmd + ", left(Props_Key.sValue,255) AS sValue";
  831. bstrOrder = _T(" ORDER BY left(Props_Key.sValue,255)");
  832. break;
  833. }
  834. wsprintf(sz, _T("(%s INNER JOIN Properties AS Props_Key")
  835. _T(" ON Objects.id = Props_Key.idObj)"),
  836. (const TCHAR *) bstrFrom);
  837. bstrFrom = sz;
  838. if (cWhere++ > 0)
  839. bstrWhere = bstrWhere + " AND ";
  840. wsprintf(sz, _T("(Props_Key.idPropType = %d) AND (Props_Key.ValueType = %d)"),
  841. m_idPropTypeKey, m_vtKey);
  842. bstrWhere = bstrWhere + sz;
  843. if (m_idProviderKey != 0)
  844. {
  845. wsprintf(sz, _T("AND (Props_Key.idProvider = %d)"), m_idProviderKey);
  846. bstrWhere = bstrWhere + sz;
  847. }
  848. if (m_idLangKey != 0)
  849. {
  850. wsprintf(sz, _T("AND (Props_Key.idLanguage = %d)"), m_idLangKey);
  851. bstrWhere = bstrWhere + sz;
  852. }
  853. }
  854. if (m_pobjRelated != NULL)
  855. {
  856. const TCHAR *szRelatedField;
  857. const TCHAR *szResultField;
  858. if (!m_fInverse)
  859. {
  860. szRelatedField = _T("idObj1");
  861. szResultField = _T("idObj2");
  862. if (qtype == Select)
  863. {
  864. bstrCmd = bstrCmd + _T(", ObjectRelationships.[order]");
  865. bstrOrder = _T(" ORDER BY ObjectRelationships.[order]");
  866. }
  867. }
  868. else
  869. {
  870. szRelatedField = _T("idObj2");
  871. szResultField = _T("idObj1");
  872. }
  873. wsprintf(sz, _T("(%s INNER JOIN ObjectRelationships")
  874. _T(" ON Objects.id = ObjectRelationships.%s)"),
  875. (const TCHAR *) bstrFrom, szResultField);
  876. bstrFrom = sz;
  877. long id;
  878. m_pdb->get_IdOf(m_pobjRelated, &id);
  879. if (cWhere++ == 0)
  880. {
  881. wsprintf(sz, _T("(ObjectRelationships.idRel = %d)")
  882. _T(" AND (ObjectRelationships.%s = %d)"),
  883. m_idRel, szRelatedField, id);
  884. }
  885. else
  886. {
  887. wsprintf(sz, _T("(%s) AND ")
  888. _T("(ObjectRelationships.idRel = %d)")
  889. _T(" AND (ObjectRelationships.%s = %d)"),
  890. (const TCHAR *) bstrWhere,
  891. m_idRel, szRelatedField, id);
  892. }
  893. bstrWhere = sz;
  894. }
  895. if (m_ppropcond != NULL)
  896. {
  897. long cProps = 0;
  898. CComQIPtr<CMetaPropertyCondition> ppropcond(m_ppropcond);
  899. _bstr_t bstrWhereClause;
  900. hr = ppropcond->get_QueryClause(cProps, &bstrWhereClause);
  901. if (FAILED(hr))
  902. return hr;
  903. if (cProps > 0)
  904. {
  905. if (cWhere > 0)
  906. {
  907. wsprintf(sz, _T("(%s) AND (%s)"),
  908. (const TCHAR *) bstrWhere,
  909. (const TCHAR *) bstrWhereClause);
  910. bstrWhere = sz;
  911. }
  912. else
  913. {
  914. bstrWhere += bstrWhereClause;
  915. }
  916. cWhere++;
  917. for (int i = 0; i < cProps; i++)
  918. {
  919. wsprintf(sz, _T("(%s INNER JOIN Properties AS Props_%d")
  920. _T(" ON Objects.id = Props_%d.idObj)"),
  921. (const TCHAR *) bstrFrom, i, i);
  922. bstrFrom = sz;
  923. }
  924. }
  925. }
  926. if (!!bstrWhere)
  927. {
  928. bstrWhere = _bstr_t(" WHERE ") + bstrWhere;
  929. }
  930. *pbstr = bstrCmd + _T(" FROM ") + bstrFrom
  931. + bstrWhere + bstrOrder + _T(";");
  932. return S_OK;
  933. }
  934. HRESULT CObjects::GetRS(ADODB::_Recordset **pprs)
  935. {
  936. HRESULT hr;
  937. #if 1
  938. BOOL fQuery = TRUE;
  939. #else
  940. BOOL fQuery = ((m_ppropcond != NULL) || (m_pobjRelated != NULL));
  941. #endif
  942. if (m_prs == NULL)
  943. {
  944. if (fQuery)
  945. {
  946. _bstr_t bstrQuery;
  947. hr = GetQuery(Select, &bstrQuery);
  948. if (FAILED(hr))
  949. return hr;
  950. hr = m_pdb->NewQuery(bstrQuery, &m_prs);
  951. if (FAILED(hr))
  952. return hr;
  953. }
  954. else
  955. {
  956. hr = m_pdb->get_ObjsByType(&m_prs);
  957. if (FAILED(hr))
  958. return hr;
  959. }
  960. }
  961. if (m_prs == NULL)
  962. return E_FAIL;
  963. (*pprs = m_prs)->AddRef();
  964. return fQuery ? S_OK : S_FALSE;
  965. }
  966. STDMETHODIMP CObjects::get_Count(long *plCount)
  967. {
  968. ENTER_API
  969. {
  970. ValidateOut<long>(plCount, 0);
  971. DeclarePerfTimer("get_Count");
  972. HRESULT hr;
  973. if (m_cItems == -1)
  974. {
  975. long idType;
  976. long cItems = 0;
  977. ADODB::_RecordsetPtr prs;
  978. hr = GetRS(&prs);
  979. if (FAILED(hr))
  980. return hr;
  981. bool fFast = (hr == S_OK);
  982. #if 0
  983. if (fFast)
  984. {
  985. PerfTimerReset();
  986. hr = prs->MoveLast();
  987. hr = prs->get_RecordCount(&cItems);
  988. if (SUCCEEDED(hr) && (cItems >= 0))
  989. {
  990. PerfTimerDump("Succeeded get_RecordCount");
  991. goto ReturnIt;
  992. }
  993. cItems = 0;
  994. PerfTimerDump("Failed get_RecordCount");
  995. }
  996. #endif
  997. PerfTimerReset();
  998. hr = m_pobjtype->get_ID(&idType);
  999. if (FAILED(hr))
  1000. return E_FAIL;
  1001. if (!fFast && idType != 0)
  1002. {
  1003. if (m_pdb->FSQLServer())
  1004. {
  1005. TCHAR szFind[32];
  1006. prs->MoveFirst();
  1007. wsprintf(szFind, _T("idType = %d"), idType);
  1008. hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
  1009. }
  1010. else
  1011. {
  1012. _variant_t var(idType);
  1013. prs->Seek(var, ADODB::adSeekAfterEQ);
  1014. }
  1015. }
  1016. else
  1017. {
  1018. hr = prs->MoveFirst();
  1019. if (hr == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, ADODB::adErrNoCurrentRecord))
  1020. goto ReturnIt;
  1021. }
  1022. // UNDONE: This is not the most efficient, but it's what works for now.
  1023. while (!prs->EndOfFile)
  1024. {
  1025. if (idType != 0)
  1026. {
  1027. long idType2 = prs->Fields->Item["idType"]->Value;
  1028. if (idType != idType2)
  1029. break;
  1030. }
  1031. cItems++;
  1032. prs->MoveNext();
  1033. }
  1034. PerfTimerDump("Count done");
  1035. ReturnIt:
  1036. m_cItems = cItems;
  1037. }
  1038. *plCount = m_cItems;
  1039. return S_OK;
  1040. }
  1041. LEAVE_API
  1042. }
  1043. STDMETHODIMP CObjects::get_Item(VARIANT varIndex, IUnknown **ppobj)
  1044. {
  1045. ENTER_API
  1046. {
  1047. ValidateOutPtr<IUnknown>(ppobj, NULL);
  1048. HRESULT hr;
  1049. _variant_t var(varIndex);
  1050. try
  1051. {
  1052. var.ChangeType(VT_I4);
  1053. }
  1054. catch (_com_error)
  1055. {
  1056. return E_INVALIDARG;
  1057. }
  1058. long i = var.lVal;
  1059. if (i < 0)
  1060. return E_INVALIDARG;
  1061. long idType;
  1062. hr = m_pobjtype->get_ID(&idType);
  1063. if (FAILED(hr))
  1064. return E_FAIL;
  1065. ADODB::_RecordsetPtr prs;
  1066. hr = GetRS(&prs);
  1067. if (FAILED(hr))
  1068. return hr;
  1069. if (hr == S_OK)
  1070. {
  1071. prs->MoveFirst();
  1072. }
  1073. else
  1074. {
  1075. if (m_pdb->FSQLServer())
  1076. {
  1077. TCHAR szFind[32];
  1078. wsprintf(szFind, _T("idType = %d"), idType);
  1079. hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
  1080. }
  1081. else
  1082. {
  1083. var = idType;
  1084. prs->Seek(var, ADODB::adSeekAfterEQ);
  1085. }
  1086. }
  1087. if (prs->EndOfFile)
  1088. return E_INVALIDARG;
  1089. if (i > 0)
  1090. prs->Move(i);
  1091. if (prs->EndOfFile)
  1092. return E_INVALIDARG;
  1093. long idType2 = prs->Fields->Item["idType"]->Value;
  1094. if ((idType != 0) && (idType != idType2))
  1095. return E_INVALIDARG;
  1096. long idObj = prs->Fields->Item["id"]->Value;
  1097. if (idType == 0)
  1098. return m_pdb->CacheObject(idObj, idType2, ppobj);
  1099. return m_pdb->CacheObject(idObj, m_pobjtype, ppobj);
  1100. }
  1101. LEAVE_API
  1102. }
  1103. STDMETHODIMP CObjects::get_ItemWithID(long id, IUnknown **ppobj)
  1104. {
  1105. ENTER_API
  1106. {
  1107. ValidateOutPtr<IUnknown>(ppobj, NULL);
  1108. HRESULT hr;
  1109. hr = m_pdb->get_Object(id, ppobj);
  1110. if (SUCCEEDED(hr))
  1111. return hr;
  1112. long idType;
  1113. hr = m_pobjtype->get_ID(&idType);
  1114. if (FAILED(hr))
  1115. return E_FAIL;
  1116. ADODB::_RecordsetPtr prs;
  1117. hr = m_pdb->get_ObjsByID(&prs);
  1118. if (m_pdb->FSQLServer())
  1119. {
  1120. TCHAR szFind[32];
  1121. prs->MoveFirst();
  1122. wsprintf(szFind, _T("idObj = %d"), id);
  1123. hr = prs->Find(_bstr_t(szFind), 0, ADODB::adSearchForward);
  1124. }
  1125. else
  1126. {
  1127. _variant_t var = id;
  1128. prs->Seek(var, ADODB::adSeekFirstEQ);
  1129. }
  1130. if (prs->EndOfFile)
  1131. return E_INVALIDARG;
  1132. if (idType != 0)
  1133. {
  1134. long idType2 = prs->Fields->Item["idType"]->Value;
  1135. if (idType != idType2)
  1136. return E_INVALIDARG;
  1137. }
  1138. return m_pdb->CacheObject(id, m_pobjtype, ppobj);
  1139. }
  1140. LEAVE_API
  1141. }
  1142. STDMETHODIMP CObjects::get_ItemWithKey(VARIANT varKey, IUnknown **ppobj)
  1143. {
  1144. ENTER_API
  1145. {
  1146. HRESULT hr;
  1147. _bstr_t bstrKey;
  1148. try
  1149. {
  1150. bstrKey = varKey;
  1151. }
  1152. catch (_com_error)
  1153. {
  1154. return E_INVALIDARG;
  1155. }
  1156. ADODB::_RecordsetPtr prs;
  1157. hr = GetRS(&prs);
  1158. if (FAILED(hr))
  1159. return hr;
  1160. _bstr_t bstrFind;
  1161. switch (varKey.vt)
  1162. {
  1163. default:
  1164. return E_INVALIDARG;
  1165. case VT_I2:
  1166. case VT_I4:
  1167. bstrFind = _T("lValue = ") + bstrKey;
  1168. break;
  1169. case VT_R4:
  1170. case VT_R8:
  1171. case VT_DATE:
  1172. bstrFind = _T("fValue = ") + bstrKey;
  1173. break;
  1174. case VT_BSTR_BLOB:
  1175. case VT_BSTR:
  1176. // UNDONE: Handle embedded quotes in bstrKey
  1177. bstrFind = _T("sValue = '") + bstrKey + _T("'");
  1178. break;
  1179. }
  1180. DeclarePerfTimer("get_ItemWithKey");
  1181. PerfTimerReset();
  1182. hr = prs->MoveFirst();
  1183. hr = prs->Find(bstrFind, 0, ADODB::adSearchForward);
  1184. if (FAILED(hr))
  1185. return hr;
  1186. PerfTimerDump("MoveFirst + Find");
  1187. if (prs->EndOfFile)
  1188. return E_INVALIDARG;
  1189. long idType = prs->Fields->Item["idType"]->Value;
  1190. long idObj = prs->Fields->Item["id"]->Value;
  1191. return m_pdb->CacheObject(idObj, idType, ppobj);
  1192. }
  1193. LEAVE_API
  1194. }
  1195. STDMETHODIMP CObjects::get_ItemsWithType(BSTR bstrCLSID, IObjects **ppobjs)
  1196. {
  1197. ENTER_API
  1198. {
  1199. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1200. CObjectType *pobjtype;
  1201. m_pdb->get_ObjectType(bstrCLSID, &pobjtype);
  1202. return get_ItemsWithType(pobjtype, ppobjs);
  1203. }
  1204. LEAVE_API
  1205. }
  1206. HRESULT CObjects::get_ItemsWithType(CObjectType *pobjtype, IObjects **ppobjs)
  1207. {
  1208. ENTER_API
  1209. {
  1210. ValidateInPtr<CObjectType>(pobjtype);
  1211. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1212. // Objects can't have two types... if this collection is for objects of
  1213. // a specific type, then it contains no objects of the requested type.
  1214. _ASSERTE(m_pobjtype != NULL);
  1215. long idType;
  1216. m_pobjtype->get_ID(&idType);
  1217. if (idType != 0)
  1218. return E_INVALIDARG;
  1219. // If there's no related object clause nor a property condition then
  1220. // we're after the base collection of objects of the specified type.
  1221. // That's cached in by m_pdb.
  1222. if ((m_pobjRelated == NULL) && (m_ppropcond == NULL))
  1223. return m_pdb->get_ObjectsWithType(pobjtype, ppobjs);
  1224. HRESULT hr = pobjtype->get_NewCollection(ppobjs);
  1225. if (FAILED(hr))
  1226. return hr;
  1227. CComQIPtr<CObjects> pobjs(*ppobjs);
  1228. pobjs->Copy(this);
  1229. return S_OK;
  1230. }
  1231. LEAVE_API
  1232. }
  1233. STDMETHODIMP CObjects::get_ItemsWithMetaProperty(IMetaProperty *pprop, IObjects **ppobjs)
  1234. {
  1235. ENTER_API
  1236. {
  1237. ValidateInPtr<IMetaProperty>(pprop);
  1238. ValidateOutPtr<IObjects>(ppobjs);
  1239. HRESULT hr;
  1240. CComPtr<IMetaPropertyCondition> pcond;
  1241. hr = pprop->get_Cond(_bstr_t("="), &pcond);
  1242. if (FAILED(hr))
  1243. return hr;
  1244. hr = get_ItemsWithMetaPropertyCond(pcond, ppobjs);
  1245. return hr;
  1246. }
  1247. LEAVE_API
  1248. }
  1249. STDMETHODIMP CObjects::UnreferencedItems(IObjects **ppobjs)
  1250. {
  1251. ENTER_API
  1252. {
  1253. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1254. HRESULT hr;
  1255. hr = Clone(ppobjs);
  1256. if (FAILED(hr))
  1257. return hr;
  1258. CComQIPtr<CObjects> pobjs(*ppobjs);
  1259. if (FAILED(hr))
  1260. return hr;
  1261. hr = pobjs->put_OnlyUnreferenced(TRUE);
  1262. return hr;
  1263. }
  1264. LEAVE_API
  1265. }
  1266. STDMETHODIMP CObjects::get_ItemsWithMetaPropertyCond(IMetaPropertyCondition *ppropcond, IObjects **ppobjs)
  1267. {
  1268. ENTER_API
  1269. {
  1270. ValidateInPtr<IMetaPropertyCondition>(ppropcond);
  1271. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1272. HRESULT hr;
  1273. CComPtr<IMetaPropertyCondition> ppropcond2;
  1274. if (m_ppropcond != NULL)
  1275. {
  1276. hr = m_ppropcond->get_And(ppropcond, &ppropcond2);
  1277. if (FAILED(hr))
  1278. return hr;
  1279. ppropcond = ppropcond2;
  1280. }
  1281. hr = Clone(ppobjs);
  1282. CComQIPtr<CObjects> pobjs(*ppobjs);
  1283. pobjs->put_MetaPropertyCond(ppropcond);
  1284. }
  1285. LEAVE_API
  1286. }
  1287. STDMETHODIMP CObjects::get_ItemsByKey(IMetaPropertyType *pproptype,
  1288. IGuideDataProvider *pprovider, long idLang, long vt, IObjects * *ppobjs)
  1289. {
  1290. ENTER_API
  1291. {
  1292. ValidateInPtr<IMetaPropertyType>(pproptype);
  1293. ValidateInPtr_NULL_OK<IGuideDataProvider>(pprovider);
  1294. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1295. switch (vt)
  1296. {
  1297. default:
  1298. return E_INVALIDARG;
  1299. case VT_I2:
  1300. case VT_I4:
  1301. case VT_R4:
  1302. case VT_R8:
  1303. case VT_DATE:
  1304. case VT_BSTR_BLOB:
  1305. case VT_BSTR:
  1306. break;
  1307. }
  1308. HRESULT hr = Clone(ppobjs);
  1309. if (FAILED(hr))
  1310. return hr;
  1311. CComQIPtr<CObjects> pobjs(*ppobjs);
  1312. pobjs->put_Key(pproptype, pprovider, idLang, vt);
  1313. return hr;
  1314. }
  1315. LEAVE_API
  1316. }
  1317. STDMETHODIMP CObjects::Reset()
  1318. {
  1319. m_cItems = -1; // UNDONE: Is "m_cItems++;" sufficient?
  1320. if (m_prs != NULL)
  1321. m_prs.Release();
  1322. return S_OK;
  1323. }
  1324. STDMETHODIMP CObjects::get_AddNew(IUnknown **ppobj)
  1325. {
  1326. ENTER_API
  1327. {
  1328. ValidateOutPtr<IUnknown>(ppobj, NULL);
  1329. HRESULT hr;
  1330. // Force the collection to be requeried so the new item shows up.
  1331. Reset();
  1332. if ((m_pobjRelated == NULL) && (m_ppropcond == NULL))
  1333. {
  1334. // This collection is the base collection for this type.
  1335. hr = m_pobjtype->get_New(ppobj);
  1336. if (FAILED(hr))
  1337. return hr;
  1338. //UNDONE: Too many AddRef()s?
  1339. if (*ppobj != NULL)
  1340. (*ppobj)->AddRef();
  1341. // Only fire the ItemAdded() event when adding to the base collection.
  1342. if (SUCCEEDED(hr))
  1343. {
  1344. long id;
  1345. long idType;
  1346. m_pdb->get_IdOf(*ppobj, &id);
  1347. m_pobjtype->get_ID(&idType);
  1348. m_pdb->Broadcast_ItemAdded(id, idType);
  1349. }
  1350. }
  1351. else
  1352. {
  1353. hr = get_AddNewAt(-1, ppobj);
  1354. }
  1355. return hr;
  1356. }
  1357. LEAVE_API
  1358. }
  1359. STDMETHODIMP CObjects::get_AddNewAt(long index, IUnknown **ppobj)
  1360. {
  1361. ENTER_API
  1362. {
  1363. ValidateOutPtr<IUnknown>(ppobj, NULL);
  1364. HRESULT hr = E_INVALIDARG;
  1365. // Can't add items if there is no object type
  1366. _ASSERTE(m_pobjtype != NULL);
  1367. // Force the collection to be requeried so the new item shows up.
  1368. Reset();
  1369. if ((m_pobjRelated == NULL) && (m_ppropcond == NULL) && (index != -1))
  1370. {
  1371. hr = get_AddNew(ppobj);
  1372. }
  1373. else if (m_pobjRelated != NULL)
  1374. {
  1375. // This is a subset of the base collection of items of this type.
  1376. // It contains only the items of the specified type which are
  1377. // related to m_pobjRelated by the relationship m_idRel.
  1378. // We must first create a new item in the base collection
  1379. // and then add it to the subset.
  1380. CComPtr<IObjects> pobjs;
  1381. hr = m_pdb->get_ObjectsWithType(m_pobjtype, &pobjs);
  1382. if (FAILED(hr))
  1383. return hr;
  1384. hr = pobjs->get_AddNew(ppobj);
  1385. if (FAILED(hr))
  1386. return hr;
  1387. hr = AddAt(*ppobj, index);
  1388. }
  1389. return hr;
  1390. }
  1391. LEAVE_API
  1392. }
  1393. STDMETHODIMP CObjects::AddAt(IUnknown *pobj, long index)
  1394. {
  1395. ENTER_API
  1396. {
  1397. ValidateInPtr<IUnknown>(pobj);
  1398. HRESULT hr;
  1399. // Can't add an item at a particular index if this is not an ordered collection.
  1400. // Order is only expressed for related objects and not for inverse related objects.
  1401. if (m_pobjRelated == NULL || m_fInverse)
  1402. return E_INVALIDARG;
  1403. m_cItems = -1; // UNDONE: Is "m_cItems++;" sufficient?
  1404. if (m_prs != NULL)
  1405. m_prs.Release();
  1406. long idObjRelated;
  1407. m_pdb->get_IdOf(m_pobjRelated, &idObjRelated);
  1408. long idObj;
  1409. m_pdb->get_IdOf(pobj, &idObj);
  1410. hr = AddRelationshipAt(idObjRelated, m_idRel, index, idObj);
  1411. return hr;
  1412. }
  1413. LEAVE_API
  1414. }
  1415. STDMETHODIMP CObjects::Remove(VARIANT varIndex)
  1416. {
  1417. ENTER_API
  1418. {
  1419. HRESULT hr;
  1420. CComPtr<IUnknown> pobj;
  1421. switch (varIndex.vt)
  1422. {
  1423. case VT_UNKNOWN:
  1424. case VT_DISPATCH:
  1425. {
  1426. hr = varIndex.punkVal->QueryInterface(__uuidof(IUnknown), (void **) &pobj);
  1427. if (FAILED(hr))
  1428. return hr;
  1429. }
  1430. break;
  1431. default:
  1432. {
  1433. hr = get_Item(varIndex, &pobj);
  1434. if (FAILED(hr))
  1435. return hr;
  1436. }
  1437. }
  1438. return (m_pobjRelated != NULL) ? RemoveFromRelationship(pobj) : Remove(pobj);
  1439. }
  1440. LEAVE_API
  1441. }
  1442. HRESULT CObjects::Remove(IUnknown *pobjRemove)
  1443. {
  1444. HRESULT hr;
  1445. CComQIPtr<CObject> pobj(pobjRemove);
  1446. long idObj;
  1447. CObjectType *pobjtype;
  1448. pobj->get_Type(&pobjtype);
  1449. long idtype;
  1450. m_pobjtype->get_ID(&idtype);
  1451. if ((idtype != NULL) && (pobjtype != m_pobjtype))
  1452. return E_INVALIDARG;
  1453. pobj->get_ID(&idObj);
  1454. m_pdb->UncacheObject(idObj);
  1455. // Delete all the properties
  1456. TCHAR sz[1024];
  1457. wsprintf(sz, _T("DELETE * FROM Properties WHERE idObj=%d;"), idObj);
  1458. hr = m_pdb->Execute(_bstr_t(sz));
  1459. // Remove any properties pointing to the deleted object
  1460. wsprintf(sz,
  1461. _T("DELETE * FROM Properties WHERE (ValueType = 13) AND (lValue = %d);"), idObj);
  1462. hr = m_pdb->Execute(_bstr_t(sz));
  1463. // Remove the object from all relationships
  1464. wsprintf(sz, _T("DELETE * FROM ObjectRelationships")
  1465. _T(" WHERE (idObj1=%d) OR (idObj2=%d);"), idObj, idObj);
  1466. hr = m_pdb->Execute(_bstr_t(sz));
  1467. // Remove the object.
  1468. wsprintf(sz, _T("DELETE * FROM Objects WHERE id=%d;"), idObj);
  1469. hr = m_pdb->Execute(_bstr_t(sz));
  1470. if (SUCCEEDED(hr))
  1471. {
  1472. long idType = 0;
  1473. pobjtype->get_ID(&idType);
  1474. m_pdb->Broadcast_ItemRemoved(idObj, idType);
  1475. }
  1476. Reset();
  1477. return hr;
  1478. }
  1479. STDMETHODIMP CObjects::RemoveAll()
  1480. {
  1481. ENTER_API
  1482. {
  1483. HRESULT hr;
  1484. _bstr_t bstrQuery;
  1485. boolean fTransacted = TRUE;
  1486. hr = GetQuery(Delete, &bstrQuery);
  1487. if (FAILED(hr))
  1488. return hr;
  1489. hr = m_pdb->BeginTrans();
  1490. if (FAILED(hr))
  1491. fTransacted = FALSE;
  1492. hr = m_pdb->Execute(bstrQuery);
  1493. if (FAILED(hr))
  1494. goto Cleanup;
  1495. // Delete all the dangling properties
  1496. hr = m_pdb->Execute( _bstr_t("DELETE * FROM Properties"
  1497. " WHERE idObj NOT IN"
  1498. " (SELECT Objects.id FROM Objects);") );
  1499. if (FAILED(hr))
  1500. goto Cleanup;
  1501. // Remove the object from all relationships
  1502. hr = m_pdb->Execute( _bstr_t("DELETE * FROM ObjectRelationships"
  1503. " WHERE idObj1 NOT IN"
  1504. " (SELECT Objects.id FROM Objects);") );
  1505. if (FAILED(hr))
  1506. goto Cleanup;
  1507. if (!m_fOnlyUnreferenced)
  1508. {
  1509. // If this collection only contains unreferenced objects then
  1510. // there is no need to execute the following.
  1511. // Otherwise...
  1512. // Remove dangling object references.
  1513. hr = m_pdb->Execute( _bstr_t("DELETE * FROM ObjectRelationships"
  1514. " WHERE idObj2 NOT IN"
  1515. " (SELECT Objects.id FROM Objects);") );
  1516. if (FAILED(hr))
  1517. goto Cleanup;
  1518. // Remove any properties pointing to deleted objects
  1519. hr = m_pdb->Execute( _bstr_t("DELETE * FROM Properties"
  1520. " WHERE (ValueType = 13) AND (lValue NOT IN"
  1521. " (SELECT Objects.id FROM Objects));") );
  1522. }
  1523. Cleanup:
  1524. if (fTransacted)
  1525. {
  1526. if (FAILED(hr))
  1527. m_pdb->RollbackTrans();
  1528. else
  1529. hr = m_pdb->CommitTrans();
  1530. }
  1531. if (SUCCEEDED(hr))
  1532. {
  1533. Reset();
  1534. m_pdb->PurgeCachedObjects();
  1535. }
  1536. return hr;
  1537. }
  1538. LEAVE_API
  1539. }
  1540. HRESULT CObjects::RemoveFromRelationship(IUnknown *pobjRemove)
  1541. {
  1542. long idObj1;
  1543. long idObj2;
  1544. m_pdb->get_IdOf(m_pobjRelated, &idObj1);
  1545. m_pdb->get_IdOf(pobjRemove, &idObj2);
  1546. if (m_fInverse)
  1547. swap(idObj1, idObj2);
  1548. TCHAR sz[1024];
  1549. wsprintf(sz, _T("DELETE *")
  1550. _T(" FROM ObjectRelationships")
  1551. _T(" WHERE ((ObjectRelationships.idObj1=%d)")
  1552. _T(" AND (ObjectRelationships.idRel=%d)")
  1553. _T(" AND (ObjectRelationships.idObj2=%d));"),
  1554. idObj1, m_idRel, idObj2);
  1555. m_cItems = -1; // UNDONE: Is "m_cItems++;" sufficient?
  1556. if (m_prs != NULL)
  1557. m_prs.Release();
  1558. return m_pdb->Execute(_bstr_t(sz));
  1559. }
  1560. HRESULT CObjects::get_ItemsRelatedToBy(IUnknown *pobj, IMetaPropertyType *pproptype,
  1561. boolean fInverse, IObjects **ppobjs)
  1562. {
  1563. // Can't handle multiple relations yet.
  1564. if (m_pobjRelated != NULL)
  1565. return E_INVALIDARG;
  1566. CComQIPtr<CMetaPropertyType> pproptypeT(pproptype);
  1567. if (pproptypeT == NULL)
  1568. return E_INVALIDARG;
  1569. long idRel = pproptypeT->GetID();
  1570. HRESULT hr;
  1571. hr = Clone(ppobjs);
  1572. if (FAILED(hr))
  1573. return hr;
  1574. CComQIPtr<CObjects> pobjs(*ppobjs);
  1575. if (FAILED(hr))
  1576. return hr;
  1577. hr = pobjs->InitRelation(pobj, idRel, fInverse);
  1578. return hr;
  1579. }
  1580. STDMETHODIMP CObjects::get_ItemsRelatedToBy(IUnknown *pobj, IMetaPropertyType *pproptype,
  1581. IObjects **ppobjs)
  1582. {
  1583. ENTER_API
  1584. {
  1585. ValidateInPtr<IUnknown>(pobj);
  1586. ValidateInPtr<IMetaPropertyType>(pproptype);
  1587. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1588. return get_ItemsRelatedToBy(pobj, pproptype, FALSE, ppobjs);
  1589. }
  1590. LEAVE_API
  1591. }
  1592. STDMETHODIMP CObjects::get_ItemsInverseRelatedToBy(IUnknown *pobj, IMetaPropertyType *pproptype,
  1593. IObjects **ppobjs)
  1594. {
  1595. ENTER_API
  1596. {
  1597. ValidateInPtr<IUnknown>(pobj);
  1598. ValidateInPtr<IMetaPropertyType>(pproptype);
  1599. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1600. return get_ItemsRelatedToBy(pobj, pproptype, TRUE, ppobjs);
  1601. }
  1602. LEAVE_API
  1603. }
  1604. STDMETHODIMP CObjects::get_ItemsInTimeRange(DATE dt1, DATE dt2, IObjects **ppobjs)
  1605. {
  1606. ENTER_API
  1607. {
  1608. ValidateOutPtr<IObjects>(ppobjs, NULL);
  1609. HRESULT hr;
  1610. // Ensure that dt1 is less than or equal to dt2
  1611. if (dt1 > dt2)
  1612. swap(dt1, dt2);
  1613. _variant_t varT1(dt1, VT_DATE);
  1614. _variant_t varT2(dt2, VT_DATE);
  1615. CComPtr<IMetaPropertyType> pproptypeStartTime = m_pdb->StartMetaPropertyType();
  1616. CComPtr<IMetaPropertyType> pproptypeEndTime = m_pdb->EndMetaPropertyType();
  1617. CComPtr<IMetaPropertyCondition> ppropcond1;
  1618. CComPtr<IMetaPropertyCondition> ppropcond2;
  1619. _bstr_t bstrLT(_T("<"));
  1620. _bstr_t bstrGT(_T(">"));
  1621. _bstr_t bstrLE(_T("<="));
  1622. if (dt1 < dt2)
  1623. {
  1624. // Look for StartTime > dt2 & EndTime < dt1
  1625. hr = pproptypeStartTime->get_Cond(bstrLT, 0, varT2, &ppropcond1);
  1626. if (FAILED(hr))
  1627. return hr;
  1628. hr = pproptypeEndTime->get_Cond(bstrGT, 0, varT1, &ppropcond2);
  1629. if (FAILED(hr))
  1630. return hr;
  1631. }
  1632. else
  1633. {
  1634. // Look for StartTime <= dt1 & EndTime > dt1
  1635. hr = pproptypeStartTime->get_Cond(bstrLE, 0, varT1, &ppropcond1);
  1636. if (FAILED(hr))
  1637. return hr;
  1638. hr = pproptypeEndTime->get_Cond(bstrGT, 0, varT1, &ppropcond2);
  1639. if (FAILED(hr))
  1640. return hr;
  1641. }
  1642. CComPtr<IMetaPropertyCondition> ppropcond;
  1643. hr = ppropcond1->get_And(ppropcond2, &ppropcond);
  1644. if (FAILED(hr))
  1645. return hr;
  1646. return get_ItemsWithMetaPropertyCond(ppropcond, ppobjs);
  1647. }
  1648. LEAVE_API
  1649. }
  1650. IMetaProperty * CObjectPropertyBag::GetProp(_bstr_t bstrPropName, boolean fCreate)
  1651. {
  1652. HRESULT hr;
  1653. if (m_pproptypes == NULL)
  1654. return NULL;
  1655. CComPtr<IMetaPropertyType> pproptype;
  1656. _variant_t varNil;
  1657. hr = m_pproptypes->get_AddNew(0, bstrPropName, &pproptype);
  1658. CComPtr<IMetaProperty> pprop;
  1659. hr = m_pprops->get_ItemWithTypeProviderLang(pproptype, NULL, 0, &pprop);
  1660. if (FAILED(hr))
  1661. {
  1662. if (!fCreate)
  1663. return NULL;
  1664. CComQIPtr<CMetaProperties> ppropsT(m_pprops);
  1665. hr = ppropsT->get_AddNew(pproptype, NULL, NULL, varNil, &pprop);
  1666. if (FAILED(hr))
  1667. return NULL;
  1668. }
  1669. return pprop.Detach();
  1670. }
  1671. STDMETHODIMP CObjectPropertyBag::Read(LPCOLESTR pszPropName, VARIANT *pvar, IErrorLog *pErrorLog)
  1672. {
  1673. ENTER_API
  1674. {
  1675. ValidateOut(pvar);
  1676. CComPtr<IMetaProperty> pprop;
  1677. pprop.Attach(GetProp(_bstr_t(pszPropName), FALSE));
  1678. if (pprop == NULL)
  1679. return E_INVALIDARG;
  1680. return pprop->get_Value(pvar);
  1681. }
  1682. LEAVE_API
  1683. }
  1684. STDMETHODIMP CObjectPropertyBag::Write(LPCOLESTR pszPropName, VARIANT *pvar)
  1685. {
  1686. ENTER_API
  1687. {
  1688. CComPtr<IMetaProperty> pprop;
  1689. pprop.Attach(GetProp(_bstr_t(pszPropName), TRUE));
  1690. if (pprop == NULL)
  1691. return E_FAIL;
  1692. CComQIPtr<CMetaProperty> ppropT(pprop);
  1693. return ppropT->PutValue(*pvar);
  1694. }
  1695. LEAVE_API
  1696. }
  1697. void CObjects::Dump()
  1698. {
  1699. #ifdef _DEBUG
  1700. TCHAR sz[256];
  1701. wsprintf(sz, _T("CObjects::Dump(%x)\n"), (long) this);
  1702. OutputDebugString(sz);
  1703. long idType;
  1704. m_pobjtype->get_ID(&idType);
  1705. wsprintf(sz, _T(" type == %d\n"), idType);
  1706. OutputDebugString(sz);
  1707. wsprintf(sz, _T(" m_cItems == %d\n"), m_cItems);
  1708. OutputDebugString(sz);
  1709. if (m_idPropTypeKey != 0)
  1710. {
  1711. wsprintf(sz, _T(" key == %d:%d:%d:%d\n"), m_idPropTypeKey,
  1712. m_idProviderKey, m_idLangKey, m_vtKey);
  1713. OutputDebugString(sz);
  1714. }
  1715. if (m_prs != NULL)
  1716. {
  1717. wsprintf(sz, _T(" m_prs == %x\n"), m_prs);
  1718. OutputDebugString(sz);
  1719. }
  1720. if (m_pobjRelated != NULL)
  1721. {
  1722. long id;
  1723. m_pdb->get_IdOf(m_pobjRelated,&id);
  1724. wsprintf(sz, _T(" %s to %d by %d\n"),
  1725. m_fInverse ? _T("Inverse Related") : _T("Related"),
  1726. id, m_idRel);
  1727. OutputDebugString(sz);
  1728. }
  1729. if (m_fOnlyUnreferenced)
  1730. {
  1731. OutputDebugString(_T(" Only Unreferenced\n"));
  1732. }
  1733. _bstr_t bstr;
  1734. GetQuery(Select, &bstr);
  1735. OutputDebugString(_T(" Query: "));
  1736. OutputDebugString(bstr);
  1737. OutputDebugString(_T("\n"));
  1738. #endif
  1739. }
  1740. #if SUPPORT_PROPBAG2
  1741. // IPropertyBag2 interface
  1742. STDMETHODIMP CObjectPropertyBag::CountProperties(long *plCount)
  1743. {
  1744. *plCount = 0;
  1745. return S_OK;
  1746. }
  1747. STDMETHODIMP CObjectPropertyBag::GetPropertyInfo(ULONG iProp, ULONG cProps, PROPBAG2 *ppropbag2, ULONG *pcProps)
  1748. {
  1749. return E_NOTIMPL;
  1750. }
  1751. STDMETHODIMP CObjectPropertyBag::LoadObject(LPCOLESTR pstrName, DWORD dwHint, IUnknown *punk, IErrorLog *perrlog)
  1752. {
  1753. return E_NOTIMPL;
  1754. }
  1755. STDMETHODIMP CObjectPropertyBag::Read(ULONG cProps, PROPBAG2 *ppropbag2, IErrorLog *perrlog, VARIANT *rgvar, HRESULT *phr)
  1756. {
  1757. return E_NOTIMPL;
  1758. }
  1759. STDMETHODIMP CObjectPropertyBag::Write(ULONG cProps, PROPBAG2 *ppropbag2, VARIANT *rgvar)
  1760. {
  1761. return E_NOTIMPL;
  1762. }
  1763. #endif