|
|
#include "pch.h"
#include <shsemip.h> // ILClone, ILIsEmpty, etc.
#include <sddl.h>
#include "security.h"
#include "shguidp.h"
#include "folder.h"
#include "strings.h"
#include <ccstock.h>
#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 <devioctl.h>
#include <shdcom.h>
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; }
|