#include "pch.h" #pragma hdrstop #include "ownerdlg.h" #include "ownerlst.h" #include "resource.h" #include "uihelp.h" #include "uiutils.h" // // Private message used to indicate that the owner list thread // has completed it's work. // const UINT PWM_OWNERLIST_COMPLETE = WM_USER + 1; // // Mask bits indicating what operations are allowed for a given // file selection set in the listview. // const DWORD ACTION_NONE = 0x00000000; const DWORD ACTION_TAKEOWNERSHIP = 0x00000001; const DWORD ACTION_MOVE = 0x00000002; const DWORD ACTION_DELETE = 0x00000004; const DWORD ACTION_ANY = 0x00000007; const static DWORD rgFileOwnerDialogHelpIDs[] = { IDC_CMB_OWNERDLG_OWNERS, IDH_CMB_OWNERDLG_OWNERS, IDC_LV_OWNERDLG, IDH_LV_OWNERDLG, IDC_BTN_OWNERDLG_DELETE, IDH_BTN_OWNERDLG_DELETE, IDC_BTN_OWNERDLG_MOVETO, IDH_BTN_OWNERDLG_MOVETO, IDC_BTN_OWNERDLG_TAKE, IDH_BTN_OWNERDLG_TAKE, IDC_BTN_OWNERDLG_BROWSE, IDH_BTN_OWNERDLG_BROWSE, IDC_EDIT_OWNERDLG_MOVETO, IDH_EDIT_OWNERDLG_MOVETO, 0,0 }; CFileOwnerDialog::CFileOwnerDialog(HINSTANCE hInstance, HWND hwndParent, LPCTSTR pszVolumeRoot, const CArray& rgpOwners ) : m_hInstance(hInstance), m_hwndParent(hwndParent), m_hwndDlg(NULL), m_hwndLV(NULL), m_hwndOwnerCombo(NULL), m_hwndEditMoveTo(NULL), m_iLastColSorted(-1), m_bSortAscending(true), m_bAbort(false), m_hOwnerListThread(NULL), m_rgpOwners(rgpOwners), m_strVolumeRoot(pszVolumeRoot) { } CFileOwnerDialog::~CFileOwnerDialog( void ) { if (NULL != m_hOwnerListThread) { CloseHandle(m_hOwnerListThread); } } INT_PTR CFileOwnerDialog::Run( void ) { DBGTRACE((DM_VIEW, DL_HIGH, TEXT("CFileOwnerDialog::Run"))); return DialogBoxParam(m_hInstance, MAKEINTRESOURCE(IDD_OWNERSANDFILES), m_hwndParent, DlgProc, (LPARAM)this); } INT_PTR CALLBACK CFileOwnerDialog::DlgProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { // // Retrieve the dialog object's ptr from the window's userdata. // Place there in response to WM_INITDIALOG. // CFileOwnerDialog *pThis = (CFileOwnerDialog *)GetWindowLongPtr(hwnd, DWLP_USER); try { switch(uMsg) { case WM_INITDIALOG: // // Store "this" ptr in window's userdata. // SetWindowLongPtr(hwnd, DWLP_USER, (INT_PTR)lParam); pThis = (CFileOwnerDialog *)lParam; // // Save the HWND in our object. We'll need it later. // pThis->m_hwndDlg = hwnd; return pThis->OnInitDialog(hwnd); case WM_DESTROY: return pThis->OnDestroy(hwnd); case WM_COMMAND: return pThis->OnCommand(hwnd, wParam, lParam); case WM_NOTIFY: return pThis->OnNotify(hwnd, wParam, lParam); case WM_CONTEXTMENU: return pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); break; case PWM_OWNERLIST_COMPLETE: pThis->OnOwnerListComplete(); break; case WM_SETCURSOR: return pThis->OnSetCursor(hwnd); break; case WM_HELP: WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, STR_DSKQUOUI_HELPFILE, HELP_WM_HELP, (DWORD_PTR)(LPTSTR) rgFileOwnerDialogHelpIDs); return TRUE; } } catch(CAllocException& me) { // // Announce any out-of-memory errors associated with running the dlg. // DiskQuotaMsgBox(GetDesktopWindow(), IDS_OUTOFMEMORY, IDS_TITLE_DISK_QUOTA, MB_ICONERROR | MB_OK); } return FALSE; } INT_PTR CFileOwnerDialog::OnInitDialog( HWND hwnd ) { DBGTRACE((DM_VIEW, DL_HIGH, TEXT("CFileOwnerDialog::OnInitDialog"))); // // Save HWNDs of controls we'll need later. // m_hwndLV = GetDlgItem(hwnd, IDC_LV_OWNERDLG); m_hwndOwnerCombo = GetDlgItem(hwnd, IDC_CMB_OWNERDLG_OWNERS); m_hwndEditMoveTo = GetDlgItem(hwnd, IDC_EDIT_OWNERDLG_MOVETO); // // We want these controls to be disabled until the owner list // has been populated. // EnableWindow(m_hwndLV, FALSE); EnableWindow(m_hwndOwnerCombo, FALSE); EnableWindow(m_hwndEditMoveTo, FALSE); EnableWindow(GetDlgItem(hwnd, IDC_BTN_OWNERDLG_BROWSE), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_CBX_OWNERDLG_EXCLUDEDIRS), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_CBX_OWNERDLG_EXCLUDEFILES), FALSE); // // Build the list of owners and filenames on the volume. // This can take a while depending on how many owners // are in m_rgpOwners, the size of the volume and how many // files each owner owns. First clear the owner list in case Run() // is being called multiple times on the same dialog object. // Note that we build this list on a background thread so that // we don't block the UI while it's building. // m_OwnerList.Clear(); DWORD idThread; m_hOwnerListThread = CreateThread(NULL, 0, OwnerListThreadProc, this, 0, &idThread); return TRUE; } INT_PTR CFileOwnerDialog::OnSetCursor( HWND hwnd ) { if (m_hOwnerListThread && (WAIT_TIMEOUT == WaitForSingleObject(m_hOwnerListThread, 0))) { SetCursor(LoadCursor(NULL, IDC_WAIT)); return TRUE; } return FALSE; } INT_PTR CFileOwnerDialog::OnDestroy( HWND hwnd ) { m_bAbort = true; if (NULL != m_hOwnerListThread) { WaitForSingleObject(m_hOwnerListThread, INFINITE); } return TRUE; } DWORD CFileOwnerDialog::OwnerListThreadProc( // [static] LPVOID pvParam ) { CFileOwnerDialog *pThis = (CFileOwnerDialog *)pvParam; pThis->BuildFileOwnerList(pThis->m_strVolumeRoot, pThis->m_rgpOwners, &(pThis->m_OwnerList)); PostMessage(pThis->m_hwndDlg, PWM_OWNERLIST_COMPLETE, 0, 0); return 0; } // // Called in response to PWM_OWNERLIST_COMPLETE which is posted // to the dialog when the OwnerListThreadProc has completed // it's processing. // void CFileOwnerDialog::OnOwnerListComplete( void ) { // // Set the message in the top of the dialog. // CString s(m_hInstance, IDS_FMT_OWNERDLG_HEADER, m_OwnerList.OwnerCount()); SetWindowText(GetDlgItem(m_hwndDlg, IDC_TXT_OWNERDLG_HEADER), s); // // Populate the listview and owner combo. // InitializeList(m_OwnerList, m_hwndLV); InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo); // // Now we can enable the controls we disabled in OnInitDialog(). // EnableWindow(m_hwndLV, TRUE); EnableWindow(m_hwndOwnerCombo, TRUE); EnableWindow(m_hwndEditMoveTo, TRUE); EnableWindow(GetDlgItem(m_hwndDlg, IDC_BTN_OWNERDLG_BROWSE), TRUE); EnableWindow(GetDlgItem(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEDIRS), TRUE); EnableWindow(GetDlgItem(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEFILES), TRUE); } INT_PTR CFileOwnerDialog::OnCommand( HWND hwnd, WPARAM wParam, LPARAM lParam ) { BOOL bResult = TRUE; // Assume not handled. WORD wID = LOWORD(wParam); WORD wNotifyCode = HIWORD(wParam); HWND hCtl = (HWND)lParam; switch(wID) { case IDCANCEL: EndDialog(hwnd, 0); bResult = FALSE; break; case IDC_CMB_OWNERDLG_OWNERS: if (CBN_SELCHANGE == wNotifyCode) { int iOwner = ComboBox_GetCurSel(m_hwndOwnerCombo); if (1 < m_OwnerList.OwnerCount()) { // // Owner list contains more than one owner. The combo // contains a leading "All owners" entry. // iOwner--; } DBGASSERT((-1 <= iOwner)); CAutoSetRedraw autoredraw(m_hwndLV, false); // // Only show "owner" column if user has selected "all owners" combo item. // CreateListColumns(m_hwndLV, -1 == iOwner); FillListView(m_OwnerList, m_hwndLV, iOwner); } bResult = FALSE; break; case IDC_BTN_OWNERDLG_BROWSE: { CString s; if (BrowseForFolder(hwnd, &s)) SetWindowText(m_hwndEditMoveTo, s); break; } case IDC_BTN_OWNERDLG_DELETE: DeleteSelectedFiles(m_hwndLV); bResult = FALSE; break; case IDC_BTN_OWNERDLG_MOVETO: { CPath strDest; CPath strRoot; int cchEdit = Edit_GetTextLength(m_hwndEditMoveTo); Edit_GetText(m_hwndEditMoveTo, strDest.GetBuffer(cchEdit + 1), cchEdit + 1); strDest.ReleaseBuffer(); strDest.Trim(); strDest.GetRoot(&strRoot); if (IsSameVolume(strRoot, m_strVolumeRoot)) { // // Don't let operator move files to a folder // on the same volume. // DiskQuotaMsgBox(m_hwndDlg, IDS_ERROR_MOVETO_SAMEVOL, IDS_TITLE_DISK_QUOTA, MB_ICONINFORMATION | MB_OK); SetWindowText(m_hwndEditMoveTo, strDest); SetFocus(m_hwndEditMoveTo); } else { // // Eveything looks OK. Try to move the files. // MoveSelectedFiles(m_hwndLV, strDest); } bResult = FALSE; break; } case IDC_BTN_OWNERDLG_TAKE: { HRESULT hr = TakeOwnershipOfSelectedFiles(m_hwndLV); if (FAILED(hr)) { DBGERROR((TEXT("TakeOwnershipOfSelectedFiles failed with hr = 0x%08X"), hr)); } break; } case IDC_EDIT_OWNERDLG_MOVETO: if (EN_UPDATE == wNotifyCode) { // // Disable the "Move" button if the destination edit field // is blank. // HWND hwnd = GetDlgItem(m_hwndDlg, IDC_BTN_OWNERDLG_MOVETO); bool bEnable = ShouldEnableControl(IDC_BTN_OWNERDLG_MOVETO); if (bEnable != boolify(IsWindowEnabled(hwnd))) { EnableWindow(hwnd, bEnable); } } break; case IDC_CBX_OWNERDLG_EXCLUDEFILES: case IDC_CBX_OWNERDLG_EXCLUDEDIRS: if (BN_CLICKED == wNotifyCode) { // // The allowable states for these two checkboxes are: // // Excl Files Excl Dirs // --------------- ---------------- // Checked Unchecked // Unchecked Checked // Unchecked Unchecked // // It makes no sense to have both checkboxes checked. // This would cause the list to be empty and might // generate user confusion. // if (IsDlgButtonChecked(m_hwndDlg, wID)) { UINT idOther = IDC_CBX_OWNERDLG_EXCLUDEFILES; if (IDC_CBX_OWNERDLG_EXCLUDEFILES == wID) { idOther = IDC_CBX_OWNERDLG_EXCLUDEDIRS; } CheckDlgButton(m_hwndDlg, idOther, BST_UNCHECKED); } FillListView(m_OwnerList, m_hwndLV, ComboBox_GetCurSel(m_hwndOwnerCombo) - 1); } break; } return bResult; } INT_PTR CFileOwnerDialog::OnContextMenu( HWND hwndItem, int xPos, int yPos ) { int idCtl = GetDlgCtrlID(hwndItem); WinHelp(hwndItem, UseWindowsHelp(idCtl) ? NULL : STR_DSKQUOUI_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)((LPTSTR)rgFileOwnerDialogHelpIDs)); return FALSE; } // // Determine what actions are allowed for the current selection. // DWORD CFileOwnerDialog::GetAllowedActions( HWND hwndLV ) { CArray rgItemHandles; BuildListOfSelectedFiles(hwndLV, NULL, &rgItemHandles); if (0 != rgItemHandles.Count()) { const int cHandles = rgItemHandles.Count(); for(int i = 0; i < cHandles; i++) { COwnerListItemHandle handle = rgItemHandles[i]; int iOwner = handle.OwnerIndex(); int iFile = handle.FileIndex(); if (m_OwnerList.IsFileDirectory(iOwner, iFile)) { // // If any directory exists in the selection, // "take ownership" is the only allowed action. // return ACTION_TAKEOWNERSHIP; } } } return ACTION_ANY; } // // Determine if one of the move/delete/take buttons should be enabled // or disabled. // bool CFileOwnerDialog::ShouldEnableControl( UINT idCtl ) { bool bEnable = true; int cLVSel = ListView_GetSelectedCount(m_hwndLV); DWORD actions = GetAllowedActions(m_hwndLV); switch(idCtl) { case IDC_BTN_OWNERDLG_DELETE: bEnable = (0 != (ACTION_DELETE & actions)) && (0 < cLVSel); break; case IDC_BTN_OWNERDLG_TAKE: bEnable = (0 != (ACTION_TAKEOWNERSHIP & actions)) && (0 < cLVSel); break; case IDC_BTN_OWNERDLG_MOVETO: bEnable = (0 != (ACTION_MOVE & actions)); if (bEnable && 0 < cLVSel) { CPath s; int cch = Edit_GetTextLength(m_hwndEditMoveTo); Edit_GetText(m_hwndEditMoveTo, s.GetBuffer(cch + 1), cch + 1); s.ReleaseBuffer(); s.Trim(); bEnable = 0 < s.Length(); } break; case IDC_BTN_OWNERDLG_BROWSE: case IDC_EDIT_OWNERDLG_MOVETO: bEnable = (0 != (ACTION_MOVE & actions)); break; default: break; } return bEnable; } INT_PTR CFileOwnerDialog::OnNotify( HWND hwnd, WPARAM wParam, LPARAM lParam ) { BOOL bResult = TRUE; LPNMHDR pnm = (LPNMHDR)lParam; switch(pnm->code) { case LVN_GETDISPINFO: OnLVN_GetDispInfo((LV_DISPINFO *)lParam); break; case LVN_COLUMNCLICK: OnLVN_ColumnClick((NM_LISTVIEW *)lParam); break; case LVN_ITEMCHANGED: OnLVN_ItemChanged((NM_LISTVIEW *)lParam); break; case LVN_KEYDOWN: OnLVN_KeyDown((NMLVKEYDOWN *)lParam); break; default: break; } return bResult; } void CFileOwnerDialog::OnLVN_GetDispInfo( LV_DISPINFO *plvdi ) { static CPath strPath; static CString strOwner; COwnerListItemHandle hItem(plvdi->item.lParam); int iOwner = hItem.OwnerIndex(); int iFile = hItem.FileIndex(); if (LVIF_TEXT & plvdi->item.mask) { switch(plvdi->item.iSubItem) { case iLVSUBITEM_FILE: { CPath s; m_OwnerList.GetFileName(iOwner, iFile, &s); if (m_OwnerList.IsFileDirectory(iOwner, iFile)) { strPath.Format(m_hInstance, IDS_FMT_OWNERDLG_FOLDERNAME, s.Cstr()); } else { strPath = s; } plvdi->item.pszText = (LPTSTR)strPath.Cstr(); } break; case iLVSUBITEM_FOLDER: m_OwnerList.GetFolderName(iOwner, iFile, &strPath); plvdi->item.pszText = (LPTSTR)strPath.Cstr(); break; case iLVSUBITEM_OWNER: m_OwnerList.GetOwnerName(iOwner, &strOwner); plvdi->item.pszText = (LPTSTR)strOwner.Cstr(); break; } } if (LVIF_IMAGE & plvdi->item.mask) { // // Not displaying any images. This is just a placeholder. // Should be optimized out by compiler. // } } int CALLBACK CFileOwnerDialog::CompareLVItems( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort ) { CFileOwnerDialog *pdlg = reinterpret_cast(lParamSort); int diff = 0; try { COwnerListItemHandle h1(lParam1); COwnerListItemHandle h2(lParam2); int iOwner1 = h1.OwnerIndex(); int iOwner2 = h2.OwnerIndex(); int iFile1 = h1.FileIndex(); int iFile2 = h2.FileIndex(); static CPath s1, s2; // // This array controls the comparison column IDs used when // values for the selected column are equal. These should // remain in order of the iLVSUBITEM_xxxxx enumeration with // respect to the first element in each row. // static const int rgColComp[3][3] = { { iLVSUBITEM_FILE, iLVSUBITEM_FOLDER, iLVSUBITEM_OWNER }, { iLVSUBITEM_FOLDER, iLVSUBITEM_FILE, iLVSUBITEM_OWNER }, { iLVSUBITEM_OWNER, iLVSUBITEM_FILE, iLVSUBITEM_FOLDER } }; int iCompare = 0; while(0 == diff && iCompare < ARRAYSIZE(rgColComp)) { switch(rgColComp[pdlg->m_iLastColSorted][iCompare++]) { case iLVSUBITEM_FILE: pdlg->m_OwnerList.GetFileName(iOwner1, iFile1, &s1); pdlg->m_OwnerList.GetFileName(iOwner2, iFile2, &s2); break; case iLVSUBITEM_FOLDER: pdlg->m_OwnerList.GetFolderName(iOwner1, iFile1, &s1); pdlg->m_OwnerList.GetFolderName(iOwner2, iFile2, &s2); break; case iLVSUBITEM_OWNER: // // Can use CPath (s1 and s2) in place of CString arg since // CPath is derived from CString. // pdlg->m_OwnerList.GetOwnerName(iOwner1, &s1); pdlg->m_OwnerList.GetOwnerName(iOwner2, &s2); break; default: // // If you hit this, you need to update this function // to handle the new column you've added to the listview. // DBGASSERT((false)); break; } diff = s1.Compare(s2); } // // Don't need contents of static strings between function invocations. // The strings are static to avoid repeated construction/destruction. // It's only a minor optimization. // s1.Empty(); s2.Empty(); } catch(CAllocException& e) { // // Do nothing. Just return diff "as is". // Don't want to throw an exception back into comctl32. // } return pdlg->m_bSortAscending ? diff : -1 * diff; } void CFileOwnerDialog::OnLVN_ColumnClick( NM_LISTVIEW *pnmlv ) { DBGTRACE((DM_VIEW, DL_LOW, TEXT("CFileOwnerDialog::OnLVN_ColumnClick"))); if (m_iLastColSorted != pnmlv->iSubItem) { m_bSortAscending = true; m_iLastColSorted = pnmlv->iSubItem; } else { m_bSortAscending = !m_bSortAscending; } ListView_SortItems(m_hwndLV, CompareLVItems, LPARAM(this)); } // // Called whenever a listview item has changed state. // I'm using this to update the "enabledness" of the // dialog buttons. If there's nothing selected in the listview, // the move/delete/take buttons are disabled. // void CFileOwnerDialog::OnLVN_ItemChanged( NM_LISTVIEW *pnmlv ) { static const int rgCtls[] = { IDC_BTN_OWNERDLG_DELETE, IDC_BTN_OWNERDLG_TAKE, IDC_BTN_OWNERDLG_MOVETO, IDC_BTN_OWNERDLG_BROWSE, IDC_EDIT_OWNERDLG_MOVETO}; // // LVN_ITEMCHANGED is sent multiple times when you move the // highlight bar in a listview. // Only run this code when the "focused" state bit is set // for the "new state". This should be the last call in // the series. // if (LVIS_FOCUSED & pnmlv->uNewState) { for (int i = 0; i < ARRAYSIZE(rgCtls); i++) { HWND hwnd = GetDlgItem(m_hwndDlg, rgCtls[i]); bool bEnable = ShouldEnableControl(rgCtls[i]); if (bEnable != boolify(IsWindowEnabled(hwnd))) { EnableWindow(hwnd, bEnable); } } } } void CFileOwnerDialog::OnLVN_KeyDown( NMLVKEYDOWN *plvkd ) { if (VK_DELETE == plvkd->wVKey) { DeleteSelectedFiles(m_hwndLV); FocusOnSomethingInListview(m_hwndLV); } } void CFileOwnerDialog::FocusOnSomethingInListview( HWND hwndLV ) { // // Focus on something. // int iFocus = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED); if (-1 == iFocus) iFocus = 0; ListView_SetItemState(hwndLV, iFocus, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); } // // Creates the listview columns and populates the listview // with filenames. // void CFileOwnerDialog::InitializeList( const COwnerList& fol, // file & owner list HWND hwndList ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::InitializeList"))); CreateListColumns(hwndList, 1 < m_OwnerList.OwnerCount()); FillListView(fol, hwndList); ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT); } void CFileOwnerDialog::CreateListColumns( HWND hwndList, bool bShowOwner // Default is true. ) { // // Clear out the listview and header. // ListView_DeleteAllItems(hwndList); HWND hwndHeader = ListView_GetHeader(hwndList); if (NULL != hwndHeader) { while(0 < Header_GetItemCount(hwndHeader)) ListView_DeleteColumn(hwndList, 0); } // // Create the header titles. // CString strFile(m_hInstance, IDS_OWNERDLG_HDR_FILE); CString strFolder(m_hInstance, IDS_OWNERDLG_HDR_FOLDER); CString strOwner(m_hInstance, IDS_OWNERDLG_HDR_OWNER); // // FEATURE: Should probably allow for vertical scroll bar also. // RECT rcList; GetClientRect(hwndList, &rcList); int cxCol = (rcList.right - rcList.left) / (bShowOwner ? 3 : 2); #define LVCOLMASK (LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM) LV_COLUMN rgCols[] = { { LVCOLMASK, LVCFMT_LEFT, cxCol, strFile, 0, iLVSUBITEM_FILE }, { LVCOLMASK, LVCFMT_LEFT, cxCol, strFolder, 0, iLVSUBITEM_FOLDER }, { LVCOLMASK, LVCFMT_LEFT, cxCol, strOwner, 0, iLVSUBITEM_OWNER } }; // // Add the columns to the listview. // int cCols = bShowOwner ? ARRAYSIZE(rgCols) : ARRAYSIZE(rgCols) - 1; for (INT i = 0; i < cCols; i++) { if (-1 == ListView_InsertColumn(hwndList, i, &rgCols[i])) { DBGERROR((TEXT("CFileOwnerDialog::CreateListColumns failed to add column %d"), i)); } } } void CFileOwnerDialog::FillListView( const COwnerList& fol, // file & owner list HWND hwndList, int iOwner // default is -1 (all owners) ) { ListView_DeleteAllItems(hwndList); LV_ITEM item; item.iSubItem = 0; item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_PARAM; item.state = 0; item.stateMask = 0; item.pszText = LPSTR_TEXTCALLBACK; item.iImage = I_IMAGECALLBACK; int iFirst = iOwner; int iLast = iOwner; if (-1 == iOwner) { iFirst = 0; iLast = fol.OwnerCount() - 1; } int iItem = 0; const bool bExclFiles = IsDlgButtonChecked(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEFILES); const bool bExclDirs = IsDlgButtonChecked(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEDIRS); // // WARNING: Reusing formal arg iOwner. It's safe to do, but you // should be aware that I'm doing it. // for (iOwner = iFirst; iOwner <= iLast; iOwner++) { int cFiles = fol.FileCount(iOwner, true); for (int iFile = 0; iFile < cFiles; iFile++) { bool bDirectory = fol.IsFileDirectory(iOwner, iFile); bool bFile = !bDirectory; if ((bDirectory && !bExclDirs) || (bFile && !bExclFiles)) { if (!fol.IsFileDeleted(iOwner, iFile)) { item.lParam = COwnerListItemHandle(iOwner, iFile); item.iItem = iItem++; if (-1 == ListView_InsertItem(hwndList, &item)) DBGERROR((TEXT("Error adding LV item for owner %d, file %d"), iOwner, iFile)); } } } } } void CFileOwnerDialog::InitializeOwnerCombo( const COwnerList& fol, // file & owner list HWND hwndCombo ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::InitializeList"))); int iSelected = ComboBox_GetCurSel(hwndCombo); ComboBox_ResetContent(hwndCombo); CString s, s2; int cOwners = fol.OwnerCount(); if (1 < cOwners) { // // Add "all owners" entry. // s.Format(m_hInstance, IDS_FMT_ALLOWNERS, fol.FileCount()); ComboBox_InsertString(hwndCombo, -1, s); } for (int iOwner = 0; iOwner < cOwners; iOwner++) { fol.GetOwnerName(iOwner, &s2); s.Format(m_hInstance, IDS_FMT_OWNER, s2.Cstr(), fol.FileCount(iOwner)); ComboBox_InsertString(hwndCombo, -1, s); } ComboBox_SetCurSel(hwndCombo, CB_ERR != iSelected ? iSelected : 0); // // Set the max height of the owner combo // RECT rcCombo; GetClientRect(m_hwndOwnerCombo, &rcCombo); SetWindowPos(m_hwndOwnerCombo, NULL, 0, 0, rcCombo.right - rcCombo.left, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); } // // Determine if two volume root strings refer to the same volume. // With volume mount points, "C:\" and "D:\DriveC" could refer to the // same physical volume. To differentiate we need to examine the unique // volume name GUID strings. // bool CFileOwnerDialog::IsSameVolume( LPCTSTR pszRoot1, LPCTSTR pszRoot2 ) { TCHAR szVolGUID1[MAX_PATH]; TCHAR szTemp[MAX_PATH]; bool bSameVolume = false; // // GetVolumeNameForVolumeMountPoint requires trailing backslash on paths. // lstrcpyn(szTemp, pszRoot1, ARRAYSIZE(szTemp)); PathAddBackslash(szTemp); if (GetVolumeNameForVolumeMountPoint(szTemp, szVolGUID1, ARRAYSIZE(szVolGUID1))) { TCHAR szVolGUID2[MAX_PATH]; lstrcpyn(szTemp, pszRoot2, ARRAYSIZE(szTemp)); PathAddBackslash(szTemp); if (GetVolumeNameForVolumeMountPoint(szTemp, szVolGUID2, ARRAYSIZE(szVolGUID2))) { if (0 == lstrcmpi(szVolGUID1, szVolGUID2)) bSameVolume = true; } } return bSameVolume; } // // Let the user browse for a folder. // The selected folder path is returned in *pstrFolder. // bool CFileOwnerDialog::BrowseForFolder( HWND hwndParent, CString *pstrFolder ) { bool bResult = false; BROWSEINFO bi; ZeroMemory(&bi, sizeof(bi)); CString strTitle(m_hInstance, IDS_BROWSEFORFOLDER); bi.hwndOwner = hwndParent; bi.pidlRoot = NULL; // Start at desktop. bi.pszDisplayName = NULL; bi.lpszTitle = strTitle.Cstr(); // // FEATURE: Setting the BIF_EDITBOX flag causes SHBrowseForFolder to invoke // autocomplete through SHAutoComplete (in shlwapi). SHAutoComplete // loads browseui.dll to implement the autocomplete feature. The bad // part is that SHAutoComplete also unloads browseui.dll before it // returns, resulting in calls to the unloaded WndProc. I've notified // ReinerF about this. Turning off the BIF_EDITBOX bit prevents // autocomplete from being used and thus prevents the problem. // I want the edit box. Turn it back on once they fix this bug. // // brianau [1/30/97] // bi.ulFlags = BIF_RETURNONLYFSDIRS; // | BIF_EDITBOX; bi.lpfn = BrowseForFolderCallback; bi.lParam = (LPARAM)pstrFolder; bi.iImage = 0; bResult = boolify(SHBrowseForFolder(&bi)); return bResult; } // // Callback called by SHBrowseForFolder. Writes selected folder path // to CString object who's pointer is passed in lpData arg. // int CFileOwnerDialog::BrowseForFolderCallback( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BrowseForFolderCallback"))); CString *pstrFolder = (CString *)lpData; if (BFFM_SELCHANGED == uMsg) { SHGetPathFromIDList((LPCITEMIDLIST)lParam, pstrFolder->GetBuffer(MAX_PATH)); pstrFolder->ReleaseBuffer(); } return 0; } // // Builds a double-nul terminated list of file paths from the listview // along with an array of "item handle" objects that acts as a cross- // reference between the list items, items in the listview and items // in the file owner list. Each handle contains an owner index and // file index into the file owner list. Each handle is also the value // stored as the lParam in the listview items. // Both pList and prgItemHandles arguments are optional. Although, // calling with neither non-null is sort of useless. // void CFileOwnerDialog::BuildListOfSelectedFiles( HWND hwndLV, DblNulTermList *pList, CArray *prgItemHandles ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BuildListOfSelectedFiles"))); int iItem = -1; CPath strPath; LV_ITEM item; if (NULL != prgItemHandles) prgItemHandles->Clear(); while(-1 != (iItem = ListView_GetNextItem(hwndLV, iItem, LVNI_SELECTED))) { item.iSubItem = 0; item.iItem = iItem; item.mask = LVIF_PARAM; if (-1 != ListView_GetItem(hwndLV, &item)) { COwnerListItemHandle hItem(item.lParam); m_OwnerList.GetFileFullPath(hItem.OwnerIndex(), hItem.FileIndex(), &strPath); if (pList) pList->AddString(strPath); if (prgItemHandles) prgItemHandles->Append(hItem); } } } // // Given an item "handle", find it's entry in the listview. // int CFileOwnerDialog::FindItemFromHandle( HWND hwndLV, const COwnerListItemHandle& handle ) { LV_FINDINFO lvfi; lvfi.flags = LVFI_PARAM; lvfi.lParam = handle; return ListView_FindItem(hwndLV, -1, &lvfi); } // // Scans an array of item handles and removes all corresponding // items from the listview. // void CFileOwnerDialog::RemoveListViewItems( HWND hwndLV, const CArray& rgItemHandles ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::RemoveListViewItems"))); LV_ITEM item; CPath strPath; CAutoSetRedraw autoredraw(hwndLV, false); int cHandles = rgItemHandles.Count(); for (int iHandle = 0; iHandle < cHandles; iHandle++) { COwnerListItemHandle handle = rgItemHandles[iHandle]; int iItem = FindItemFromHandle(hwndLV, handle); if (-1 != iItem) { int iOwner = handle.OwnerIndex(); int iFile = handle.FileIndex(); m_OwnerList.GetFileFullPath(iOwner, iFile, &strPath); if ((DWORD)-1 == GetFileAttributes(strPath)) { // // File doesn't exist any more. // Delete from the listview. // Mark it as "deleted" in the ownerlist container. // ListView_DeleteItem(hwndLV, iItem); m_OwnerList.MarkFileDeleted(iOwner, iFile); DBGPRINT((DM_VIEW, DL_LOW, TEXT("Removed item %d \"%s\""), iItem, strPath.Cstr())); } } } // // Refresh the owner combo to update the file counts. // InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo); } // // Delete the files selected in the listview. // Files deleted are removed from the listview. // void CFileOwnerDialog::DeleteSelectedFiles( HWND hwndLV ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::DeleteSelectedFiles"))); DblNulTermList list(1024); // 1024 is the buffer growth size in chars. CArray rgItemHandles; BuildListOfSelectedFiles(hwndLV, &list, &rgItemHandles); if (0 < list.Count()) { SHFILEOPSTRUCT fo; fo.hwnd = m_hwndDlg; fo.wFunc = FO_DELETE; fo.pFrom = list; fo.pTo = NULL; fo.fFlags = 0; if (0 != SHFileOperation(&fo)) { DBGERROR((TEXT("SHFileOperation [FO_DELETE] failed"))); } // // Remove listview items if their files were really deleted. // RemoveListViewItems(hwndLV, rgItemHandles); } } // // Move the selected files to a new location. // Moved files are removed from the listview. // void CFileOwnerDialog::MoveSelectedFiles( HWND hwndLV, LPCTSTR pszDest ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::DeleteSelectedFiles"))); DblNulTermList list(1024); // 1024 is the buffer growth size in chars. CArray rgItemHandles; BuildListOfSelectedFiles(hwndLV, &list, &rgItemHandles); if (0 < list.Count()) { CPath strDest(pszDest); if (1 == list.Count()) { // // If we have only a single file we MUST create a fully-qualified // path to the destination file. Oddities in the shell's move/copy // engine won't let us pass merely a destination folder in the // case where that folder doesn't exist. If we give the full path // including filename we'll get the "folder doesn't exist, create // now?" messagebox as we would expect. If we're moving multiple // files the shell accepts a single directory path. // LPCTSTR psz; DblNulTermListIter iter(list); if (iter.Next(&psz)) { CPath strSrc(psz); // Copy the source CPath strFile; strSrc.GetFileSpec(&strFile);// Extract the filename. strDest.Append(strFile); // Append to the dest path. } } SHFILEOPSTRUCT fo; fo.hwnd = m_hwndDlg; fo.wFunc = FO_MOVE; fo.pFrom = list; fo.pTo = strDest; fo.fFlags = FOF_RENAMEONCOLLISION; if (0 != SHFileOperation(&fo)) { DBGERROR((TEXT("SHFileOperation [FO_MOVE] failed"))); } // // Remove listview items if their file was really deleted. // RemoveListViewItems(hwndLV, rgItemHandles); } } // // Get the SID to use for taking ownership of files. // First try to get the first group SID with the SE_GROUP_OWNER attribute. // If none found, use the operator's account SID. The SID is in a // dynamic buffer attached to the ptrSid autoptr argument. // HRESULT CFileOwnerDialog::GetOwnershipSid( array_autoptr *ptrSid ) { HRESULT hr = E_FAIL; DWORD dwErr = 0; // // Get the token handle. First try the thread token then the process // token. If these fail we return early. No sense in continuing // on if we can't get a user token. // CWin32Handle hToken; if (!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, hToken.HandlePtr())) { if (ERROR_NO_TOKEN == GetLastError()) { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, hToken.HandlePtr())) { dwErr = GetLastError(); DBGERROR((TEXT("Error %d opening process token"), dwErr)); return HRESULT_FROM_WIN32(dwErr); } } else { dwErr = GetLastError(); DBGERROR((TEXT("Error %d opening thread token"), dwErr)); return HRESULT_FROM_WIN32(dwErr); } } // // Get the required size of the group token information buffer. // array_autoptr ptrTokenInfo; DWORD cbTokenInfo = 0; if (!GetTokenInformation(hToken, TokenGroups, NULL, cbTokenInfo, &cbTokenInfo)) { dwErr = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwErr) { ptrTokenInfo = new BYTE[cbTokenInfo]; } else { dwErr = GetLastError(); DBGERROR((TEXT("Error %d getting TokenGroups info [for size]"), dwErr)); hr = HRESULT_FROM_WIN32(dwErr); } } // // Get the group token information. // if (NULL != ptrTokenInfo.get()) { if (!GetTokenInformation(hToken, TokenGroups, ptrTokenInfo.get(), cbTokenInfo, &cbTokenInfo)) { dwErr = GetLastError(); DBGERROR((TEXT("Error %d getting TokenGroups info"), dwErr)); hr = HRESULT_FROM_WIN32(dwErr); } else { // // Extract the first SID with the GROUP_OWNER bit set. // TOKEN_GROUPS *ptg = (TOKEN_GROUPS *)ptrTokenInfo.get(); DBGASSERT((NULL != ptg)); for (DWORD i = 0; i < ptg->GroupCount; i++) { SID_AND_ATTRIBUTES *psa = (SID_AND_ATTRIBUTES *)&ptg->Groups[i]; DBGASSERT((NULL != psa)); if (SE_GROUP_OWNER & psa->Attributes) { int cbSid = GetLengthSid(psa->Sid); *ptrSid = new BYTE[cbSid]; CopySid(cbSid, ptrSid->get(), psa->Sid); hr = NOERROR; break; } } } } if (FAILED(hr)) { // // Didn't find a SID from the group information. // Use the operator's SID. // cbTokenInfo = 0; if (!GetTokenInformation(hToken, TokenUser, NULL, cbTokenInfo, &cbTokenInfo)) { dwErr = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwErr) { ptrTokenInfo = new BYTE[cbTokenInfo]; } else { DBGERROR((TEXT("Error %d getting TokenUser info [for size]"), dwErr)); hr = HRESULT_FROM_WIN32(dwErr); } } if (SUCCEEDED(hr)) { // // Get the user token information. // if (!GetTokenInformation(hToken, TokenUser, ptrTokenInfo.get(), cbTokenInfo, &cbTokenInfo)) { dwErr = GetLastError(); DBGERROR((TEXT("Error %d getting TokenUser info"), dwErr)); hr = HRESULT_FROM_WIN32(dwErr); } else { SID_AND_ATTRIBUTES *psa = (SID_AND_ATTRIBUTES *)ptrTokenInfo.get(); DBGASSERT((NULL != psa)); int cbSid = GetLengthSid(psa->Sid); *ptrSid = new BYTE[cbSid]; CopySid(cbSid, ptrSid->get(), psa->Sid); hr = NOERROR; } } } if (SUCCEEDED(hr) && NULL != ptrSid->get() && !IsValidSid(ptrSid->get())) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_SID); } return hr; } // // Transfers ownership of selected files in the listview to the // currently logged-on user. // HRESULT CFileOwnerDialog::TakeOwnershipOfSelectedFiles( HWND hwndLV ) { HRESULT hr = NOERROR; DWORD dwErr = 0; CArray rgItemHandles; BuildListOfSelectedFiles(hwndLV, NULL, &rgItemHandles); if (0 == rgItemHandles.Count()) return S_OK; array_autoptr ptrSid; hr = GetOwnershipSid(&ptrSid); if (FAILED(hr)) return hr; CPath strFile; int cHandles = rgItemHandles.Count(); for (int i = 0; i < cHandles; i++) { COwnerListItemHandle handle = rgItemHandles[i]; int iItem = FindItemFromHandle(hwndLV, handle); if (-1 != iItem) { SECURITY_DESCRIPTOR sd; if (InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { int iOwner = handle.OwnerIndex(); int iFile = handle.FileIndex(); m_OwnerList.GetFileFullPath(iOwner, iFile, &strFile); if (SetSecurityDescriptorOwner(&sd, ptrSid.get(), FALSE)) { if (SetFileSecurity(strFile, OWNER_SECURITY_INFORMATION, &sd)) { ListView_DeleteItem(hwndLV, iItem); m_OwnerList.MarkFileDeleted(iOwner, iFile); } else { dwErr = GetLastError(); DBGERROR((TEXT("Error %d setting new owner for \"%s\""), dwErr, strFile.Cstr())); hr = HRESULT_FROM_WIN32(dwErr); } } else { dwErr = GetLastError(); DBGERROR((TEXT("Error %d setting security descriptor owner"), dwErr)); hr = HRESULT_FROM_WIN32(dwErr); } } else { dwErr = GetLastError(); DBGERROR((TEXT("Error %d initing security descriptor"), GetLastError())); hr = HRESULT_FROM_WIN32(dwErr); } } else { DBGERROR((TEXT("Can't find listview item for owner %d, file %d"), handle.OwnerIndex(), handle.FileIndex())); } } // // Refresh the owner combo with the new file counts. // InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo); return hr; } // // The original code for listing files owned by a user was // contributed by MarkZ. I made some minor modifications // to fit it into the diskquota project and make it more // exception safe. // inline VOID * Add2Ptr(VOID *pv, ULONG cb) { return((BYTE *) pv + cb); } inline ULONG QuadAlign( ULONG Value ) { return (Value + 7) & ~7; } // // Add files owned by a particular user on a particular volume. // HRESULT CFileOwnerDialog::AddFilesToOwnerList( LPCTSTR pszVolumeRoot, HANDLE hVolumeRoot, IDiskQuotaUser *pOwner, COwnerList *pOwnerList ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::AddFilesToOwnerList"))); DBGASSERT((NULL != hVolumeRoot)); DBGASSERT((NULL != pOwner)); DBGASSERT((NULL != pOwnerList)); struct { ULONG Restart; BYTE Sid[MAX_SID_LEN]; }FsCtlInput; NTSTATUS status = ERROR_SUCCESS; if (m_bAbort) { return S_OK; } // // Get owner's SID. // HRESULT hr = pOwner->GetSid(FsCtlInput.Sid, sizeof(FsCtlInput.Sid)); if (FAILED(hr)) { DBGERROR((TEXT("IDiskQuotaUser::GetSid failed with hr = 0x%08X"), hr)); return hr; } // // Add the owner to the owner-file list. // int iOwner = pOwnerList->AddOwner(pOwner); IO_STATUS_BLOCK iosb; FsCtlInput.Restart = 1; BYTE Output[1024]; bool bPathIsRemote = false; FILE_FS_DEVICE_INFORMATION DeviceInfo; // // Determine if the volume is a remote device. This will affect // our handling of the paths returned by NtQueryInformationFile. // status = NtQueryVolumeInformationFile( hVolumeRoot, &iosb, &DeviceInfo, sizeof(DeviceInfo), FileFsDeviceInformation); if (NT_SUCCESS(status)) { bPathIsRemote = (FILE_REMOTE_DEVICE == DeviceInfo.Characteristics); } while (!m_bAbort) { status = NtFsControlFile(hVolumeRoot, NULL, NULL, NULL, &iosb, FSCTL_FIND_FILES_BY_SID, &FsCtlInput, sizeof(FsCtlInput), Output, sizeof(Output)); FsCtlInput.Restart = 0; if (!NT_SUCCESS(status) && STATUS_BUFFER_OVERFLOW != status) { DBGERROR((TEXT("NtFsControlFile failed with status 0x%08X"), status)); return HRESULT_FROM_NT(status); } if (0 == iosb.Information) { // // No more data. // break; } PFILE_NAME_INFORMATION pFileNameInfo = (PFILE_NAME_INFORMATION)Output; while (!m_bAbort && ((PBYTE)pFileNameInfo < Output + iosb.Information)) { ULONG Length = sizeof(FILE_NAME_INFORMATION) - sizeof(WCHAR) + pFileNameInfo->FileNameLength; CNtHandle hChild; WCHAR szChild[MAX_PATH]; RtlMoveMemory(szChild, pFileNameInfo->FileName, pFileNameInfo->FileNameLength); szChild[pFileNameInfo->FileNameLength / sizeof(WCHAR)] = L'\0'; status = OpenNtObject(szChild, hVolumeRoot, FILE_SYNCHRONOUS_IO_NONALERT, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, hChild.HandlePtr()); if (!NT_SUCCESS(status)) { DBGERROR((TEXT("Unable to open file \"%s\". Status = 0x%08X"), szChild, status)); } else if (!m_bAbort) { // // Directory entries get a slightly different treatment so // we need to know if an entry is a directory or not. // bool bIsDirectory = false; IO_STATUS_BLOCK iosb2; FILE_BASIC_INFORMATION fbi; status = NtQueryInformationFile(hChild, &iosb2, &fbi, sizeof(fbi), FileBasicInformation); if (!NT_SUCCESS(status)) { DBGERROR((TEXT("NtQueryInformationFile failed with status 0x%08X for \"%s\""), status, szChild)); } else if (0 != (FILE_ATTRIBUTE_DIRECTORY & fbi.FileAttributes)) { bIsDirectory = true; } // // Get the file's name (full path). // WCHAR szFile[MAX_PATH + 10]; status = NtQueryInformationFile(hChild, &iosb2, szFile, sizeof(szFile), FileNameInformation); if (!NT_SUCCESS(status)) { DBGERROR((TEXT("NtQueryInformation file failed with status 0x%08X for \"%s\""), status, szChild)); } else if (!m_bAbort) { PFILE_NAME_INFORMATION pfn = (PFILE_NAME_INFORMATION)szFile; pfn->FileName[pfn->FileNameLength / sizeof(WCHAR)] = L'\0'; CPath path; // // If the path is remote, NtQueryInformationFile returns // a string like this: // // \server\share\dir1\dir2\file.ext // // If the path is local, NtQueryInformationFile returns // a string like this: // // \dir1\dir2\file.ext // // For remote paths we merely prepend a '\' to create a // valid UNC path. For local paths we prepend the local // drive specification. // if (bPathIsRemote) { path = L"\\"; path += CString(pfn->FileName); } else { path = pszVolumeRoot; path.Append(pfn->FileName); } DBGPRINT((DM_VIEW, DL_LOW, TEXT("Adding \"%s\""), path.Cstr())); pOwnerList->AddFile(iOwner, path, bIsDirectory); } } hChild.Close(); pFileNameInfo = (PFILE_NAME_INFORMATION) Add2Ptr(pFileNameInfo, QuadAlign(Length)); } } return NOERROR; } // // Build a list of files owned by a set of users on a particular volume. // pszVolumeRoot is the volume root directory (i.e. "C:\"). // rgpOwners is an array of user object pointers, one for each owner. // pOwnerList is the container where the resulting filenames are placed. // Calls AddFilesToOwnerList() for each owner in rgpOwners. // HRESULT CFileOwnerDialog::BuildFileOwnerList( LPCTSTR pszVolumeRoot, const CArray& rgpOwners, COwnerList *pOwnerList ) { DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BuildFileOwnerList"))); HRESULT hr = NOERROR; CNtHandle hVolumeRoot; NTSTATUS status = OpenNtObject(pszVolumeRoot, NULL, FILE_SYNCHRONOUS_IO_NONALERT, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, hVolumeRoot.HandlePtr()); if (!NT_SUCCESS(status)) return HRESULT_FROM_NT(status); int cOwners = rgpOwners.Count(); for (int i = 0; i < cOwners && !m_bAbort; i++) { hr = AddFilesToOwnerList(pszVolumeRoot, hVolumeRoot, rgpOwners[i], pOwnerList); } return hr; } // // MarkZ had this function in his original implementation so I just // kept it. I did need to fix a bug in the original code. He was // calling RtlFreeHeap() on str.Buffer for all cases. This is was // not applicable in the RtlInitUnicodeString() case where the // unicode string is merely bound to the pszFile argument. // NTSTATUS CFileOwnerDialog::OpenNtObject ( LPCWSTR pszFile, HANDLE RelatedObject, ULONG CreateOptions, ULONG DesiredAccess, ULONG ShareAccess, ULONG CreateDisposition, HANDLE *ph) { NTSTATUS status; OBJECT_ATTRIBUTES oa; UNICODE_STRING str; IO_STATUS_BLOCK isb; bool bFreeString = false; if (NULL == RelatedObject) { RtlDosPathNameToNtPathName_U(pszFile, &str, NULL, NULL); bFreeString = true; } else { // // This just attaches pszFile to the rtl string. // We don't free it. // RtlInitUnicodeString(&str, pszFile); } InitializeObjectAttributes(&oa, &str, OBJ_CASE_INSENSITIVE, RelatedObject, NULL); status = NtCreateFile(ph, DesiredAccess | SYNCHRONIZE, &oa, &isb, NULL, // pallocationsize (none!) FILE_ATTRIBUTE_NORMAL, ShareAccess, CreateDisposition, CreateOptions, NULL, // EA buffer (none!) 0); if (bFreeString) RtlFreeHeap(RtlProcessHeap(), 0, str.Buffer); return(status); }