Leaked source code of windows server 2003
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.

1596 lines
37 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. /*++
  3. Copyright (C) Microsoft Corporation, 1998 - 2001
  4. Module Name:
  5. NTGroups.cpp
  6. Abstract:
  7. Implementation file for the CIASGroupsAttributeEditor class.
  8. Revision History:
  9. mmaguire 08/10/98 - added new intermediate dialog for picking groups using some
  10. of byao's original implementation
  11. --*/
  12. //////////////////////////////////////////////////////////////////////////////
  13. //////////////////////////////////////////////////////////////////////////////
  14. // BEGIN INCLUDES
  15. //
  16. // standard includes:
  17. //
  18. #include "Precompiled.h"
  19. //
  20. // where we can find declaration for main class in this file:
  21. //
  22. #include <objsel.h>
  23. #include "NTGroups.h"
  24. //
  25. // where we can find declarations needed in this file:
  26. //
  27. #include <vector>
  28. #include <utility> // For "pair"
  29. #include <atltmp.h>
  30. #include <initguid.h>
  31. #include <activeds.h>
  32. #include <lmcons.h>
  33. #include <objsel.h>
  34. #include "textsid.h"
  35. #include "dialog.h"
  36. #include "dsrole.h"
  37. //
  38. // END INCLUDES
  39. //////////////////////////////////////////////////////////////////////////////
  40. //#define OLD_OBJECT_PICKER
  41. // Small wrapper class for a BYTE pointer to avoid memory leaks.
  42. template <class Pointer>
  43. class SmartPointer
  44. {
  45. public:
  46. SmartPointer()
  47. {
  48. m_Pointer = NULL;
  49. }
  50. operator Pointer()
  51. {
  52. return( m_Pointer );
  53. }
  54. Pointer * operator&()
  55. {
  56. return( & m_Pointer );
  57. }
  58. virtual ~SmartPointer()
  59. {
  60. // Override as necessary.
  61. };
  62. protected:
  63. Pointer m_Pointer;
  64. };
  65. /////////////////////////////////////////////////////////////////////////////
  66. // Declarations needed in this file:
  67. PWSTR g_wzObjectSID = _T("objectSid");
  68. // Group list delimiter:
  69. #define DELIMITER L";"
  70. // Utility functions:
  71. static HRESULT ConvertSidToTextualRepresentation( PSID pSid, CComBSTR &bstrTextualSid );
  72. static HRESULT ConvertSidToHumanReadable( PSID pSid, CComBSTR &bstrHumanReadable, LPCTSTR lpSystemName = NULL );
  73. // We needed this because the standard macro doesn't return the value from SNDMSG and
  74. // sometimes we need to know whether the operation succeeded or failed.
  75. static inline LRESULT CustomListView_SetItemState( HWND hwndLV, int i, UINT data, UINT mask)
  76. {
  77. LV_ITEM _ms_lvi;
  78. _ms_lvi.stateMask = mask;
  79. _ms_lvi.state = data;
  80. return SNDMSG((hwndLV), LVM_SETITEMSTATE, (WPARAM)i, (LPARAM)(LV_ITEM FAR *)&_ms_lvi);
  81. }
  82. /////////////////////////////////////////////////////////////////////////////
  83. // CDisplayGroupsDialog
  84. class CDisplayGroupsDialog;
  85. typedef CIASDialog<CDisplayGroupsDialog, FALSE> DISPLAY_GROUPS_FALSE;
  86. class CDisplayGroupsDialog : public DISPLAY_GROUPS_FALSE
  87. {
  88. public:
  89. CDisplayGroupsDialog( GroupList *pGroups );
  90. ~CDisplayGroupsDialog();
  91. enum { IDD = IDD_DIALOG_DISPLAY_GROUPS };
  92. BEGIN_MSG_MAP(CDisplayGroupsDialog)
  93. MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
  94. COMMAND_ID_HANDLER(IDC_BUTTON_ADD_GROUP, OnAdd)
  95. COMMAND_ID_HANDLER(IDC_BUTTON_REMOVE_GROUP, OnRemove)
  96. COMMAND_ID_HANDLER(IDOK, OnOK)
  97. COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
  98. NOTIFY_CODE_HANDLER(LVN_ITEMCHANGED, OnListViewItemChanged)
  99. // NOTIFY_CODE_HANDLER(NM_DBLCLK, OnListViewDbclk)
  100. CHAIN_MSG_MAP(DISPLAY_GROUPS_FALSE)
  101. END_MSG_MAP()
  102. LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  103. LRESULT OnRemove(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
  104. LRESULT OnAdd(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
  105. LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
  106. LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
  107. LRESULT OnListViewItemChanged(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
  108. LRESULT OnListViewDbclk(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
  109. protected:
  110. BOOL PopulateGroupList( int iStartIndex );
  111. private:
  112. HWND m_hWndGroupList;
  113. GroupList * m_pGroups;
  114. };
  115. //////////////////////////////////////////////////////////////////////////////
  116. /*++
  117. CIASGroupsAttributeEditor::Edit
  118. IIASAttributeEditor implementation.
  119. --*/
  120. //////////////////////////////////////////////////////////////////////////////
  121. STDMETHODIMP CIASGroupsAttributeEditor::Edit(IIASAttributeInfo * pIASAttributeInfo, /*[in]*/ VARIANT *pAttributeValue, /*[in, out]*/ BSTR *pReserved )
  122. {
  123. TRACE_FUNCTION("CIASGroupsAttributeEditor::Edit");
  124. HRESULT hr = S_OK;
  125. try // new could throw as could DoModal
  126. {
  127. WCHAR * pszMachineName = NULL;
  128. // Check for preconditions.
  129. // We will ignore the pIASAttributeInfo interface pointer -- it is not
  130. // needed for this attribute editor.
  131. if( ! pAttributeValue )
  132. {
  133. return E_INVALIDARG;
  134. }
  135. if( V_VT(pAttributeValue ) != VT_BSTR )
  136. {
  137. return E_INVALIDARG;
  138. }
  139. GroupList Groups;
  140. // We need to pass the machine name in somehow, so we use the
  141. // otherwise unused pReserved BSTR *.
  142. if( pReserved )
  143. {
  144. Groups.m_bstrServerName = *pReserved;
  145. }
  146. Groups.PopulateGroupsFromVariant( pAttributeValue );
  147. // ISSUE: Need to get szServerAddress in here somehow -- could use reserved.
  148. CDisplayGroupsDialog * pDisplayGroupsDialog = new CDisplayGroupsDialog( &Groups );
  149. _ASSERTE( pDisplayGroupsDialog );
  150. int iResult = pDisplayGroupsDialog->DoModal();
  151. if( IDOK == iResult )
  152. {
  153. // Clear out the old value of the variant.
  154. VariantClear(pAttributeValue);
  155. Groups.PopulateVariantFromGroups( pAttributeValue );
  156. hr = S_OK;
  157. }
  158. else
  159. {
  160. hr = S_FALSE;
  161. }
  162. }
  163. catch(...)
  164. {
  165. return E_FAIL;
  166. }
  167. return hr;
  168. }
  169. //////////////////////////////////////////////////////////////////////////////
  170. /*++
  171. CIASGroupsAttributeEditor::GetDisplayInfo
  172. IIASAttributeEditor implementation.
  173. --*/
  174. //////////////////////////////////////////////////////////////////////////////
  175. STDMETHODIMP CIASGroupsAttributeEditor::GetDisplayInfo(IIASAttributeInfo * pIASAttributeInfo, /*[in]*/ VARIANT *pAttributeValue, BSTR * pServerName, BSTR * pValueAsString, /*[in, out]*/ BSTR *pReserved )
  176. {
  177. TRACE_FUNCTION("CIASGroupsAttributeEditor::GetDisplayInfo");
  178. HRESULT hr = S_OK;
  179. // Check for preconditions.
  180. // We will ignore the pIASAttributeInfo interface pointer -- it is not
  181. // needed for this attribute editor.
  182. // We will also ignore the pVendorName BSTR pointer -- this doesn't
  183. // make sense for this attribute editor.
  184. if( ! pAttributeValue )
  185. {
  186. return E_INVALIDARG;
  187. }
  188. if( V_VT(pAttributeValue ) != VT_BSTR )
  189. {
  190. return E_INVALIDARG;
  191. }
  192. if( ! pValueAsString )
  193. {
  194. return E_INVALIDARG;
  195. }
  196. try
  197. {
  198. GroupList Groups;
  199. // We need to pass the machine name in somehow, so we use the
  200. // otherwise unused pReserved BSTR *.
  201. if( pReserved )
  202. {
  203. Groups.m_bstrServerName = *pReserved;
  204. }
  205. hr = Groups.PopulateGroupsFromVariant( pAttributeValue );
  206. if( FAILED( hr ) )
  207. {
  208. return hr;
  209. }
  210. CComBSTR bstrDisplay;
  211. GroupList::iterator thePair = Groups.begin();
  212. while( thePair != Groups.end() )
  213. {
  214. bstrDisplay += thePair->second;
  215. thePair++;
  216. if( thePair != Groups.end() )
  217. {
  218. bstrDisplay += DELIMITER;
  219. }
  220. }
  221. *pValueAsString = bstrDisplay.Copy();
  222. }
  223. catch(...)
  224. {
  225. return E_FAIL;
  226. }
  227. return hr;
  228. }
  229. //////////////////////////////////////////////////////////////////////////////
  230. /*++
  231. ConvertSidToTextualRepresentation
  232. Converts a SID to a BSTR representation. e.g. "S-1-5-32-544"
  233. --*/
  234. //////////////////////////////////////////////////////////////////////////////
  235. HRESULT ConvertSidToTextualRepresentation( PSID pSid, CComBSTR &bstrTextualSid )
  236. {
  237. HRESULT hr = S_OK;
  238. // convert the SID value to texual format
  239. WCHAR text[1024];
  240. DWORD cbText = sizeof(text)/sizeof(WCHAR);
  241. if( NO_ERROR == IASSidToTextW(pSid, text, &cbText) )
  242. {
  243. bstrTextualSid = text;
  244. }
  245. else
  246. {
  247. return E_FAIL;
  248. }
  249. return hr;
  250. }
  251. //////////////////////////////////////////////////////////////////////////////
  252. /*++
  253. ConvertSidToTextualRepresentation
  254. Converts a SID to a humanBSTR representation. e.g. "ias-domain\Users"
  255. --*/
  256. //////////////////////////////////////////////////////////////////////////////
  257. HRESULT ConvertSidToHumanReadable( PSID pSid, CComBSTR &bstrHumanReadable, LPCTSTR lpSystemName )
  258. {
  259. HRESULT hr = S_OK;
  260. // Find the group name for this sid.
  261. WCHAR wzUserName[MAX_PATH+1];
  262. WCHAR wzDomainName[MAX_PATH+1];
  263. DWORD dwUserNameLen, dwDomainNameLen;
  264. SID_NAME_USE sidUser = SidTypeGroup;
  265. ATLTRACE(_T("looking up the account name from the SID value\n"));
  266. dwUserNameLen = sizeof(wzUserName);
  267. dwDomainNameLen = sizeof(wzDomainName);
  268. if (LookupAccountSid(
  269. lpSystemName,
  270. pSid,
  271. wzUserName,
  272. &dwUserNameLen,
  273. wzDomainName,
  274. &dwDomainNameLen,
  275. &sidUser
  276. )
  277. )
  278. {
  279. bstrHumanReadable = wzDomainName;
  280. bstrHumanReadable += L"\\";
  281. bstrHumanReadable += wzUserName;
  282. }
  283. else
  284. {
  285. #ifdef DEBUG
  286. DWORD dwError = GetLastError();
  287. ATLTRACE(_T("Error: %ld\n"), dwError);
  288. #endif // DEBUG
  289. return E_FAIL;
  290. }
  291. return hr;
  292. }
  293. //////////////////////////////////////////////////////////////////////////////
  294. /*++
  295. GroupList::PopulateGroupsFromVariant
  296. Takes a pointer to a variant and populates a GroupList with that data.
  297. --*/
  298. //////////////////////////////////////////////////////////////////////////////
  299. HRESULT GroupList::PopulateGroupsFromVariant( VARIANT * pvarGroups )
  300. {
  301. TRACE_FUNCTION("GroupList::PopulateGroupsFromVariant");
  302. // Check for preconditions.
  303. _ASSERTE( V_VT(pvarGroups) == VT_BSTR );
  304. HRESULT hr = S_OK;
  305. // First, make a local copy.
  306. // ISSUE: Make sure this copies.
  307. CComBSTR bstrGroups = V_BSTR(pvarGroups);
  308. WCHAR *pwzGroupText = bstrGroups;
  309. // Each group should be separated by a comma or semicolon.
  310. PWSTR pwzToken = wcstok(pwzGroupText, DELIMITER);
  311. while (pwzToken)
  312. {
  313. PSID pSid = NULL;
  314. try
  315. {
  316. CComBSTR bstrGroupTextualSid = pwzToken;
  317. CComBSTR bstrGroupName;
  318. if( NO_ERROR != IASSidFromTextW( pwzToken, &pSid ) )
  319. {
  320. // Try the next one.
  321. throw E_FAIL;
  322. }
  323. if( FAILED( ConvertSidToHumanReadable( pSid, bstrGroupName, m_bstrServerName ) ) )
  324. {
  325. // Try the next one.
  326. throw E_FAIL;
  327. }
  328. GROUPPAIR thePair = std::make_pair( bstrGroupTextualSid, bstrGroupName );
  329. push_back( thePair );
  330. FreeSid( pSid );
  331. pwzToken = wcstok(NULL, DELIMITER);
  332. }
  333. catch(...)
  334. {
  335. if( pSid )
  336. {
  337. FreeSid( pSid );
  338. }
  339. pwzToken = wcstok(NULL, DELIMITER);
  340. }
  341. }
  342. return hr;
  343. }
  344. //////////////////////////////////////////////////////////////////////////////
  345. /*++
  346. GroupList::PopulateVariantFromGroups
  347. Takes a pointer to a variant and populates the variant with data from a GroupList.
  348. --*/
  349. //////////////////////////////////////////////////////////////////////////////
  350. HRESULT GroupList::PopulateVariantFromGroups( VARIANT * pAttributeValue )
  351. {
  352. TRACE_FUNCTION("GroupList::PopulateVariantFromGroups");
  353. HRESULT hr = S_OK;
  354. CComBSTR bstrGroupsString;
  355. GroupList::iterator thePair = begin();
  356. while( thePair != end() )
  357. {
  358. bstrGroupsString += thePair->first;
  359. thePair++;
  360. if( thePair != end() )
  361. {
  362. bstrGroupsString += DELIMITER;
  363. }
  364. }
  365. V_VT(pAttributeValue) = VT_BSTR;
  366. V_BSTR( pAttributeValue ) = bstrGroupsString.Copy();
  367. return hr;
  368. }
  369. //////////////////////////////////////////////////////////////////////////////
  370. /*++
  371. GroupList::AddPairToGroups
  372. Adds a pair to a GroupList if a pair with the same SID isn't already in the list.
  373. Note: Does nothing and return S_FALSE if Pair already in groups list.
  374. --*/
  375. //////////////////////////////////////////////////////////////////////////////
  376. HRESULT GroupList::AddPairToGroups( GROUPPAIR &thePair )
  377. {
  378. TRACE_FUNCTION("GroupList::AddPairToGroups");
  379. HRESULT hr = S_OK;
  380. try
  381. {
  382. // First, check to see if the pair is already in the group.
  383. GroupList::iterator theIterator;
  384. for( theIterator = begin(); theIterator != end(); ++theIterator )
  385. {
  386. if( 0 == wcscmp( theIterator->first, thePair.first ) )
  387. {
  388. return S_FALSE;
  389. }
  390. }
  391. push_back( thePair );
  392. }
  393. catch(...)
  394. {
  395. return E_FAIL;
  396. }
  397. return hr;
  398. }
  399. //////////////////////////////////////////////////////////////////////////////
  400. /*++
  401. GroupList::AddSelectionSidsToGroup
  402. #ifndef OLD_OBJECT_PICKER
  403. Takes a PDS_SELECTION_LIST pointer and adds all groups it points to to a
  404. GroupList.
  405. #else // OLD_OBJECT_PICKER
  406. Takes a PDSSELECTIONLIST pointer and adds all groups it points to to a
  407. GroupList.
  408. #endif // OLD_OBJECT_PICKER
  409. Returns S_OK if it adds any new groups.
  410. Returns S_FALSE and does not add entries if they are already in the GroupList.
  411. E_FAIL on error.
  412. --*/
  413. //////////////////////////////////////////////////////////////////////////////
  414. #ifndef OLD_OBJECT_PICKER
  415. HRESULT GroupList::AddSelectionSidsToGroup( PDS_SELECTION_LIST pDsSelList )
  416. #else // OLD_OBJECT_PICKER
  417. HRESULT GroupList::AddSelectionSidsToGroup( PDSSELECTIONLIST pDsSelList )
  418. #endif // OLD_OBJECT_PICKER
  419. {
  420. TRACE_FUNCTION("GroupList::AddSelectionSidsToGroup");
  421. HRESULT hr = S_OK;
  422. ULONG i;
  423. #ifndef OLD_OBJECT_PICKER
  424. PDS_SELECTION pCur = &pDsSelList->aDsSelection[0];
  425. #else // OLD_OBJECT_PICKER
  426. PDSSELECTION pCur = &pDsSelList->aDsSelection[0];
  427. #endif // OLD_OBJECT_PICKER
  428. BOOL bAtLeastOneAdded = FALSE;
  429. //
  430. // now let's get the sid value for each selection!
  431. //
  432. pCur = &pDsSelList->aDsSelection[0];
  433. for (i = 0; i < pDsSelList->cItems; ++i, ++pCur)
  434. {
  435. #ifndef OLD_OBJECT_PICKER
  436. if (V_VT(&pCur->pvarFetchedAttributes[0]) == (VT_ARRAY|VT_UI1))
  437. #else // OLD_OBJECT_PICKER
  438. if (V_VT(&pCur->pvarOtherAttributes[0]) == (VT_ARRAY|VT_UI1))
  439. #endif // OLD_OBJECT_PICKER
  440. {
  441. // succeeded: we got the SID value back!
  442. PSID pSid = NULL;
  443. #ifndef OLD_OBJECT_PICKER
  444. hr = SafeArrayAccessData(V_ARRAY(&pCur->pvarFetchedAttributes[0]), &pSid);
  445. #else // OLD_OBJECT_PICKER
  446. hr = SafeArrayAccessData(V_ARRAY(&pCur->pvarOtherAttributes[0]), &pSid);
  447. #endif // OLD_OBJECT_PICKER
  448. if ( SUCCEEDED(hr) && pSid )
  449. {
  450. CComBSTR bstrTextualSid;
  451. CComBSTR bstrHumanReadable;
  452. hr = ConvertSidToTextualRepresentation( pSid, bstrTextualSid );
  453. if( FAILED( hr ) )
  454. {
  455. // If we can't get the textual representation of the SID,
  456. // then we're hosed for this group -- we'll have nothing
  457. // to save it away as.
  458. continue;
  459. }
  460. hr = ConvertSidToHumanReadable( pSid, bstrHumanReadable, m_bstrServerName );
  461. if( FAILED( hr ) )
  462. {
  463. // For some reason, we couldn't look up a group name.
  464. // Use the textual SID to display this group.
  465. bstrHumanReadable = bstrTextualSid;
  466. }
  467. GROUPPAIR thePair = std::make_pair( bstrTextualSid, bstrHumanReadable );
  468. hr = AddPairToGroups( thePair );
  469. if( S_OK == hr )
  470. {
  471. bAtLeastOneAdded = TRUE;
  472. }
  473. }
  474. #ifndef OLD_OBJECT_PICKER
  475. SafeArrayUnaccessData(V_ARRAY(&pCur->pvarFetchedAttributes[0]));
  476. #else // OLD_OBJECT_PICKER
  477. SafeArrayUnaccessData(V_ARRAY(&pCur->pvarOtherAttributes[0]));
  478. #endif // OLD_OBJECT_PICKER
  479. }
  480. else
  481. {
  482. // we couldn't get the sid value
  483. hr = E_FAIL;
  484. }
  485. } // for
  486. // We don't seem to have encountered any errors.
  487. // Decide on return value based on whether we added any new
  488. // groups to the list that weren't already there.
  489. if( bAtLeastOneAdded )
  490. {
  491. return S_OK;
  492. }
  493. else
  494. {
  495. return S_FALSE;
  496. }
  497. }
  498. // Small wrapper class for a DsRole BYTE pointer to avoid memory leaks.
  499. class MyDsRoleBytePointer : public SmartPointer<PBYTE>
  500. {
  501. public:
  502. // We override the destructor to do DsRole specific release.
  503. ~MyDsRoleBytePointer()
  504. {
  505. if( m_Pointer )
  506. {
  507. DsRoleFreeMemory( m_Pointer );
  508. }
  509. }
  510. };
  511. //+---------------------------------------------------------------------------
  512. //
  513. // Function: GroupList::PickNtGroups
  514. //
  515. // Synopsis: pop up the objectPicker UI and choose a set of NT groups
  516. //
  517. // Arguments:
  518. // [in] HWND hWndParent: parent window;
  519. // [in] LPTSTR pszServerAddress:the machine name
  520. //
  521. //
  522. // Returns: S_OK if added new groups.
  523. // S_FALSE if no new groups in selection.
  524. // Error value on fail.
  525. //
  526. // History: Created Header byao 2/15/98 12:09:53 AM
  527. // Modified byao 3/11/98 to get domain/group names as well
  528. // Modified mmaguire 08/12/98 made method of GroupList class
  529. //
  530. //+---------------------------------------------------------------------------
  531. HRESULT GroupList::PickNtGroups( HWND hWndParent )
  532. {
  533. #ifndef OLD_OBJECT_PICKER
  534. HRESULT hr;
  535. CComPtr<IDsObjectPicker> spDsObjectPicker;
  536. hr = CoCreateInstance( CLSID_DsObjectPicker
  537. , NULL
  538. , CLSCTX_INPROC_SERVER
  539. , IID_IDsObjectPicker
  540. , (void **) &spDsObjectPicker
  541. );
  542. if( FAILED( hr ) )
  543. {
  544. return hr;
  545. }
  546. // Check to see if we are a DC -- we will use DsRoleGetPrimaryDomainInformation
  547. LPWSTR szServer = NULL;
  548. if ( m_bstrServerName && _tcslen(m_bstrServerName) )
  549. {
  550. // use machine name for remote machine
  551. szServer = m_bstrServerName;
  552. }
  553. MyDsRoleBytePointer dsInfo;
  554. if( ERROR_SUCCESS != DsRoleGetPrimaryDomainInformation( szServer, DsRolePrimaryDomainInfoBasic, &dsInfo ) )
  555. {
  556. return E_FAIL;
  557. }
  558. PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo = (PDSROLE_PRIMARY_DOMAIN_INFO_BASIC) (PBYTE) dsInfo;
  559. if( ! pInfo )
  560. {
  561. return E_FAIL;
  562. }
  563. BOOL bNotDc;
  564. if( pInfo->MachineRole == DsRole_RoleBackupDomainController || pInfo->MachineRole == DsRole_RolePrimaryDomainController )
  565. {
  566. bNotDc = FALSE;
  567. }
  568. else
  569. {
  570. bNotDc = TRUE;
  571. }
  572. // At the most, we need only three scopes (we may use less).
  573. DSOP_SCOPE_INIT_INFO aScopes[3];
  574. ZeroMemory( aScopes, sizeof(aScopes) );
  575. int iScopeCount = 0;
  576. // We need to add this first, DSOP_SCOPE_TYPE_TARGET_COMPUTER type
  577. // scope only if we are not on the DC.
  578. if( bNotDc )
  579. {
  580. // Include a scope for the target computer's scope.
  581. aScopes[iScopeCount].cbSize = sizeof( DSOP_SCOPE_INIT_INFO );
  582. aScopes[iScopeCount].flType = DSOP_SCOPE_TYPE_TARGET_COMPUTER;
  583. // Set what filters to apply for this scope.
  584. aScopes[iScopeCount].FilterFlags.flDownlevel= DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS
  585. | DSOP_DOWNLEVEL_FILTER_EXCLUDE_BUILTIN_GROUPS
  586. ;
  587. }
  588. // Move on to next scope.
  589. ++iScopeCount;
  590. // Set the downlevel scopes
  591. aScopes[iScopeCount].cbSize = sizeof( DSOP_SCOPE_INIT_INFO );
  592. aScopes[iScopeCount].flType = DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN
  593. | DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN
  594. ;
  595. // Set what filters to apply for this scope.
  596. aScopes[iScopeCount].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS
  597. | DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS
  598. | DSOP_DOWNLEVEL_FILTER_EXCLUDE_BUILTIN_GROUPS
  599. ;
  600. // Move on to next scope.
  601. ++iScopeCount;
  602. // For all other scopes, use same as target computer but exclude BUILTIN_GROUPS
  603. aScopes[iScopeCount].cbSize = sizeof( DSOP_SCOPE_INIT_INFO );
  604. aScopes[iScopeCount].flType = DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN
  605. | DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN
  606. ;
  607. // Set what filters to apply for this scope.
  608. aScopes[iScopeCount].FilterFlags.Uplevel.flBothModes = 0
  609. /* BUG 263302 -- not to show domain local groups
  610. | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE
  611. ~ BUG */
  612. | DSOP_FILTER_GLOBAL_GROUPS_SE
  613. ;
  614. aScopes[iScopeCount].FilterFlags.Uplevel.flNativeModeOnly = DSOP_FILTER_UNIVERSAL_GROUPS_SE;
  615. // Now fill up the correct structures and call Initialize.
  616. DSOP_INIT_INFO InitInfo;
  617. ZeroMemory( &InitInfo, sizeof(InitInfo) );
  618. InitInfo.cbSize = sizeof( InitInfo );
  619. InitInfo.cDsScopeInfos = iScopeCount + 1;
  620. InitInfo.aDsScopeInfos = aScopes;
  621. InitInfo.flOptions = DSOP_FLAG_MULTISELECT;
  622. // Requested attributes:
  623. InitInfo.cAttributesToFetch = 1; // We only need the SID value.
  624. LPTSTR pSidAttr = g_wzObjectSID;
  625. LPCTSTR aptzRequestedAttributes[1];
  626. aptzRequestedAttributes[0] = pSidAttr;
  627. InitInfo.apwzAttributeNames = (const WCHAR **)aptzRequestedAttributes;
  628. if ( m_bstrServerName && _tcslen(m_bstrServerName) )
  629. {
  630. // use machine name for remote machine
  631. InitInfo.pwzTargetComputer = m_bstrServerName;
  632. }
  633. else
  634. {
  635. // or use NULL for local machine
  636. InitInfo.pwzTargetComputer = NULL;
  637. }
  638. hr = spDsObjectPicker->Initialize(&InitInfo);
  639. if( FAILED( hr ) )
  640. {
  641. return hr;
  642. }
  643. CComPtr<IDataObject> spDataObject;
  644. hr = spDsObjectPicker->InvokeDialog( hWndParent, &spDataObject );
  645. if( FAILED( hr ) || hr == S_FALSE )
  646. {
  647. // When user selected "Cancel", ObjectPicker will return S_FALSE.
  648. return hr;
  649. }
  650. STGMEDIUM stgmedium =
  651. {
  652. TYMED_HGLOBAL
  653. , NULL
  654. };
  655. UINT cf = RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST);
  656. if( 0 == cf )
  657. {
  658. return E_FAIL;
  659. }
  660. FORMATETC formatetc =
  661. {
  662. (CLIPFORMAT)cf
  663. , NULL
  664. , DVASPECT_CONTENT
  665. , -1
  666. , TYMED_HGLOBAL
  667. };
  668. hr = spDataObject->GetData( &formatetc, &stgmedium );
  669. if( FAILED( hr ) )
  670. {
  671. return hr;
  672. }
  673. PDS_SELECTION_LIST pDsSelList = (PDS_SELECTION_LIST) GlobalLock( stgmedium.hGlobal );
  674. if( ! pDsSelList )
  675. {
  676. return E_FAIL;
  677. }
  678. hr = AddSelectionSidsToGroup( pDsSelList );
  679. GlobalUnlock( stgmedium.hGlobal );
  680. ReleaseStgMedium( &stgmedium );
  681. #else // OLD_OBJECT_PICKER
  682. HRESULT hr;
  683. BOOL fBadArg = FALSE;
  684. ULONG flDsObjectPicker = 0;
  685. ULONG flUserGroupObjectPicker = 0;
  686. ULONG flComputerObjectPicker = 0;
  687. ULONG flInitialScope = DSOP_SCOPE_SPECIFIED_MACHINE;
  688. flDsObjectPicker = flInitialScope
  689. | DSOP_SCOPE_DIRECTORY
  690. | DSOP_SCOPE_DOMAIN_TREE
  691. | DSOP_SCOPE_EXTERNAL_TRUSTED_DOMAINS
  692. ;
  693. flUserGroupObjectPicker =
  694. UGOP_GLOBAL_GROUPS
  695. | UGOP_ACCOUNT_GROUPS_SE
  696. | UGOP_UNIVERSAL_GROUPS_SE
  697. | UGOP_RESOURCE_GROUPS_SE
  698. | UGOP_LOCAL_GROUPS
  699. ;
  700. //
  701. // Call the API
  702. //
  703. PDSSELECTIONLIST pDsSelList = NULL;
  704. GETUSERGROUPSELECTIONINFO ugsi;
  705. ZeroMemory(&ugsi, sizeof ugsi);
  706. ugsi.cbSize = sizeof(GETUSERGROUPSELECTIONINFO);
  707. ugsi.hwndParent = hWndParent; // parent window
  708. if ( m_bstrServerName && _tcslen(m_bstrServerName) )
  709. {
  710. // use machine name for remote machine
  711. ugsi.ptzComputerName= m_bstrServerName;
  712. }
  713. else
  714. {
  715. // or use NULL for local machine
  716. ugsi.ptzComputerName= NULL;
  717. }
  718. ugsi.ptzDomainName = NULL;
  719. ugsi.flObjectPicker = OP_MULTISELECT;
  720. ugsi.flDsObjectPicker = flDsObjectPicker;
  721. ugsi.flStartingScope = flInitialScope;
  722. ugsi.flUserGroupObjectPickerSpecifiedDomain = flUserGroupObjectPicker;
  723. ugsi.flUserGroupObjectPickerOtherDomains = flUserGroupObjectPicker;
  724. ugsi.ppDsSelList = &pDsSelList;
  725. // requested attributes:
  726. LPTSTR pSidAttr = g_wzObjectSID;
  727. LPCTSTR aptzRequestedAttributes[1];
  728. aptzRequestedAttributes[0] = pSidAttr;
  729. ugsi.cRequestedAttributes = 1; // we only need the SID value
  730. ugsi.aptzRequestedAttributes = (const WCHAR **)aptzRequestedAttributes;
  731. hr = GetUserGroupSelection(&ugsi);
  732. if (SUCCEEDED(hr) && hr != S_FALSE )
  733. {
  734. // when user selected "Cancel", ObjectPicker will return S_FALSE
  735. // get selected SIDs
  736. hr = AddSelectionSidsToGroup( pDsSelList );
  737. }
  738. if (pDsSelList)
  739. {
  740. FreeDsSelectionList(pDsSelList);
  741. }
  742. #endif // OLD_OBJECT_PICKER
  743. return hr;
  744. }
  745. /////////////////////////////////////////////////////////////////////////////
  746. // CDisplayGroupsDialog
  747. //////////////////////////////////////////////////////////////////////////////
  748. /*++
  749. CDisplayGroupsDialog::CDisplayGroupsDialog
  750. Constructor
  751. --*/
  752. //////////////////////////////////////////////////////////////////////////////
  753. CDisplayGroupsDialog::CDisplayGroupsDialog( GroupList *pGroups )
  754. {
  755. TRACE_FUNCTION("CDisplayGroupsDialog::CDisplayGroupsDialog");
  756. m_pGroups = pGroups;
  757. }
  758. //////////////////////////////////////////////////////////////////////////////
  759. /*++
  760. CDisplayGroupsDialog::~CDisplayGroupsDialog
  761. Destructor
  762. --*/
  763. //////////////////////////////////////////////////////////////////////////////
  764. CDisplayGroupsDialog::~CDisplayGroupsDialog()
  765. {
  766. }
  767. //////////////////////////////////////////////////////////////////////////////
  768. /*++
  769. CDisplayGroupsDialog::OnInitDialog
  770. --*/
  771. //////////////////////////////////////////////////////////////////////////////
  772. LRESULT CDisplayGroupsDialog::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  773. {
  774. TRACE_FUNCTION("CDisplayGroupsDialog::OnInitDialog");
  775. m_hWndGroupList = GetDlgItem(IDC_LIST_GROUPS);
  776. //
  777. // first, set the list box to 2 columns
  778. //
  779. LVCOLUMN lvc;
  780. int iCol;
  781. WCHAR achColumnHeader[256];
  782. HINSTANCE hInst;
  783. // initialize the LVCOLUMN structure
  784. lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  785. lvc.fmt = LVCFMT_LEFT;
  786. lvc.cx = 300;
  787. lvc.pszText = achColumnHeader;
  788. // first column header: name
  789. hInst = _Module.GetModuleInstance();
  790. ::LoadStringW(hInst, IDS_DISPLAY_GROUPS_FIRSTCOLUMN, achColumnHeader, sizeof(achColumnHeader)/sizeof(achColumnHeader[0]));
  791. lvc.iSubItem = 0;
  792. ListView_InsertColumn(m_hWndGroupList, 0, &lvc);
  793. //
  794. // populate the list control with data
  795. //
  796. if ( ! PopulateGroupList( 0 ) )
  797. {
  798. ErrorTrace(ERROR_NAPMMC_SELATTRDLG, "PopulateRuleAttrs() failed");
  799. return 0;
  800. }
  801. // Set some items based on whether the list is empty or not.
  802. if( m_pGroups->size() )
  803. {
  804. // Select the first item.
  805. ListView_SetItemState(m_hWndGroupList, 0, LVIS_SELECTED, LVIS_SELECTED);
  806. }
  807. else
  808. {
  809. // The list is empty -- disable the OK button.
  810. ::EnableWindow(GetDlgItem(IDOK), FALSE);
  811. // Make sure the Remove button is not enabled initially.
  812. ::EnableWindow(GetDlgItem(IDC_BUTTON_REMOVE_GROUP), FALSE);
  813. }
  814. #ifdef DEBUG
  815. m_pGroups->DebugPrintGroups();
  816. #endif // DEBUG
  817. // Set the listview control so that double-click anywhere in row selects.
  818. ListView_SetExtendedListViewStyleEx(m_hWndGroupList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
  819. return 1; // Let the system set the focus
  820. }
  821. //+---------------------------------------------------------------------------
  822. //
  823. // Function: OnListViewDbclk
  824. //
  825. // Class: CDisplayGroupsDialog
  826. //
  827. // Synopsis: handle the case where the user has changed a selection
  828. // enable/disable OK, CANCEL button accordingly
  829. //
  830. // Arguments: int idCtrl - ID of the list control
  831. // LPNMHDR pnmh - notification message
  832. // BOOL& bHandled - handled or not?
  833. //
  834. // Returns: LRESULT -
  835. //
  836. // History: Created Header byao 2/19/98 11:15:30 PM
  837. // modified mmaguire 08/12/98 for use in Group List dialog
  838. //
  839. //+---------------------------------------------------------------------------
  840. //LRESULT CDisplayGroupsDialog::OnListViewDbclk(int idCtrl,
  841. // LPNMHDR pnmh,
  842. // BOOL& bHandled)
  843. //{
  844. // TRACE_FUNCTION("CDisplayGroupsDialog::OnListViewDbclk");
  845. //
  846. // return OnAdd(idCtrl, IDC_BUTTON_ADD_CONDITION, m_hWndGroupList, bHandled); // the same as ok;
  847. //}
  848. //////////////////////////////////////////////////////////////////////////////
  849. /*++
  850. CDisplayGroupsDialog::OnAdd
  851. --*/
  852. //////////////////////////////////////////////////////////////////////////////
  853. LRESULT CDisplayGroupsDialog::OnAdd(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  854. {
  855. TRACE_FUNCTION("CDisplayGroupsDialog::OnAdd");
  856. HRESULT hr;
  857. #ifdef DEBUG
  858. m_pGroups->DebugPrintGroups();
  859. #endif // DEBUG
  860. // Store the previous size of the list.
  861. int iSize = m_pGroups->size();
  862. //
  863. // NTGroups Picker
  864. //
  865. hr = m_pGroups->PickNtGroups( m_hWnd );
  866. #ifdef DEBUG
  867. m_pGroups->DebugPrintGroups();
  868. #endif // DEBUG
  869. //
  870. // PickNtGroups will return S_FALSE when user cancelled out of the dialog.
  871. //
  872. if ( SUCCEEDED(hr) && hr != S_FALSE )
  873. {
  874. // Add the new groups to the display's list.
  875. PopulateGroupList( iSize );
  876. // Make sure the OK button is enabled.
  877. ::EnableWindow(GetDlgItem(IDOK), TRUE);
  878. }
  879. else
  880. {
  881. ErrorTrace(ERROR_NAPMMC_NTGCONDITION, "NTGroup picker failed, err = %x", hr);
  882. if ( hr == E_NOTIMPL )
  883. {
  884. // we return this error whether the SID value can't be retrieved
  885. ShowErrorDialog( m_hWnd,
  886. IDS_ERROR_OBJECT_PICKER_NO_SIDS,
  887. NULL,
  888. hr
  889. );
  890. }
  891. else if ( hr != S_FALSE )
  892. {
  893. ShowErrorDialog( m_hWnd,
  894. IDS_ERROR_OBJECT_PICKER,
  895. NULL,
  896. hr
  897. );
  898. }
  899. }
  900. // ISSUE: This function wants an LRESULT, not and HRESULT
  901. // -- not sure of importance of return code here.
  902. return S_OK;
  903. }
  904. //////////////////////////////////////////////////////////////////////////////
  905. /*++
  906. CDisplayGroupsDialog::OnRemove
  907. --*/
  908. //////////////////////////////////////////////////////////////////////////////
  909. LRESULT CDisplayGroupsDialog::OnRemove(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  910. {
  911. TRACE_FUNCTION("CDisplayGroupsDialog::OnRemove");
  912. #ifdef DEBUG
  913. m_pGroups->DebugPrintGroups();
  914. #endif // DEBUG
  915. //
  916. // Has the user chosen any condition type yet?
  917. //
  918. LVITEM lvi;
  919. // Find out what's selected.
  920. // MAM: This is not what we want here: int iIndex = ListView_GetSelectionMark(m_hWndGroupList);
  921. int iSelected = ListView_GetNextItem(m_hWndGroupList, -1, LVNI_SELECTED);
  922. DebugTrace(DEBUG_NAPMMC_SELATTRDLG, "Selected item: %d", iSelected );
  923. if( -1 != iSelected )
  924. {
  925. // The index inside the attribute list is stored as the lParam of this item.
  926. m_pGroups->erase( m_pGroups->begin() + iSelected );
  927. ListView_DeleteItem(m_hWndGroupList, iSelected );
  928. // The user may have removed all of the groups, leaving an
  929. // empty listnd out if the user should be able to click OK.
  930. if( ! m_pGroups->size() )
  931. {
  932. // Yes, disable the ok button.
  933. ::EnableWindow(GetDlgItem(IDOK), FALSE);
  934. }
  935. // Try to make sure that the same position remains selected.
  936. if( ! CustomListView_SetItemState(m_hWndGroupList, iSelected, LVIS_SELECTED, LVIS_SELECTED) )
  937. {
  938. // We failed to select the same position, probably because we just
  939. // deleted the last element. Try to select the position before it.
  940. ListView_SetItemState(m_hWndGroupList, iSelected -1, LVIS_SELECTED, LVIS_SELECTED);
  941. }
  942. }
  943. #ifdef DEBUG
  944. m_pGroups->DebugPrintGroups();
  945. #endif // DEBUG
  946. return 0;
  947. }
  948. //////////////////////////////////////////////////////////////////////////////
  949. /*++
  950. CDisplayGroupsDialog::OnOK
  951. --*/
  952. //////////////////////////////////////////////////////////////////////////////
  953. LRESULT CDisplayGroupsDialog::OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  954. {
  955. TRACE_FUNCTION("CDisplayGroupsDialog::OnOK");
  956. EndDialog(TRUE);
  957. return 0;
  958. }
  959. //////////////////////////////////////////////////////////////////////////////
  960. /*++
  961. CDisplayGroupsDialog::OnCancel
  962. --*/
  963. //////////////////////////////////////////////////////////////////////////////
  964. LRESULT CDisplayGroupsDialog::OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  965. {
  966. TRACE_FUNCTION("+NAPMMC+:# CDisplayGroupsDialog::OnCancel\n");
  967. // FALSE will be the return value of the DoModal call on this dialog.
  968. EndDialog(FALSE);
  969. return 0;
  970. }
  971. //////////////////////////////////////////////////////////////////////////////
  972. /*++
  973. CDisplayGroupsDialog::PopulateGroupList
  974. --*/
  975. //////////////////////////////////////////////////////////////////////////////
  976. BOOL CDisplayGroupsDialog::PopulateGroupList( int iStartIndex )
  977. {
  978. TRACE_FUNCTION("CDisplayGroupsDialog::PopulateCondAttrs");
  979. int iIndex;
  980. WCHAR wzText[MAX_PATH];
  981. WCHAR * pszNextGroup;
  982. LVITEM lvi;
  983. lvi.mask = LVIF_TEXT | LVIF_STATE;
  984. lvi.state = 0;
  985. lvi.stateMask = 0;
  986. lvi.iSubItem = 0;
  987. lvi.iItem = iStartIndex;
  988. GroupList::iterator thePair;
  989. for( thePair = m_pGroups->begin() + iStartIndex ; thePair != m_pGroups->end(); ++thePair )
  990. {
  991. lvi.pszText = thePair->second;
  992. ListView_InsertItem(m_hWndGroupList, &lvi);
  993. // ListView_SetItemText(m_hWndGroupList, iIndex, 1, L"@Not yet implemented");
  994. ++lvi.iItem;
  995. }
  996. return TRUE;
  997. }
  998. //////////////////////////////////////////////////////////////////////////////
  999. /*++
  1000. CDisplayGroupsDialog::OnListViewItemChanged
  1001. We enable or disable the Remove button depending on whether an item is selected.
  1002. --*/
  1003. //////////////////////////////////////////////////////////////////////////////
  1004. LRESULT CDisplayGroupsDialog::OnListViewItemChanged(int idCtrl,
  1005. LPNMHDR pnmh,
  1006. BOOL& bHandled)
  1007. {
  1008. TRACE_FUNCTION("CDisplayGroupsDialog::OnListViewItemChanged");
  1009. // Find out what's selected.
  1010. int iSelected = ListView_GetNextItem(m_hWndGroupList, -1, LVNI_SELECTED);
  1011. if (-1 == iSelected )
  1012. {
  1013. if( ::GetFocus() == GetDlgItem(IDC_BUTTON_REMOVE_GROUP))
  1014. ::SetFocus(GetDlgItem(IDC_BUTTON_ADD_GROUP));
  1015. // The user selected nothing, let's disable the remove button.
  1016. ::EnableWindow(GetDlgItem(IDC_BUTTON_REMOVE_GROUP), FALSE);
  1017. }
  1018. else
  1019. {
  1020. // Yes, enable the remove button.
  1021. ::EnableWindow(GetDlgItem(IDC_BUTTON_REMOVE_GROUP), TRUE);
  1022. }
  1023. bHandled = FALSE;
  1024. return 0;
  1025. }
  1026. #ifdef DEBUG
  1027. HRESULT GroupList::DebugPrintGroups()
  1028. {
  1029. TRACE_FUNCTION("GroupList::DebugPrintGroups");
  1030. DebugTrace(DEBUG_NAPMMC_SELATTRDLG, "Begin GroupList dump" );
  1031. GroupList::iterator thePair;
  1032. for( thePair = begin(); thePair != end(); ++thePair )
  1033. {
  1034. thePair->second;
  1035. DebugTrace(DEBUG_NAPMMC_SELATTRDLG, "Group: %ws %ws", thePair->first, thePair->second );
  1036. }
  1037. DebugTrace(DEBUG_NAPMMC_SELATTRDLG, "End GroupList dump" );
  1038. return S_OK;
  1039. }
  1040. #endif // DEBUG
  1041. //////////////////////////////////////////////////////////////////////////////
  1042. /*++
  1043. NTGroup_ListView::AddMoreGroups
  1044. --*/
  1045. //////////////////////////////////////////////////////////////////////////////
  1046. DWORD NTGroup_ListView::AddMoreGroups()
  1047. {
  1048. if ( m_hListView == NULL )
  1049. return 0;
  1050. // Store the previous size of the list.
  1051. int iSize = GroupList::size();
  1052. //
  1053. // NTGroups Picker
  1054. //
  1055. HRESULT hr = GroupList::PickNtGroups( m_hParent );
  1056. //
  1057. // PickNtGroups will return S_FALSE when user cancelled out of the dialog.
  1058. //
  1059. if ( SUCCEEDED(hr) && hr != S_FALSE )
  1060. {
  1061. // Add the new groups to the display's list.
  1062. PopulateGroupList( iSize );
  1063. }
  1064. else
  1065. {
  1066. if ( hr == E_NOTIMPL )
  1067. {
  1068. // we return this error whether the SID value can't be retrieved
  1069. ShowErrorDialog( m_hParent,
  1070. IDS_ERROR_OBJECT_PICKER_NO_SIDS,
  1071. NULL,
  1072. hr
  1073. );
  1074. }
  1075. else if ( hr != S_FALSE )
  1076. {
  1077. ShowErrorDialog( m_hParent,
  1078. IDS_ERROR_OBJECT_PICKER,
  1079. NULL,
  1080. hr
  1081. );
  1082. }
  1083. }
  1084. // ISSUE: This function wants an LRESULT, not and HRESULT
  1085. // -- not sure of importance of return code here.
  1086. return S_OK;
  1087. }
  1088. //////////////////////////////////////////////////////////////////////////////
  1089. /*++
  1090. NTGroup_ListView::RemoveSelectedGroups
  1091. --*/
  1092. //////////////////////////////////////////////////////////////////////////////
  1093. DWORD NTGroup_ListView::RemoveSelectedGroups()
  1094. {
  1095. if ( m_hListView == NULL)
  1096. return 0;
  1097. //
  1098. // Has the user chosen any condition type yet?
  1099. //
  1100. LVITEM lvi;
  1101. // Find out what's selected.
  1102. int iSelected = ListView_GetNextItem(m_hListView, -1, LVNI_SELECTED);
  1103. if( -1 != iSelected )
  1104. {
  1105. // The index inside the attribute list is stored as the lParam of this item.
  1106. GroupList::erase( GroupList::begin() + iSelected );
  1107. ListView_DeleteItem(m_hListView, iSelected );
  1108. // Try to make sure that the same position remains selected.
  1109. if( ! CustomListView_SetItemState(m_hListView, iSelected, LVIS_SELECTED, LVIS_SELECTED) )
  1110. {
  1111. // We failed to select the same position, probably because we just
  1112. // deleted the last element. Try to select the position before it.
  1113. ListView_SetItemState(m_hListView, iSelected -1, LVIS_SELECTED, LVIS_SELECTED);
  1114. }
  1115. }
  1116. return (iSelected != -1 ? 1 : 0);
  1117. }
  1118. //////////////////////////////////////////////////////////////////////////////
  1119. /*++
  1120. NTGroup_ListView::PopulateGroupList
  1121. --*/
  1122. //////////////////////////////////////////////////////////////////////////////
  1123. BOOL NTGroup_ListView::PopulateGroupList( int iStartIndex )
  1124. {
  1125. if ( m_hListView == NULL)
  1126. return 0;
  1127. int iIndex;
  1128. WCHAR wzText[MAX_PATH];
  1129. WCHAR * pszNextGroup;
  1130. LVITEM lvi;
  1131. lvi.mask = LVIF_TEXT | LVIF_STATE;
  1132. lvi.state = 0;
  1133. lvi.stateMask = 0;
  1134. lvi.iSubItem = 0;
  1135. lvi.iItem = iStartIndex;
  1136. GroupList::iterator thePair;
  1137. for( thePair = GroupList::begin() + iStartIndex ; thePair != GroupList::end(); ++thePair )
  1138. {
  1139. lvi.pszText = thePair->second;
  1140. ListView_InsertItem(m_hListView, &lvi);
  1141. ++lvi.iItem;
  1142. }
  1143. return TRUE;
  1144. }