Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2467 lines
70 KiB

#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;
}