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.

1715 lines
40 KiB

  1. /*===================================================================
  2. Microsoft Denali
  3. Microsoft Confidential.
  4. Copyright 1997 Microsoft Corporation. All Rights Reserved.
  5. Component: MetaUtil object
  6. File: PropCol.cpp
  7. Owner: t-BrianM
  8. This file contains implementation of the property collection and
  9. property object.
  10. ===================================================================*/
  11. #include "stdafx.h"
  12. #include "MetaUtil.h"
  13. #include "MUtilObj.h"
  14. #include "PropCol.h"
  15. /*------------------------------------------------------------------
  16. * C P r o p e r t y C o l l e c t i o n
  17. */
  18. /*===================================================================
  19. CPropertyCollection::CPropertyCollection
  20. Constructor
  21. Parameters:
  22. None
  23. Returns:
  24. Nothing
  25. ===================================================================*/
  26. CPropertyCollection::CPropertyCollection() : m_pCSchemaTable(NULL),
  27. m_tszKey(NULL)
  28. {
  29. }
  30. /*===================================================================
  31. CPropertyCollection::Init
  32. Constructor
  33. Parameters:
  34. pIMeta ATL Smart pointer to the metabase
  35. tszKey Name of key to enumerate properties of
  36. Returns:
  37. E_OUTOFMEMORY if allocation fails
  38. S_OK on success
  39. ===================================================================*/
  40. HRESULT CPropertyCollection::Init(const CComPtr<IMSAdminBase> &pIMeta,
  41. CMetaSchemaTable *pCSchemaTable,
  42. LPTSTR tszKey)
  43. {
  44. ASSERT(pIMeta.p != NULL);
  45. ASSERT_NULL_OR_STRING(tszKey);
  46. USES_CONVERSION;
  47. HRESULT hr;
  48. m_pIMeta = pIMeta;
  49. m_pCSchemaTable = pCSchemaTable;
  50. m_pCSchemaTable->AddRef();
  51. // Copy tszKey to m_tszKey
  52. if (tszKey == NULL) {
  53. // Key is root
  54. m_tszKey = NULL;
  55. }
  56. else {
  57. // Allocate and copy the passed string to the member string
  58. m_tszKey = new TCHAR[_tcslen(tszKey) + 1];
  59. if (m_tszKey == NULL) {
  60. return ::ReportError(E_OUTOFMEMORY);
  61. }
  62. _tcscpy(m_tszKey, tszKey);
  63. CannonizeKey(m_tszKey);
  64. // Make sure the key exists by opening and closing it
  65. METADATA_HANDLE hMDKey;
  66. hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  67. T2W(m_tszKey),
  68. METADATA_PERMISSION_READ,
  69. MUTIL_OPEN_KEY_TIMEOUT,
  70. &hMDKey);
  71. if (FAILED(hr)) {
  72. return ::ReportError(hr);
  73. }
  74. m_pIMeta->CloseKey(hMDKey);
  75. }
  76. return S_OK;
  77. }
  78. /*===================================================================
  79. CPropertyCollection::~CPropertyCollection
  80. Destructor
  81. Parameters:
  82. None
  83. Returns:
  84. Nothing
  85. ===================================================================*/
  86. CPropertyCollection::~CPropertyCollection()
  87. {
  88. m_pCSchemaTable->Release();
  89. if (m_tszKey != NULL) {
  90. delete m_tszKey;
  91. }
  92. }
  93. /*===================================================================
  94. CPropertyCollection::InterfaceSupportsErrorInfo
  95. Standard ATL implementation
  96. ===================================================================*/
  97. STDMETHODIMP CPropertyCollection::InterfaceSupportsErrorInfo(REFIID riid)
  98. {
  99. static const IID* arr[] =
  100. {
  101. &IID_IPropertyCollection,
  102. };
  103. for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
  104. {
  105. if (InlineIsEqualGUID(*arr[i],riid))
  106. return S_OK;
  107. }
  108. return S_FALSE;
  109. }
  110. /*===================================================================
  111. CPropertyCollection::get_Count
  112. Get method for Count property. Counts the number of properties for
  113. this key.
  114. Parameters:
  115. plReturn [out, retval] Value to return to client.
  116. Returns:
  117. E_INVALIDARG if plReturn == NULL
  118. S_OK on success
  119. Notes:
  120. Actually counts all of the properties. Do not call in a loop!
  121. ===================================================================*/
  122. STDMETHODIMP CPropertyCollection::get_Count(long * plReturn)
  123. {
  124. TRACE0("MetaUtil: CPropertyCollection::get_Count\n");
  125. ASSERT_NULL_OR_POINTER(plReturn, long);
  126. if (plReturn == NULL) {
  127. return ::ReportError(E_INVALIDARG);
  128. }
  129. USES_CONVERSION;
  130. HRESULT hr;
  131. METADATA_RECORD mdr;
  132. BYTE *pbData;
  133. DWORD dwDataLen;
  134. DWORD dwReqDataLen;
  135. dwDataLen = 1024;
  136. pbData = new BYTE[dwDataLen];
  137. if (pbData == NULL) {
  138. return ::ReportError(E_OUTOFMEMORY);
  139. }
  140. *plReturn = 0;
  141. for(;;) { // FOREVER, will return out of loop
  142. // Get a property
  143. mdr.dwMDIdentifier = 0;
  144. mdr.dwMDAttributes = 0;
  145. mdr.dwMDUserType = ALL_METADATA;
  146. mdr.dwMDDataType = ALL_METADATA;
  147. mdr.pbMDData = pbData;
  148. mdr.dwMDDataLen = dwDataLen;
  149. mdr.dwMDDataTag = 0;
  150. hr = m_pIMeta->EnumData(METADATA_MASTER_ROOT_HANDLE,
  151. T2W(m_tszKey),
  152. &mdr,
  153. *plReturn,
  154. &dwReqDataLen);
  155. if (FAILED(hr)) {
  156. if (HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) {
  157. // Done, cleanup and return the result
  158. delete pbData;
  159. return S_OK;
  160. }
  161. else if (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER) {
  162. // Make a bigger buffer and try again
  163. delete pbData;
  164. dwDataLen = dwReqDataLen;
  165. pbData = new BYTE[dwDataLen];
  166. if (pbData == NULL) {
  167. return ::ReportError(E_OUTOFMEMORY);
  168. }
  169. }
  170. else {
  171. delete pbData;
  172. return ::ReportError(hr);
  173. }
  174. }
  175. else { // SUCCEEDED(hr)
  176. // Count it
  177. (*plReturn)++;
  178. }
  179. }
  180. }
  181. /*===================================================================
  182. CPropertyCollection::get_Item
  183. Get method for Item property. Returns a key given its index.
  184. Parameters:
  185. varId [in] 1 based index or Name of the property to get
  186. ppIReturn [out, retval] Interface for the property object
  187. Returns:
  188. E_INVALIDARG if ppIReturn == NULL or lIndex <= 0
  189. E_OUTOFMEMORY if allocation fails
  190. S_OK on success
  191. ===================================================================*/
  192. STDMETHODIMP CPropertyCollection::get_Item(long lIndex,
  193. LPDISPATCH * ppIReturn)
  194. {
  195. TRACE0("MetaUtil: CPropertyCollection::get_Item\n");
  196. ASSERT_NULL_OR_POINTER(ppIReturn, LPDISPATCH);
  197. if ((ppIReturn == NULL) || (lIndex <= 0)) {
  198. return ::ReportError(E_INVALIDARG);
  199. }
  200. USES_CONVERSION;
  201. HRESULT hr;
  202. // Get the requested property
  203. METADATA_RECORD mdr;
  204. BYTE *pbData;
  205. DWORD dwReqDataLen;
  206. pbData = new BYTE[1024];
  207. if (pbData == NULL) {
  208. return ::ReportError(E_OUTOFMEMORY);
  209. }
  210. mdr.dwMDIdentifier = 0;
  211. mdr.dwMDAttributes = 0;
  212. mdr.dwMDUserType = ALL_METADATA;
  213. mdr.dwMDDataType = ALL_METADATA;
  214. mdr.pbMDData = pbData;
  215. mdr.dwMDDataLen = 1024;
  216. mdr.dwMDDataTag = 0;
  217. hr = m_pIMeta->EnumData(METADATA_MASTER_ROOT_HANDLE,
  218. T2W(m_tszKey),
  219. &mdr,
  220. lIndex - 1,
  221. &dwReqDataLen);
  222. // If the buffer was too small, try again with a bigger one
  223. if (FAILED(hr) && (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER)) {
  224. delete pbData;
  225. pbData = new BYTE[dwReqDataLen];
  226. if (pbData == NULL) {
  227. return ::ReportError(hr);
  228. }
  229. mdr.dwMDIdentifier = 0;
  230. mdr.dwMDAttributes = 0;
  231. mdr.dwMDUserType = ALL_METADATA;
  232. mdr.dwMDDataType = ALL_METADATA;
  233. mdr.pbMDData = pbData;
  234. mdr.dwMDDataLen = dwReqDataLen;
  235. mdr.dwMDDataTag = 0;
  236. hr = m_pIMeta->EnumData(METADATA_MASTER_ROOT_HANDLE,
  237. T2W(m_tszKey),
  238. &mdr,
  239. lIndex - 1,
  240. &dwReqDataLen);
  241. }
  242. // If we got it create a properties object
  243. if (SUCCEEDED(hr)) {
  244. // Create the property object
  245. CComObject<CProperty> *pObj = NULL;
  246. ATLTRY(pObj = new CComObject<CProperty>);
  247. if (pObj == NULL) {
  248. delete pbData;
  249. return ::ReportError(E_OUTOFMEMORY);
  250. }
  251. hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, &mdr);
  252. if (FAILED(hr)) {
  253. delete pbData;
  254. return ::ReportError(hr);
  255. }
  256. // Set the interface to IDispatch
  257. hr = pObj->QueryInterface(IID_IDispatch, (void **) ppIReturn);
  258. if (FAILED(hr)) {
  259. delete pbData;
  260. return ::ReportError(hr);
  261. }
  262. ASSERT(*ppIReturn != NULL);
  263. }
  264. else { // FAILED(hr)
  265. delete pbData;
  266. return ::ReportError(hr);
  267. }
  268. delete pbData;
  269. return S_OK;
  270. }
  271. /*===================================================================
  272. CPropertyCollection::get__NewEnum
  273. Get method for _NewEnum property. Returns an enumeration object for
  274. the properties.
  275. Parameters:
  276. ppIReturn [out, retval] Interface for the enumeration object
  277. Returns:
  278. E_INVALIDARG if ppIReturn == NULL
  279. E_OUTOFMEMORY if allocation failed
  280. S_OK on success
  281. ===================================================================*/
  282. STDMETHODIMP CPropertyCollection::get__NewEnum(LPUNKNOWN * ppIReturn)
  283. {
  284. TRACE0("MetaUtil: CPropertyCollection::get__NewEnum\n");
  285. ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN);
  286. if (ppIReturn == NULL) {
  287. return ::ReportError(E_INVALIDARG);
  288. }
  289. HRESULT hr;
  290. // Create the property enumeration
  291. CComObject<CPropertyEnum> *pObj = NULL;
  292. ATLTRY(pObj = new CComObject<CPropertyEnum>);
  293. if (pObj == NULL) {
  294. return ::ReportError(E_OUTOFMEMORY);
  295. }
  296. hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, 0);
  297. if (FAILED(hr)) {
  298. return ::ReportError(hr);
  299. }
  300. // Set the interface to IUnknown
  301. hr = pObj->QueryInterface(IID_IUnknown, (void **) ppIReturn);
  302. if (FAILED(hr)) {
  303. return ::ReportError(hr);
  304. }
  305. ASSERT(*ppIReturn != NULL);
  306. return S_OK;
  307. }
  308. /*===================================================================
  309. CPropertyCollection::Get
  310. Get a property object from the base key of the collection.
  311. Parameters:
  312. varId [in] Identifier of property to get. Either the
  313. Id (number) or Name (string).
  314. ppIReturn [out, retval] Interface for the property object
  315. Returns:
  316. E_INVALIDARG if ppIReturn == NULL
  317. S_OK on success
  318. ===================================================================*/
  319. STDMETHODIMP CPropertyCollection::Get(VARIANT varId, IProperty **ppIReturn)
  320. {
  321. TRACE0("MetaUtil: CPropertyCollection::Get\n");
  322. ASSERT_NULL_OR_POINTER(ppIReturn, IProperty *);
  323. if (ppIReturn == NULL) {
  324. return E_INVALIDARG;
  325. }
  326. return ::GetProperty(m_pIMeta, m_pCSchemaTable, m_tszKey, varId, ppIReturn);
  327. }
  328. /*===================================================================
  329. CPropertyCollection::Add
  330. Add a property object to the base key of the collection.
  331. Parameters:
  332. varId [in] Identifier of property to get. Either the
  333. Id (number) or Name (string).
  334. ppIReturn [out, retval] Interface for the property object
  335. Returns:
  336. E_INVALIDARG if ppIReturn == NULL
  337. S_OK on success
  338. ===================================================================*/
  339. STDMETHODIMP CPropertyCollection::Add(VARIANT varId, IProperty **ppIReturn)
  340. {
  341. TRACE0("MetaUtil: CPropertyCollection::Add\n");
  342. ASSERT_NULL_OR_POINTER(ppIReturn, IProperty *);
  343. if (ppIReturn == NULL) {
  344. return E_INVALIDARG;
  345. }
  346. return ::CreateProperty(m_pIMeta, m_pCSchemaTable, m_tszKey, varId, ppIReturn);
  347. }
  348. /*===================================================================
  349. CPropertyCollection::Remove
  350. Remove a property from the base key of the collection.
  351. Parameters:
  352. varId [in] Identifier of property to remove. Either the
  353. Id (number) or Name (string).
  354. Returns:
  355. S_OK on success
  356. ===================================================================*/
  357. STDMETHODIMP CPropertyCollection::Remove(VARIANT varId)
  358. {
  359. TRACE0("MetaUtil: CPropertyCollection::Remove\n");
  360. return ::DeleteProperty(m_pIMeta, m_pCSchemaTable, m_tszKey, varId);
  361. }
  362. /*------------------------------------------------------------------
  363. * C P r o p e r t y E n u m
  364. */
  365. /*===================================================================
  366. CPropertyEnum::CPropertyEnum
  367. Constructor
  368. Parameters:
  369. None
  370. Returns:
  371. Nothing
  372. ===================================================================*/
  373. CPropertyEnum::CPropertyEnum() : m_pCSchemaTable(NULL),
  374. m_tszKey(NULL),
  375. m_iIndex(0)
  376. {
  377. }
  378. /*===================================================================
  379. CPropertyEnum::Init
  380. Constructor
  381. Parameters:
  382. pIMeta ATL Smart pointer to the metabase
  383. tszKey Name of key to enumerate properties of
  384. iIndex Index of next element in enumeration
  385. Returns:
  386. E_OUTOFMEMORY if allocation fails
  387. S_OK on success
  388. ===================================================================*/
  389. HRESULT CPropertyEnum::Init(const CComPtr<IMSAdminBase> &pIMeta,
  390. CMetaSchemaTable *pCSchemaTable,
  391. LPCTSTR tszKey,
  392. int iIndex)
  393. {
  394. ASSERT(pIMeta.p != NULL);
  395. ASSERT_NULL_OR_STRING(tszKey);
  396. ASSERT(iIndex >= 0);
  397. m_pIMeta = pIMeta;
  398. m_pCSchemaTable = pCSchemaTable;
  399. m_pCSchemaTable->AddRef();
  400. // Copy m_tszKey
  401. if (tszKey == NULL) {
  402. // Key is root
  403. m_tszKey = NULL;
  404. }
  405. else {
  406. // Allocate and copy the passed string to the member string
  407. m_tszKey = new TCHAR[_tcslen(tszKey) + 1];
  408. if (m_tszKey == NULL) {
  409. return ::ReportError(E_OUTOFMEMORY);
  410. }
  411. _tcscpy(m_tszKey, tszKey);
  412. CannonizeKey(m_tszKey);
  413. }
  414. m_iIndex = iIndex;
  415. return S_OK;
  416. }
  417. /*===================================================================
  418. CPropertyEnum::~CPropertyEnum
  419. Destructor
  420. Parameters:
  421. None
  422. Returns:
  423. Nothing
  424. ===================================================================*/
  425. CPropertyEnum::~CPropertyEnum()
  426. {
  427. m_pCSchemaTable->Release();
  428. if (m_tszKey != NULL) {
  429. delete m_tszKey;
  430. }
  431. }
  432. /*===================================================================
  433. CPropertyEnum::Next
  434. Gets the next n items from the enumberation.
  435. Parameters:
  436. ulNumToGet [in] Number of elements to get
  437. rgvarDest [out] Array to put them in
  438. pulNumGot [out] If not NULL, number of elements rgvarDest got
  439. Returns:
  440. E_INVALIDARG if rgvarDest == NULL
  441. E_OUTOFMEMORY if allocation failed
  442. S_OK if outputs ulNumToGet items
  443. S_FALSE if outputs less than ulNumToGet items
  444. ===================================================================*/
  445. STDMETHODIMP CPropertyEnum::Next(unsigned long ulNumToGet,
  446. VARIANT FAR* rgvarDest,
  447. unsigned long FAR* pulNumGot)
  448. {
  449. TRACE0("MetaUtil: CPropertyEnum::Next\n");
  450. ASSERT_NULL_OR_POINTER(pulNumGot, unsigned long);
  451. // Make sure the array is big enough and we can write to it
  452. ASSERT((rgvarDest == NULL) || IsValidAddress(rgvarDest, ulNumToGet * sizeof(VARIANT), TRUE));
  453. if (rgvarDest == NULL) {
  454. return ::ReportError(E_INVALIDARG);
  455. }
  456. USES_CONVERSION;
  457. HRESULT hr;
  458. METADATA_RECORD mdr;
  459. BYTE *pbData;
  460. DWORD dwDataLen;
  461. DWORD dwReqDataLen;
  462. unsigned int uiDestIndex;
  463. IDispatch *pIDispatch;
  464. dwDataLen = 1024;
  465. pbData = new BYTE[dwDataLen];
  466. if (pbData == NULL) {
  467. return ::ReportError(E_OUTOFMEMORY);
  468. }
  469. // For each property to get
  470. uiDestIndex = 0;
  471. while (uiDestIndex < ulNumToGet) {
  472. // Get a property
  473. mdr.dwMDIdentifier = 0;
  474. mdr.dwMDAttributes = 0;
  475. mdr.dwMDUserType = ALL_METADATA;
  476. mdr.dwMDDataType = ALL_METADATA;
  477. mdr.pbMDData = pbData;
  478. mdr.dwMDDataLen = dwDataLen;
  479. mdr.dwMDDataTag = 0;
  480. hr = m_pIMeta->EnumData(METADATA_MASTER_ROOT_HANDLE,
  481. T2W(m_tszKey),
  482. &mdr,
  483. m_iIndex,
  484. &dwReqDataLen);
  485. if (FAILED(hr)) {
  486. if (HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) {
  487. // Done, cleanup and return the result
  488. if (pulNumGot != NULL) {
  489. *pulNumGot = uiDestIndex;
  490. }
  491. delete pbData;
  492. return S_FALSE;
  493. }
  494. else if (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER) {
  495. // Try again with a bigger buffer
  496. delete pbData;
  497. dwDataLen = dwReqDataLen;
  498. pbData = new BYTE[dwDataLen];
  499. if (pbData == NULL) {
  500. return ::ReportError(E_OUTOFMEMORY);
  501. }
  502. }
  503. else {
  504. delete pbData;
  505. return ::ReportError(hr);
  506. }
  507. }
  508. else { // SUCCEEDED(hr)
  509. // Create the property object
  510. CComObject<CProperty> *pObj = NULL;
  511. ATLTRY(pObj = new CComObject<CProperty>);
  512. if (pObj == NULL) {
  513. delete pbData;
  514. return ::ReportError(E_OUTOFMEMORY);
  515. }
  516. hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, &mdr);
  517. if (FAILED(hr)) {
  518. delete pbData;
  519. return ::ReportError(hr);
  520. }
  521. // Set the interface to IDispatch
  522. hr = pObj->QueryInterface(IID_IDispatch, (void **) &pIDispatch);
  523. if (FAILED(hr)) {
  524. delete pbData;
  525. return ::ReportError(hr);
  526. }
  527. ASSERT(pIDispatch != NULL);
  528. // Put it in the output array
  529. VariantInit(&(rgvarDest[uiDestIndex]));
  530. rgvarDest[uiDestIndex].vt = VT_DISPATCH;
  531. rgvarDest[uiDestIndex].pdispVal = pIDispatch;
  532. // Next element
  533. m_iIndex++;
  534. uiDestIndex++;
  535. }
  536. }
  537. delete pbData;
  538. if (pulNumGot != NULL) {
  539. *pulNumGot = uiDestIndex;
  540. }
  541. return S_OK;
  542. }
  543. /*===================================================================
  544. CPropertyEnum::Skip
  545. Skips the next n items in an enumeration
  546. Parameters:
  547. ulNumToSkip [in] Number of elements to skip
  548. Returns:
  549. S_OK always
  550. ===================================================================*/
  551. STDMETHODIMP CPropertyEnum::Skip(unsigned long ulNumToSkip)
  552. {
  553. TRACE0("MetaUtil: CPropertyEnum::Skip\n");
  554. m_iIndex += ulNumToSkip;
  555. return S_OK;
  556. }
  557. /*===================================================================
  558. CPropertyEnum::Reset
  559. Rests the enumeration to the first item
  560. Parameters:
  561. None
  562. Returns:
  563. S_OK always
  564. ===================================================================*/
  565. STDMETHODIMP CPropertyEnum::Reset()
  566. {
  567. TRACE0("MetaUtil: CPropertyEnum::Reset\n");
  568. m_iIndex = 0;
  569. return S_OK;
  570. }
  571. /*===================================================================
  572. CPropertyEnum::Clone
  573. Gets an interface pointer to a copy of the enumeration at its
  574. current state.
  575. Parameters:
  576. ppIReturn [out] Pointer to interface for copy
  577. Returns:
  578. E_INVALIDARG if ppIReturn == NULL
  579. E_OUTOFMEMORY if not enough memory to create clone
  580. S_OK on success
  581. ===================================================================*/
  582. STDMETHODIMP CPropertyEnum::Clone(IEnumVARIANT FAR* FAR* ppIReturn)
  583. {
  584. TRACE0("MetaUtil: CPropertyEnum::Clone\n");
  585. ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN);
  586. if (ppIReturn == NULL) {
  587. return ::ReportError(E_INVALIDARG);
  588. }
  589. HRESULT hr;
  590. // Create a copy of the enumeration
  591. CComObject<CPropertyEnum> *pObj = NULL;
  592. ATLTRY(pObj = new CComObject<CPropertyEnum>);
  593. if (pObj == NULL) {
  594. return ::ReportError(E_OUTOFMEMORY);
  595. }
  596. hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, m_iIndex);
  597. if (FAILED(hr)) {
  598. return ::ReportError(hr);
  599. }
  600. // Set the interface to IEnumVARIANT
  601. hr = pObj->QueryInterface(IID_IEnumVARIANT, (void **) ppIReturn);
  602. if (FAILED(hr)) {
  603. return ::ReportError(hr);
  604. }
  605. ASSERT(*ppIReturn != NULL);
  606. return S_OK;
  607. }
  608. /*------------------------------------------------------------------
  609. * C P r o p e r t y
  610. */
  611. /*===================================================================
  612. CProperty::CProperty
  613. Constructor
  614. Parameters:
  615. None
  616. Returns:
  617. Nothing
  618. ===================================================================*/
  619. CProperty::CProperty() : m_pCSchemaTable(NULL),
  620. m_tszKey(NULL),
  621. m_dwId(0),
  622. m_dwAttributes(0),
  623. m_dwUserType(0),
  624. m_dwDataType(0)
  625. {
  626. VariantInit(&m_varData);
  627. }
  628. /*===================================================================
  629. CProperty::Init
  630. Constructor
  631. Parameters:
  632. tszKey Name of key where the property is located
  633. dwId Id of property
  634. bCreate TRUE if this property can be created (does not have to exist)
  635. Returns:
  636. E_OUTOFMEMORY if allocation fails
  637. S_OK on success
  638. ===================================================================*/
  639. HRESULT CProperty::Init(const CComPtr<IMSAdminBase> &pIMeta,
  640. CMetaSchemaTable *pCSchemaTable,
  641. LPCTSTR tszKey,
  642. DWORD dwId,
  643. BOOL bCreate)
  644. {
  645. ASSERT(pIMeta.p != NULL);
  646. ASSERT_NULL_OR_STRING(tszKey);
  647. USES_CONVERSION;
  648. HRESULT hr;
  649. m_pIMeta = pIMeta;
  650. m_pCSchemaTable = pCSchemaTable;
  651. m_pCSchemaTable->AddRef();
  652. // Set the Key and Id members
  653. if (tszKey == NULL) {
  654. m_tszKey = NULL;
  655. }
  656. else {
  657. m_tszKey = new TCHAR[_tcslen(tszKey) + 1];
  658. if (m_tszKey == NULL) {
  659. return ::ReportError(E_OUTOFMEMORY);
  660. }
  661. _tcscpy(m_tszKey, tszKey);
  662. CannonizeKey(m_tszKey);
  663. }
  664. m_dwId = dwId;
  665. // Open the key (to be sure it exists)
  666. METADATA_HANDLE hMDKey;
  667. hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  668. T2W(m_tszKey),
  669. METADATA_PERMISSION_READ,
  670. MUTIL_OPEN_KEY_TIMEOUT,
  671. &hMDKey);
  672. if (FAILED(hr)) {
  673. return ::ReportError(hr);
  674. }
  675. // Get the property
  676. METADATA_RECORD mdr;
  677. BYTE *pbData;
  678. DWORD dwReqLen;
  679. pbData = new BYTE[1024];
  680. if (pbData == NULL) {
  681. m_pIMeta->CloseKey(hMDKey);
  682. return ::ReportError(hr);
  683. }
  684. mdr.dwMDIdentifier = m_dwId;
  685. mdr.dwMDAttributes = 0;
  686. mdr.dwMDUserType = ALL_METADATA;
  687. mdr.dwMDDataType = ALL_METADATA;
  688. mdr.pbMDData = pbData;
  689. mdr.dwMDDataLen = 1024;
  690. mdr.dwMDDataTag = 0;
  691. hr = m_pIMeta->GetData(hMDKey,
  692. NULL,
  693. &mdr,
  694. &dwReqLen);
  695. // If the buffer was too small, try again with a bigger one
  696. if (FAILED(hr) && (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER)) {
  697. delete pbData;
  698. pbData = new BYTE[dwReqLen];
  699. if (pbData == NULL) {
  700. m_pIMeta->CloseKey(hMDKey);
  701. return ::ReportError(hr);
  702. }
  703. mdr.dwMDIdentifier = m_dwId;
  704. mdr.dwMDAttributes = 0;
  705. mdr.dwMDUserType = ALL_METADATA;
  706. mdr.dwMDDataType = ALL_METADATA;
  707. mdr.pbMDData = pbData;
  708. mdr.dwMDDataLen = dwReqLen;
  709. mdr.dwMDDataTag = 0;
  710. hr = m_pIMeta->GetData(hMDKey,
  711. NULL,
  712. &mdr,
  713. &dwReqLen);
  714. }
  715. // If we got it fill in the fields
  716. if (SUCCEEDED(hr)) {
  717. m_dwAttributes = mdr.dwMDAttributes;
  718. m_dwUserType = mdr.dwMDUserType;
  719. m_dwDataType = mdr.dwMDDataType;
  720. SetDataToVar(mdr.pbMDData, mdr.dwMDDataLen);
  721. }
  722. // If the property doesn't exist and we're creating, set defaults
  723. else if ((bCreate) && (hr == MD_ERROR_DATA_NOT_FOUND)) {
  724. m_dwAttributes = 0;
  725. m_dwUserType = 0;
  726. m_dwDataType = 0;
  727. VariantClear(&m_varData);
  728. }
  729. else { //(FAILED(hr))
  730. delete pbData;
  731. m_pIMeta->CloseKey(hMDKey);
  732. return ::ReportError(hr);
  733. }
  734. delete pbData;
  735. // Close the key
  736. m_pIMeta->CloseKey(hMDKey);
  737. return S_OK;
  738. }
  739. /*===================================================================
  740. CProperty::Init
  741. Constructor
  742. Parameters:
  743. tszKey Name of key where property is located
  744. mdr METADATA_RECORD containing the current property info
  745. Returns:
  746. E_OUTOFMEMORY if allocation fails
  747. S_OK on success
  748. ===================================================================*/
  749. HRESULT CProperty::Init(const CComPtr<IMSAdminBase> &pIMeta,
  750. CMetaSchemaTable *pCSchemaTable,
  751. LPCTSTR tszKey,
  752. METADATA_RECORD *mdr)
  753. {
  754. ASSERT(pIMeta.p != NULL);
  755. ASSERT_NULL_OR_STRING(tszKey);
  756. HRESULT hr;
  757. m_pIMeta = pIMeta;
  758. m_pCSchemaTable = pCSchemaTable;
  759. m_pCSchemaTable->AddRef();
  760. // Set the Key member
  761. if (tszKey == NULL) {
  762. m_tszKey = NULL;
  763. }
  764. else {
  765. m_tszKey = new TCHAR[_tcslen(tszKey) + 1];
  766. if (m_tszKey == NULL) {
  767. return ::ReportError(E_OUTOFMEMORY);
  768. }
  769. _tcscpy(m_tszKey, tszKey);
  770. }
  771. // Use mdr to set the rest
  772. m_dwId = mdr->dwMDIdentifier;
  773. m_dwAttributes = mdr->dwMDAttributes;
  774. m_dwUserType = mdr->dwMDUserType;
  775. m_dwDataType = mdr->dwMDDataType;
  776. hr = SetDataToVar(mdr->pbMDData, mdr->dwMDDataLen);
  777. if (FAILED(hr)) {
  778. ::ReportError(hr);
  779. }
  780. return S_OK;
  781. }
  782. /*===================================================================
  783. CProperty::~CProperty
  784. Destructor
  785. Parameters:
  786. None
  787. Returns:
  788. Nothing
  789. ===================================================================*/
  790. CProperty::~CProperty()
  791. {
  792. m_pCSchemaTable->Release();
  793. if (m_tszKey != NULL) {
  794. delete m_tszKey;
  795. }
  796. VariantClear(&m_varData);
  797. }
  798. /*===================================================================
  799. CProperty::InterfaceSupportsErrorInfo
  800. Standard ATL implementation
  801. ===================================================================*/
  802. STDMETHODIMP CProperty::InterfaceSupportsErrorInfo(REFIID riid)
  803. {
  804. static const IID* arr[] =
  805. {
  806. &IID_IProperty,
  807. };
  808. for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
  809. {
  810. if (InlineIsEqualGUID(*arr[i],riid))
  811. return S_OK;
  812. }
  813. return S_FALSE;
  814. }
  815. /*===================================================================
  816. CProperty::get_Id
  817. Get method for Id property. Identifier for this metabase property.
  818. Parameters:
  819. plId [out, retval] Value to return to client.
  820. Returns:
  821. E_INVALIDARG if pulId == NULL
  822. S_OK on success
  823. ===================================================================*/
  824. STDMETHODIMP CProperty::get_Id(long *plId)
  825. {
  826. //TRACE0("MetaUtil: CProperty::get_Id\n");
  827. ASSERT_NULL_OR_POINTER(plId, long);
  828. if (plId == NULL) {
  829. return ::ReportError(E_INVALIDARG);
  830. }
  831. *plId = (long) m_dwId;
  832. return S_OK;
  833. }
  834. /*===================================================================
  835. CProperty::get_Name
  836. Get method for Name property. Name of this metabase property.
  837. Parameters:
  838. pbstrName [out, retval] Value to return to client. If property
  839. has no name "" is returned
  840. Returns:
  841. E_INVALIDARG if pbstrName == NULL
  842. S_OK on success
  843. ===================================================================*/
  844. STDMETHODIMP CProperty::get_Name(BSTR *pbstrName)
  845. {
  846. TRACE0("MetaUtil: CProperty::get_Name\n");
  847. ASSERT_NULL_OR_POINTER(pbstrName, BSTR);
  848. if (pbstrName == NULL) {
  849. return ::ReportError(E_INVALIDARG);
  850. }
  851. CPropInfo *pCPropInfo;
  852. // Get the property info from the Schema Table
  853. pCPropInfo = m_pCSchemaTable->GetPropInfo(m_tszKey, m_dwId);
  854. // Did we find it? Is there a name entry
  855. if ((pCPropInfo == NULL) || (pCPropInfo->GetName() == NULL)) {
  856. // No, return ""
  857. *pbstrName = T2BSTR(_T(""));
  858. }
  859. else {
  860. // Yes, return the name
  861. *pbstrName = T2BSTR(pCPropInfo->GetName());
  862. }
  863. return S_OK;
  864. }
  865. /*===================================================================
  866. CProperty::get_Attributes
  867. Get method for Attributes property. Gets the attribute flags for
  868. this property.
  869. Parameters:
  870. plAttributes [out, retval] Value to return to client.
  871. Returns:
  872. E_INVALIDARG if pulAttributes == NULL
  873. S_OK on success
  874. ===================================================================*/
  875. STDMETHODIMP CProperty::get_Attributes(long *plAttributes)
  876. {
  877. //TRACE0("MetaUtil: CProperty::get_Attributes\n");
  878. ASSERT_NULL_OR_POINTER(plAttributes, long);
  879. if (plAttributes == NULL) {
  880. return ::ReportError(E_INVALIDARG);
  881. }
  882. *plAttributes = (long) m_dwAttributes;
  883. return S_OK;
  884. }
  885. /*===================================================================
  886. CProperty::put_Attributes
  887. Put method for Attributes property. Sets the attribute flags for
  888. this property.
  889. Parameters:
  890. lAttributes [in] New value for attributes.
  891. Returns:
  892. S_OK always
  893. ===================================================================*/
  894. STDMETHODIMP CProperty::put_Attributes(long lAttributes)
  895. {
  896. TRACE0("MetaUtil: CProperty::put_Attributes\n");
  897. m_dwAttributes = (DWORD) lAttributes;
  898. return S_OK;
  899. }
  900. /*===================================================================
  901. CProperty::get_UserType
  902. Get method for UserType property. Gets the User Type for this
  903. metabase property.
  904. Parameters:
  905. plUserType [out, retval] Value to return to client.
  906. Returns:
  907. E_INVALIDARG if pulUserType == NULL
  908. S_OK on success
  909. ===================================================================*/
  910. STDMETHODIMP CProperty::get_UserType(long *plUserType)
  911. {
  912. //TRACE0("MetaUtil: CProperty::get_UserType\n");
  913. ASSERT_NULL_OR_POINTER(plUserType, long);
  914. if (plUserType == NULL) {
  915. return ::ReportError(E_INVALIDARG);
  916. }
  917. *plUserType = (long) m_dwUserType;
  918. return S_OK;
  919. }
  920. /*===================================================================
  921. CProperty::put_UserType
  922. Put method for UserType property. Sets the user type
  923. Parameters:
  924. lUserType [in] New value for user type.
  925. Returns:
  926. S_OK always
  927. ===================================================================*/
  928. STDMETHODIMP CProperty::put_UserType(long lUserType)
  929. {
  930. TRACE0("MetaUtil: CProperty::put_UserType\n");
  931. m_dwUserType = (DWORD) lUserType;
  932. return S_OK;
  933. }
  934. /*===================================================================
  935. CProperty::get_DataType
  936. Get method for DataType property. Gets the type of data stored in
  937. the metabase property.
  938. Parameters:
  939. plDataType [out, retval] Value to return to client.
  940. Returns:
  941. E_INVALIDARG if pulDataType == NULL
  942. S_OK on success
  943. ===================================================================*/
  944. STDMETHODIMP CProperty::get_DataType(long *plDataType)
  945. {
  946. //TRACE0("MetaUtil: CProperty::get_DataType\n");
  947. ASSERT_NULL_OR_POINTER(plDataType, long);
  948. if (plDataType == NULL) {
  949. return ::ReportError(E_INVALIDARG);
  950. }
  951. *plDataType = (long) m_dwDataType;
  952. return S_OK;
  953. }
  954. /*===================================================================
  955. CProperty::put_DataType
  956. Put method for DataType property. Sets the data type
  957. Parameters:
  958. lDataType [in] New value for data type.
  959. Returns:
  960. S_OK always
  961. ===================================================================*/
  962. STDMETHODIMP CProperty::put_DataType(long lDataType)
  963. {
  964. TRACE0("MetaUtil: CProperty::put_DataType\n");
  965. m_dwDataType = (DWORD) lDataType;
  966. return S_OK;
  967. }
  968. /*===================================================================
  969. CProperty::get_Data
  970. Get method for Data property. Gets the data for this metabase
  971. property.
  972. Parameters:
  973. pvarData [out, retval] Value to return to client.
  974. Returns:
  975. E_INVALIDARG if pvarData == NULL
  976. S_OK on success
  977. ===================================================================*/
  978. STDMETHODIMP CProperty::get_Data(VARIANT *pvarData)
  979. {
  980. //TRACE0("MetaUtil: CProperty::get_Data\n");
  981. ASSERT_NULL_OR_POINTER(pvarData, VARIANT);
  982. if (pvarData == NULL) {
  983. return E_INVALIDARG;
  984. }
  985. HRESULT hr;
  986. hr = VariantCopy(pvarData, &m_varData);
  987. if (FAILED(hr)) {
  988. return ::ReportError(hr);
  989. }
  990. return S_OK;
  991. }
  992. /*===================================================================
  993. CProperty::put_Data
  994. Put method for Data property. Sets the data
  995. Parameters:
  996. varData [in] New value for data
  997. ===================================================================*/
  998. STDMETHODIMP CProperty::put_Data(VARIANT varData)
  999. {
  1000. TRACE0("MetaUtil: CProperty::put_Data\n");
  1001. HRESULT hr;
  1002. hr = VariantCopy(&m_varData, &varData);
  1003. if (FAILED(hr)) {
  1004. return ::ReportError(hr);
  1005. }
  1006. return S_OK;
  1007. }
  1008. /*===================================================================
  1009. CProperty::Write
  1010. Writes changes made to this object to the metabase
  1011. Parameters:
  1012. None
  1013. Returns:
  1014. S_OK on success
  1015. ===================================================================*/
  1016. STDMETHODIMP CProperty::Write()
  1017. {
  1018. USES_CONVERSION;
  1019. TRACE0("MetaUtil: CProperty::Write\n");
  1020. HRESULT hr;
  1021. // Open the key for write access
  1022. METADATA_HANDLE hMDKey;
  1023. hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  1024. T2W(m_tszKey),
  1025. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
  1026. MUTIL_OPEN_KEY_TIMEOUT,
  1027. &hMDKey);
  1028. if (FAILED(hr)) {
  1029. return ::ReportError(hr);
  1030. }
  1031. // Create the data record
  1032. METADATA_RECORD mdr;
  1033. mdr.dwMDIdentifier = m_dwId;
  1034. mdr.dwMDAttributes = m_dwAttributes;
  1035. mdr.dwMDUserType = m_dwUserType;
  1036. mdr.dwMDDataType = m_dwDataType;
  1037. hr = GetDataFromVar(mdr.pbMDData, mdr.dwMDDataLen);
  1038. if (FAILED(hr)) {
  1039. m_pIMeta->CloseKey(hMDKey);
  1040. return ::ReportError(hr);
  1041. }
  1042. mdr.dwMDDataTag = 0;
  1043. // Set the data
  1044. hr = m_pIMeta->SetData(hMDKey,
  1045. L"",
  1046. &mdr);
  1047. if (FAILED(hr)) {
  1048. m_pIMeta->CloseKey(hMDKey);
  1049. delete mdr.pbMDData;
  1050. return ::ReportError(hr);
  1051. }
  1052. // Close the key
  1053. m_pIMeta->CloseKey(hMDKey);
  1054. delete mdr.pbMDData;
  1055. return S_OK;
  1056. }
  1057. /*===================================================================
  1058. CProperty::SetDataToVar
  1059. Private function to save property data from its raw form to the
  1060. variant data member.
  1061. Parameters:
  1062. pbData Raw property data to convert to variant
  1063. dwDataLen Length of property data
  1064. Returns:
  1065. ERROR_INVALID_DATA if m_dwDataType is not recognized
  1066. E_OUTOFMEMORY if allocation failed
  1067. S_OK on success
  1068. ===================================================================*/
  1069. HRESULT CProperty::SetDataToVar(BYTE *pbData, DWORD dwDataLen)
  1070. {
  1071. ASSERT((pbData == NULL) || IsValidAddress(pbData, dwDataLen, FALSE));
  1072. HRESULT hr;
  1073. hr = VariantClear(&m_varData);
  1074. if (FAILED(hr)) {
  1075. return ::ReportError(hr);
  1076. }
  1077. switch(m_dwDataType) {
  1078. case DWORD_METADATA:
  1079. // I4 subtype
  1080. V_VT(&m_varData) = VT_I4;
  1081. V_I4(&m_varData) = *(reinterpret_cast<long *> (pbData));
  1082. break;
  1083. case STRING_METADATA:
  1084. case EXPANDSZ_METADATA:
  1085. // BSTR subtype
  1086. V_VT(&m_varData) = VT_BSTR;
  1087. V_BSTR(&m_varData) = W2BSTR(reinterpret_cast<LPCWSTR> (pbData));
  1088. break;
  1089. case MULTISZ_METADATA: {
  1090. ULONG cStrings = 0;
  1091. // Metabase string are Unicode
  1092. LPCWSTR pwsz = reinterpret_cast<LPCWSTR> (pbData);
  1093. LPCWSTR pwszEnd = reinterpret_cast<LPCWSTR> (pbData + dwDataLen);
  1094. // Data is a series of null-terminated strings terminated by two nulls.
  1095. // Figure out how many values we have
  1096. while ((*pwsz != L'\0') && (pwsz < pwszEnd))
  1097. {
  1098. cStrings++;
  1099. pwsz += wcslen(pwsz) + 1; // skip string and trailing \0
  1100. }
  1101. // Create a SAFEARRAY to hold the return result. The array
  1102. // has to be of VARIANTs, not BSTRs, as you might expect, because
  1103. // VBScript will not accept an array of BSTRs (although VB5 will).
  1104. SAFEARRAYBOUND rgsabound[1] = {{cStrings, 0L}};
  1105. SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
  1106. if (psa == NULL)
  1107. return ::ReportError(E_OUTOFMEMORY);
  1108. // now stuff the values into the array
  1109. LONG i = 0;
  1110. pwsz = reinterpret_cast<LPCWSTR> (pbData);
  1111. while ((*pwsz != L'\0') && (pwsz < pwszEnd))
  1112. {
  1113. // Stuff the string into a BSTR VARIANT
  1114. CComVariant vt = W2BSTR(pwsz);
  1115. ASSERT(V_VT(&vt) == VT_BSTR);
  1116. HRESULT hr = SafeArrayPutElement(psa, &i, (void*) &vt);
  1117. if (FAILED(hr))
  1118. ::ReportError(hr);
  1119. i++;
  1120. pwsz += wcslen(pwsz) + 1; // skip string and trailing \0
  1121. }
  1122. V_VT(&m_varData) = VT_ARRAY | VT_VARIANT;
  1123. V_ARRAY(&m_varData) = psa;
  1124. break;
  1125. }
  1126. case BINARY_METADATA:
  1127. // BSTR of byte data subtype
  1128. V_VT(&m_varData) = VT_BSTR;
  1129. V_BSTR(&m_varData) = SysAllocStringByteLen((char *) pbData, dwDataLen);
  1130. break;
  1131. default:
  1132. // Unknown data type
  1133. return ::ReportError(ERROR_INVALID_DATA);
  1134. }
  1135. return S_OK;
  1136. }
  1137. /*===================================================================
  1138. CProperty::GetDataFromVar
  1139. Private function to get data from the variant data member to its
  1140. raw form.
  1141. Supported SubTypes:
  1142. DWORD_METADATA:
  1143. I1, I2, I4, I8, UI1, UI2, UI4, UI8
  1144. STRING_METADATA and EXPANDSZ_METADATA:
  1145. BSTR
  1146. MULTISZ_METADATA
  1147. VT_ARRAY | VT_VARIANT (1 Dimension, stops on NULL or EMPTY)
  1148. VT_ARRAY | VT_BSTR (1 Dimension)
  1149. BINARY_METADATA
  1150. BSTR
  1151. Parameters:
  1152. pbData Pointer to output buffer (allocated by this function)
  1153. dwDataLen Length of data in output buffer
  1154. Returns:
  1155. ERROR_INVALID_DATA if m_dwDataType is not recognized or does not
  1156. match the expected variant subtype.
  1157. E_OUTOFMEMORY on allocation failure
  1158. S_OK on succes
  1159. Notes:
  1160. Case statements are used for each dwMDDataType value to facilitate
  1161. adding support for additional VariantSubType to Data conversions.
  1162. MULTISZ_METADATA with VT_ARRAY | VT_VARIANT stops at a NULL or
  1163. EMPTY entry because it is easy to allocate an array one bigger
  1164. than you need in VBScript. Instead of erroring in this case, I
  1165. stop when I hit such an entry. This also allows a larger array
  1166. to be allocated that is terminated by NULL or EMPTY.
  1167. ===================================================================*/
  1168. HRESULT CProperty::GetDataFromVar(BYTE * &pbData, DWORD &dwDataLen)
  1169. {
  1170. USES_CONVERSION;
  1171. HRESULT hr;
  1172. // Cleanup any IDispatch or byref stuff
  1173. CComVariant varData;
  1174. hr = VariantResolveDispatch(&m_varData, &varData);
  1175. if (FAILED(hr)) {
  1176. return hr;
  1177. }
  1178. switch(m_dwDataType) {
  1179. case DWORD_METADATA:
  1180. // I4 subtype
  1181. switch (V_VT(&varData)) {
  1182. case VT_I1: case VT_I2: case VT_I4: case VT_I8:
  1183. case VT_UI1: case VT_UI2: case VT_UI8:
  1184. // Coerce all integral types to VT_UI4, which is the same as DWORD_METADATA
  1185. if (FAILED(hr = VariantChangeType(&varData, &varData, 0, VT_UI4)))
  1186. return ::ReportError(hr);
  1187. // fallthru to VT_UI4
  1188. case VT_UI4:
  1189. dwDataLen = sizeof(DWORD);
  1190. pbData = reinterpret_cast<BYTE *> (new DWORD);
  1191. if (pbData == NULL) {
  1192. return ::ReportError(E_OUTOFMEMORY);
  1193. }
  1194. *(reinterpret_cast<DWORD *> (pbData)) = V_UI4(&varData);
  1195. break;
  1196. default:
  1197. // Unexpected data type
  1198. return ::ReportError(ERROR_INVALID_DATA);
  1199. }
  1200. break;
  1201. case STRING_METADATA:
  1202. case EXPANDSZ_METADATA:
  1203. // BSTR subtype
  1204. switch (V_VT(&varData)) {
  1205. case VT_BSTR:
  1206. // Ignores the length field, terminate at the first NULL
  1207. dwDataLen = (wcslen(OLE2W(V_BSTR(&varData))) + 1) * sizeof(wchar_t);
  1208. pbData = new BYTE[dwDataLen];
  1209. if( pbData == NULL )
  1210. {
  1211. return ::ReportError(E_OUTOFMEMORY);
  1212. }
  1213. memcpy(pbData, OLE2W(V_BSTR(&varData)), dwDataLen);
  1214. default:
  1215. // Unexpected data type
  1216. return ::ReportError(ERROR_INVALID_DATA);
  1217. }
  1218. break;
  1219. case MULTISZ_METADATA:
  1220. // ARRAY of BSTR subtype
  1221. // if it's a 1 Dimentional Array subtype
  1222. if (((V_VT(&varData) & VT_ARRAY) == VT_ARRAY) &&
  1223. (SafeArrayGetDim(V_ARRAY(&varData)) == 1) ) {
  1224. // Get Array Bounds
  1225. long lLBound;
  1226. long lUBound;
  1227. long lNumElements;
  1228. hr = SafeArrayGetLBound(V_ARRAY(&varData), 1, &lLBound);
  1229. if (FAILED(hr)) {
  1230. return ::ReportError(hr);
  1231. }
  1232. hr = SafeArrayGetUBound(V_ARRAY(&varData), 1, &lUBound);
  1233. if (FAILED(hr)) {
  1234. return ::ReportError(hr);
  1235. }
  1236. lNumElements = lUBound - lLBound + 1;
  1237. // Process the element types
  1238. switch (V_VT(&varData)) {
  1239. case VT_ARRAY | VT_VARIANT : {
  1240. VARIANT *rgvarRaw; // Before resolveIDispatch
  1241. CComVariant *rgvar; // After resolveIDispatch
  1242. LPWSTR wszIndex;
  1243. int i;
  1244. int iStrLen;
  1245. rgvar = new CComVariant[lUBound - lLBound + 1];
  1246. if (rgvar == NULL) {
  1247. return ::ReportError(E_OUTOFMEMORY);
  1248. }
  1249. hr = SafeArrayAccessData(V_ARRAY(&varData), (void **) &rgvarRaw);
  1250. if (FAILED(hr)) {
  1251. return ::ReportError(hr);
  1252. }
  1253. // Pass 1, resolve IDispatch, check types and figure out how much memory is needed
  1254. dwDataLen = 0;
  1255. for (i = 0; i < lNumElements; i++) {
  1256. hr = VariantResolveDispatch(&(rgvarRaw[i]), &(rgvar[i]));
  1257. if (FAILED(hr)) {
  1258. return hr;
  1259. }
  1260. if (V_VT(&(rgvar[i])) != VT_BSTR) {
  1261. if ((V_VT(&(rgvar[i])) == VT_EMPTY) ||
  1262. (V_VT(&(rgvar[i])) == VT_NULL)) {
  1263. // NULL or EMPTY, Stop Here
  1264. lNumElements = i;
  1265. break;
  1266. }
  1267. else {
  1268. SafeArrayUnaccessData(V_ARRAY(&varData));
  1269. return ::ReportError(ERROR_INVALID_DATA);
  1270. }
  1271. }
  1272. dwDataLen += (wcslen(OLE2W(V_BSTR(&(rgvar[i])))) + 1) * sizeof(wchar_t);
  1273. }
  1274. dwDataLen += sizeof(wchar_t);
  1275. // Allocate
  1276. pbData = new BYTE[dwDataLen];
  1277. if (pbData == NULL) {
  1278. SafeArrayUnaccessData(V_ARRAY(&varData));
  1279. return ::ReportError(E_OUTOFMEMORY);
  1280. }
  1281. // Pass 2, copy to desination
  1282. wszIndex = reinterpret_cast<LPWSTR> (pbData);
  1283. for (i = 0; i < lNumElements; i++) {
  1284. iStrLen = (wcslen(OLE2W(V_BSTR(&(rgvar[i])))) + 1);
  1285. memcpy(wszIndex, OLE2W(V_BSTR(&(rgvar[i]))), iStrLen * sizeof(wchar_t));
  1286. wszIndex += iStrLen;
  1287. }
  1288. *wszIndex = L'\0';
  1289. SafeArrayUnaccessData(V_ARRAY(&varData));
  1290. break;
  1291. }
  1292. case VT_ARRAY | VT_BSTR : {
  1293. BSTR *rgbstr;
  1294. LPWSTR wszIndex;
  1295. int i;
  1296. int iStrLen;
  1297. hr = SafeArrayAccessData(V_ARRAY(&varData), (void **) &rgbstr);
  1298. if (FAILED(hr)) {
  1299. return ::ReportError(hr);
  1300. }
  1301. // Pass 1, figure out how much memory is needed
  1302. dwDataLen = 0;
  1303. for (i = 0; i < lNumElements; i++) {
  1304. dwDataLen += (wcslen(OLE2W(rgbstr[i])) + 1) * sizeof(wchar_t);
  1305. }
  1306. dwDataLen += sizeof(wchar_t);
  1307. // Allocate
  1308. pbData = new BYTE[dwDataLen];
  1309. if (pbData == NULL) {
  1310. SafeArrayUnaccessData(V_ARRAY(&varData));
  1311. return ::ReportError(E_OUTOFMEMORY);
  1312. }
  1313. // Pass 2, copy to desination
  1314. wszIndex = reinterpret_cast<LPWSTR> (pbData);
  1315. for (i = 0; i < lNumElements; i++) {
  1316. iStrLen = (wcslen(OLE2W(rgbstr[i])) + 1);
  1317. memcpy(wszIndex, OLE2W(rgbstr[i]), iStrLen * sizeof(wchar_t));
  1318. wszIndex += iStrLen;
  1319. }
  1320. *wszIndex = L'\0';
  1321. SafeArrayUnaccessData(V_ARRAY(&varData));
  1322. break;
  1323. }
  1324. default:
  1325. // Unexpected data type
  1326. return ::ReportError(ERROR_INVALID_DATA);
  1327. }
  1328. }
  1329. else { // Array is not one dimensional
  1330. // Unexpected data type
  1331. return ::ReportError(ERROR_INVALID_DATA);
  1332. }
  1333. break;
  1334. case BINARY_METADATA:
  1335. // BSTR of bytes subtype
  1336. switch (V_VT(&varData)) {
  1337. case VT_BSTR:
  1338. // Use the length field, since NULL values are allowed
  1339. dwDataLen = SysStringByteLen(V_BSTR(&varData));
  1340. pbData = new BYTE[dwDataLen];
  1341. if( pbData == NULL )
  1342. {
  1343. return ::ReportError(E_OUTOFMEMORY);
  1344. }
  1345. memcpy(pbData, V_BSTR(&varData), dwDataLen);
  1346. default:
  1347. // Unexpected data type
  1348. return ::ReportError(ERROR_INVALID_DATA);
  1349. }
  1350. break;
  1351. default:
  1352. // Unknown metabase data type
  1353. return ::ReportError(ERROR_INVALID_DATA);
  1354. }
  1355. return S_OK;
  1356. }