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.

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