#include "pch.h" #include // ILClone, ILIsEmpty, etc. #include #include "security.h" #include "shguidp.h" #include "folder.h" #include "strings.h" #include #ifdef DEBUG #define SZ_DEBUGINI "ccshell.ini" #define SZ_DEBUGSECTION "CSC UI" #define SZ_MODULE "CSCUI.DLL" // (These are deliberately CHAR) EXTERN_C const CHAR c_szCcshellIniFile[] = SZ_DEBUGINI; EXTERN_C const CHAR c_szCcshellIniSecDebug[] = SZ_DEBUGSECTION; EXTERN_C const WCHAR c_wszTrace[] = L"t " TEXTW(SZ_MODULE) L" "; EXTERN_C const WCHAR c_wszErrorDbg[] = L"err " TEXTW(SZ_MODULE) L" "; EXTERN_C const WCHAR c_wszWarningDbg[] = L"wn " TEXTW(SZ_MODULE) L" "; EXTERN_C const WCHAR c_wszAssertMsg[] = TEXTW(SZ_MODULE) L" Assert: "; EXTERN_C const WCHAR c_wszAssertFailed[] = TEXTW(SZ_MODULE) L" Assert %ls, line %d: (%ls)\r\n"; EXTERN_C const WCHAR c_wszRip[] = TEXTW(SZ_MODULE) L" RIP in %s at %s, line %d: (%s)\r\n"; EXTERN_C const WCHAR c_wszRipNoFn[] = TEXTW(SZ_MODULE) L" RIP at %s, line %d: (%s)\r\n"; // (These are deliberately CHAR) EXTERN_C const CHAR c_szTrace[] = "t " SZ_MODULE " "; EXTERN_C const CHAR c_szErrorDbg[] = "err " SZ_MODULE " "; EXTERN_C const CHAR c_szWarningDbg[] = "wn " SZ_MODULE " "; EXTERN_C const CHAR c_szAssertMsg[] = SZ_MODULE " Assert: "; EXTERN_C const CHAR c_szAssertFailed[] = SZ_MODULE " Assert %s, line %d: (%s)\r\n"; EXTERN_C const CHAR c_szRip[] = SZ_MODULE " RIP in %s at %s, line %d: (%s)\r\n"; EXTERN_C const CHAR c_szRipNoFn[] = SZ_MODULE " RIP at %s, line %d: (%s)\r\n"; EXTERN_C const CHAR c_szRipMsg[] = SZ_MODULE " RIP: "; #endif // // Purpose: Return UNC version of a path // // Parameters: pszInName - initial path // ppszOutName - UNC path returned here // // // Return: HRESULT // S_OK - UNC path returned // S_FALSE - drive not connected (UNC not returned) // or failure code // // Notes: The function fails is the path is not a valid // network path. If the path is already UNC, // a copy is made without validating the path. // *ppszOutName must be LocalFree'd by the caller. // HRESULT GetRemotePath(LPCTSTR pszInName, LPTSTR *ppszOutName) { HRESULT hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); *ppszOutName = NULL; // Don't bother calling GetFullPathName first, since we always // deal with full (complete) paths. if (pszInName[1] == TEXT(':')) { TCHAR szLocalName[3]; szLocalName[0] = pszInName[0]; szLocalName[1] = pszInName[1]; szLocalName[2] = 0; // Call GetDriveType before WNetGetConnection, to avoid loading // MPR.DLL until absolutely necessary. if (DRIVE_REMOTE == GetDriveType(szLocalName)) { TCHAR szRemoteName[MAX_PATH]; DWORD dwLen = ARRAYSIZE(szRemoteName); DWORD dwErr = WNetGetConnection(szLocalName, szRemoteName, &dwLen); if (NO_ERROR == dwErr) { size_t cch = lstrlen(szRemoteName); // Skip the drive letter and add the length of the rest of the path // (including NULL) pszInName += 2; cch += lstrlen(pszInName) + 1; // We should never get incomplete paths, so we should always // see a backslash after the "X:". If this isn't true, then // we should call GetFullPathName above. TraceAssert(TEXT('\\') == *pszInName); // Allocate the return buffer *ppszOutName = (LPTSTR)LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (*ppszOutName) { LPTSTR pszRemaining; hr = StringCchCopyEx(*ppszOutName, cch, szRemoteName, &pszRemaining, &cch, 0); // root part // We allocated a big enough buffer, so this should never fail ASSERT(SUCCEEDED(hr)); hr = StringCchCopy(pszRemaining, cch, pszInName); // rest of path ASSERT(SUCCEEDED(hr)); } else { hr = E_OUTOFMEMORY; } } else if (ERROR_NOT_CONNECTED == dwErr) { hr = S_FALSE; } else { hr = HRESULT_FROM_WIN32(dwErr); } } else { hr = S_FALSE; } } else if (PathIsUNC(pszInName)) { // Just copy the path without validating it hr = LocalAllocString(ppszOutName, pszInName) ? S_OK : E_OUTOFMEMORY; } if (S_OK == hr) PathRemoveBackslash(*ppszOutName); return hr; } // // Purpose: TCHAR version of itoa // // Parameters: UINT i - unsigned integer to convert // LPTSTR psz - location to store string result // UINT cchMax - size of buffer pointed to by psz // LPTSTR ULongToString(ULONG i, LPTSTR psz, ULONG cchMax) { wnsprintf(psz, cchMax, TEXT("%d"), i); return psz; } // // Purpose: Free a string allocated with LocalAlloc[String] // // Parameters: LPTSTR *ppsz - location of pointer to string // void LocalFreeString(LPTSTR *ppsz) { if (ppsz && *ppsz) { LocalFree(*ppsz); *ppsz = NULL; } } // // Purpose: Copy a string into a newly allocated buffer // // Parameters: LPTSTR *ppszDest - location to store string copy // LPCTSTR pszSrc - string to copy // // Return: BOOL - FALSE if LocalAlloc fails or invalid parameters // BOOL LocalAllocString(LPTSTR *ppszDest, LPCTSTR pszSrc) { if (!ppszDest) return FALSE; *ppszDest = StrDup(pszSrc); return *ppszDest ? TRUE : FALSE; } // // Purpose: Find the length (in chars) of a string resource // // Parameters: HINSTANCE hInstance - module containing the string // UINT idStr - ID of string // // // Return: UINT - # of chars in string, not including NULL // // Notes: Based on code from user32. // UINT SizeofStringResource(HINSTANCE hInstance, UINT idStr) { UINT cch = 0; HRSRC hRes = FindResource(hInstance, (LPTSTR)((LONG_PTR)(((USHORT)idStr >> 4) + 1)), RT_STRING); if (NULL != hRes) { HGLOBAL hStringSeg = LoadResource(hInstance, hRes); if (NULL != hStringSeg) { LPWSTR psz = (LPWSTR)LockResource(hStringSeg); if (NULL != psz) { idStr &= 0x0F; while(true) { cch = *psz++; if (idStr-- == 0) break; psz += cch; } } } } return cch; } // // Purpose: Loads a string resource into an alloc'd buffer // // Parameters: ppszResult - string resource returned here // hInstance - module to load string from // idStr - string resource ID // // Return: same as LoadString // // Notes: On successful return, the caller must // LocalFree *ppszResult // int LoadStringAlloc(LPTSTR *ppszResult, HINSTANCE hInstance, UINT idStr) { int nResult = 0; UINT cch = SizeofStringResource(hInstance, idStr); if (cch) { cch++; // for NULL *ppszResult = (LPTSTR)LocalAlloc(LPTR, cch * sizeof(TCHAR)); if (*ppszResult) nResult = LoadString(hInstance, idStr, *ppszResult, cch); } return nResult; } // // Purpose: Wrapper for SHChangeNotify // // Parameters: pszPath - path of file that changed // bFlush - TRUE forces a flush of the shell's // notify queue. // // Return: none // // Notes: SHCNF_PATH doesn't work outside of the shell, // so we create a pidl and use SHCNF_IDLIST. // // Force a flush every 8 calls so the shell // doesn't start ignoring notifications. // void ShellChangeNotify( LPCTSTR pszPath, WIN32_FIND_DATA *pfd, BOOL bFlush, LONG nEvent ) { LPITEMIDLIST pidlFile = NULL; LPCVOID pvItem = NULL; UINT uFlags = 0; static int cNoFlush = 0; if (pszPath) { if ((pfd && SUCCEEDED(SHSimpleIDListFromFindData(pszPath, pfd, &pidlFile))) || (pidlFile = ILCreateFromPath(pszPath))) { uFlags = SHCNF_IDLIST; pvItem = pidlFile; } else { // ILCreateFromPath sometimes fails when we're in disconnected // mode, so try the path instead. uFlags = SHCNF_PATH; pvItem = pszPath; } if (0 == nEvent) nEvent = SHCNE_UPDATEITEM; } else nEvent = 0; if (8 < cNoFlush++) bFlush = TRUE; if (bFlush) { uFlags |= (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT); cNoFlush = 0; } SHChangeNotify(nEvent, uFlags, pvItem, NULL); if (pidlFile) SHFree(pidlFile); } // // Purpose: Get the path to the target file of a link // // Parameters: pszShortcut - name of link file // ppszTarget - target path returned here // // // Return: HRESULT // S_OK - target file returned // S_FALSE - target not returned // or failure code // // Notes: COM must be initialized before calling. // The function fails is the target is a folder. // *ppszTarget must be LocalFree'd by the caller. // HRESULT GetLinkTarget(LPCTSTR pszShortcut, LPTSTR *ppszTarget, DWORD *pdwAttr) { *ppszTarget = NULL; if (pdwAttr) *pdwAttr = 0; IShellLink *psl; HRESULT hr = LoadFromFile(CLSID_ShellLink, pszShortcut, IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { // Get the pidl of the target LPITEMIDLIST pidlTarget; hr = psl->GetIDList(&pidlTarget); if (SUCCEEDED(hr)) { hr = S_FALSE; // means no target returned TCHAR szTarget[MAX_PATH]; DWORD dwAttr = SFGAO_FOLDER; if (SUCCEEDED(SHGetNameAndFlags(pidlTarget, SHGDN_FORPARSING, szTarget, ARRAYSIZE(szTarget), &dwAttr))) { if (!(dwAttr & SFGAO_FOLDER)) { if (pdwAttr) { *pdwAttr = GetFileAttributes(szTarget); } hr = GetRemotePath(szTarget, ppszTarget); } } SHFree(pidlTarget); } psl->Release(); } TraceLeaveResult(hr); } //************************************************************* // // _CSCEnumDatabase // // Purpose: Enumerate CSC database recursively // // Parameters: pszFolder - name of folder to begin enumeration // (can be NULL to enum shares) // bRecurse - TRUE to recurse into child folders // pfnCB - callback function called once for each child // lpContext - extra data passed to callback function // // Return: One of CSCPROC_RETURN_* // // Notes: Return CSCPROC_RETURN_SKIP from the callback to prevent // recursion into a child folder. CSCPROC_RETURN_ABORT // will terminate the entire operation (unwind all recursive // calls). CSCPROC_RETURN_CONTINUE will continue normally. // Other CSCPROC_RETURN_* values are treated as ABORT. // //************************************************************* #define PATH_BUFFER_SIZE 1024 typedef struct { LPTSTR szPath; int cchPathBuffer; BOOL bRecurse; PFN_CSCENUMPROC pfnCB; LPARAM lpContext; } CSC_ENUM_CONTEXT, *PCSC_ENUM_CONTEXT; DWORD _CSCEnumDatabaseInternal(PCSC_ENUM_CONTEXT pContext) { DWORD dwResult = CSCPROC_RETURN_CONTINUE; HANDLE hFind; DWORD dwStatus = 0; DWORD dwPinCount = 0; DWORD dwHintFlags = 0; LPTSTR pszPath; int cchBuffer; LPTSTR pszFind = NULL; int cchDir = 0; WIN32_FIND_DATA fd; TraceEnter(TRACE_UTIL, "_CSCEnumDatabaseInternal"); TraceAssert(pContext); TraceAssert(pContext->pfnCB); TraceAssert(pContext->szPath); TraceAssert(pContext->cchPathBuffer); pszPath = pContext->szPath; cchBuffer = pContext->cchPathBuffer; if (*pszPath) { cchDir = lstrlen(pszPath); TraceAssert(cchDir > 0 && pszPath[cchDir-1] != TEXT('\\')); // no backslash yet TraceAssert(cchDir + 1 < cchBuffer); // room for backslash pszPath[cchDir++] = TEXT('\\'); pszPath[cchDir] = TEXT('\0'); pszFind = pszPath; } // skips "." and ".." hFind = CacheFindFirst(pszFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, NULL); if (hFind != INVALID_HANDLE_VALUE) { do { int cchFile; ENUM_REASON eReason = ENUM_REASON_FILE; cchFile = lstrlen(fd.cFileName); if (cchFile >= cchBuffer - cchDir) { // Realloc the path buffer TraceMsg("Reallocating path buffer"); cchBuffer += max(PATH_BUFFER_SIZE, cchFile + 1); pszPath = (LPTSTR)LocalReAlloc(pContext->szPath, cchBuffer * sizeof(TCHAR), LMEM_MOVEABLE); if (pszPath) { pContext->szPath = pszPath; pContext->cchPathBuffer = cchBuffer; } else { pszPath = pContext->szPath; cchBuffer = pContext->cchPathBuffer; TraceMsg("Unable to reallocate path buffer"); Trace((pszPath)); Trace((fd.cFileName)); continue; } } // Build full path. We just reallocated the buffer // if necessary, so this should never fail. StringCchCopy(pszPath + cchDir, cchBuffer - cchDir, fd.cFileName); cchFile = lstrlen(pszPath); if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || !pszFind) eReason = ENUM_REASON_FOLDER_BEGIN; // Call the callback dwResult = (*pContext->pfnCB)(pszPath, eReason, dwStatus, dwHintFlags, dwPinCount, &fd, pContext->lpContext); // Recurse into folders if (CSCPROC_RETURN_CONTINUE == dwResult && pContext->bRecurse && ENUM_REASON_FOLDER_BEGIN == eReason) { dwResult = _CSCEnumDatabaseInternal(pContext); // Call the callback again pszPath[cchFile] = 0; dwResult = (*pContext->pfnCB)(pszPath, ENUM_REASON_FOLDER_END, 0, // dwStatus, // these have probably changed 0, // dwHintFlags, 0, // dwPinCount, &fd, pContext->lpContext); } if (CSCPROC_RETURN_SKIP == dwResult) dwResult = CSCPROC_RETURN_CONTINUE; if (CSCPROC_RETURN_CONTINUE != dwResult) break; } while (CacheFindNext(hFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, NULL)); CSCFindClose(hFind); } TraceLeaveValue(dwResult); } DWORD _CSCEnumDatabase(LPCTSTR pszFolder, BOOL bRecurse, PFN_CSCENUMPROC pfnCB, LPARAM lpContext) { DWORD dwResult = CSCPROC_RETURN_CONTINUE; CSC_ENUM_CONTEXT ec; TraceEnter(TRACE_UTIL, "_CSCEnumDatabase"); TraceAssert(pfnCB); if (!pfnCB) TraceLeaveValue(CSCPROC_RETURN_ABORT); // Allocate the single buffer used for the entire enumeration. // It will be reallocated later if necessary. size_t cchFolder = pszFolder ? lstrlen(pszFolder) : 0; ec.cchPathBuffer = ((cchFolder/PATH_BUFFER_SIZE) + 1) * PATH_BUFFER_SIZE; ec.szPath = (LPTSTR)LocalAlloc(LMEM_FIXED, ec.cchPathBuffer*sizeof(TCHAR)); if (!ec.szPath) TraceLeaveValue(CSCPROC_RETURN_ABORT); ec.szPath[0] = TEXT('\0'); // Assume pszFolder is valid a directory path or NULL if (pszFolder) { // We made sure the buffer was big enough for this above StringCchCopy(ec.szPath, ec.cchPathBuffer, pszFolder); // _CSCEnumDatabaseInternal assumes there is no trailing backslash if (cchFolder && ec.szPath[cchFolder-1] == TEXT('\\')) { ec.szPath[cchFolder-1] = TEXT('\0'); } } ec.bRecurse = bRecurse; ec.pfnCB = pfnCB; ec.lpContext = lpContext; dwResult = _CSCEnumDatabaseInternal(&ec); LocalFree(ec.szPath); TraceLeaveValue(dwResult); } //************************************************************* // // _Win32EnumFolder // // Purpose: Enumerate a directory recursively // // Parameters: pszFolder - name of folder to begin enumeration // bRecurse - TRUE to recurse into child folders // pfnCB - callback function called once for each child // lpContext - extra data passed to callback function // // Return: One of CSCPROC_RETURN_* // // Notes: Same as _CSCEnumDatabase except using FindFirstFile // instead of CSCFindFirstFile. // //************************************************************* typedef struct { LPTSTR szPath; int cchPathBuffer; BOOL bRecurse; PFN_WIN32ENUMPROC pfnCB; LPARAM lpContext; } W32_ENUM_CONTEXT, *PW32_ENUM_CONTEXT; DWORD _Win32EnumFolderInternal(PW32_ENUM_CONTEXT pContext) { DWORD dwResult = CSCPROC_RETURN_CONTINUE; HANDLE hFind; LPTSTR pszPath; int cchBuffer; int cchDir = 0; WIN32_FIND_DATA fd; TraceEnter(TRACE_UTIL, "_Win32EnumFolderInternal"); TraceAssert(pContext); TraceAssert(pContext->pfnCB); TraceAssert(pContext->szPath && pContext->szPath[0]); TraceAssert(pContext->cchPathBuffer); pszPath = pContext->szPath; cchBuffer = pContext->cchPathBuffer; // Build wildcard path cchDir = lstrlen(pszPath); TraceAssert(cchDir > 0 && pszPath[cchDir-1] != TEXT('\\')); // no backslash yet TraceAssert(cchDir + 2 < cchBuffer); // room for "\\*" pszPath[cchDir++] = TEXT('\\'); pszPath[cchDir] = TEXT('*'); pszPath[cchDir+1] = 0; hFind = FindFirstFile(pszPath, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { int cchFile; ENUM_REASON eReason = ENUM_REASON_FILE; // skip "." and ".." if (PathIsDotOrDotDot(fd.cFileName)) continue; cchFile = lstrlen(fd.cFileName); if (cchFile >= cchBuffer - cchDir) { // Realloc the path buffer TraceMsg("Reallocating path buffer"); cchBuffer += max(PATH_BUFFER_SIZE, cchFile + 1); pszPath = (LPTSTR)LocalReAlloc(pContext->szPath, cchBuffer * sizeof(TCHAR), LMEM_MOVEABLE); if (pszPath) { pContext->szPath = pszPath; pContext->cchPathBuffer = cchBuffer; } else { pszPath = pContext->szPath; cchBuffer = pContext->cchPathBuffer; TraceMsg("Unable to reallocate path buffer"); Trace((pszPath)); Trace((fd.cFileName)); continue; } } // Build full path. We just reallocated the buffer // if necessary, so this should never fail. StringCchCopy(pszPath + cchDir, cchBuffer - cchDir, fd.cFileName); cchFile = lstrlen(pszPath); if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) eReason = ENUM_REASON_FOLDER_BEGIN; // Call the callback dwResult = (*pContext->pfnCB)(pszPath, eReason, &fd, pContext->lpContext); // Recurse into folders if (CSCPROC_RETURN_CONTINUE == dwResult && pContext->bRecurse && ENUM_REASON_FOLDER_BEGIN == eReason) { dwResult = _Win32EnumFolderInternal(pContext); // Call the callback again pszPath[cchFile] = 0; dwResult = (*pContext->pfnCB)(pszPath, ENUM_REASON_FOLDER_END, &fd, pContext->lpContext); } if (CSCPROC_RETURN_SKIP == dwResult) dwResult = CSCPROC_RETURN_CONTINUE; if (CSCPROC_RETURN_CONTINUE != dwResult) break; } while (FindNextFile(hFind, &fd)); FindClose(hFind); } TraceLeaveValue(dwResult); } DWORD _Win32EnumFolder(LPCTSTR pszFolder, BOOL bRecurse, PFN_WIN32ENUMPROC pfnCB, LPARAM lpContext) { DWORD dwResult = CSCPROC_RETURN_CONTINUE; W32_ENUM_CONTEXT ec; TraceEnter(TRACE_UTIL, "_Win32EnumFolder"); TraceAssert(pszFolder); TraceAssert(pfnCB); if (!pszFolder || !*pszFolder || !pfnCB) TraceLeaveValue(CSCPROC_RETURN_ABORT); // Allocate the single buffer used for the entire enumeration. // It will be reallocated later if necessary. size_t cchFolder = lstrlen(pszFolder); ec.cchPathBuffer = ((cchFolder/PATH_BUFFER_SIZE) + 1) * PATH_BUFFER_SIZE; ec.szPath = (LPTSTR)LocalAlloc(LMEM_FIXED, ec.cchPathBuffer*sizeof(TCHAR)); if (!ec.szPath) TraceLeaveValue(CSCPROC_RETURN_ABORT); // Assume pszFolder is valid a directory path // We made sure the buffer was big enough for this above StringCchCopy(ec.szPath, ec.cchPathBuffer, pszFolder); // _Win32EnumFolderInternal assumes there is no trailing backslash if (cchFolder && ec.szPath[cchFolder-1] == TEXT('\\')) { ec.szPath[cchFolder-1] = TEXT('\0'); } ec.bRecurse = bRecurse; ec.pfnCB = pfnCB; ec.lpContext = lpContext; dwResult = _Win32EnumFolderInternal(&ec); LocalFree(ec.szPath); TraceLeaveValue(dwResult); } CIDArray::~CIDArray() { DoRelease(m_psf); if (m_pIDA) { GlobalUnlock(m_Medium.hGlobal); m_pIDA = NULL; } ReleaseStgMedium(&m_Medium); } HRESULT CIDArray::Initialize(IDataObject *pdobj) { TraceAssert(NULL == m_pIDA); FORMATETC fe = { g_cfShellIDList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; HRESULT hr = pdobj->GetData(&fe, &m_Medium); if (SUCCEEDED(hr)) { m_pIDA = (LPIDA)GlobalLock(m_Medium.hGlobal); if (m_pIDA) { LPCITEMIDLIST pidlFolder = (LPCITEMIDLIST)ByteOffset(m_pIDA, m_pIDA->aoffset[0]); hr = SHBindToObjectEx(NULL, pidlFolder, NULL, IID_PPV_ARG(IShellFolder, &m_psf)); } else { hr = E_FAIL; } } return hr; } HRESULT CIDArray::GetItemPath(UINT iItem, LPTSTR pszPath, UINT cchPath, DWORD *pdwAttribs) { HRESULT hr = E_INVALIDARG; if (m_psf && m_pIDA && (iItem < m_pIDA->cidl)) { LPCITEMIDLIST pidlChild, pidl = (LPCITEMIDLIST)ByteOffset(m_pIDA, m_pIDA->aoffset[iItem + 1]); IShellFolder *psf; hr = SHBindToFolderIDListParent(m_psf, pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); if (SUCCEEDED(hr)) { if (pszPath) { hr = DisplayNameOf(psf, pidlChild, SHGDN_FORPARSING, pszPath, cchPath); if (SUCCEEDED(hr)) { LPTSTR pszRemote; if (S_OK == GetRemotePath(pszPath, &pszRemote)) { hr = StringCchCopy(pszPath, cchPath, pszRemote); LocalFree(pszRemote); } } } if (SUCCEEDED(hr) && pdwAttribs) hr = psf->GetAttributesOf(1, &pidlChild, pdwAttribs); psf->Release(); } } return hr; } HRESULT CIDArray::GetFolderPath(LPTSTR pszPath, UINT cchPath) { HRESULT hr = GetItemPath(0, pszPath, cchPath, NULL); if (SUCCEEDED(hr)) { PathRemoveFileSpec(pszPath); } return hr; } //************************************************************* // // CCscFileHandle non-inline member functions. // //************************************************************* CCscFindHandle& CCscFindHandle::operator = ( const CCscFindHandle& rhs ) { if (this != &rhs) { Attach(rhs.Detach()); } return *this; } void CCscFindHandle::Close( void ) { if (m_bOwns && INVALID_HANDLE_VALUE != m_handle) { CSCFindClose(m_handle); } m_bOwns = false; m_handle = INVALID_HANDLE_VALUE; } //************************************************************* // // String formatting functions // //************************************************************* DWORD FormatStringID(LPTSTR *ppszResult, HINSTANCE hInstance, UINT idStr, ...) { DWORD dwResult; va_list args; va_start(args, idStr); dwResult = vFormatStringID(ppszResult, hInstance, idStr, &args); va_end(args); return dwResult; } DWORD FormatString(LPTSTR *ppszResult, LPCTSTR pszFormat, ...) { DWORD dwResult; va_list args; va_start(args, pszFormat); dwResult = vFormatString(ppszResult, pszFormat, &args); va_end(args); return dwResult; } DWORD vFormatStringID(LPTSTR *ppszResult, HINSTANCE hInstance, UINT idStr, va_list *pargs) { DWORD dwResult = 0; LPTSTR pszFormat = NULL; if (LoadStringAlloc(&pszFormat, hInstance, idStr)) { dwResult = vFormatString(ppszResult, pszFormat, pargs); LocalFree(pszFormat); } return dwResult; } DWORD vFormatString(LPTSTR *ppszResult, LPCTSTR pszFormat, va_list *pargs) { return FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, pszFormat, 0, 0, (LPTSTR)ppszResult, 1, pargs); } DWORD FormatSystemError(LPTSTR *ppszResult, DWORD dwSysError) { LPTSTR pszBuffer = NULL; DWORD dwResult = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwSysError, 0, (LPTSTR)&pszBuffer, 1, NULL); if (NULL != pszBuffer) { *ppszResult = pszBuffer; } return dwResult; } // // Center a window in it's parent. // If hwndParent is NULL, the window's parent is used. // If hwndParent is not NULL, hwnd is centered in it. // If hwndParent is NULL and hwnd doesn't have a parent, it is centered // on the desktop. // void CenterWindow( HWND hwnd, HWND hwndParent ) { RECT rcScreen; if (NULL != hwnd) { rcScreen.left = rcScreen.top = 0; rcScreen.right = GetSystemMetrics(SM_CXSCREEN); rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); if (NULL == hwndParent) { hwndParent = GetParent(hwnd); if (NULL == hwndParent) hwndParent = GetDesktopWindow(); } RECT rcWnd; RECT rcParent; GetWindowRect(hwnd, &rcWnd); GetWindowRect(hwndParent, &rcParent); INT cxWnd = rcWnd.right - rcWnd.left; INT cyWnd = rcWnd.bottom - rcWnd.top; INT cxParent = rcParent.right - rcParent.left; INT cyParent = rcParent.bottom - rcParent.top; POINT ptParentCtr; ptParentCtr.x = rcParent.left + (cxParent / 2); ptParentCtr.y = rcParent.top + (cyParent / 2); if ((ptParentCtr.x + (cxWnd / 2)) > rcScreen.right) { // // Window would run off the right edge of the screen. // rcWnd.left = rcScreen.right - cxWnd; } else if ((ptParentCtr.x - (cxWnd / 2)) < rcScreen.left) { // // Window would run off the left edge of the screen. // rcWnd.left = rcScreen.left; } else { rcWnd.left = ptParentCtr.x - (cxWnd / 2); } if ((ptParentCtr.y + (cyWnd / 2)) > rcScreen.bottom) { // // Window would run off the bottom edge of the screen. // rcWnd.top = rcScreen.bottom - cyWnd; } else if ((ptParentCtr.y - (cyWnd / 2)) < rcScreen.top) { // // Window would run off the top edge of the screen. // rcWnd.top = rcScreen.top; } else { rcWnd.top = ptParentCtr.y - (cyWnd / 2); } MoveWindow(hwnd, rcWnd.left, rcWnd.top, cxWnd, cyWnd, TRUE); } } // // We have some extra stuff to pass to the stats callback so we wrap the // CSCSHARESTATS in a larger structure. // typedef struct { CSCSHARESTATS ss; // The stats data. DWORD dwUnityFlagsReq; // SSUF_XXXX flags set by user (requested). DWORD dwUnityFlagsSum; // SSUF_XXXX flags set during enum (sum total). DWORD dwExcludeFlags; // SSEF_XXXX flags. bool bEnumAborted; // true if unity flags satisfied. } CSCSHARESTATS_CBKINFO, *PCSCSHARESTATS_CBKINFO; // // Called by CSCEnumForStats for each CSC item enumerated. // DWORD _CscShareStatisticsCallback(LPCTSTR lpszName, DWORD dwStatus, DWORD dwHintFlags, DWORD dwPinCount, WIN32_FIND_DATA *lpFind32, DWORD dwReason, DWORD dwParam1, DWORD dwParam2, DWORD_PTR dwContext) { DWORD dwResult = CSCPROC_RETURN_CONTINUE; if (CSCPROC_REASON_BEGIN != dwReason && // Not "start of data" notification. CSCPROC_REASON_END != dwReason && // Not "end of data" notification. 1 != dwParam2) // Not "share root" entry. { PCSCSHARESTATS_CBKINFO pssci = (PCSCSHARESTATS_CBKINFO)(dwContext); PCSCSHARESTATS pss = &(pssci->ss); const DWORD dwExcludeFlags = pssci->dwExcludeFlags; const DWORD dwUnityFlagsReq = pssci->dwUnityFlagsReq; const bool bIsDir = (0 == dwParam1); const bool bAccessUser = CscAccessUser(dwStatus); const bool bAccessGuest = CscAccessGuest(dwStatus); const bool bAccessOther = CscAccessOther(dwStatus); if (0 != dwExcludeFlags) { // // Caller want's to exclude some items from the enumeration. // If item is in "excluded" specification, return early. // if (0 != (dwExcludeFlags & (dwStatus & SSEF_CSCMASK))) { return dwResult; } if ((bIsDir && (dwExcludeFlags & SSEF_DIRECTORY)) || (!bIsDir && (dwExcludeFlags & SSEF_FILE))) { return dwResult; } const struct { DWORD fExclude; bool bAccess; BYTE fMask; } rgExclAccess[] = {{ SSEF_NOACCUSER, bAccessUser, 0x01 }, { SSEF_NOACCGUEST, bAccessGuest, 0x02 }, { SSEF_NOACCOTHER, bAccessOther, 0x04 }}; BYTE fExcludeMask = 0; BYTE fNoAccessMask = 0; for (int i = 0; i < ARRAYSIZE(rgExclAccess); i++) { if (dwExcludeFlags & rgExclAccess[i].fExclude) fExcludeMask |= rgExclAccess[i].fMask; if (!rgExclAccess[i].bAccess) fNoAccessMask |= rgExclAccess[i].fMask; } if (SSEF_NOACCAND & dwExcludeFlags) { // // Treat all access exclusion flags as a single unit. // if (fExcludeMask == fNoAccessMask) return dwResult; } else { // // Treat each access flag individually. Only one specified access // condition must be true to exclude this file. // if (fExcludeMask & fNoAccessMask) return dwResult; } } if (0 == (SSEF_DIRECTORY & dwExcludeFlags) || !bIsDir) { pss->cTotal++; pssci->dwUnityFlagsSum |= SSUF_TOTAL; if (0 != (dwHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN))) { pss->cPinned++; pssci->dwUnityFlagsSum |= SSUF_PINNED; } if (0 != (dwStatus & FLAG_CSCUI_COPY_STATUS_ALL_DIRTY)) { // // If the current user doesn't have sufficient access // to merge offline changes, then someone else must have // modified the file, so don't count it for this user. // if (bIsDir || CscCanUserMergeFile(dwStatus)) { pss->cModified++; pssci->dwUnityFlagsSum |= SSUF_MODIFIED; } } const struct { DWORD flag; int *pCount; bool bAccess; } rgUnity[] = {{ SSUF_ACCUSER, &pss->cAccessUser, bAccessUser }, { SSUF_ACCGUEST, &pss->cAccessGuest, bAccessGuest }, { SSUF_ACCOTHER, &pss->cAccessOther, bAccessOther }}; DWORD fUnityMask = 0; DWORD fAccessMask = 0; for (int i = 0; i < ARRAYSIZE(rgUnity); i++) { if (dwUnityFlagsReq & rgUnity[i].flag) fUnityMask |= rgUnity[i].flag; if (rgUnity[i].bAccess) { (*rgUnity[i].pCount)++; fAccessMask |= rgUnity[i].flag; } } if (SSUF_ACCAND & dwUnityFlagsReq) { // // Treat all access unity flags as a single unit. // We only signal unity if all of the specified access // unity conditions are true. // if (fUnityMask == fAccessMask) pssci->dwUnityFlagsSum |= fUnityMask; } else { // // Treat all access exclusion flags individually. // if (fUnityMask & fAccessMask) { if (SSUF_ACCOR & dwUnityFlagsReq) pssci->dwUnityFlagsSum |= fUnityMask; else pssci->dwUnityFlagsSum |= fAccessMask; } } if (bIsDir) { pss->cDirs++; pssci->dwUnityFlagsSum |= SSUF_DIRS; } // Note the 'else': don't count dirs in the sparse total else if (0 != (dwStatus & FLAG_CSC_COPY_STATUS_SPARSE)) { pss->cSparse++; pssci->dwUnityFlagsSum |= SSUF_SPARSE; } if (0 != dwUnityFlagsReq) { // // Abort enumeration if all of the requested SSUF_XXXX unity flags // have been set. // if (dwUnityFlagsReq == (dwUnityFlagsReq & pssci->dwUnityFlagsSum)) { dwResult = CSCPROC_RETURN_ABORT; pssci->bEnumAborted; } } } } return dwResult; } // // Enumerate all items for a given share and tally up the // relevant information like file count, pinned count etc. // Information is returned through *pss. // BOOL _GetShareStatistics( LPCTSTR pszShare, PCSCGETSTATSINFO pi, PCSCSHARESTATS pss ) { typedef BOOL (WINAPI * PFNENUMFORSTATS)(LPCTSTR, LPCSCPROC, DWORD_PTR); CSCSHARESTATS_CBKINFO ssci; BOOL bResult; DWORD dwShareStatus = 0; PFNENUMFORSTATS pfnEnumForStats = CSCEnumForStats; ZeroMemory(&ssci, sizeof(ssci)); ssci.dwUnityFlagsReq = pi->dwUnityFlags; ssci.dwExcludeFlags = pi->dwExcludeFlags; if (pi->bAccessInfo || (pi->dwUnityFlags & (SSUF_ACCUSER | SSUF_ACCGUEST | SSUF_ACCOTHER)) || (pi->dwExcludeFlags & (SSEF_NOACCUSER | SSEF_NOACCGUEST | SSEF_NOACCOTHER))) { // // If the enumeration requires access information, use the "ex" version // of the EnumForStats CSC api. Only use it if necessary because gathering // the access information has a perf cost. // pfnEnumForStats = CSCEnumForStatsEx; } pi->bEnumAborted = false; bResult = (*pfnEnumForStats)(pszShare, _CscShareStatisticsCallback, (DWORD_PTR)&ssci); *pss = ssci.ss; if (CSCQueryFileStatus(pszShare, &dwShareStatus, NULL, NULL)) { if (FLAG_CSC_SHARE_STATUS_FILES_OPEN & dwShareStatus) { pss->bOpenFiles = true; } if (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus) { pss->bOffline = true; } } pi->bEnumAborted = ssci.bEnumAborted; return bResult; } // // Retrieve the statistics for the entire cache. // This is a simple wrapper that calls _GetShareStatistics for each share // in the cache then sums the results for the entire cache. It accepts // the same unity and exclusion flags used by _GetShareStatistics. // BOOL _GetCacheStatistics( PCSCGETSTATSINFO pi, PCSCCACHESTATS pcs ) { BOOL bResult = TRUE; WIN32_FIND_DATA fd; CSCSHARESTATS ss; ZeroMemory(pcs, sizeof(*pcs)); pi->bEnumAborted = false; CCscFindHandle hFind(CacheFindFirst(NULL, &fd, NULL, NULL, NULL, NULL)); if (hFind.IsValid()) { do { pcs->cShares++; if (bResult = _GetShareStatistics(fd.cFileName, pi, &ss)) { pcs->cTotal += ss.cTotal; pcs->cPinned += ss.cPinned; pcs->cModified += ss.cModified; pcs->cSparse += ss.cSparse; pcs->cDirs += ss.cDirs; pcs->cAccessUser += ss.cAccessUser; pcs->cAccessGuest += ss.cAccessGuest; pcs->cAccessOther += ss.cAccessOther; pcs->cSharesOffline += int(ss.bOffline); pcs->cSharesWithOpenFiles += int(ss.bOpenFiles); } } while(bResult && !pi->bEnumAborted && CacheFindNext(hFind, &fd, NULL, NULL, NULL, NULL)); } return bResult; } // // Sets the proper exclusion flags to report only on files accessible by the // logged on user. Otherwise it's the same as calling _GetShareStatistics. // BOOL _GetShareStatisticsForUser( LPCTSTR pszShare, PCSCGETSTATSINFO pi, PCSCSHARESTATS pss ) { pi->dwExcludeFlags |= SSEF_NOACCUSER | SSEF_NOACCGUEST | SSEF_NOACCAND; return _GetShareStatistics(pszShare, pi, pss); } // // Sets the proper exclusion flags to report only on files accessible by the // logged on user. Otherwise it's the same as calling _GetCacheStatistics. // BOOL _GetCacheStatisticsForUser( PCSCGETSTATSINFO pi, PCSCCACHESTATS pcs ) { pi->dwExcludeFlags |= SSEF_NOACCUSER | SSEF_NOACCGUEST | SSEF_NOACCAND; return _GetCacheStatistics(pi, pcs); } // // CSCUI version of reboot. Requires security goo. // This code was pattered after that found in \shell\shell32\restart.c // function CommonRestart(). // DWORD CSCUIRebootSystem( void ) { TraceEnter(TRACE_UTIL, "CSCUIRebootSystem"); DWORD dwOldState, dwStatus, dwSecError; DWORD dwRebootError = ERROR_SUCCESS; SetLastError(0); // Be really safe about last error value! dwStatus = Security_SetPrivilegeAttrib(SE_SHUTDOWN_NAME, SE_PRIVILEGE_ENABLED, &dwOldState); dwSecError = GetLastError(); // ERROR_NOT_ALL_ASSIGNED sometimes if (!ExitWindowsEx(EWX_REBOOT, 0)) { dwRebootError = GetLastError(); Trace((TEXT("Error %d rebooting system"), dwRebootError)); } if (NT_SUCCESS(dwStatus)) { if (ERROR_SUCCESS == dwSecError) { Security_SetPrivilegeAttrib(SE_SHUTDOWN_NAME, dwOldState, NULL); } else { Trace((TEXT("Error %d setting SE_SHUTDOWN_NAME privilege"), dwSecError)); } } else { Trace((TEXT("Error %d setting SE_SHUTDOWN_NAME privilege"), dwStatus)); } TraceLeaveResult(dwRebootError); } // // Retrieve location, size and file/directory count information for the // CSC cache. If CSC is disabled, information is gathered about the // system volume. That's where the CSC agent will put the cache when // one is created. // void GetCscSpaceUsageInfo( CSCSPACEUSAGEINFO *psui ) { ULARGE_INTEGER ulTotalBytes = {0, 0}; ULARGE_INTEGER ulUsedBytes = {0, 0}; ZeroMemory(psui, sizeof(*psui)); CSCGetSpaceUsage(psui->szVolume, ARRAYSIZE(psui->szVolume), &ulTotalBytes.HighPart, &ulTotalBytes.LowPart, &ulUsedBytes.HighPart, &ulUsedBytes.LowPart, &psui->dwNumFilesInCache, &psui->dwNumDirsInCache); if (0 == psui->szVolume[0]) { // // CSCGetSpaceUsage didn't give us a volume name. Probably because // CSC hasn't been enabled on the system. Default to the system // drive because that's what CSC uses anyway. // GetSystemDirectory(psui->szVolume, ARRAYSIZE(psui->szVolume)); psui->dwNumFilesInCache = 0; psui->dwNumDirsInCache = 0; } PathStripToRoot(psui->szVolume); DWORD spc = 0; // Sectors per cluster. DWORD bps = 0; // Bytes per sector. DWORD fc = 0; // Free clusters. DWORD nc = 0; // Total clusters. GetDiskFreeSpace(psui->szVolume, &spc, &bps, &fc, &nc); psui->llBytesOnVolume = (LONGLONG)nc * (LONGLONG)spc * (LONGLONG)bps; psui->llBytesTotalInCache = ulTotalBytes.QuadPart; psui->llBytesUsedInCache = ulUsedBytes.QuadPart; } //----------------------------------------------------------------------------- // This is code taken from shell32's utils.cpp file. // We need the function SHSimpleIDListFromFindData() but it's not exported // from shell32. Therefore, until it is, we just lifted the code. // [brianau - 9/28/98] //----------------------------------------------------------------------------- class CFileSysBindData: public IFileSystemBindData { public: CFileSysBindData(); // *** IUnknown methods *** STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IFileSystemBindData STDMETHODIMP SetFindData(const WIN32_FIND_DATAW *pfd); STDMETHODIMP GetFindData(WIN32_FIND_DATAW *pfd); private: ~CFileSysBindData(); LONG _cRef; WIN32_FIND_DATAW _fd; }; CFileSysBindData::CFileSysBindData() : _cRef(1) { ZeroMemory(&_fd, sizeof(_fd)); } CFileSysBindData::~CFileSysBindData() { } HRESULT CFileSysBindData::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFileSysBindData, IFileSystemBindData), // IID_IFileSystemBindData { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CFileSysBindData::AddRef(void) { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CFileSysBindData::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CFileSysBindData::SetFindData(const WIN32_FIND_DATAW *pfd) { _fd = *pfd; return S_OK; } HRESULT CFileSysBindData::GetFindData(WIN32_FIND_DATAW *pfd) { *pfd = _fd; return S_OK; } HRESULT SHCreateFileSysBindCtx( const WIN32_FIND_DATA *pfd, IBindCtx **ppbc ) { HRESULT hres; IFileSystemBindData *pfsbd = new CFileSysBindData(); if (pfsbd) { if (pfd) { WIN32_FIND_DATAW fdw; memcpy(&fdw, pfd, FIELD_OFFSET(WIN32_FIND_DATAW, cFileName)); SHTCharToUnicode(pfd->cFileName, fdw.cFileName, ARRAYSIZE(fdw.cFileName)); SHTCharToUnicode(pfd->cAlternateFileName, fdw.cAlternateFileName, ARRAYSIZE(fdw.cAlternateFileName)); pfsbd->SetFindData(&fdw); } hres = CreateBindCtx(0, ppbc); if (SUCCEEDED(hres)) { BIND_OPTS bo = {sizeof(bo)}; // Requires size filled in. bo.grfMode = STGM_CREATE; (*ppbc)->SetBindOptions(&bo); (*ppbc)->RegisterObjectParam(STR_FILE_SYS_BIND_DATA, pfsbd); } pfsbd->Release(); } else { *ppbc = NULL; hres = E_OUTOFMEMORY; } return hres; } HRESULT SHSimpleIDListFromFindData( LPCTSTR pszPath, const WIN32_FIND_DATA *pfd, LPITEMIDLIST *ppidl ) { IShellFolder *psfDesktop; HRESULT hres = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hres)) { IBindCtx *pbc; hres = SHCreateFileSysBindCtx(pfd, &pbc); if (SUCCEEDED(hres)) { WCHAR wszPath[MAX_PATH]; SHTCharToUnicode(pszPath, wszPath, ARRAYSIZE(wszPath)); hres = psfDesktop->ParseDisplayName(NULL, pbc, wszPath, NULL, ppidl, NULL); pbc->Release(); } psfDesktop->Release(); } if (FAILED(hres)) *ppidl = NULL; return hres; } // // Number of times a CSC API will be repeated if it fails. // In particular, this is used for CSCDelete and CSCFillSparseFiles; both of // which can fail on one call but succeed the next. This isn't designed // behavior but it is reality. ShishirP knows about it and may be able to // investigate later. [brianau - 4/2/98] // const int CSC_API_RETRIES = 3; // // Occasionally if a call to a CSC API fails with ERROR_ACCESS_DENIED, // repeating the call will succeed. // Here we wrap up the call to CSCDelete so that it is called multiple // times in the case of these failures. // DWORD CscDelete( LPCTSTR pszPath ) { DWORD dwError = ERROR_SUCCESS; int nRetries = CSC_API_RETRIES; while(0 < nRetries--) { if (CSCDelete(pszPath)) return ERROR_SUCCESS; dwError = GetLastError(); if (ERROR_ACCESS_DENIED != dwError) return dwError; } if (ERROR_SUCCESS == dwError) { // // Hack for some CSC APIs returning // ERROR_SUCCESS even though they fail. // dwError = ERROR_GEN_FAILURE; } return dwError; } void EnableDlgItems( HWND hwndDlg, const UINT* pCtlIds, int cCtls, bool bEnable ) { for (int i = 0; i < cCtls; i++) { EnableWindow(GetDlgItem(hwndDlg, *(pCtlIds + i)), bEnable); } } void ShowDlgItems( HWND hwndDlg, const UINT* pCtlIds, int cCtls, bool bShow ) { const int nCmdShow = bShow ? SW_NORMAL : SW_HIDE; for (int i = 0; i < cCtls; i++) { ShowWindow(GetDlgItem(hwndDlg, *(pCtlIds + i)), nCmdShow); } } // // Wrapper around GetVolumeInformation that accounts for mounted // volumes. This was borrowed from shell32\mulprsht.c // BOOL GetVolumeFlags(LPCTSTR pszPath, DWORD *pdwFlags) { TraceAssert(NULL != pszPath); TraceAssert(NULL != pdwFlags); TCHAR szRoot[MAX_PATH]; *pdwFlags = NULL; // // Is this a mount point, e.g. c:\ or c:\hostfolder\ // if (!GetVolumePathName(pszPath, szRoot, ARRAYSIZE(szRoot))) { // // No. Use path provided by caller. // StringCchCopy(szRoot, ARRAYSIZE(szRoot), pszPath); PathStripToRoot(szRoot); } // // GetVolumeInformation requires a trailing backslash. // PathAddBackslash(szRoot); return GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, pdwFlags, NULL, 0); } // // Determine if a net share has an open connection on the local machine. // // Returns: // // S_OK = There is an open connection to the share. // S_FALSE = No open connection to the share. // other = Some error code. // HRESULT IsOpenConnectionShare( LPCTSTR pszShare ) { DWORD dwStatus; if (CSCQueryFileStatus(pszShare, &dwStatus, NULL, NULL)) { if (FLAG_CSC_SHARE_STATUS_CONNECTED & dwStatus) return S_OK; } return S_FALSE; } // With this version of CSCIsCSCEnabled, we can delay all extra dll loads // (including cscdll.dll) until we actually see a net file/folder. #include #include static TCHAR const c_szShadowDevice[] = TEXT("\\\\.\\shadow"); BOOL IsCSCEnabled(void) { BOOL bIsCSCEnabled = FALSE; if (!IsOS(OS_PERSONAL)) { SHADOWINFO sSI = {0}; ULONG ulBytesReturned; HANDLE hShadowDB = CreateFile(c_szShadowDevice, FILE_EXECUTE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hShadowDB) return FALSE; sSI.uStatus = SHADOW_SWITCH_SHADOWING; sSI.uOp = SHADOW_SWITCH_GET_STATE; if (DeviceIoControl(hShadowDB, IOCTL_SWITCHES, (void *)(&sSI), 0, NULL, 0, &ulBytesReturned, NULL)) { bIsCSCEnabled = (sSI.uStatus & SHADOW_SWITCH_SHADOWING); } CloseHandle(hShadowDB); } return bIsCSCEnabled; } // // The bit-masking used by this function is dependent upon the way // Shishir defined the database status flags in cscapi.h // // FLAG_DATABASESTATUS_ENCRYPTION_MASK 0x00000006 (0000 0110) // FLAG_DATABASESTATUS_UNENCRYPTED 0x00000000 (0000 0000) // FLAG_DATABASESTATUS_PARTIALLY_UNENCRYPTED 0x00000004 (0000 0100) // FLAG_DATABASESTATUS_ENCRYPTED 0x00000002 (0000 0010) // FLAG_DATABASESTATUS_PARTIALLY_ENCRYPTED 0x00000006 (0000 0110) // // Things to note: // 1. Bit 1 == encryption status. // 2. Bit 2 == partial completion status. // // // Returns: // TRUE == Database is encrypted. May be fully or partially encrypted. // FALSE == Database is not encrypted. May be fully or partially not encrypted. // // *pbPartial == Indicates if state is "partial" or not. // // Partial encryption means an encryption operation was started // but not successfully completed. All new files created will be encrypted. // Partial decryption means a decryption operation was started // but not successfully completed. All new files created will be unencrypted. // BOOL IsCacheEncrypted(BOOL *pbPartial) { ULONG ulStatus; ULONG ulErrors; BOOL bEncrypted = FALSE; if (CSCQueryDatabaseStatus(&ulStatus, &ulErrors)) { ulStatus &= FLAG_DATABASESTATUS_ENCRYPTION_MASK; bEncrypted = (0 != (FLAG_DATABASESTATUS_ENCRYPTED & ulStatus)); if (NULL != pbPartial) { const ULONG FLAGS_PARTIAL = (FLAG_DATABASESTATUS_PARTIALLY_ENCRYPTED & FLAG_DATABASESTATUS_PARTIALLY_UNENCRYPTED); *pbPartial = (0 != (FLAGS_PARTIAL & ulStatus)); } } return bEncrypted; } bool CscVolumeSupportsEncryption( LPCTSTR pszPathIn // Path of CSC volume. Can be NULL. ) { CSCSPACEUSAGEINFO sui; DWORD dwVolFlags; bool bSupportsEncryption = false; if (NULL == pszPathIn) { // // Caller didn't provide path of CSC volume. // Get it from CSC. // sui.szVolume[0] = 0; GetCscSpaceUsageInfo(&sui); pszPathIn = sui.szVolume; } if (GetVolumeFlags(pszPathIn, &dwVolFlags)) { if (0 != (FILE_SUPPORTS_ENCRYPTION & dwVolFlags)) { bSupportsEncryption = true; } } return bSupportsEncryption; } // // Returns: // // NULL == Mutex is owned by another thread. // non-NULL == Handle of mutex object. This thread now owns the mutex. // Caller is responsible for releasing the mutex and closing // the mutex handle. // // *pbAbandoned indicates if mutex was abandoned by its thread. // // HANDLE RequestNamedMutexOwnership( LPCTSTR pszMutexName, BOOL *pbAbandoned // [optional] ) { BOOL bAbandoned = FALSE; HANDLE hMutex = CreateMutex(NULL, FALSE, pszMutexName); if (NULL != hMutex) { // // Whether we created or opened the mutex, wait on it // to gain ownership. // switch(WaitForSingleObject(hMutex, 0)) { case WAIT_ABANDONED: bAbandoned = TRUE; // // Fall through... // case WAIT_OBJECT_0: // // Current thread now owns the mutex. // We'll return the handle to the caller. // break; case WAIT_TIMEOUT: default: // // Couldn't gain ownership of the mutex. // Close the handle. // CloseHandle(hMutex); hMutex = NULL; break; } } if (NULL != pbAbandoned) { *pbAbandoned = bAbandoned; } return hMutex; } // // Determine if a named mutex is currently owned by another thread // or not. This function only determines ownership then immediately // releases the mutex. If you need to determine ownership and want // to retain ownership if previously unowned call // RequestNamedMutexOwnership instead. // BOOL IsNamedMutexOwned( LPCTSTR pszMutexName, BOOL *pbAbandoned ) { HANDLE hMutex = RequestNamedMutexOwnership(pszMutexName, pbAbandoned); if (NULL != hMutex) { // // Mutex was not owned (now owned by current thread). // Since we're only interested in determining prior ownership // we release it and close the handle. // ReleaseMutex(hMutex); CloseHandle(hMutex); return FALSE; } return TRUE; } long GetGlobalCounterValue(LPCTSTR pszCounterName) { long lValue = 0; HANDLE hCounter = SHGlobalCounterCreateNamed(pszCounterName, 0); if (hCounter) { lValue = SHGlobalCounterGetValue(hCounter); SHGlobalCounterDestroy(hCounter); } return lValue; } BOOL IsSyncInProgress(void) { return GetGlobalCounterValue(c_szSyncInProgCounter) > 0; } BOOL IsPurgeInProgress(void) { return GetGlobalCounterValue(c_szPurgeInProgCounter) > 0; } BOOL IsEncryptionInProgress(void) { return IsNamedMutexOwned(c_szEncryptionInProgMutex, NULL); } // // Requests ownership of the global cache encryption mutex. // // Returns: // NULL == Mutex already owned by another thread. // Non-NULL == Mutex now owned by current thread. // Caller is responsible for releasing the mutex // and closing the mutex handle. // HANDLE RequestPermissionToEncryptCache(void) { return RequestNamedMutexOwnership(c_szEncryptionInProgMutex, NULL); } //--------------------------------------------------------------- // DataObject helper functions. // These are roughly taken from similar functions in // shell\shell32\datautil.cpp //--------------------------------------------------------------- HRESULT DataObject_SetBlob( IDataObject *pdtobj, CLIPFORMAT cf, LPCVOID pvBlob, UINT cbBlob ) { HRESULT hr = E_OUTOFMEMORY; void * pv = GlobalAlloc(GPTR, cbBlob); if (pv) { CopyMemory(pv, pvBlob, cbBlob); hr = DataObject_SetGlobal(pdtobj, cf, pv); if (FAILED(hr)) GlobalFree((HGLOBAL)pv); } return hr; } HRESULT DataObject_GetBlob( IDataObject *pdtobj, CLIPFORMAT cf, void * pvBlob, UINT cbBlob ) { STGMEDIUM medium = {0}; FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HRESULT hr = pdtobj->GetData(&fmte, &medium); if (SUCCEEDED(hr)) { void * pv = GlobalLock(medium.hGlobal); if (pv) { ASSERT(GlobalSize(medium.hGlobal) >= cbBlob); CopyMemory(pvBlob, pv, cbBlob); GlobalUnlock(medium.hGlobal); } else { hr = E_UNEXPECTED; } ReleaseStgMedium(&medium); } return hr; } HRESULT DataObject_SetGlobal( IDataObject *pdtobj, CLIPFORMAT cf, HGLOBAL hGlobal ) { FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; medium.tymed = TYMED_HGLOBAL; medium.hGlobal = hGlobal; medium.pUnkForRelease = NULL; return pdtobj->SetData(&fmte, &medium, TRUE); } HRESULT DataObject_SetDWORD( IDataObject *pdtobj, CLIPFORMAT cf, DWORD dw ) { return DataObject_SetBlob(pdtobj, cf, &dw, sizeof(dw)); } HRESULT DataObject_GetDWORD( IDataObject *pdtobj, CLIPFORMAT cf, DWORD *pdwOut ) { return DataObject_GetBlob(pdtobj, cf, pdwOut, sizeof(DWORD)); } HRESULT SetGetLogicalPerformedDropEffect( IDataObject *pdtobj, DWORD *pdwEffect, bool bSet ) { HRESULT hr = NOERROR; static CLIPFORMAT cf; if ((CLIPFORMAT)0 == cf) cf = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_LOGICALPERFORMEDDROPEFFECT); if (bSet) { hr = DataObject_SetDWORD(pdtobj, cf, *pdwEffect); } else { *pdwEffect = DROPEFFECT_NONE; DataObject_GetDWORD(pdtobj, cf, pdwEffect); } return hr; } DWORD GetLogicalPerformedDropEffect( IDataObject *pdtobj ) { DWORD dwEffect = DROPEFFECT_NONE; SetGetLogicalPerformedDropEffect(pdtobj, &dwEffect, false); return dwEffect; } HRESULT SetLogicalPerformedDropEffect( IDataObject *pdtobj, DWORD dwEffect ) { return SetGetLogicalPerformedDropEffect(pdtobj, &dwEffect, true); } HRESULT SetGetPreferredDropEffect( IDataObject *pdtobj, DWORD *pdwEffect, bool bSet ) { HRESULT hr = NOERROR; static CLIPFORMAT cf; if ((CLIPFORMAT)0 == cf) cf = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT); if (bSet) { hr = DataObject_SetDWORD(pdtobj, cf, *pdwEffect); } else { *pdwEffect = DROPEFFECT_NONE; DataObject_GetDWORD(pdtobj, cf, pdwEffect); } return hr; } DWORD GetPreferredDropEffect( IDataObject *pdtobj ) { DWORD dwEffect = DROPEFFECT_NONE; SetGetPreferredDropEffect(pdtobj, &dwEffect, false); return dwEffect; } HRESULT SetPreferredDropEffect( IDataObject *pdtobj, DWORD dwEffect ) { return SetGetPreferredDropEffect(pdtobj, &dwEffect, true); } // // Wrap CSCFindFirstFile so we don't enumerate "." or "..". // Wrapper also helps code readability. // HANDLE CacheFindFirst( LPCTSTR pszPath, PSID psid, WIN32_FIND_DATA *pfd, DWORD *pdwStatus, DWORD *pdwPinCount, DWORD *pdwHintFlags, FILETIME *pft ) { HANDLE hFind = CSCFindFirstFileForSid(pszPath, psid, pfd, pdwStatus, pdwPinCount, pdwHintFlags, pft); while(INVALID_HANDLE_VALUE != hFind && PathIsDotOrDotDot(pfd->cFileName)) { if (!CSCFindNextFile(hFind, pfd, pdwStatus, pdwPinCount, pdwHintFlags, pft)) { CSCFindClose(hFind); hFind = INVALID_HANDLE_VALUE; } } return hFind; } // // Wrap CSCFindFirstFile so we don't enumerate "." or "..". // Wrapper also helps code readability. // BOOL CacheFindNext( HANDLE hFind, WIN32_FIND_DATA *pfd, DWORD *pdwStatus, DWORD *pdwPinCount, DWORD *pdwHintFlags, FILETIME *pft ) { BOOL bResult = FALSE; do { bResult = CSCFindNextFile(hFind, pfd, pdwStatus, pdwPinCount, pdwHintFlags, pft); } while(bResult && PathIsDotOrDotDot(pfd->cFileName)); return bResult; } // // If there's a link to the Offline Files folder on the // user's desktop, delete the link. This version checks a flag in the registry // before enumerating all LNK's on the desktop. If the flag doesn't exist, // we don't continue. This is a perf enhancement used at logon. // BOOL DeleteOfflineFilesFolderLink_PerfSensitive( HWND hwndParent ) { BOOL bResult = FALSE; // // Before enumerating links on the desktop, check to see if the user // has created a link. // DWORD dwValue; DWORD cbValue = sizeof(dwValue); DWORD dwType; DWORD dwResult = SHGetValue(HKEY_CURRENT_USER, REGSTR_KEY_OFFLINEFILES, REGSTR_VAL_FOLDERSHORTCUTCREATED, &dwType, &dwValue, &cbValue); if (ERROR_SUCCESS == dwResult) { // // We don't care about the value or it's type. // Presence/absence of the value is all that matters. // bResult = DeleteOfflineFilesFolderLink(hwndParent); } return bResult; } // // This version of the "delete link" function does not check the // flag in the registry. It finds the link file on the desktop and deletes it. // BOOL DeleteOfflineFilesFolderLink( HWND hwndParent ) { BOOL bResult = FALSE; TCHAR szLinkPath[MAX_PATH]; if (SUCCEEDED(COfflineFilesFolder::IsLinkOnDesktop(hwndParent, szLinkPath, ARRAYSIZE(szLinkPath)))) { bResult = DeleteFile(szLinkPath); } // // Remove the "folder shortcut created" flag from the registry. // SHDeleteValue(HKEY_CURRENT_USER, REGSTR_KEY_OFFLINEFILES, REGSTR_VAL_FOLDERSHORTCUTCREATED); return bResult; } // // This was taken from shell\shell32\util.cpp. // BOOL ShowSuperHidden(void) { BOOL bRet = FALSE; if (!SHRestricted(REST_DONTSHOWSUPERHIDDEN)) { SHELLSTATE ss; SHGetSetSettings(&ss, SSF_SHOWSUPERHIDDEN, FALSE); bRet = ss.fShowSuperHidden; } return bRet; } BOOL ShowHidden(void) { SHELLSTATE ss; SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE); return ss.fShowAllObjects; } BOOL IsSyncMgrInitialized(void) { // // Is this the first time this user has used run CSCUI? // DWORD dwValue = 0; DWORD cbData = sizeof(dwValue); DWORD dwType; SHGetValue(HKEY_CURRENT_USER, c_szCSCKey, c_szSyncMgrInitialized, &dwType, (void *)&dwValue, &cbData); return (0 != dwValue); } void SetSyncMgrInitialized(void) { // // Set the "initialized" flag so our logoff code in cscst.cpp doesn't // try to re-register for sync-at-logon/logoff. // DWORD dwSyncMgrInitialized = 1; SHSetValue(HKEY_CURRENT_USER, c_szCSCKey, c_szSyncMgrInitialized, REG_DWORD, &dwSyncMgrInitialized, sizeof(dwSyncMgrInitialized)); } // // Return the HWND for a standard progress dialog. // HWND GetProgressDialogWindow(IProgressDialog *ppd) { HWND hwndProgress = NULL; // // Get the progress dialog's window handle. We'll use // it as a parent window for error UI. // HRESULT hr = IUnknown_GetWindow(ppd, &hwndProgress); return hwndProgress; } void CAutoWaitCursor::Reset( void ) { ShowCursor(FALSE); if (NULL != m_hCursor) SetCursor(m_hCursor); m_hCursor = NULL; } // // Expand all environment strings in a text string. // HRESULT ExpandStringInPlace( LPTSTR psz, DWORD cch ) { HRESULT hr = E_OUTOFMEMORY; LPTSTR pszCopy; if (LocalAllocString(&pszCopy, psz)) { DWORD cchExpanded = ExpandEnvironmentStrings(pszCopy, psz, cch); if (0 == cchExpanded) hr = HRESULT_FROM_WIN32(GetLastError()); else if (cchExpanded > cch) hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); else hr = S_OK; LocalFreeString(&pszCopy); } if (FAILED(hr) && 0 < cch) { *psz = 0; } return hr; } // // Version of RegEnumValue that expands environment variables // in all string values. // LONG _RegEnumValueExp( HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, LPDWORD lpcbValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData ) { DWORD cchNameDest = lpcbValueName ? *lpcbValueName / sizeof(TCHAR) : 0; DWORD cchDataDest = lpcbData ? *lpcbData / sizeof(TCHAR) : 0; DWORD dwType; if (NULL == lpType) lpType = &dwType; LONG lResult = RegEnumValue(hKey, dwIndex, lpValueName, lpcbValueName, lpReserved, lpType, lpData, lpcbData); if (ERROR_SUCCESS == lResult) { HRESULT hr = ExpandStringInPlace(lpValueName, cchNameDest); if ((NULL != lpData) && (REG_SZ == *lpType || REG_EXPAND_SZ == *lpType)) { hr = ExpandStringInPlace((LPTSTR)lpData, cchDataDest); } lResult = HRESULT_CODE(hr); } return lResult; }