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.

1829 lines
53 KiB

  1. #include "pch.h"
  2. #pragma hdrstop
  3. #include "ownerdlg.h"
  4. #include "ownerlst.h"
  5. #include "resource.h"
  6. #include "uihelp.h"
  7. #include "uiutils.h"
  8. //
  9. // Private message used to indicate that the owner list thread
  10. // has completed it's work.
  11. //
  12. const UINT PWM_OWNERLIST_COMPLETE = WM_USER + 1;
  13. //
  14. // Mask bits indicating what operations are allowed for a given
  15. // file selection set in the listview.
  16. //
  17. const DWORD ACTION_NONE = 0x00000000;
  18. const DWORD ACTION_TAKEOWNERSHIP = 0x00000001;
  19. const DWORD ACTION_MOVE = 0x00000002;
  20. const DWORD ACTION_DELETE = 0x00000004;
  21. const DWORD ACTION_ANY = 0x00000007;
  22. const static DWORD rgFileOwnerDialogHelpIDs[] =
  23. {
  24. IDC_CMB_OWNERDLG_OWNERS, IDH_CMB_OWNERDLG_OWNERS,
  25. IDC_LV_OWNERDLG, IDH_LV_OWNERDLG,
  26. IDC_BTN_OWNERDLG_DELETE, IDH_BTN_OWNERDLG_DELETE,
  27. IDC_BTN_OWNERDLG_MOVETO, IDH_BTN_OWNERDLG_MOVETO,
  28. IDC_BTN_OWNERDLG_TAKE, IDH_BTN_OWNERDLG_TAKE,
  29. IDC_BTN_OWNERDLG_BROWSE, IDH_BTN_OWNERDLG_BROWSE,
  30. IDC_EDIT_OWNERDLG_MOVETO, IDH_EDIT_OWNERDLG_MOVETO,
  31. 0,0
  32. };
  33. CFileOwnerDialog::CFileOwnerDialog(HINSTANCE hInstance,
  34. HWND hwndParent,
  35. LPCTSTR pszVolumeRoot,
  36. const CArray<IDiskQuotaUser *>& rgpOwners
  37. ) : m_hInstance(hInstance),
  38. m_hwndParent(hwndParent),
  39. m_hwndDlg(NULL),
  40. m_hwndLV(NULL),
  41. m_hwndOwnerCombo(NULL),
  42. m_hwndEditMoveTo(NULL),
  43. m_iLastColSorted(-1),
  44. m_bSortAscending(true),
  45. m_bAbort(false),
  46. m_hOwnerListThread(NULL),
  47. m_rgpOwners(rgpOwners),
  48. m_strVolumeRoot(pszVolumeRoot)
  49. {
  50. }
  51. CFileOwnerDialog::~CFileOwnerDialog(
  52. void
  53. )
  54. {
  55. if (NULL != m_hOwnerListThread)
  56. {
  57. CloseHandle(m_hOwnerListThread);
  58. }
  59. }
  60. INT_PTR
  61. CFileOwnerDialog::Run(
  62. void
  63. )
  64. {
  65. DBGTRACE((DM_VIEW, DL_HIGH, TEXT("CFileOwnerDialog::Run")));
  66. return DialogBoxParam(m_hInstance,
  67. MAKEINTRESOURCE(IDD_OWNERSANDFILES),
  68. m_hwndParent,
  69. DlgProc,
  70. (LPARAM)this);
  71. }
  72. INT_PTR CALLBACK
  73. CFileOwnerDialog::DlgProc(
  74. HWND hwnd,
  75. UINT uMsg,
  76. WPARAM wParam,
  77. LPARAM lParam
  78. )
  79. {
  80. //
  81. // Retrieve the dialog object's ptr from the window's userdata.
  82. // Place there in response to WM_INITDIALOG.
  83. //
  84. CFileOwnerDialog *pThis = (CFileOwnerDialog *)GetWindowLongPtr(hwnd, DWLP_USER);
  85. try
  86. {
  87. switch(uMsg)
  88. {
  89. case WM_INITDIALOG:
  90. //
  91. // Store "this" ptr in window's userdata.
  92. //
  93. SetWindowLongPtr(hwnd, DWLP_USER, (INT_PTR)lParam);
  94. pThis = (CFileOwnerDialog *)lParam;
  95. //
  96. // Save the HWND in our object. We'll need it later.
  97. //
  98. pThis->m_hwndDlg = hwnd;
  99. return pThis->OnInitDialog(hwnd);
  100. case WM_DESTROY:
  101. return pThis->OnDestroy(hwnd);
  102. case WM_COMMAND:
  103. return pThis->OnCommand(hwnd, wParam, lParam);
  104. case WM_NOTIFY:
  105. return pThis->OnNotify(hwnd, wParam, lParam);
  106. case WM_CONTEXTMENU:
  107. return pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  108. break;
  109. case PWM_OWNERLIST_COMPLETE:
  110. pThis->OnOwnerListComplete();
  111. break;
  112. case WM_SETCURSOR:
  113. return pThis->OnSetCursor(hwnd);
  114. break;
  115. case WM_HELP:
  116. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, STR_DSKQUOUI_HELPFILE,
  117. HELP_WM_HELP, (DWORD_PTR)(LPTSTR) rgFileOwnerDialogHelpIDs);
  118. return TRUE;
  119. }
  120. }
  121. catch(CAllocException& me)
  122. {
  123. //
  124. // Announce any out-of-memory errors associated with running the dlg.
  125. //
  126. DiskQuotaMsgBox(GetDesktopWindow(),
  127. IDS_OUTOFMEMORY,
  128. IDS_TITLE_DISK_QUOTA,
  129. MB_ICONERROR | MB_OK);
  130. }
  131. return FALSE;
  132. }
  133. INT_PTR
  134. CFileOwnerDialog::OnInitDialog(
  135. HWND hwnd
  136. )
  137. {
  138. DBGTRACE((DM_VIEW, DL_HIGH, TEXT("CFileOwnerDialog::OnInitDialog")));
  139. //
  140. // Save HWNDs of controls we'll need later.
  141. //
  142. m_hwndLV = GetDlgItem(hwnd, IDC_LV_OWNERDLG);
  143. m_hwndOwnerCombo = GetDlgItem(hwnd, IDC_CMB_OWNERDLG_OWNERS);
  144. m_hwndEditMoveTo = GetDlgItem(hwnd, IDC_EDIT_OWNERDLG_MOVETO);
  145. //
  146. // We want these controls to be disabled until the owner list
  147. // has been populated.
  148. //
  149. EnableWindow(m_hwndLV, FALSE);
  150. EnableWindow(m_hwndOwnerCombo, FALSE);
  151. EnableWindow(m_hwndEditMoveTo, FALSE);
  152. EnableWindow(GetDlgItem(hwnd, IDC_BTN_OWNERDLG_BROWSE), FALSE);
  153. EnableWindow(GetDlgItem(hwnd, IDC_CBX_OWNERDLG_EXCLUDEDIRS), FALSE);
  154. EnableWindow(GetDlgItem(hwnd, IDC_CBX_OWNERDLG_EXCLUDEFILES), FALSE);
  155. //
  156. // Build the list of owners and filenames on the volume.
  157. // This can take a while depending on how many owners
  158. // are in m_rgpOwners, the size of the volume and how many
  159. // files each owner owns. First clear the owner list in case Run()
  160. // is being called multiple times on the same dialog object.
  161. // Note that we build this list on a background thread so that
  162. // we don't block the UI while it's building.
  163. //
  164. m_OwnerList.Clear();
  165. DWORD idThread;
  166. m_hOwnerListThread = CreateThread(NULL,
  167. 0,
  168. OwnerListThreadProc,
  169. this,
  170. 0,
  171. &idThread);
  172. return TRUE;
  173. }
  174. INT_PTR
  175. CFileOwnerDialog::OnSetCursor(
  176. HWND hwnd
  177. )
  178. {
  179. if (m_hOwnerListThread && (WAIT_TIMEOUT == WaitForSingleObject(m_hOwnerListThread, 0)))
  180. {
  181. SetCursor(LoadCursor(NULL, IDC_WAIT));
  182. return TRUE;
  183. }
  184. return FALSE;
  185. }
  186. INT_PTR
  187. CFileOwnerDialog::OnDestroy(
  188. HWND hwnd
  189. )
  190. {
  191. m_bAbort = true;
  192. if (NULL != m_hOwnerListThread)
  193. {
  194. WaitForSingleObject(m_hOwnerListThread, INFINITE);
  195. }
  196. return TRUE;
  197. }
  198. DWORD
  199. CFileOwnerDialog::OwnerListThreadProc( // [static]
  200. LPVOID pvParam
  201. )
  202. {
  203. CFileOwnerDialog *pThis = (CFileOwnerDialog *)pvParam;
  204. pThis->BuildFileOwnerList(pThis->m_strVolumeRoot,
  205. pThis->m_rgpOwners,
  206. &(pThis->m_OwnerList));
  207. PostMessage(pThis->m_hwndDlg, PWM_OWNERLIST_COMPLETE, 0, 0);
  208. return 0;
  209. }
  210. //
  211. // Called in response to PWM_OWNERLIST_COMPLETE which is posted
  212. // to the dialog when the OwnerListThreadProc has completed
  213. // it's processing.
  214. //
  215. void
  216. CFileOwnerDialog::OnOwnerListComplete(
  217. void
  218. )
  219. {
  220. //
  221. // Set the message in the top of the dialog.
  222. //
  223. CString s(m_hInstance, IDS_FMT_OWNERDLG_HEADER, m_OwnerList.OwnerCount());
  224. SetWindowText(GetDlgItem(m_hwndDlg, IDC_TXT_OWNERDLG_HEADER), s);
  225. //
  226. // Populate the listview and owner combo.
  227. //
  228. InitializeList(m_OwnerList, m_hwndLV);
  229. InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo);
  230. //
  231. // Now we can enable the controls we disabled in OnInitDialog().
  232. //
  233. EnableWindow(m_hwndLV, TRUE);
  234. EnableWindow(m_hwndOwnerCombo, TRUE);
  235. EnableWindow(m_hwndEditMoveTo, TRUE);
  236. EnableWindow(GetDlgItem(m_hwndDlg, IDC_BTN_OWNERDLG_BROWSE), TRUE);
  237. EnableWindow(GetDlgItem(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEDIRS), TRUE);
  238. EnableWindow(GetDlgItem(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEFILES), TRUE);
  239. }
  240. INT_PTR
  241. CFileOwnerDialog::OnCommand(
  242. HWND hwnd,
  243. WPARAM wParam,
  244. LPARAM lParam
  245. )
  246. {
  247. BOOL bResult = TRUE; // Assume not handled.
  248. WORD wID = LOWORD(wParam);
  249. WORD wNotifyCode = HIWORD(wParam);
  250. HWND hCtl = (HWND)lParam;
  251. switch(wID)
  252. {
  253. case IDCANCEL:
  254. EndDialog(hwnd, 0);
  255. bResult = FALSE;
  256. break;
  257. case IDC_CMB_OWNERDLG_OWNERS:
  258. if (CBN_SELCHANGE == wNotifyCode)
  259. {
  260. int iOwner = ComboBox_GetCurSel(m_hwndOwnerCombo);
  261. if (1 < m_OwnerList.OwnerCount())
  262. {
  263. //
  264. // Owner list contains more than one owner. The combo
  265. // contains a leading "All owners" entry.
  266. //
  267. iOwner--;
  268. }
  269. DBGASSERT((-1 <= iOwner));
  270. CAutoSetRedraw autoredraw(m_hwndLV, false);
  271. //
  272. // Only show "owner" column if user has selected "all owners" combo item.
  273. //
  274. CreateListColumns(m_hwndLV, -1 == iOwner);
  275. FillListView(m_OwnerList, m_hwndLV, iOwner);
  276. }
  277. bResult = FALSE;
  278. break;
  279. case IDC_BTN_OWNERDLG_BROWSE:
  280. {
  281. CString s;
  282. if (BrowseForFolder(hwnd, &s))
  283. SetWindowText(m_hwndEditMoveTo, s);
  284. break;
  285. }
  286. case IDC_BTN_OWNERDLG_DELETE:
  287. DeleteSelectedFiles(m_hwndLV);
  288. bResult = FALSE;
  289. break;
  290. case IDC_BTN_OWNERDLG_MOVETO:
  291. {
  292. CPath strDest;
  293. CPath strRoot;
  294. int cchEdit = Edit_GetTextLength(m_hwndEditMoveTo);
  295. Edit_GetText(m_hwndEditMoveTo,
  296. strDest.GetBuffer(cchEdit + 1),
  297. cchEdit + 1);
  298. strDest.ReleaseBuffer();
  299. strDest.Trim();
  300. strDest.GetRoot(&strRoot);
  301. if (IsSameVolume(strRoot, m_strVolumeRoot))
  302. {
  303. //
  304. // Don't let operator move files to a folder
  305. // on the same volume.
  306. //
  307. DiskQuotaMsgBox(m_hwndDlg,
  308. IDS_ERROR_MOVETO_SAMEVOL,
  309. IDS_TITLE_DISK_QUOTA,
  310. MB_ICONINFORMATION | MB_OK);
  311. SetWindowText(m_hwndEditMoveTo, strDest);
  312. SetFocus(m_hwndEditMoveTo);
  313. }
  314. else
  315. {
  316. //
  317. // Eveything looks OK. Try to move the files.
  318. //
  319. MoveSelectedFiles(m_hwndLV, strDest);
  320. }
  321. bResult = FALSE;
  322. break;
  323. }
  324. case IDC_BTN_OWNERDLG_TAKE:
  325. {
  326. HRESULT hr = TakeOwnershipOfSelectedFiles(m_hwndLV);
  327. if (FAILED(hr))
  328. {
  329. DBGERROR((TEXT("TakeOwnershipOfSelectedFiles failed with hr = 0x%08X"), hr));
  330. }
  331. break;
  332. }
  333. case IDC_EDIT_OWNERDLG_MOVETO:
  334. if (EN_UPDATE == wNotifyCode)
  335. {
  336. //
  337. // Disable the "Move" button if the destination edit field
  338. // is blank.
  339. //
  340. HWND hwnd = GetDlgItem(m_hwndDlg, IDC_BTN_OWNERDLG_MOVETO);
  341. bool bEnable = ShouldEnableControl(IDC_BTN_OWNERDLG_MOVETO);
  342. if (bEnable != boolify(IsWindowEnabled(hwnd)))
  343. {
  344. EnableWindow(hwnd, bEnable);
  345. }
  346. }
  347. break;
  348. case IDC_CBX_OWNERDLG_EXCLUDEFILES:
  349. case IDC_CBX_OWNERDLG_EXCLUDEDIRS:
  350. if (BN_CLICKED == wNotifyCode)
  351. {
  352. //
  353. // The allowable states for these two checkboxes are:
  354. //
  355. // Excl Files Excl Dirs
  356. // --------------- ----------------
  357. // Checked Unchecked
  358. // Unchecked Checked
  359. // Unchecked Unchecked
  360. //
  361. // It makes no sense to have both checkboxes checked.
  362. // This would cause the list to be empty and might
  363. // generate user confusion.
  364. //
  365. if (IsDlgButtonChecked(m_hwndDlg, wID))
  366. {
  367. UINT idOther = IDC_CBX_OWNERDLG_EXCLUDEFILES;
  368. if (IDC_CBX_OWNERDLG_EXCLUDEFILES == wID)
  369. {
  370. idOther = IDC_CBX_OWNERDLG_EXCLUDEDIRS;
  371. }
  372. CheckDlgButton(m_hwndDlg, idOther, BST_UNCHECKED);
  373. }
  374. FillListView(m_OwnerList, m_hwndLV, ComboBox_GetCurSel(m_hwndOwnerCombo) - 1);
  375. }
  376. break;
  377. }
  378. return bResult;
  379. }
  380. INT_PTR
  381. CFileOwnerDialog::OnContextMenu(
  382. HWND hwndItem,
  383. int xPos,
  384. int yPos
  385. )
  386. {
  387. int idCtl = GetDlgCtrlID(hwndItem);
  388. WinHelp(hwndItem,
  389. UseWindowsHelp(idCtl) ? NULL : STR_DSKQUOUI_HELPFILE,
  390. HELP_CONTEXTMENU,
  391. (DWORD_PTR)((LPTSTR)rgFileOwnerDialogHelpIDs));
  392. return FALSE;
  393. }
  394. //
  395. // Determine what actions are allowed for the current selection.
  396. //
  397. DWORD
  398. CFileOwnerDialog::GetAllowedActions(
  399. HWND hwndLV
  400. )
  401. {
  402. CArray<COwnerListItemHandle> rgItemHandles;
  403. BuildListOfSelectedFiles(hwndLV, NULL, &rgItemHandles);
  404. if (0 != rgItemHandles.Count())
  405. {
  406. const int cHandles = rgItemHandles.Count();
  407. for(int i = 0; i < cHandles; i++)
  408. {
  409. COwnerListItemHandle handle = rgItemHandles[i];
  410. int iOwner = handle.OwnerIndex();
  411. int iFile = handle.FileIndex();
  412. if (m_OwnerList.IsFileDirectory(iOwner, iFile))
  413. {
  414. //
  415. // If any directory exists in the selection,
  416. // "take ownership" is the only allowed action.
  417. //
  418. return ACTION_TAKEOWNERSHIP;
  419. }
  420. }
  421. }
  422. return ACTION_ANY;
  423. }
  424. //
  425. // Determine if one of the move/delete/take buttons should be enabled
  426. // or disabled.
  427. //
  428. bool
  429. CFileOwnerDialog::ShouldEnableControl(
  430. UINT idCtl
  431. )
  432. {
  433. bool bEnable = true;
  434. int cLVSel = ListView_GetSelectedCount(m_hwndLV);
  435. DWORD actions = GetAllowedActions(m_hwndLV);
  436. switch(idCtl)
  437. {
  438. case IDC_BTN_OWNERDLG_DELETE:
  439. bEnable = (0 != (ACTION_DELETE & actions)) && (0 < cLVSel);
  440. break;
  441. case IDC_BTN_OWNERDLG_TAKE:
  442. bEnable = (0 != (ACTION_TAKEOWNERSHIP & actions)) && (0 < cLVSel);
  443. break;
  444. case IDC_BTN_OWNERDLG_MOVETO:
  445. bEnable = (0 != (ACTION_MOVE & actions));
  446. if (bEnable && 0 < cLVSel)
  447. {
  448. CPath s;
  449. int cch = Edit_GetTextLength(m_hwndEditMoveTo);
  450. Edit_GetText(m_hwndEditMoveTo, s.GetBuffer(cch + 1), cch + 1);
  451. s.ReleaseBuffer();
  452. s.Trim();
  453. bEnable = 0 < s.Length();
  454. }
  455. break;
  456. case IDC_BTN_OWNERDLG_BROWSE:
  457. case IDC_EDIT_OWNERDLG_MOVETO:
  458. bEnable = (0 != (ACTION_MOVE & actions));
  459. break;
  460. default:
  461. break;
  462. }
  463. return bEnable;
  464. }
  465. INT_PTR
  466. CFileOwnerDialog::OnNotify(
  467. HWND hwnd,
  468. WPARAM wParam,
  469. LPARAM lParam
  470. )
  471. {
  472. BOOL bResult = TRUE;
  473. LPNMHDR pnm = (LPNMHDR)lParam;
  474. switch(pnm->code)
  475. {
  476. case LVN_GETDISPINFO:
  477. OnLVN_GetDispInfo((LV_DISPINFO *)lParam);
  478. break;
  479. case LVN_COLUMNCLICK:
  480. OnLVN_ColumnClick((NM_LISTVIEW *)lParam);
  481. break;
  482. case LVN_ITEMCHANGED:
  483. OnLVN_ItemChanged((NM_LISTVIEW *)lParam);
  484. break;
  485. case LVN_KEYDOWN:
  486. OnLVN_KeyDown((NMLVKEYDOWN *)lParam);
  487. break;
  488. default:
  489. break;
  490. }
  491. return bResult;
  492. }
  493. void
  494. CFileOwnerDialog::OnLVN_GetDispInfo(
  495. LV_DISPINFO *plvdi
  496. )
  497. {
  498. static CPath strPath;
  499. static CString strOwner;
  500. COwnerListItemHandle hItem(plvdi->item.lParam);
  501. int iOwner = hItem.OwnerIndex();
  502. int iFile = hItem.FileIndex();
  503. if (LVIF_TEXT & plvdi->item.mask)
  504. {
  505. switch(plvdi->item.iSubItem)
  506. {
  507. case iLVSUBITEM_FILE:
  508. {
  509. CPath s;
  510. m_OwnerList.GetFileName(iOwner, iFile, &s);
  511. if (m_OwnerList.IsFileDirectory(iOwner, iFile))
  512. {
  513. strPath.Format(m_hInstance, IDS_FMT_OWNERDLG_FOLDERNAME, s.Cstr());
  514. }
  515. else
  516. {
  517. strPath = s;
  518. }
  519. plvdi->item.pszText = (LPTSTR)strPath.Cstr();
  520. }
  521. break;
  522. case iLVSUBITEM_FOLDER:
  523. m_OwnerList.GetFolderName(iOwner, iFile, &strPath);
  524. plvdi->item.pszText = (LPTSTR)strPath.Cstr();
  525. break;
  526. case iLVSUBITEM_OWNER:
  527. m_OwnerList.GetOwnerName(iOwner, &strOwner);
  528. plvdi->item.pszText = (LPTSTR)strOwner.Cstr();
  529. break;
  530. }
  531. }
  532. if (LVIF_IMAGE & plvdi->item.mask)
  533. {
  534. //
  535. // Not displaying any images. This is just a placeholder.
  536. // Should be optimized out by compiler.
  537. //
  538. }
  539. }
  540. int CALLBACK
  541. CFileOwnerDialog::CompareLVItems(
  542. LPARAM lParam1,
  543. LPARAM lParam2,
  544. LPARAM lParamSort
  545. )
  546. {
  547. CFileOwnerDialog *pdlg = reinterpret_cast<CFileOwnerDialog *>(lParamSort);
  548. int diff = 0;
  549. try
  550. {
  551. COwnerListItemHandle h1(lParam1);
  552. COwnerListItemHandle h2(lParam2);
  553. int iOwner1 = h1.OwnerIndex();
  554. int iOwner2 = h2.OwnerIndex();
  555. int iFile1 = h1.FileIndex();
  556. int iFile2 = h2.FileIndex();
  557. static CPath s1, s2;
  558. //
  559. // This array controls the comparison column IDs used when
  560. // values for the selected column are equal. These should
  561. // remain in order of the iLVSUBITEM_xxxxx enumeration with
  562. // respect to the first element in each row.
  563. //
  564. static const int rgColComp[3][3] = {
  565. { iLVSUBITEM_FILE, iLVSUBITEM_FOLDER, iLVSUBITEM_OWNER },
  566. { iLVSUBITEM_FOLDER, iLVSUBITEM_FILE, iLVSUBITEM_OWNER },
  567. { iLVSUBITEM_OWNER, iLVSUBITEM_FILE, iLVSUBITEM_FOLDER }
  568. };
  569. int iCompare = 0;
  570. while(0 == diff && iCompare < ARRAYSIZE(rgColComp))
  571. {
  572. switch(rgColComp[pdlg->m_iLastColSorted][iCompare++])
  573. {
  574. case iLVSUBITEM_FILE:
  575. pdlg->m_OwnerList.GetFileName(iOwner1, iFile1, &s1);
  576. pdlg->m_OwnerList.GetFileName(iOwner2, iFile2, &s2);
  577. break;
  578. case iLVSUBITEM_FOLDER:
  579. pdlg->m_OwnerList.GetFolderName(iOwner1, iFile1, &s1);
  580. pdlg->m_OwnerList.GetFolderName(iOwner2, iFile2, &s2);
  581. break;
  582. case iLVSUBITEM_OWNER:
  583. //
  584. // Can use CPath (s1 and s2) in place of CString arg since
  585. // CPath is derived from CString.
  586. //
  587. pdlg->m_OwnerList.GetOwnerName(iOwner1, &s1);
  588. pdlg->m_OwnerList.GetOwnerName(iOwner2, &s2);
  589. break;
  590. default:
  591. //
  592. // If you hit this, you need to update this function
  593. // to handle the new column you've added to the listview.
  594. //
  595. DBGASSERT((false));
  596. break;
  597. }
  598. diff = s1.Compare(s2);
  599. }
  600. //
  601. // Don't need contents of static strings between function invocations.
  602. // The strings are static to avoid repeated construction/destruction.
  603. // It's only a minor optimization.
  604. //
  605. s1.Empty();
  606. s2.Empty();
  607. }
  608. catch(CAllocException& e)
  609. {
  610. //
  611. // Do nothing. Just return diff "as is".
  612. // Don't want to throw an exception back into comctl32.
  613. //
  614. }
  615. return pdlg->m_bSortAscending ? diff : -1 * diff;
  616. }
  617. void
  618. CFileOwnerDialog::OnLVN_ColumnClick(
  619. NM_LISTVIEW *pnmlv
  620. )
  621. {
  622. DBGTRACE((DM_VIEW, DL_LOW, TEXT("CFileOwnerDialog::OnLVN_ColumnClick")));
  623. if (m_iLastColSorted != pnmlv->iSubItem)
  624. {
  625. m_bSortAscending = true;
  626. m_iLastColSorted = pnmlv->iSubItem;
  627. }
  628. else
  629. {
  630. m_bSortAscending = !m_bSortAscending;
  631. }
  632. ListView_SortItems(m_hwndLV, CompareLVItems, LPARAM(this));
  633. }
  634. //
  635. // Called whenever a listview item has changed state.
  636. // I'm using this to update the "enabledness" of the
  637. // dialog buttons. If there's nothing selected in the listview,
  638. // the move/delete/take buttons are disabled.
  639. //
  640. void
  641. CFileOwnerDialog::OnLVN_ItemChanged(
  642. NM_LISTVIEW *pnmlv
  643. )
  644. {
  645. static const int rgCtls[] = { IDC_BTN_OWNERDLG_DELETE,
  646. IDC_BTN_OWNERDLG_TAKE,
  647. IDC_BTN_OWNERDLG_MOVETO,
  648. IDC_BTN_OWNERDLG_BROWSE,
  649. IDC_EDIT_OWNERDLG_MOVETO};
  650. //
  651. // LVN_ITEMCHANGED is sent multiple times when you move the
  652. // highlight bar in a listview.
  653. // Only run this code when the "focused" state bit is set
  654. // for the "new state". This should be the last call in
  655. // the series.
  656. //
  657. if (LVIS_FOCUSED & pnmlv->uNewState)
  658. {
  659. for (int i = 0; i < ARRAYSIZE(rgCtls); i++)
  660. {
  661. HWND hwnd = GetDlgItem(m_hwndDlg, rgCtls[i]);
  662. bool bEnable = ShouldEnableControl(rgCtls[i]);
  663. if (bEnable != boolify(IsWindowEnabled(hwnd)))
  664. {
  665. EnableWindow(hwnd, bEnable);
  666. }
  667. }
  668. }
  669. }
  670. void
  671. CFileOwnerDialog::OnLVN_KeyDown(
  672. NMLVKEYDOWN *plvkd
  673. )
  674. {
  675. if (VK_DELETE == plvkd->wVKey)
  676. {
  677. DeleteSelectedFiles(m_hwndLV);
  678. FocusOnSomethingInListview(m_hwndLV);
  679. }
  680. }
  681. void
  682. CFileOwnerDialog::FocusOnSomethingInListview(
  683. HWND hwndLV
  684. )
  685. {
  686. //
  687. // Focus on something.
  688. //
  689. int iFocus = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED);
  690. if (-1 == iFocus)
  691. iFocus = 0;
  692. ListView_SetItemState(hwndLV, iFocus, LVIS_FOCUSED | LVIS_SELECTED,
  693. LVIS_FOCUSED | LVIS_SELECTED);
  694. }
  695. //
  696. // Creates the listview columns and populates the listview
  697. // with filenames.
  698. //
  699. void
  700. CFileOwnerDialog::InitializeList(
  701. const COwnerList& fol, // file & owner list
  702. HWND hwndList
  703. )
  704. {
  705. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::InitializeList")));
  706. CreateListColumns(hwndList, 1 < m_OwnerList.OwnerCount());
  707. FillListView(fol, hwndList);
  708. ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT);
  709. }
  710. void
  711. CFileOwnerDialog::CreateListColumns(
  712. HWND hwndList,
  713. bool bShowOwner // Default is true.
  714. )
  715. {
  716. //
  717. // Clear out the listview and header.
  718. //
  719. ListView_DeleteAllItems(hwndList);
  720. HWND hwndHeader = ListView_GetHeader(hwndList);
  721. if (NULL != hwndHeader)
  722. {
  723. while(0 < Header_GetItemCount(hwndHeader))
  724. ListView_DeleteColumn(hwndList, 0);
  725. }
  726. //
  727. // Create the header titles.
  728. //
  729. CString strFile(m_hInstance, IDS_OWNERDLG_HDR_FILE);
  730. CString strFolder(m_hInstance, IDS_OWNERDLG_HDR_FOLDER);
  731. CString strOwner(m_hInstance, IDS_OWNERDLG_HDR_OWNER);
  732. //
  733. // FEATURE: Should probably allow for vertical scroll bar also.
  734. //
  735. RECT rcList;
  736. GetClientRect(hwndList, &rcList);
  737. int cxCol = (rcList.right - rcList.left) / (bShowOwner ? 3 : 2);
  738. #define LVCOLMASK (LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM)
  739. LV_COLUMN rgCols[] = {
  740. { LVCOLMASK, LVCFMT_LEFT, cxCol, strFile, 0, iLVSUBITEM_FILE },
  741. { LVCOLMASK, LVCFMT_LEFT, cxCol, strFolder, 0, iLVSUBITEM_FOLDER },
  742. { LVCOLMASK, LVCFMT_LEFT, cxCol, strOwner, 0, iLVSUBITEM_OWNER }
  743. };
  744. //
  745. // Add the columns to the listview.
  746. //
  747. int cCols = bShowOwner ? ARRAYSIZE(rgCols) : ARRAYSIZE(rgCols) - 1;
  748. for (INT i = 0; i < cCols; i++)
  749. {
  750. if (-1 == ListView_InsertColumn(hwndList, i, &rgCols[i]))
  751. {
  752. DBGERROR((TEXT("CFileOwnerDialog::CreateListColumns failed to add column %d"), i));
  753. }
  754. }
  755. }
  756. void
  757. CFileOwnerDialog::FillListView(
  758. const COwnerList& fol, // file & owner list
  759. HWND hwndList,
  760. int iOwner // default is -1 (all owners)
  761. )
  762. {
  763. ListView_DeleteAllItems(hwndList);
  764. LV_ITEM item;
  765. item.iSubItem = 0;
  766. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_PARAM;
  767. item.state = 0;
  768. item.stateMask = 0;
  769. item.pszText = LPSTR_TEXTCALLBACK;
  770. item.iImage = I_IMAGECALLBACK;
  771. int iFirst = iOwner;
  772. int iLast = iOwner;
  773. if (-1 == iOwner)
  774. {
  775. iFirst = 0;
  776. iLast = fol.OwnerCount() - 1;
  777. }
  778. int iItem = 0;
  779. const bool bExclFiles = IsDlgButtonChecked(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEFILES);
  780. const bool bExclDirs = IsDlgButtonChecked(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEDIRS);
  781. //
  782. // WARNING: Reusing formal arg iOwner. It's safe to do, but you
  783. // should be aware that I'm doing it.
  784. //
  785. for (iOwner = iFirst; iOwner <= iLast; iOwner++)
  786. {
  787. int cFiles = fol.FileCount(iOwner, true);
  788. for (int iFile = 0; iFile < cFiles; iFile++)
  789. {
  790. bool bDirectory = fol.IsFileDirectory(iOwner, iFile);
  791. bool bFile = !bDirectory;
  792. if ((bDirectory && !bExclDirs) || (bFile && !bExclFiles))
  793. {
  794. if (!fol.IsFileDeleted(iOwner, iFile))
  795. {
  796. item.lParam = COwnerListItemHandle(iOwner, iFile);
  797. item.iItem = iItem++;
  798. if (-1 == ListView_InsertItem(hwndList, &item))
  799. DBGERROR((TEXT("Error adding LV item for owner %d, file %d"), iOwner, iFile));
  800. }
  801. }
  802. }
  803. }
  804. }
  805. void
  806. CFileOwnerDialog::InitializeOwnerCombo(
  807. const COwnerList& fol, // file & owner list
  808. HWND hwndCombo
  809. )
  810. {
  811. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::InitializeList")));
  812. int iSelected = ComboBox_GetCurSel(hwndCombo);
  813. ComboBox_ResetContent(hwndCombo);
  814. CString s, s2;
  815. int cOwners = fol.OwnerCount();
  816. if (1 < cOwners)
  817. {
  818. //
  819. // Add "all owners" entry.
  820. //
  821. s.Format(m_hInstance, IDS_FMT_ALLOWNERS, fol.FileCount());
  822. ComboBox_InsertString(hwndCombo, -1, s);
  823. }
  824. for (int iOwner = 0; iOwner < cOwners; iOwner++)
  825. {
  826. fol.GetOwnerName(iOwner, &s2);
  827. s.Format(m_hInstance, IDS_FMT_OWNER, s2.Cstr(), fol.FileCount(iOwner));
  828. ComboBox_InsertString(hwndCombo, -1, s);
  829. }
  830. ComboBox_SetCurSel(hwndCombo, CB_ERR != iSelected ? iSelected : 0);
  831. //
  832. // Set the max height of the owner combo
  833. //
  834. RECT rcCombo;
  835. GetClientRect(m_hwndOwnerCombo, &rcCombo);
  836. SetWindowPos(m_hwndOwnerCombo,
  837. NULL,
  838. 0, 0,
  839. rcCombo.right - rcCombo.left,
  840. 200,
  841. SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
  842. }
  843. //
  844. // Determine if two volume root strings refer to the same volume.
  845. // With volume mount points, "C:\" and "D:\DriveC" could refer to the
  846. // same physical volume. To differentiate we need to examine the unique
  847. // volume name GUID strings.
  848. //
  849. bool
  850. CFileOwnerDialog::IsSameVolume(
  851. LPCTSTR pszRoot1,
  852. LPCTSTR pszRoot2
  853. )
  854. {
  855. TCHAR szVolGUID1[MAX_PATH];
  856. TCHAR szTemp[MAX_PATH];
  857. bool bSameVolume = false;
  858. //
  859. // GetVolumeNameForVolumeMountPoint requires trailing backslash on paths.
  860. //
  861. lstrcpyn(szTemp, pszRoot1, ARRAYSIZE(szTemp));
  862. PathAddBackslash(szTemp);
  863. if (GetVolumeNameForVolumeMountPoint(szTemp, szVolGUID1, ARRAYSIZE(szVolGUID1)))
  864. {
  865. TCHAR szVolGUID2[MAX_PATH];
  866. lstrcpyn(szTemp, pszRoot2, ARRAYSIZE(szTemp));
  867. PathAddBackslash(szTemp);
  868. if (GetVolumeNameForVolumeMountPoint(szTemp, szVolGUID2, ARRAYSIZE(szVolGUID2)))
  869. {
  870. if (0 == lstrcmpi(szVolGUID1, szVolGUID2))
  871. bSameVolume = true;
  872. }
  873. }
  874. return bSameVolume;
  875. }
  876. //
  877. // Let the user browse for a folder.
  878. // The selected folder path is returned in *pstrFolder.
  879. //
  880. bool
  881. CFileOwnerDialog::BrowseForFolder(
  882. HWND hwndParent,
  883. CString *pstrFolder
  884. )
  885. {
  886. bool bResult = false;
  887. BROWSEINFO bi;
  888. ZeroMemory(&bi, sizeof(bi));
  889. CString strTitle(m_hInstance, IDS_BROWSEFORFOLDER);
  890. bi.hwndOwner = hwndParent;
  891. bi.pidlRoot = NULL; // Start at desktop.
  892. bi.pszDisplayName = NULL;
  893. bi.lpszTitle = strTitle.Cstr();
  894. //
  895. // FEATURE: Setting the BIF_EDITBOX flag causes SHBrowseForFolder to invoke
  896. // autocomplete through SHAutoComplete (in shlwapi). SHAutoComplete
  897. // loads browseui.dll to implement the autocomplete feature. The bad
  898. // part is that SHAutoComplete also unloads browseui.dll before it
  899. // returns, resulting in calls to the unloaded WndProc. I've notified
  900. // ReinerF about this. Turning off the BIF_EDITBOX bit prevents
  901. // autocomplete from being used and thus prevents the problem.
  902. // I want the edit box. Turn it back on once they fix this bug.
  903. //
  904. // brianau [1/30/97]
  905. //
  906. bi.ulFlags = BIF_RETURNONLYFSDIRS; // | BIF_EDITBOX;
  907. bi.lpfn = BrowseForFolderCallback;
  908. bi.lParam = (LPARAM)pstrFolder;
  909. bi.iImage = 0;
  910. bResult = boolify(SHBrowseForFolder(&bi));
  911. return bResult;
  912. }
  913. //
  914. // Callback called by SHBrowseForFolder. Writes selected folder path
  915. // to CString object who's pointer is passed in lpData arg.
  916. //
  917. int
  918. CFileOwnerDialog::BrowseForFolderCallback(
  919. HWND hwnd,
  920. UINT uMsg,
  921. LPARAM lParam,
  922. LPARAM lpData
  923. )
  924. {
  925. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BrowseForFolderCallback")));
  926. CString *pstrFolder = (CString *)lpData;
  927. if (BFFM_SELCHANGED == uMsg)
  928. {
  929. SHGetPathFromIDList((LPCITEMIDLIST)lParam, pstrFolder->GetBuffer(MAX_PATH));
  930. pstrFolder->ReleaseBuffer();
  931. }
  932. return 0;
  933. }
  934. //
  935. // Builds a double-nul terminated list of file paths from the listview
  936. // along with an array of "item handle" objects that acts as a cross-
  937. // reference between the list items, items in the listview and items
  938. // in the file owner list. Each handle contains an owner index and
  939. // file index into the file owner list. Each handle is also the value
  940. // stored as the lParam in the listview items.
  941. // Both pList and prgItemHandles arguments are optional. Although,
  942. // calling with neither non-null is sort of useless.
  943. //
  944. void
  945. CFileOwnerDialog::BuildListOfSelectedFiles(
  946. HWND hwndLV,
  947. DblNulTermList *pList,
  948. CArray<COwnerListItemHandle> *prgItemHandles
  949. )
  950. {
  951. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BuildListOfSelectedFiles")));
  952. int iItem = -1;
  953. CPath strPath;
  954. LV_ITEM item;
  955. if (NULL != prgItemHandles)
  956. prgItemHandles->Clear();
  957. while(-1 != (iItem = ListView_GetNextItem(hwndLV, iItem, LVNI_SELECTED)))
  958. {
  959. item.iSubItem = 0;
  960. item.iItem = iItem;
  961. item.mask = LVIF_PARAM;
  962. if (-1 != ListView_GetItem(hwndLV, &item))
  963. {
  964. COwnerListItemHandle hItem(item.lParam);
  965. m_OwnerList.GetFileFullPath(hItem.OwnerIndex(),
  966. hItem.FileIndex(),
  967. &strPath);
  968. if (pList)
  969. pList->AddString(strPath);
  970. if (prgItemHandles)
  971. prgItemHandles->Append(hItem);
  972. }
  973. }
  974. }
  975. //
  976. // Given an item "handle", find it's entry in the listview.
  977. //
  978. int
  979. CFileOwnerDialog::FindItemFromHandle(
  980. HWND hwndLV,
  981. const COwnerListItemHandle& handle
  982. )
  983. {
  984. LV_FINDINFO lvfi;
  985. lvfi.flags = LVFI_PARAM;
  986. lvfi.lParam = handle;
  987. return ListView_FindItem(hwndLV, -1, &lvfi);
  988. }
  989. //
  990. // Scans an array of item handles and removes all corresponding
  991. // items from the listview.
  992. //
  993. void
  994. CFileOwnerDialog::RemoveListViewItems(
  995. HWND hwndLV,
  996. const CArray<COwnerListItemHandle>& rgItemHandles
  997. )
  998. {
  999. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::RemoveListViewItems")));
  1000. LV_ITEM item;
  1001. CPath strPath;
  1002. CAutoSetRedraw autoredraw(hwndLV, false);
  1003. int cHandles = rgItemHandles.Count();
  1004. for (int iHandle = 0; iHandle < cHandles; iHandle++)
  1005. {
  1006. COwnerListItemHandle handle = rgItemHandles[iHandle];
  1007. int iItem = FindItemFromHandle(hwndLV, handle);
  1008. if (-1 != iItem)
  1009. {
  1010. int iOwner = handle.OwnerIndex();
  1011. int iFile = handle.FileIndex();
  1012. m_OwnerList.GetFileFullPath(iOwner, iFile, &strPath);
  1013. if ((DWORD)-1 == GetFileAttributes(strPath))
  1014. {
  1015. //
  1016. // File doesn't exist any more.
  1017. // Delete from the listview.
  1018. // Mark it as "deleted" in the ownerlist container.
  1019. //
  1020. ListView_DeleteItem(hwndLV, iItem);
  1021. m_OwnerList.MarkFileDeleted(iOwner, iFile);
  1022. DBGPRINT((DM_VIEW, DL_LOW, TEXT("Removed item %d \"%s\""),
  1023. iItem, strPath.Cstr()));
  1024. }
  1025. }
  1026. }
  1027. //
  1028. // Refresh the owner combo to update the file counts.
  1029. //
  1030. InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo);
  1031. }
  1032. //
  1033. // Delete the files selected in the listview.
  1034. // Files deleted are removed from the listview.
  1035. //
  1036. void
  1037. CFileOwnerDialog::DeleteSelectedFiles(
  1038. HWND hwndLV
  1039. )
  1040. {
  1041. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::DeleteSelectedFiles")));
  1042. DblNulTermList list(1024); // 1024 is the buffer growth size in chars.
  1043. CArray<COwnerListItemHandle> rgItemHandles;
  1044. BuildListOfSelectedFiles(hwndLV, &list, &rgItemHandles);
  1045. if (0 < list.Count())
  1046. {
  1047. SHFILEOPSTRUCT fo;
  1048. fo.hwnd = m_hwndDlg;
  1049. fo.wFunc = FO_DELETE;
  1050. fo.pFrom = list;
  1051. fo.pTo = NULL;
  1052. fo.fFlags = 0;
  1053. if (0 != SHFileOperation(&fo))
  1054. {
  1055. DBGERROR((TEXT("SHFileOperation [FO_DELETE] failed")));
  1056. }
  1057. //
  1058. // Remove listview items if their files were really deleted.
  1059. //
  1060. RemoveListViewItems(hwndLV, rgItemHandles);
  1061. }
  1062. }
  1063. //
  1064. // Move the selected files to a new location.
  1065. // Moved files are removed from the listview.
  1066. //
  1067. void
  1068. CFileOwnerDialog::MoveSelectedFiles(
  1069. HWND hwndLV,
  1070. LPCTSTR pszDest
  1071. )
  1072. {
  1073. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::DeleteSelectedFiles")));
  1074. DblNulTermList list(1024); // 1024 is the buffer growth size in chars.
  1075. CArray<COwnerListItemHandle> rgItemHandles;
  1076. BuildListOfSelectedFiles(hwndLV, &list, &rgItemHandles);
  1077. if (0 < list.Count())
  1078. {
  1079. CPath strDest(pszDest);
  1080. if (1 == list.Count())
  1081. {
  1082. //
  1083. // If we have only a single file we MUST create a fully-qualified
  1084. // path to the destination file. Oddities in the shell's move/copy
  1085. // engine won't let us pass merely a destination folder in the
  1086. // case where that folder doesn't exist. If we give the full path
  1087. // including filename we'll get the "folder doesn't exist, create
  1088. // now?" messagebox as we would expect. If we're moving multiple
  1089. // files the shell accepts a single directory path.
  1090. //
  1091. LPCTSTR psz;
  1092. DblNulTermListIter iter(list);
  1093. if (iter.Next(&psz))
  1094. {
  1095. CPath strSrc(psz); // Copy the source
  1096. CPath strFile;
  1097. strSrc.GetFileSpec(&strFile);// Extract the filename.
  1098. strDest.Append(strFile); // Append to the dest path.
  1099. }
  1100. }
  1101. SHFILEOPSTRUCT fo;
  1102. fo.hwnd = m_hwndDlg;
  1103. fo.wFunc = FO_MOVE;
  1104. fo.pFrom = list;
  1105. fo.pTo = strDest;
  1106. fo.fFlags = FOF_RENAMEONCOLLISION;
  1107. if (0 != SHFileOperation(&fo))
  1108. {
  1109. DBGERROR((TEXT("SHFileOperation [FO_MOVE] failed")));
  1110. }
  1111. //
  1112. // Remove listview items if their file was really deleted.
  1113. //
  1114. RemoveListViewItems(hwndLV, rgItemHandles);
  1115. }
  1116. }
  1117. //
  1118. // Get the SID to use for taking ownership of files.
  1119. // First try to get the first group SID with the SE_GROUP_OWNER attribute.
  1120. // If none found, use the operator's account SID. The SID is in a
  1121. // dynamic buffer attached to the ptrSid autoptr argument.
  1122. //
  1123. HRESULT
  1124. CFileOwnerDialog::GetOwnershipSid(
  1125. array_autoptr<BYTE> *ptrSid
  1126. )
  1127. {
  1128. HRESULT hr = E_FAIL;
  1129. DWORD dwErr = 0;
  1130. //
  1131. // Get the token handle. First try the thread token then the process
  1132. // token. If these fail we return early. No sense in continuing
  1133. // on if we can't get a user token.
  1134. //
  1135. CWin32Handle hToken;
  1136. if (!OpenThreadToken(GetCurrentThread(),
  1137. TOKEN_READ,
  1138. TRUE,
  1139. hToken.HandlePtr()))
  1140. {
  1141. if (ERROR_NO_TOKEN == GetLastError())
  1142. {
  1143. if (!OpenProcessToken(GetCurrentProcess(),
  1144. TOKEN_READ,
  1145. hToken.HandlePtr()))
  1146. {
  1147. dwErr = GetLastError();
  1148. DBGERROR((TEXT("Error %d opening process token"), dwErr));
  1149. return HRESULT_FROM_WIN32(dwErr);
  1150. }
  1151. }
  1152. else
  1153. {
  1154. dwErr = GetLastError();
  1155. DBGERROR((TEXT("Error %d opening thread token"), dwErr));
  1156. return HRESULT_FROM_WIN32(dwErr);
  1157. }
  1158. }
  1159. //
  1160. // Get the required size of the group token information buffer.
  1161. //
  1162. array_autoptr<BYTE> ptrTokenInfo;
  1163. DWORD cbTokenInfo = 0;
  1164. if (!GetTokenInformation(hToken,
  1165. TokenGroups,
  1166. NULL,
  1167. cbTokenInfo,
  1168. &cbTokenInfo))
  1169. {
  1170. dwErr = GetLastError();
  1171. if (ERROR_INSUFFICIENT_BUFFER == dwErr)
  1172. {
  1173. ptrTokenInfo = new BYTE[cbTokenInfo];
  1174. }
  1175. else
  1176. {
  1177. dwErr = GetLastError();
  1178. DBGERROR((TEXT("Error %d getting TokenGroups info [for size]"), dwErr));
  1179. hr = HRESULT_FROM_WIN32(dwErr);
  1180. }
  1181. }
  1182. //
  1183. // Get the group token information.
  1184. //
  1185. if (NULL != ptrTokenInfo.get())
  1186. {
  1187. if (!GetTokenInformation(hToken,
  1188. TokenGroups,
  1189. ptrTokenInfo.get(),
  1190. cbTokenInfo,
  1191. &cbTokenInfo))
  1192. {
  1193. dwErr = GetLastError();
  1194. DBGERROR((TEXT("Error %d getting TokenGroups info"), dwErr));
  1195. hr = HRESULT_FROM_WIN32(dwErr);
  1196. }
  1197. else
  1198. {
  1199. //
  1200. // Extract the first SID with the GROUP_OWNER bit set.
  1201. //
  1202. TOKEN_GROUPS *ptg = (TOKEN_GROUPS *)ptrTokenInfo.get();
  1203. DBGASSERT((NULL != ptg));
  1204. for (DWORD i = 0; i < ptg->GroupCount; i++)
  1205. {
  1206. SID_AND_ATTRIBUTES *psa = (SID_AND_ATTRIBUTES *)&ptg->Groups[i];
  1207. DBGASSERT((NULL != psa));
  1208. if (SE_GROUP_OWNER & psa->Attributes)
  1209. {
  1210. int cbSid = GetLengthSid(psa->Sid);
  1211. *ptrSid = new BYTE[cbSid];
  1212. CopySid(cbSid, ptrSid->get(), psa->Sid);
  1213. hr = NOERROR;
  1214. break;
  1215. }
  1216. }
  1217. }
  1218. }
  1219. if (FAILED(hr))
  1220. {
  1221. //
  1222. // Didn't find a SID from the group information.
  1223. // Use the operator's SID.
  1224. //
  1225. cbTokenInfo = 0;
  1226. if (!GetTokenInformation(hToken,
  1227. TokenUser,
  1228. NULL,
  1229. cbTokenInfo,
  1230. &cbTokenInfo))
  1231. {
  1232. dwErr = GetLastError();
  1233. if (ERROR_INSUFFICIENT_BUFFER == dwErr)
  1234. {
  1235. ptrTokenInfo = new BYTE[cbTokenInfo];
  1236. }
  1237. else
  1238. {
  1239. DBGERROR((TEXT("Error %d getting TokenUser info [for size]"), dwErr));
  1240. hr = HRESULT_FROM_WIN32(dwErr);
  1241. }
  1242. }
  1243. if (SUCCEEDED(hr))
  1244. {
  1245. //
  1246. // Get the user token information.
  1247. //
  1248. if (!GetTokenInformation(hToken,
  1249. TokenUser,
  1250. ptrTokenInfo.get(),
  1251. cbTokenInfo,
  1252. &cbTokenInfo))
  1253. {
  1254. dwErr = GetLastError();
  1255. DBGERROR((TEXT("Error %d getting TokenUser info"), dwErr));
  1256. hr = HRESULT_FROM_WIN32(dwErr);
  1257. }
  1258. else
  1259. {
  1260. SID_AND_ATTRIBUTES *psa = (SID_AND_ATTRIBUTES *)ptrTokenInfo.get();
  1261. DBGASSERT((NULL != psa));
  1262. int cbSid = GetLengthSid(psa->Sid);
  1263. *ptrSid = new BYTE[cbSid];
  1264. CopySid(cbSid, ptrSid->get(), psa->Sid);
  1265. hr = NOERROR;
  1266. }
  1267. }
  1268. }
  1269. if (SUCCEEDED(hr) && NULL != ptrSid->get() && !IsValidSid(ptrSid->get()))
  1270. {
  1271. hr = HRESULT_FROM_WIN32(ERROR_INVALID_SID);
  1272. }
  1273. return hr;
  1274. }
  1275. //
  1276. // Transfers ownership of selected files in the listview to the
  1277. // currently logged-on user.
  1278. //
  1279. HRESULT
  1280. CFileOwnerDialog::TakeOwnershipOfSelectedFiles(
  1281. HWND hwndLV
  1282. )
  1283. {
  1284. HRESULT hr = NOERROR;
  1285. DWORD dwErr = 0;
  1286. CArray<COwnerListItemHandle> rgItemHandles;
  1287. BuildListOfSelectedFiles(hwndLV, NULL, &rgItemHandles);
  1288. if (0 == rgItemHandles.Count())
  1289. return S_OK;
  1290. array_autoptr<BYTE> ptrSid;
  1291. hr = GetOwnershipSid(&ptrSid);
  1292. if (FAILED(hr))
  1293. return hr;
  1294. CPath strFile;
  1295. int cHandles = rgItemHandles.Count();
  1296. for (int i = 0; i < cHandles; i++)
  1297. {
  1298. COwnerListItemHandle handle = rgItemHandles[i];
  1299. int iItem = FindItemFromHandle(hwndLV, handle);
  1300. if (-1 != iItem)
  1301. {
  1302. SECURITY_DESCRIPTOR sd;
  1303. if (InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
  1304. {
  1305. int iOwner = handle.OwnerIndex();
  1306. int iFile = handle.FileIndex();
  1307. m_OwnerList.GetFileFullPath(iOwner, iFile, &strFile);
  1308. if (SetSecurityDescriptorOwner(&sd, ptrSid.get(), FALSE))
  1309. {
  1310. if (SetFileSecurity(strFile, OWNER_SECURITY_INFORMATION, &sd))
  1311. {
  1312. ListView_DeleteItem(hwndLV, iItem);
  1313. m_OwnerList.MarkFileDeleted(iOwner, iFile);
  1314. }
  1315. else
  1316. {
  1317. dwErr = GetLastError();
  1318. DBGERROR((TEXT("Error %d setting new owner for \"%s\""),
  1319. dwErr, strFile.Cstr()));
  1320. hr = HRESULT_FROM_WIN32(dwErr);
  1321. }
  1322. }
  1323. else
  1324. {
  1325. dwErr = GetLastError();
  1326. DBGERROR((TEXT("Error %d setting security descriptor owner"), dwErr));
  1327. hr = HRESULT_FROM_WIN32(dwErr);
  1328. }
  1329. }
  1330. else
  1331. {
  1332. dwErr = GetLastError();
  1333. DBGERROR((TEXT("Error %d initing security descriptor"), GetLastError()));
  1334. hr = HRESULT_FROM_WIN32(dwErr);
  1335. }
  1336. }
  1337. else
  1338. {
  1339. DBGERROR((TEXT("Can't find listview item for owner %d, file %d"),
  1340. handle.OwnerIndex(), handle.FileIndex()));
  1341. }
  1342. }
  1343. //
  1344. // Refresh the owner combo with the new file counts.
  1345. //
  1346. InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo);
  1347. return hr;
  1348. }
  1349. //
  1350. // The original code for listing files owned by a user was
  1351. // contributed by MarkZ. I made some minor modifications
  1352. // to fit it into the diskquota project and make it more
  1353. // exception safe.
  1354. //
  1355. inline VOID *
  1356. Add2Ptr(VOID *pv, ULONG cb)
  1357. {
  1358. return((BYTE *) pv + cb);
  1359. }
  1360. inline ULONG
  1361. QuadAlign( ULONG Value )
  1362. {
  1363. return (Value + 7) & ~7;
  1364. }
  1365. //
  1366. // Add files owned by a particular user on a particular volume.
  1367. //
  1368. HRESULT
  1369. CFileOwnerDialog::AddFilesToOwnerList(
  1370. LPCTSTR pszVolumeRoot,
  1371. HANDLE hVolumeRoot,
  1372. IDiskQuotaUser *pOwner,
  1373. COwnerList *pOwnerList
  1374. )
  1375. {
  1376. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::AddFilesToOwnerList")));
  1377. DBGASSERT((NULL != hVolumeRoot));
  1378. DBGASSERT((NULL != pOwner));
  1379. DBGASSERT((NULL != pOwnerList));
  1380. struct
  1381. {
  1382. ULONG Restart;
  1383. BYTE Sid[MAX_SID_LEN];
  1384. }FsCtlInput;
  1385. NTSTATUS status = ERROR_SUCCESS;
  1386. if (m_bAbort)
  1387. {
  1388. return S_OK;
  1389. }
  1390. //
  1391. // Get owner's SID.
  1392. //
  1393. HRESULT hr = pOwner->GetSid(FsCtlInput.Sid, sizeof(FsCtlInput.Sid));
  1394. if (FAILED(hr))
  1395. {
  1396. DBGERROR((TEXT("IDiskQuotaUser::GetSid failed with hr = 0x%08X"), hr));
  1397. return hr;
  1398. }
  1399. //
  1400. // Add the owner to the owner-file list.
  1401. //
  1402. int iOwner = pOwnerList->AddOwner(pOwner);
  1403. IO_STATUS_BLOCK iosb;
  1404. FsCtlInput.Restart = 1;
  1405. BYTE Output[1024];
  1406. bool bPathIsRemote = false;
  1407. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  1408. //
  1409. // Determine if the volume is a remote device. This will affect
  1410. // our handling of the paths returned by NtQueryInformationFile.
  1411. //
  1412. status = NtQueryVolumeInformationFile(
  1413. hVolumeRoot,
  1414. &iosb,
  1415. &DeviceInfo,
  1416. sizeof(DeviceInfo),
  1417. FileFsDeviceInformation);
  1418. if (NT_SUCCESS(status))
  1419. {
  1420. bPathIsRemote = (FILE_REMOTE_DEVICE == DeviceInfo.Characteristics);
  1421. }
  1422. while (!m_bAbort)
  1423. {
  1424. status = NtFsControlFile(hVolumeRoot,
  1425. NULL,
  1426. NULL,
  1427. NULL,
  1428. &iosb,
  1429. FSCTL_FIND_FILES_BY_SID,
  1430. &FsCtlInput,
  1431. sizeof(FsCtlInput),
  1432. Output,
  1433. sizeof(Output));
  1434. FsCtlInput.Restart = 0;
  1435. if (!NT_SUCCESS(status) && STATUS_BUFFER_OVERFLOW != status)
  1436. {
  1437. DBGERROR((TEXT("NtFsControlFile failed with status 0x%08X"), status));
  1438. return HRESULT_FROM_NT(status);
  1439. }
  1440. if (0 == iosb.Information)
  1441. {
  1442. //
  1443. // No more data.
  1444. //
  1445. break;
  1446. }
  1447. PFILE_NAME_INFORMATION pFileNameInfo = (PFILE_NAME_INFORMATION)Output;
  1448. while (!m_bAbort && ((PBYTE)pFileNameInfo < Output + iosb.Information))
  1449. {
  1450. ULONG Length = sizeof(FILE_NAME_INFORMATION) - sizeof(WCHAR) +
  1451. pFileNameInfo->FileNameLength;
  1452. CNtHandle hChild;
  1453. WCHAR szChild[MAX_PATH];
  1454. RtlMoveMemory(szChild, pFileNameInfo->FileName, pFileNameInfo->FileNameLength);
  1455. szChild[pFileNameInfo->FileNameLength / sizeof(WCHAR)] = L'\0';
  1456. status = OpenNtObject(szChild,
  1457. hVolumeRoot,
  1458. FILE_SYNCHRONOUS_IO_NONALERT,
  1459. FILE_READ_ATTRIBUTES,
  1460. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1461. FILE_OPEN,
  1462. hChild.HandlePtr());
  1463. if (!NT_SUCCESS(status))
  1464. {
  1465. DBGERROR((TEXT("Unable to open file \"%s\". Status = 0x%08X"),
  1466. szChild, status));
  1467. }
  1468. else if (!m_bAbort)
  1469. {
  1470. //
  1471. // Directory entries get a slightly different treatment so
  1472. // we need to know if an entry is a directory or not.
  1473. //
  1474. bool bIsDirectory = false;
  1475. IO_STATUS_BLOCK iosb2;
  1476. FILE_BASIC_INFORMATION fbi;
  1477. status = NtQueryInformationFile(hChild,
  1478. &iosb2,
  1479. &fbi,
  1480. sizeof(fbi),
  1481. FileBasicInformation);
  1482. if (!NT_SUCCESS(status))
  1483. {
  1484. DBGERROR((TEXT("NtQueryInformationFile failed with status 0x%08X for \"%s\""),
  1485. status, szChild));
  1486. }
  1487. else if (0 != (FILE_ATTRIBUTE_DIRECTORY & fbi.FileAttributes))
  1488. {
  1489. bIsDirectory = true;
  1490. }
  1491. //
  1492. // Get the file's name (full path).
  1493. //
  1494. WCHAR szFile[MAX_PATH + 10];
  1495. status = NtQueryInformationFile(hChild,
  1496. &iosb2,
  1497. szFile,
  1498. sizeof(szFile),
  1499. FileNameInformation);
  1500. if (!NT_SUCCESS(status))
  1501. {
  1502. DBGERROR((TEXT("NtQueryInformation file failed with status 0x%08X for \"%s\""),
  1503. status, szChild));
  1504. }
  1505. else if (!m_bAbort)
  1506. {
  1507. PFILE_NAME_INFORMATION pfn = (PFILE_NAME_INFORMATION)szFile;
  1508. pfn->FileName[pfn->FileNameLength / sizeof(WCHAR)] = L'\0';
  1509. CPath path;
  1510. //
  1511. // If the path is remote, NtQueryInformationFile returns
  1512. // a string like this:
  1513. //
  1514. // \server\share\dir1\dir2\file.ext
  1515. //
  1516. // If the path is local, NtQueryInformationFile returns
  1517. // a string like this:
  1518. //
  1519. // \dir1\dir2\file.ext
  1520. //
  1521. // For remote paths we merely prepend a '\' to create a
  1522. // valid UNC path. For local paths we prepend the local
  1523. // drive specification.
  1524. //
  1525. if (bPathIsRemote)
  1526. {
  1527. path = L"\\";
  1528. path += CString(pfn->FileName);
  1529. }
  1530. else
  1531. {
  1532. path = pszVolumeRoot;
  1533. path.Append(pfn->FileName);
  1534. }
  1535. DBGPRINT((DM_VIEW, DL_LOW, TEXT("Adding \"%s\""), path.Cstr()));
  1536. pOwnerList->AddFile(iOwner, path, bIsDirectory);
  1537. }
  1538. }
  1539. hChild.Close();
  1540. pFileNameInfo =
  1541. (PFILE_NAME_INFORMATION) Add2Ptr(pFileNameInfo, QuadAlign(Length));
  1542. }
  1543. }
  1544. return NOERROR;
  1545. }
  1546. //
  1547. // Build a list of files owned by a set of users on a particular volume.
  1548. // pszVolumeRoot is the volume root directory (i.e. "C:\").
  1549. // rgpOwners is an array of user object pointers, one for each owner.
  1550. // pOwnerList is the container where the resulting filenames are placed.
  1551. // Calls AddFilesToOwnerList() for each owner in rgpOwners.
  1552. //
  1553. HRESULT
  1554. CFileOwnerDialog::BuildFileOwnerList(
  1555. LPCTSTR pszVolumeRoot,
  1556. const CArray<IDiskQuotaUser *>& rgpOwners,
  1557. COwnerList *pOwnerList
  1558. )
  1559. {
  1560. DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BuildFileOwnerList")));
  1561. HRESULT hr = NOERROR;
  1562. CNtHandle hVolumeRoot;
  1563. NTSTATUS status = OpenNtObject(pszVolumeRoot,
  1564. NULL,
  1565. FILE_SYNCHRONOUS_IO_NONALERT,
  1566. FILE_READ_ATTRIBUTES,
  1567. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1568. FILE_OPEN,
  1569. hVolumeRoot.HandlePtr());
  1570. if (!NT_SUCCESS(status))
  1571. return HRESULT_FROM_NT(status);
  1572. int cOwners = rgpOwners.Count();
  1573. for (int i = 0; i < cOwners && !m_bAbort; i++)
  1574. {
  1575. hr = AddFilesToOwnerList(pszVolumeRoot, hVolumeRoot, rgpOwners[i], pOwnerList);
  1576. }
  1577. return hr;
  1578. }
  1579. //
  1580. // MarkZ had this function in his original implementation so I just
  1581. // kept it. I did need to fix a bug in the original code. He was
  1582. // calling RtlFreeHeap() on str.Buffer for all cases. This is was
  1583. // not applicable in the RtlInitUnicodeString() case where the
  1584. // unicode string is merely bound to the pszFile argument.
  1585. //
  1586. NTSTATUS
  1587. CFileOwnerDialog::OpenNtObject (
  1588. LPCWSTR pszFile,
  1589. HANDLE RelatedObject,
  1590. ULONG CreateOptions,
  1591. ULONG DesiredAccess,
  1592. ULONG ShareAccess,
  1593. ULONG CreateDisposition,
  1594. HANDLE *ph)
  1595. {
  1596. NTSTATUS status;
  1597. OBJECT_ATTRIBUTES oa;
  1598. UNICODE_STRING str;
  1599. IO_STATUS_BLOCK isb;
  1600. bool bFreeString = false;
  1601. if (NULL == RelatedObject)
  1602. {
  1603. RtlDosPathNameToNtPathName_U(pszFile, &str, NULL, NULL);
  1604. bFreeString = true;
  1605. }
  1606. else
  1607. {
  1608. //
  1609. // This just attaches pszFile to the rtl string.
  1610. // We don't free it.
  1611. //
  1612. RtlInitUnicodeString(&str, pszFile);
  1613. }
  1614. InitializeObjectAttributes(&oa,
  1615. &str,
  1616. OBJ_CASE_INSENSITIVE,
  1617. RelatedObject,
  1618. NULL);
  1619. status = NtCreateFile(ph,
  1620. DesiredAccess | SYNCHRONIZE,
  1621. &oa,
  1622. &isb,
  1623. NULL, // pallocationsize (none!)
  1624. FILE_ATTRIBUTE_NORMAL,
  1625. ShareAccess,
  1626. CreateDisposition,
  1627. CreateOptions,
  1628. NULL, // EA buffer (none!)
  1629. 0);
  1630. if (bFreeString)
  1631. RtlFreeHeap(RtlProcessHeap(), 0, str.Buffer);
  1632. return(status);
  1633. }