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.

1451 lines
32 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1998 - 1999
  6. //
  7. // File: attredit.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #include <SnapBase.h>
  12. #include "resource.h"
  13. #include "common.h"
  14. #include "attredit.h"
  15. #include "connection.h"
  16. #include "attrqry.h"
  17. #ifdef DEBUG_ALLOCATOR
  18. #ifdef _DEBUG
  19. #define new DEBUG_NEW
  20. #undef THIS_FILE
  21. static char THIS_FILE[] = __FILE__;
  22. #endif
  23. #endif
  24. ////////////////////////////////////////////////////////////////////////////
  25. // this is used to fill in the attributes for RootDSE
  26. //
  27. typedef struct tagRootDSEAttr
  28. {
  29. LPCWSTR lpszAttr;
  30. LPCWSTR lpszSyntax;
  31. BOOL bMulti;
  32. } SYNTAXMAP;
  33. extern SYNTAXMAP g_ldapRootDSESyntax[];
  34. extern LPCWSTR g_lpszGC;
  35. extern LPCWSTR g_lpszRootDSE;
  36. ///////////////////////////////////////////////////////////////////////////
  37. // CAttrList
  38. POSITION CAttrList::FindProperty(LPCWSTR lpszAttr)
  39. {
  40. CADSIAttr* pAttr;
  41. for (POSITION p = GetHeadPosition(); p != NULL; GetNext(p))
  42. {
  43. // I use GetAt here because I don't want to advance the POSITION
  44. // because it is returned if they are equal
  45. //
  46. pAttr = GetAt(p);
  47. CString sName;
  48. pAttr->GetProperty(sName);
  49. if (wcscmp(sName, lpszAttr) == 0)
  50. {
  51. break;
  52. }
  53. }
  54. return p;
  55. }
  56. BOOL CAttrList::HasProperty(LPCWSTR lpszAttr)
  57. {
  58. POSITION pos = FindProperty(lpszAttr);
  59. return pos != NULL;
  60. }
  61. // Searches through the cache for the attribute
  62. // ppAttr will point to the CADSIAttr if found, NULL if not
  63. //
  64. void CAttrList::GetNextDirty(POSITION& pos, CADSIAttr** ppAttr)
  65. {
  66. *ppAttr = GetNext(pos);
  67. if (pos == NULL && !(*ppAttr)->IsDirty())
  68. {
  69. *ppAttr = NULL;
  70. return;
  71. }
  72. while (!(*ppAttr)->IsDirty() && pos != NULL)
  73. {
  74. *ppAttr = GetNext(pos);
  75. if (!(*ppAttr)->IsDirty() && pos == NULL)
  76. {
  77. *ppAttr = NULL;
  78. break;
  79. }
  80. }
  81. }
  82. BOOL CAttrList::HasDirty()
  83. {
  84. POSITION pos = GetHeadPosition();
  85. while (pos != NULL)
  86. {
  87. CADSIAttr* pAttr = GetNext(pos);
  88. if (pAttr->IsDirty())
  89. {
  90. return TRUE;
  91. }
  92. }
  93. return FALSE;
  94. }
  95. ///////////////////////////////////////////////////////////////////////////
  96. // CDNSManageButtonTextHelper
  97. CDNSManageButtonTextHelper::CDNSManageButtonTextHelper(int nStates)
  98. {
  99. m_nID = 0;
  100. m_pParentWnd = NULL;
  101. m_nStates = nStates;
  102. m_lpszText = NULL;
  103. m_lpszArr = (LPWSTR*)malloc(sizeof(LPWSTR*)*m_nStates);
  104. if (m_lpszArr != NULL)
  105. {
  106. memset(m_lpszArr, 0x0, sizeof(LPWSTR*)*m_nStates);
  107. }
  108. }
  109. CDNSManageButtonTextHelper::~CDNSManageButtonTextHelper()
  110. {
  111. for (int k = 0; k < m_nStates; k++)
  112. {
  113. if (m_lpszArr[k] != NULL)
  114. free(m_lpszArr[k]);
  115. }
  116. free(m_lpszArr);
  117. }
  118. void CDNSManageButtonTextHelper::SetStateX(int nIndex)
  119. {
  120. CWnd* pWnd = m_pParentWnd->GetDlgItem(m_nID);
  121. ASSERT(pWnd != NULL);
  122. ASSERT( (nIndex >0) || (nIndex < m_nStates));
  123. pWnd->SetWindowText(m_lpszArr[nIndex]);
  124. }
  125. BOOL CDNSManageButtonTextHelper::Init(CWnd* pParentWnd, UINT nButtonID, UINT* nStrArray)
  126. {
  127. ASSERT(m_pParentWnd == NULL);
  128. ASSERT(pParentWnd != NULL);
  129. m_pParentWnd = pParentWnd;
  130. m_nID = nButtonID;
  131. CWnd* pWnd = m_pParentWnd->GetDlgItem(m_nID);
  132. if (pWnd == NULL)
  133. return FALSE;
  134. // get the text for the window
  135. int nSuccessEntries;
  136. LoadStringArrayFromResource(m_lpszArr, nStrArray, m_nStates, &nSuccessEntries);
  137. ASSERT(nSuccessEntries == m_nStates);
  138. return TRUE;
  139. }
  140. ///////////////////////////////////////////////////////////////////////////
  141. // CDNSButtonToggleTextHelper
  142. CDNSButtonToggleTextHelper::CDNSButtonToggleTextHelper()
  143. : CDNSManageButtonTextHelper(2)
  144. {
  145. }
  146. ///////////////////////////////////////////////////////////////////////////////
  147. BEGIN_MESSAGE_MAP(CADSIEditBox, CEdit)
  148. ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
  149. END_MESSAGE_MAP()
  150. void CADSIEditBox::OnChange()
  151. {
  152. m_pEditor->OnEditChange();
  153. }
  154. ////////////////////////////////////////////////////////////////
  155. // CADSIValueBox
  156. BEGIN_MESSAGE_MAP(CADSIValueBox, CEdit)
  157. // ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
  158. END_MESSAGE_MAP()
  159. ////////////////////////////////////////////////////////////////
  160. // CADSIValueList
  161. BEGIN_MESSAGE_MAP(CADSIValueList, CListBox)
  162. ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelChange)
  163. END_MESSAGE_MAP()
  164. void CADSIValueList::OnSelChange()
  165. {
  166. m_pEditor->OnValueSelChange();
  167. }
  168. ////////////////////////////////////////////////////////////////
  169. // CADSIAddButton
  170. BEGIN_MESSAGE_MAP(CADSIAddButton, CButton)
  171. ON_CONTROL_REFLECT(BN_CLICKED, OnAdd)
  172. END_MESSAGE_MAP()
  173. void CADSIAddButton::OnAdd()
  174. {
  175. m_pEditor->OnAddValue();
  176. }
  177. ////////////////////////////////////////////////////////////////
  178. // CADSIRemoveButton
  179. BEGIN_MESSAGE_MAP(CADSIRemoveButton, CButton)
  180. ON_CONTROL_REFLECT(BN_CLICKED, OnRemove)
  181. END_MESSAGE_MAP()
  182. void CADSIRemoveButton::OnRemove()
  183. {
  184. m_pEditor->OnRemoveValue();
  185. }
  186. ////////////////////////////////////////////////////////////////
  187. // CAttrEditor
  188. CAttrEditor::CAttrEditor() : m_AttrEditBox(this),
  189. m_SyntaxBox(this),
  190. m_ValueBox(this),
  191. m_ValueList(this),
  192. m_AddButton(this),
  193. m_RemoveButton(this),
  194. m_AddButtonHelper(),
  195. m_RemoveButtonHelper()
  196. {
  197. m_bExisting = TRUE;
  198. m_ptouchedAttr = NULL;
  199. }
  200. BOOL CAttrEditor::Initialize(CPropertyPageBase* pParentWnd, CTreeNode* pTreeNode,
  201. LPCWSTR lpszServer,
  202. UINT nIDEdit, UINT nIDSyntax,
  203. UINT nIDValueBox, UINT nIDValueList,
  204. UINT nIDAddButton, UINT nIDRemoveButton,
  205. BOOL bComplete)
  206. {
  207. ASSERT(pParentWnd != NULL);
  208. if (pParentWnd == NULL)
  209. return FALSE;
  210. m_pParentWnd = pParentWnd;
  211. m_ptouchedAttr = new CAttrList();
  212. ASSERT(m_ptouchedAttr != NULL);
  213. if (pTreeNode == NULL)
  214. {
  215. m_bExisting = FALSE;
  216. }
  217. else
  218. {
  219. m_bExisting = TRUE;
  220. }
  221. m_sServer = lpszServer;
  222. if (m_bExisting)
  223. {
  224. // This gets the CConnectionData from the ConnectionNode by finding a valid treenode and using its
  225. // CADsObject to get the ConnectionNode and then the CConnectionData
  226. //
  227. m_pTreeNode = pTreeNode;
  228. CADSIEditContainerNode* pContNode = dynamic_cast<CADSIEditContainerNode*>(m_pTreeNode);
  229. if (pContNode == NULL)
  230. {
  231. CADSIEditLeafNode* pLeafNode = dynamic_cast<CADSIEditLeafNode*>(m_pTreeNode);
  232. ASSERT(pLeafNode != NULL);
  233. m_pConnectData = pLeafNode->GetADsObject()->GetConnectionNode()->GetConnectionData();
  234. }
  235. else
  236. {
  237. m_pConnectData = pContNode->GetADsObject()->GetConnectionNode()->GetConnectionData();
  238. }
  239. }
  240. // sublclass controls
  241. //
  242. BOOL bRes = m_AttrEditBox.SubclassDlgItem(nIDEdit, m_pParentWnd);
  243. ASSERT(bRes);
  244. if (!bRes) return FALSE;
  245. bRes = m_SyntaxBox.SubclassDlgItem(nIDSyntax, m_pParentWnd);
  246. ASSERT(bRes);
  247. if (!bRes) return FALSE;
  248. bRes = m_ValueBox.SubclassDlgItem(nIDValueBox, m_pParentWnd);
  249. ASSERT(bRes);
  250. if (!bRes) return FALSE;
  251. bRes = m_ValueList.SubclassDlgItem(nIDValueList, m_pParentWnd);
  252. ASSERT(bRes);
  253. if (!bRes) return FALSE;
  254. bRes = m_AddButton.SubclassDlgItem(nIDAddButton, m_pParentWnd);
  255. ASSERT(bRes);
  256. if (!bRes) return FALSE;
  257. UINT nAddButtonTextIDs[2] = { IDS_BUTTON_TEXT_ADD, IDS_BUTTON_TEXT_SET };
  258. m_AddButtonHelper.Init(m_pParentWnd,
  259. nIDAddButton,
  260. nAddButtonTextIDs);
  261. bRes = m_RemoveButton.SubclassDlgItem(nIDRemoveButton, m_pParentWnd);
  262. ASSERT(bRes);
  263. if (!bRes) return FALSE;
  264. UINT nRemoveButtonTextIDs[2] = { IDS_BUTTON_TEXT_REMOVE, IDS_BUTTON_TEXT_CLEAR };
  265. m_RemoveButtonHelper.Init(m_pParentWnd,
  266. nIDRemoveButton,
  267. nRemoveButtonTextIDs);
  268. if (!m_sNotSet.LoadString(IDS_NOT_SET))
  269. {
  270. return FALSE;
  271. }
  272. if ( bComplete)
  273. {
  274. // Show property values as single and without the ability to set or clear
  275. //
  276. SetPropertyUI(0, FALSE, TRUE);
  277. }
  278. else
  279. {
  280. m_AttrEditBox.ShowWindow(SW_HIDE);
  281. m_SyntaxBox.ShowWindow(SW_HIDE);
  282. m_ValueBox.ShowWindow(SW_HIDE);
  283. m_ValueList.ShowWindow(SW_HIDE);
  284. m_AddButton.ShowWindow(SW_HIDE);
  285. m_RemoveButton.ShowWindow(SW_HIDE);
  286. }
  287. return bRes;
  288. }
  289. BOOL CAttrEditor::Initialize(CPropertyPageBase* pParentWnd, CConnectionData* pConnectData,
  290. LPCWSTR lpszServer,
  291. UINT nIDEdit, UINT nIDSyntax,
  292. UINT nIDValueBox, UINT nIDValueList,
  293. UINT nIDAddButton, UINT nIDRemoveButton,
  294. BOOL bComplete, CAttrList* pAttrList)
  295. {
  296. ASSERT(pParentWnd != NULL);
  297. if (pParentWnd == NULL)
  298. return FALSE;
  299. m_pParentWnd = pParentWnd;
  300. m_bExisting = FALSE;
  301. m_sServer = lpszServer;
  302. m_pConnectData = pConnectData;
  303. ASSERT(pAttrList != NULL);
  304. m_ptouchedAttr = pAttrList;
  305. // sublclass controls
  306. //
  307. BOOL bRes = m_AttrEditBox.SubclassDlgItem(nIDEdit, m_pParentWnd);
  308. ASSERT(bRes);
  309. if (!bRes) return FALSE;
  310. bRes = m_SyntaxBox.SubclassDlgItem(nIDSyntax, m_pParentWnd);
  311. ASSERT(bRes);
  312. if (!bRes) return FALSE;
  313. bRes = m_ValueBox.SubclassDlgItem(nIDValueBox, m_pParentWnd);
  314. ASSERT(bRes);
  315. if (!bRes) return FALSE;
  316. bRes = m_ValueList.SubclassDlgItem(nIDValueList, m_pParentWnd);
  317. ASSERT(bRes);
  318. if (!bRes) return FALSE;
  319. bRes = m_AddButton.SubclassDlgItem(nIDAddButton, m_pParentWnd);
  320. ASSERT(bRes);
  321. if (!bRes) return FALSE;
  322. UINT nAddButtonTextIDs[2] = { IDS_BUTTON_TEXT_ADD, IDS_BUTTON_TEXT_SET };
  323. m_AddButtonHelper.Init(m_pParentWnd,
  324. nIDAddButton,
  325. nAddButtonTextIDs);
  326. bRes = m_RemoveButton.SubclassDlgItem(nIDRemoveButton, m_pParentWnd);
  327. ASSERT(bRes);
  328. if (!bRes) return FALSE;
  329. UINT nRemoveButtonTextIDs[2] = { IDS_BUTTON_TEXT_REMOVE, IDS_BUTTON_TEXT_CLEAR };
  330. m_RemoveButtonHelper.Init(m_pParentWnd,
  331. nIDRemoveButton,
  332. nRemoveButtonTextIDs);
  333. if (!m_sNotSet.LoadString(IDS_NOT_SET))
  334. {
  335. return FALSE;
  336. }
  337. if ( bComplete)
  338. {
  339. // Show property values as single and without the ability to set or clear
  340. //
  341. SetPropertyUI(0, FALSE, TRUE);
  342. }
  343. else
  344. {
  345. m_AttrEditBox.ShowWindow(SW_HIDE);
  346. m_SyntaxBox.ShowWindow(SW_HIDE);
  347. m_ValueBox.ShowWindow(SW_HIDE);
  348. m_ValueList.ShowWindow(SW_HIDE);
  349. m_AddButton.ShowWindow(SW_HIDE);
  350. m_RemoveButton.ShowWindow(SW_HIDE);
  351. }
  352. return bRes;
  353. }
  354. void CAttrEditor::SetAttribute(LPCWSTR lpszAttr, LPCWSTR lpszPath)
  355. {
  356. m_sAttr = lpszAttr;
  357. m_sPath = lpszPath;
  358. DisplayAttribute();
  359. }
  360. BOOL CAttrEditor::OnApply()
  361. {
  362. if (m_bExisting && m_ptouchedAttr->HasDirty() &&
  363. !m_pConnectData->IsRootDSE() &&
  364. !m_pConnectData->IsGC())
  365. {
  366. CComPtr<IDirectoryObject> pDirObject;
  367. // bind to object with authentication
  368. //
  369. HRESULT hr, hCredResult;
  370. hr = OpenObjectWithCredentials(
  371. m_pConnectData,
  372. m_pConnectData->GetCredentialObject()->UseCredentials(),
  373. (LPWSTR)(LPCWSTR)m_sPath,
  374. IID_IDirectoryObject,
  375. (LPVOID*) &pDirObject,
  376. NULL,
  377. hCredResult
  378. );
  379. if (FAILED(hr))
  380. {
  381. if (SUCCEEDED(hCredResult))
  382. {
  383. ADSIEditErrorMessage(hr);
  384. m_pParentWnd->SetModified(FALSE);
  385. }
  386. // Need to change the focus or we will not be able to navigate with the keyboard
  387. m_AttrEditBox.SetFocus();
  388. return FALSE;
  389. }
  390. // Change or add values to ADSI cache that have changed
  391. //
  392. hr = CADSIAttr::SetValuesInDS(m_ptouchedAttr, pDirObject);
  393. if (FAILED(hr))
  394. {
  395. //Format Error message and pop up a dialog
  396. ADSIEditErrorMessage(hr);
  397. m_ptouchedAttr->RemoveAllAttr();
  398. DisplayAttribute();
  399. m_pParentWnd->SetModified(FALSE);
  400. // Need to change the focus or we will not be able to navigate with the keyboard
  401. m_AttrEditBox.SetFocus();
  402. return FALSE;
  403. }
  404. }
  405. m_pParentWnd->SetModified(FALSE);
  406. return TRUE;
  407. }
  408. void CAttrEditor::OnAddValue()
  409. {
  410. ASSERT(!m_pConnectData->IsRootDSE());
  411. ASSERT(!m_pConnectData->IsGC());
  412. CString s;
  413. m_AttrEditBox.GetWindowText(s);
  414. CStringList sList;
  415. m_pAttr->GetValues(sList);
  416. if (m_pAttr->GetMultivalued())
  417. {
  418. // if it is the first value to be added we need to get rid of the "<not set>"
  419. //
  420. CString sNotSet;
  421. m_ValueList.GetText(0, sNotSet);
  422. if (sNotSet == m_sNotSet)
  423. {
  424. m_ValueList.ResetContent();
  425. }
  426. // then add the new value
  427. //
  428. sList.AddTail(s);
  429. }
  430. else
  431. {
  432. // since it is single valued, remove the old one and add the new one
  433. //
  434. sList.RemoveAll();
  435. sList.AddTail(s);
  436. }
  437. HRESULT hr = m_pAttr->SetValues(sList);
  438. if (FAILED(hr))
  439. {
  440. DisplayFormatError();
  441. }
  442. else
  443. {
  444. if ( m_pAttr->GetMultivalued())
  445. {
  446. m_ValueList.AddString(s);
  447. }
  448. else
  449. {
  450. m_ValueBox.SetWindowText(s);
  451. }
  452. m_AttrEditBox.SetWindowText(_T(""));
  453. m_pAttr->SetDirty(TRUE);
  454. m_pParentWnd->SetModified(TRUE);
  455. // Make the UI reflect the new data
  456. //
  457. m_AttrEditBox.SetFocus();
  458. SetPropertyUI(~TN_FLAG_ENABLE_ADD, TRUE);
  459. // Enable the clear button if the attribute is not multivalued
  460. //
  461. if ( !m_pAttr->GetMultivalued())
  462. {
  463. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  464. }
  465. }
  466. }
  467. void CAttrEditor::DisplayFormatError()
  468. {
  469. switch (m_pAttr->GetADsType())
  470. {
  471. case ADSTYPE_DN_STRING :
  472. case ADSTYPE_CASE_EXACT_STRING :
  473. case ADSTYPE_CASE_IGNORE_STRING :
  474. case ADSTYPE_PRINTABLE_STRING :
  475. case ADSTYPE_NUMERIC_STRING :
  476. {
  477. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  478. if (m_pAttr->GetMultivalued())
  479. {
  480. if (m_ValueList.GetCount() < 1)
  481. {
  482. m_ValueList.AddString(m_sNotSet);
  483. }
  484. }
  485. break;
  486. }
  487. case ADSTYPE_BOOLEAN :
  488. {
  489. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_BOOLEAN, MB_OK);
  490. if (m_pAttr->GetMultivalued())
  491. {
  492. if (m_ValueList.GetCount() < 1)
  493. {
  494. m_ValueList.AddString(m_sNotSet);
  495. }
  496. }
  497. break;
  498. }
  499. case ADSTYPE_INTEGER :
  500. {
  501. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  502. if (m_pAttr->GetMultivalued())
  503. {
  504. if (m_ValueList.GetCount() < 1)
  505. {
  506. m_ValueList.AddString(m_sNotSet);
  507. }
  508. }
  509. break;
  510. }
  511. case ADSTYPE_OCTET_STRING :
  512. {
  513. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_OCTET, MB_OK);
  514. if (m_pAttr->GetMultivalued())
  515. {
  516. if (m_ValueList.GetCount() < 1)
  517. {
  518. m_ValueList.AddString(m_sNotSet);
  519. }
  520. }
  521. break;
  522. }
  523. case ADSTYPE_UTC_TIME :
  524. {
  525. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT_TIME, MB_OK);
  526. if (m_pAttr->GetMultivalued())
  527. {
  528. if (m_ValueList.GetCount() < 1)
  529. {
  530. m_ValueList.AddString(m_sNotSet);
  531. }
  532. }
  533. break;
  534. }
  535. case ADSTYPE_LARGE_INTEGER :
  536. case ADSTYPE_OBJECT_CLASS :
  537. case ADSTYPE_UNKNOWN :
  538. {
  539. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  540. if (m_pAttr->GetMultivalued())
  541. {
  542. if (m_ValueList.GetCount() < 1)
  543. {
  544. m_ValueList.AddString(m_sNotSet);
  545. }
  546. }
  547. break;
  548. }
  549. default :
  550. {
  551. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  552. if (m_pAttr->GetMultivalued())
  553. {
  554. if (m_ValueList.GetCount() < 1)
  555. {
  556. m_ValueList.AddString(m_sNotSet);
  557. }
  558. }
  559. break;
  560. }
  561. }
  562. }
  563. void CAttrEditor::OnRemoveValue()
  564. {
  565. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  566. {
  567. CStringList sList;
  568. m_pAttr->GetValues(sList);
  569. DWORD dwNumVals = m_pAttr->GetNumValues();
  570. if (m_pAttr->GetMultivalued())
  571. {
  572. CString s, sVal;
  573. int iCount = m_ValueList.GetCurSel();
  574. m_ValueList.GetText(iCount, sVal);
  575. m_AttrEditBox.SetWindowText(sVal);
  576. m_ValueList.DeleteString(iCount);
  577. // Add "<not set>" to the UI if this is the last value being removed
  578. //
  579. if (m_ValueList.GetCount() == 0)
  580. {
  581. m_AttrEditBox.SetFocus();
  582. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  583. m_ValueList.AddString(m_sNotSet);
  584. if (!m_bExisting)
  585. {
  586. m_pAttr->SetDirty(FALSE);
  587. }
  588. }
  589. POSITION pos = sList.FindIndex(iCount);
  590. sList.RemoveAt(pos);
  591. HRESULT hr = m_pAttr->SetValues(sList);
  592. if (FAILED(hr))
  593. {
  594. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  595. }
  596. if (m_bExisting || m_ValueList.GetCount() > 0)
  597. {
  598. m_pAttr->SetDirty(TRUE);
  599. }
  600. }
  601. else
  602. {
  603. CString sVal;
  604. m_ValueBox.GetWindowText(sVal);
  605. m_AttrEditBox.SetWindowText(sVal);
  606. m_ValueBox.SetWindowText( m_sNotSet);
  607. sList.RemoveAll();
  608. HRESULT hr = m_pAttr->SetValues(sList);
  609. if (FAILED(hr))
  610. {
  611. ADSIEditMessageBox(IDS_MSG_INCORRECT_FORMAT, MB_OK);
  612. }
  613. if (!m_bExisting)
  614. {
  615. m_pAttr->SetDirty(FALSE);
  616. }
  617. else
  618. {
  619. m_pAttr->SetDirty(TRUE);
  620. }
  621. }
  622. m_AttrEditBox.SetFocus();
  623. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  624. dwNumVals--;
  625. m_pParentWnd->SetModified(TRUE);
  626. }
  627. }
  628. void CAttrEditor::OnEditChange()
  629. {
  630. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  631. {
  632. CString s;
  633. m_AttrEditBox.GetWindowText(s);
  634. if (s != _T(""))
  635. {
  636. SetPropertyUI(TN_FLAG_ENABLE_ADD, FALSE);
  637. }
  638. else
  639. {
  640. SetPropertyUI(~TN_FLAG_ENABLE_ADD, TRUE);
  641. }
  642. }
  643. }
  644. void CAttrEditor::OnValueSelChange()
  645. {
  646. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  647. {
  648. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  649. }
  650. }
  651. void CAttrEditor::GetAttrFailed()
  652. {
  653. CString sSyntax;
  654. GetSyntax(m_sAttr, sSyntax);
  655. m_SyntaxBox.SetWindowText(sSyntax);
  656. m_ValueList.ResetContent();
  657. m_ValueList.AddString(m_sNotSet);
  658. m_ValueBox.SetWindowText(m_sNotSet);
  659. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  660. }
  661. void CAttrEditor::FillWithExisting()
  662. {
  663. CString s;
  664. m_pAttr = m_ptouchedAttr->GetAt(m_ptouchedAttr->FindProperty(m_sAttr));
  665. ASSERT(m_pAttr != NULL);
  666. CStringList slValues;
  667. m_pAttr->GetValues(slValues);
  668. if (m_pAttr->GetMultivalued())
  669. {
  670. m_ValueList.ResetContent();
  671. POSITION pos;
  672. if (slValues.GetCount() == 0)
  673. {
  674. slValues.AddTail(m_sNotSet);
  675. }
  676. for (pos = slValues.GetHeadPosition(); pos != NULL; slValues.GetNext(pos) )
  677. {
  678. s = slValues.GetAt(pos);
  679. m_ValueList.AddString(s);
  680. }
  681. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  682. }
  683. else
  684. {
  685. if (slValues.GetCount() > 0)
  686. {
  687. s = slValues.GetAt(slValues.GetHeadPosition());
  688. m_ValueBox.SetWindowText(s);
  689. if (!m_pConnectData->IsRootDSE() && !m_pConnectData->IsGC())
  690. {
  691. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  692. }
  693. }
  694. else
  695. {
  696. m_ValueBox.SetWindowText(m_sNotSet);
  697. SetPropertyUI(~TN_FLAG_ENABLE_REMOVE, TRUE);
  698. }
  699. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  700. }
  701. }
  702. void CAttrEditor::DisplayAttribute()
  703. {
  704. int iCount;
  705. HRESULT hr, hCredResult;
  706. if (m_ptouchedAttr->HasProperty(m_sAttr))
  707. {
  708. FillWithExisting();
  709. }
  710. else
  711. {
  712. if (m_pConnectData->IsRootDSE())
  713. {
  714. DisplayRootDSE();
  715. }
  716. else if (!m_bExisting)
  717. {
  718. ADS_ATTR_INFO *pAttrInfo = NULL;
  719. GetAttrFailed();
  720. m_pAttr = TouchAttr(m_sAttr);
  721. ASSERT(m_pAttr != NULL);
  722. if (m_pAttr != NULL)
  723. {
  724. if (m_pAttr->GetMultivalued())
  725. {
  726. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  727. }
  728. else
  729. {
  730. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  731. }
  732. }
  733. return;
  734. }
  735. else
  736. {
  737. CComPtr<IDirectoryObject> pDirObject;
  738. hr = OpenObjectWithCredentials(
  739. m_pConnectData,
  740. m_pConnectData->GetCredentialObject()->UseCredentials(),
  741. (LPWSTR)(LPCWSTR)m_sPath,
  742. IID_IDirectoryObject,
  743. (LPVOID*) &pDirObject,
  744. NULL,
  745. hCredResult
  746. );
  747. if ( FAILED(hr) )
  748. {
  749. if (SUCCEEDED(hCredResult))
  750. {
  751. ADSIEditErrorMessage(hr);
  752. }
  753. return;
  754. }
  755. ASSERT(pDirObject != NULL);
  756. // Get attribute
  757. //
  758. CString szAttrName;
  759. szAttrName = m_sAttr + _T(";range=0-*");
  760. CString szFormat = m_sAttr + _T(";range=%ld-*");
  761. DWORD dwReturn = 0;
  762. DWORD dwNumAttr = 1;
  763. ADS_ATTR_INFO *pAttrInfo;
  764. const WCHAR wcSep = L'-';
  765. const WCHAR wcEnd = L'*';
  766. BOOL fMoreRemain = FALSE;
  767. CStringList sList;
  768. do
  769. {
  770. LPWSTR lpszAttrs[] = {(LPWSTR)(LPCWSTR)szAttrName};
  771. hr = pDirObject->GetObjectAttributes(lpszAttrs, dwNumAttr, &pAttrInfo, &dwReturn);
  772. if (FAILED(hr))
  773. {
  774. ADSIEditErrorMessage(hr);
  775. return;
  776. }
  777. if (pAttrInfo == NULL)
  778. {
  779. GetAttrFailed();
  780. m_pAttr = TouchAttr(m_sAttr);
  781. ASSERT(m_pAttr != NULL);
  782. if (m_pAttr != NULL)
  783. {
  784. if (m_pAttr->GetMultivalued())
  785. {
  786. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  787. }
  788. else
  789. {
  790. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  791. }
  792. }
  793. return;
  794. }
  795. if (dwReturn > 0)
  796. {
  797. GetStringFromADs(pAttrInfo, sList);
  798. }
  799. //
  800. // Check to see if there is more data. If the last char of the
  801. // attribute name string is an asterisk, then we have everything.
  802. //
  803. int cchEnd = wcslen(pAttrInfo->pszAttrName);
  804. fMoreRemain = pAttrInfo->pszAttrName[cchEnd - 1] != wcEnd;
  805. if (fMoreRemain)
  806. {
  807. PWSTR pwz = wcsrchr(pAttrInfo->pszAttrName, wcSep);
  808. if (!pwz)
  809. {
  810. ASSERT(FALSE && pAttrInfo->pszAttrName);
  811. fMoreRemain = FALSE;
  812. }
  813. else
  814. {
  815. pwz++; // move past the hyphen to the range end value.
  816. ASSERT(*pwz);
  817. long lEnd = _wtol(pwz);
  818. lEnd++; // start with the next value.
  819. szAttrName.Format(szFormat, lEnd);
  820. TRACE(L"Range returned is %ws, now asking for %ws\n",
  821. pAttrInfo->pszAttrName, szAttrName);
  822. }
  823. }
  824. } while (fMoreRemain);
  825. BOOL bMulti = FALSE;
  826. if (m_pConnectData->IsGC())
  827. {
  828. bMulti = IsMultiValued(pAttrInfo);
  829. }
  830. else
  831. {
  832. bMulti = IsMultiValued(m_sAttr);
  833. }
  834. if (pAttrInfo != NULL)
  835. {
  836. if (bMulti)
  837. {
  838. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  839. m_ValueList.ResetContent();
  840. POSITION pos = sList.GetHeadPosition();
  841. while (pos != NULL)
  842. {
  843. CString sValue = sList.GetNext(pos);
  844. m_ValueList.AddString(sValue);
  845. }
  846. }
  847. else
  848. {
  849. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  850. if (sList.GetCount() > 0)
  851. {
  852. m_ValueBox.SetWindowText(sList.GetHead());
  853. }
  854. if (!m_pConnectData->IsGC())
  855. {
  856. SetPropertyUI(TN_FLAG_ENABLE_REMOVE, FALSE);
  857. }
  858. }
  859. }
  860. else
  861. {
  862. GetAttrFailed();
  863. CStringList sTempList;
  864. m_pAttr = TouchAttr(pAttrInfo, bMulti);
  865. if (bMulti)
  866. {
  867. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE, TRUE);
  868. }
  869. else
  870. {
  871. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  872. }
  873. return;
  874. }
  875. m_pAttr = TouchAttr(pAttrInfo, bMulti);
  876. }
  877. }
  878. CString sSyntax;
  879. GetSyntax(m_sAttr, sSyntax);
  880. m_SyntaxBox.SetWindowText(sSyntax);
  881. }
  882. void CAttrEditor::DisplayRootDSE()
  883. {
  884. CString s = m_sPath;
  885. CComPtr<IADs> pADs;
  886. HRESULT hr, hCredResult;
  887. hr = OpenObjectWithCredentials(
  888. m_pConnectData,
  889. m_pConnectData->GetCredentialObject()->UseCredentials(),
  890. (LPWSTR)(LPCWSTR)s,
  891. IID_IADs,
  892. (LPVOID*)&pADs,
  893. NULL,
  894. hCredResult
  895. );
  896. if ( FAILED(hr) )
  897. {
  898. if (SUCCEEDED(hCredResult))
  899. {
  900. ADSIEditErrorMessage(hr);
  901. }
  902. return;
  903. }
  904. // This is to insure that the ADSI cache is current
  905. //
  906. hr = pADs->GetInfo();
  907. VARIANT var;
  908. hr = pADs->GetEx( (LPWSTR)(LPCWSTR)m_sAttr , &var );
  909. if ( FAILED(hr) )
  910. {
  911. GetAttrFailed();
  912. m_pAttr = TouchAttr(m_sAttr);
  913. return;
  914. }
  915. /////////////////////////////////////////
  916. // Convert and populate
  917. ///////////////////////////////////////////
  918. CStringList sList;
  919. hr = VariantToStringList( var, sList );
  920. if ( FAILED(hr) )
  921. {
  922. GetAttrFailed();
  923. VariantClear(&var);
  924. CStringList sTempList;
  925. m_pAttr = TouchAttr(m_sAttr);
  926. return;
  927. }
  928. VariantClear( &var );
  929. if ( IsRootDSEAttrMultiValued(m_sAttr) )
  930. {
  931. SetPropertyUI(TN_FLAG_SHOW_MULTI, FALSE);
  932. m_ValueList.ResetContent();
  933. POSITION pos = sList.GetHeadPosition();
  934. while (pos != NULL)
  935. {
  936. CString sValue = sList.GetNext(pos);
  937. m_ValueList.AddString(sValue);
  938. }
  939. }
  940. else
  941. {
  942. SetPropertyUI(~TN_FLAG_SHOW_MULTI, TRUE);
  943. s = sList.GetHead();
  944. m_ValueBox.SetWindowText(s);
  945. }
  946. // m_pAttr = TouchAttr(m_sAttr);
  947. CString sSyntax;
  948. GetSyntax(m_sAttr, sSyntax);
  949. m_SyntaxBox.SetWindowText(sSyntax);
  950. // REVEIW : this is the only occurrance of "UTCTime", if there
  951. // becomes more we may need to make a global string or something
  952. //
  953. if (sSyntax == _T("UTCTime"))
  954. {
  955. CString sFormatted, sRemainder;
  956. CString sYear, sMonth, sDay, sHour, sMinute, sSecond;
  957. int iCount = 0;
  958. sYear = s.Left(4);
  959. iCount = s.GetLength();
  960. sRemainder = s.Right(iCount - 4);
  961. sMonth = sRemainder.Left(2);
  962. iCount = sRemainder.GetLength();
  963. sRemainder = sRemainder.Right(iCount - 2);
  964. sDay = sRemainder.Left(2);
  965. iCount = sRemainder.GetLength();
  966. sRemainder = sRemainder.Right(iCount - 2);
  967. sHour = sRemainder.Left(2);
  968. iCount = sRemainder.GetLength();
  969. sRemainder = sRemainder.Right(iCount - 2);
  970. sMinute = sRemainder.Left(2);
  971. iCount = sRemainder.GetLength();
  972. sRemainder = sRemainder.Right(iCount - 2);
  973. sSecond = sRemainder.Left(2);
  974. sFormatted = sMonth + _T("/") + sDay + _T("/") + sYear + _T(" ")
  975. + sHour + _T(":") + sMinute + _T(":") + sSecond;
  976. m_ValueBox.SetWindowText(sFormatted);
  977. }
  978. }
  979. BOOL CAttrEditor::IsRootDSEAttrMultiValued(LPCWSTR lpszAttr)
  980. {
  981. int idx=0, iCount = 0;
  982. iCount = wcslen(lpszAttr);
  983. while( g_ldapRootDSESyntax[idx].lpszAttr)
  984. {
  985. if ( _wcsnicmp(g_ldapRootDSESyntax[idx].lpszAttr, lpszAttr, iCount) == 0)
  986. {
  987. return g_ldapRootDSESyntax[idx].bMulti;
  988. }
  989. idx++;
  990. }
  991. return FALSE;
  992. }
  993. // TODO : This is extremely ugly, redo it
  994. //
  995. void CAttrEditor::SetPropertyUI(DWORD dwFlags, BOOL bAnd, BOOL bReset)
  996. {
  997. if (bReset)
  998. {
  999. m_dwMultiFlags = dwFlags;
  1000. }
  1001. if (bAnd)
  1002. {
  1003. m_dwMultiFlags &= dwFlags;
  1004. }
  1005. else
  1006. {
  1007. m_dwMultiFlags |= dwFlags;
  1008. }
  1009. if (m_dwMultiFlags & TN_FLAG_SHOW_MULTI)
  1010. {
  1011. m_AddButtonHelper.SetToggleState(TRUE);
  1012. m_RemoveButtonHelper.SetToggleState(TRUE);
  1013. m_ValueList.ShowWindow(SW_SHOW);
  1014. m_ValueBox.ShowWindow(SW_HIDE);
  1015. }
  1016. else
  1017. {
  1018. m_AddButtonHelper.SetToggleState(FALSE);
  1019. m_RemoveButtonHelper.SetToggleState(FALSE);
  1020. m_ValueList.ShowWindow(SW_HIDE);
  1021. m_ValueBox.ShowWindow(SW_SHOW);
  1022. }
  1023. if (m_dwMultiFlags & TN_FLAG_ENABLE_REMOVE)
  1024. {
  1025. m_RemoveButton.EnableWindow(TRUE);
  1026. }
  1027. else
  1028. {
  1029. m_RemoveButton.EnableWindow(FALSE);
  1030. }
  1031. if (m_dwMultiFlags & TN_FLAG_ENABLE_ADD)
  1032. {
  1033. m_AddButton.EnableWindow(TRUE);
  1034. }
  1035. else
  1036. {
  1037. m_AddButton.EnableWindow(FALSE);
  1038. }
  1039. if (m_bExisting && (m_pConnectData->IsGC() || m_pConnectData->IsRootDSE()))
  1040. {
  1041. m_AttrEditBox.EnableWindow(FALSE);
  1042. }
  1043. else
  1044. {
  1045. m_AttrEditBox.EnableWindow(TRUE);
  1046. }
  1047. }
  1048. void CAttrEditor::GetSyntax(LPCWSTR lpszProp, CString& sSyntax)
  1049. {
  1050. if (m_bExisting && m_pConnectData->IsRootDSE())
  1051. {
  1052. int idx=0;
  1053. while( g_ldapRootDSESyntax[idx].lpszAttr )
  1054. {
  1055. if ( wcscmp(lpszProp, g_ldapRootDSESyntax[idx].lpszAttr) == 0 )
  1056. {
  1057. sSyntax = g_ldapRootDSESyntax[idx].lpszSyntax;
  1058. return;
  1059. }
  1060. idx++;
  1061. }
  1062. }
  1063. else
  1064. {
  1065. CComPtr<IADsProperty> pProp;
  1066. HRESULT hr, hCredResult;
  1067. CString schema;
  1068. m_pConnectData->GetAbstractSchemaPath(schema);
  1069. schema = schema + lpszProp;
  1070. hr = OpenObjectWithCredentials(
  1071. m_pConnectData,
  1072. m_pConnectData->GetCredentialObject()->UseCredentials(),
  1073. (LPWSTR)(LPCWSTR)schema,
  1074. IID_IADsProperty,
  1075. (LPVOID*) &pProp,
  1076. NULL,
  1077. hCredResult
  1078. );
  1079. if ( FAILED(hr) )
  1080. {
  1081. if (SUCCEEDED(hCredResult))
  1082. {
  1083. ADSIEditErrorMessage(hr);
  1084. }
  1085. return;
  1086. }
  1087. ///////////////////////////////////////////////////
  1088. // Create a new cached attribute and populate
  1089. //////////////////////////////////////////////////
  1090. BSTR bstr;
  1091. hr = pProp->get_Syntax( &bstr );
  1092. if ( SUCCEEDED(hr) )
  1093. {
  1094. sSyntax = bstr;
  1095. }
  1096. SysFreeString(bstr);
  1097. }
  1098. }
  1099. BOOL CAttrEditor::IsMultiValued(ADS_ATTR_INFO* pAttrInfo)
  1100. {
  1101. return (pAttrInfo->dwNumValues > 1) ? TRUE : FALSE;
  1102. }
  1103. BOOL CAttrEditor::IsMultiValued(LPCWSTR lpszProp)
  1104. {
  1105. CString schema;
  1106. BOOL bResult = FALSE;
  1107. CADSIEditContainerNode* pContNode = dynamic_cast<CADSIEditContainerNode*>(m_pTreeNode);
  1108. if (pContNode == NULL)
  1109. {
  1110. CADSIEditLeafNode* pLeafNode = dynamic_cast<CADSIEditLeafNode*>(m_pTreeNode);
  1111. ASSERT(pLeafNode != NULL);
  1112. bResult = pLeafNode->BuildSchemaPath(schema);
  1113. }
  1114. else
  1115. {
  1116. bResult = pContNode->BuildSchemaPath(schema);
  1117. }
  1118. if (!bResult)
  1119. {
  1120. return FALSE;
  1121. }
  1122. CADSIQueryObject schemaSearch;
  1123. // Initialize search object with path, username and password
  1124. //
  1125. HRESULT hr = schemaSearch.Init(schema, m_pConnectData->GetCredentialObject());
  1126. if (FAILED(hr))
  1127. {
  1128. ADSIEditErrorMessage(hr);
  1129. return FALSE;
  1130. }
  1131. int cCols = 1;
  1132. LPWSTR pszAttributes[] = {L"isSingleValued"};
  1133. LPWSTR pszDesiredAttr = L"attributeSyntax";
  1134. ADS_SEARCH_COLUMN ColumnData;
  1135. hr = schemaSearch.SetSearchPrefs(ADS_SCOPE_ONELEVEL);
  1136. if (FAILED(hr))
  1137. {
  1138. ADSIEditErrorMessage(hr);
  1139. return FALSE;
  1140. }
  1141. BOOL bMulti = FALSE;
  1142. CString csFilter;
  1143. csFilter.Format(L"(&(objectClass=attributeSchema)(lDAPDisplayName=%s))", lpszProp);
  1144. schemaSearch.SetFilterString((LPWSTR)(LPCWSTR)csFilter);
  1145. schemaSearch.SetAttributeList (pszAttributes, cCols);
  1146. hr = schemaSearch.DoQuery ();
  1147. if (SUCCEEDED(hr))
  1148. {
  1149. hr = schemaSearch.GetNextRow();
  1150. if (SUCCEEDED(hr))
  1151. {
  1152. hr = schemaSearch.GetColumn(pszAttributes[0], &ColumnData);
  1153. if (SUCCEEDED(hr))
  1154. {
  1155. TRACE(_T("\t\tisSingleValued: %d\n"),
  1156. ColumnData.pADsValues->Boolean);
  1157. bMulti = !ColumnData.pADsValues->Boolean;
  1158. }
  1159. }
  1160. }
  1161. return bMulti;
  1162. }
  1163. // NOTE : this is only called for the RootDSE or if we failed to get
  1164. // values for the attribute. An empty ADS_ATTR_INFO object is
  1165. // created but should not be modified. If values are to be changed
  1166. // or set for this object a new ADS_ATTR_INFO should be created
  1167. // with the desired block of memory allocated for the values
  1168. //
  1169. CADSIAttr* CAttrEditor::TouchAttr(LPCWSTR lpszAttr)
  1170. {
  1171. POSITION pos = m_ptouchedAttr->FindProperty(lpszAttr);
  1172. if (pos == NULL)
  1173. {
  1174. ADS_ATTR_INFO* pADsInfo = new ADS_ATTR_INFO;
  1175. if (!pADsInfo)
  1176. {
  1177. return 0;
  1178. }
  1179. memset(pADsInfo, 0, sizeof(ADS_ATTR_INFO));
  1180. int iLength = wcslen(lpszAttr);
  1181. pADsInfo->pszAttrName = new WCHAR[iLength + 1];
  1182. wcscpy(pADsInfo->pszAttrName, lpszAttr);
  1183. CADSIQueryObject schemaSearch;
  1184. BOOL bResult;
  1185. CString schema;
  1186. CADSIEditContainerNode* pContNode = m_pConnectData->GetConnectionNode();
  1187. bResult = pContNode->BuildSchemaPath(schema);
  1188. if (!bResult)
  1189. {
  1190. return NULL;
  1191. }
  1192. // Initialize search object with path, username and password
  1193. //
  1194. HRESULT hr = schemaSearch.Init(schema, m_pConnectData->GetCredentialObject());
  1195. if (FAILED(hr))
  1196. {
  1197. ADSIEditErrorMessage(hr);
  1198. return NULL;
  1199. }
  1200. int cCols = 3;
  1201. LPWSTR pszAttributes[] = {L"lDAPDisplayName", L"attributeSyntax", L"isSingleValued"};
  1202. LPWSTR pszDesiredAttr = _T("attributeSyntax");
  1203. ADS_SEARCH_COLUMN ColumnData;
  1204. hr = schemaSearch.SetSearchPrefs(ADS_SCOPE_ONELEVEL);
  1205. if (FAILED(hr))
  1206. {
  1207. ADSIEditErrorMessage(hr);
  1208. return NULL;
  1209. }
  1210. BOOL bMulti = FALSE;
  1211. CString szSyntax;
  1212. CString csFilter;
  1213. csFilter.Format(L"(&(objectClass=attributeSchema)(lDAPDisplayName=%s))", lpszAttr);
  1214. schemaSearch.SetFilterString((LPWSTR)(LPCWSTR)csFilter);
  1215. schemaSearch.SetAttributeList (pszAttributes, cCols);
  1216. hr = schemaSearch.DoQuery ();
  1217. if (SUCCEEDED(hr))
  1218. {
  1219. hr = schemaSearch.GetNextRow();
  1220. if (SUCCEEDED(hr))
  1221. {
  1222. hr = schemaSearch.GetColumn(pszDesiredAttr,
  1223. &ColumnData);
  1224. if (SUCCEEDED(hr))
  1225. {
  1226. TRACE(_T("\t\tattributeSyntax: %s\n"),
  1227. ColumnData.pADsValues->CaseIgnoreString);
  1228. ADSTYPE dwType;
  1229. dwType = GetADsTypeFromString(ColumnData.pADsValues->CaseIgnoreString, szSyntax);
  1230. pADsInfo->dwADsType = dwType;
  1231. }
  1232. hr = schemaSearch.GetColumn(pszAttributes[2], &ColumnData);
  1233. if (SUCCEEDED(hr))
  1234. {
  1235. TRACE(_T("\t\tisSingleValued: %d\n"),
  1236. ColumnData.pADsValues->Boolean);
  1237. pADsInfo->dwNumValues = 0;
  1238. bMulti = !ColumnData.pADsValues->Boolean;
  1239. }
  1240. }
  1241. }
  1242. CADSIAttr* pAttr = new CADSIAttr(pADsInfo, bMulti, szSyntax, FALSE);
  1243. m_ptouchedAttr->AddTail(pAttr);
  1244. return pAttr;
  1245. }
  1246. return m_ptouchedAttr->GetAt(pos);
  1247. }
  1248. CADSIAttr* CAttrEditor::TouchAttr(ADS_ATTR_INFO* pADsInfo, BOOL bMulti)
  1249. {
  1250. POSITION pos = m_ptouchedAttr->FindProperty(pADsInfo->pszAttrName);
  1251. if (pos == NULL)
  1252. {
  1253. CADSIAttr* pAttr = new CADSIAttr(pADsInfo, bMulti, L"");
  1254. m_ptouchedAttr->AddTail(pAttr);
  1255. return pAttr;
  1256. }
  1257. return m_ptouchedAttr->GetAt(pos);
  1258. }