3480 lines
119 KiB
C++
3480 lines
119 KiB
C++
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "propsht.h"
|
|
#include <winbase.h>
|
|
#include <shellids.h>
|
|
#include "util.h" // for GetFileDescription
|
|
#include "prshtcpp.h" // for progress dlg and recursive apply
|
|
#include "shlexec.h" // for SIDKEYNAME
|
|
#include "datautil.h"
|
|
#include <efsui.h> // for EfsDetail
|
|
#include "ascstr.h" // for IAssocStore
|
|
|
|
// drivesx.c
|
|
STDAPI_(DWORD) PathGetClusterSize(LPCTSTR pszPath);
|
|
STDAPI_(DWORD) DrivesPropertiesThreadProc(void *pv);
|
|
|
|
// version.c
|
|
STDAPI_(void) AddVersionPage(LPCTSTR pszFilePath, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
|
|
|
|
// link.c
|
|
STDAPI_(BOOL) AddLinkPage(LPCTSTR pszFile, LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam);
|
|
|
|
BOOL _IsBiDiCalendar(void);
|
|
|
|
const DWORD aFileGeneralHelpIds[] = {
|
|
IDD_LINE_1, NO_HELP,
|
|
IDD_LINE_2, NO_HELP,
|
|
IDD_LINE_3, NO_HELP,
|
|
IDD_ITEMICON, IDH_FPROP_GEN_ICON,
|
|
IDD_NAMEEDIT, IDH_FPROP_GEN_NAME,
|
|
IDC_CHANGEFILETYPE, IDH_FPROP_GEN_CHANGE,
|
|
IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
|
|
IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
|
|
IDD_OPENSWITH_TXT, IDH_FPROP_GEN_OPENSWITH,
|
|
IDD_OPENSWITH, IDH_FPROP_GEN_OPENSWITH,
|
|
IDD_LOCATION_TXT, IDH_FPROP_GEN_LOCATION,
|
|
IDD_LOCATION, IDH_FPROP_GEN_LOCATION,
|
|
IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
|
|
IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
|
|
IDD_FILESIZE_COMPRESSED, IDH_FPROP_GEN_COMPRESSED_SIZE,
|
|
IDD_FILESIZE_COMPRESSED_TXT, IDH_FPROP_GEN_COMPRESSED_SIZE,
|
|
IDD_CONTAINS_TXT, IDH_FPROP_FOLDER_CONTAINS,
|
|
IDD_CONTAINS, IDH_FPROP_FOLDER_CONTAINS,
|
|
IDD_CREATED_TXT, IDH_FPROP_GEN_DATE_CREATED,
|
|
IDD_CREATED, IDH_FPROP_GEN_DATE_CREATED,
|
|
IDD_LASTMODIFIED_TXT, IDH_FPROP_GEN_LASTCHANGE,
|
|
IDD_LASTMODIFIED, IDH_FPROP_GEN_LASTCHANGE,
|
|
IDD_LASTACCESSED_TXT, IDH_FPROP_GEN_LASTACCESS,
|
|
IDD_LASTACCESSED, IDH_FPROP_GEN_LASTACCESS,
|
|
IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
|
|
IDD_READONLY, IDH_FPROP_GEN_READONLY,
|
|
IDD_HIDDEN, IDH_FPROP_GEN_HIDDEN,
|
|
IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
|
|
IDC_ADVANCED, IDH_FPROP_GEN_ADVANCED,
|
|
IDC_DRV_PROPERTIES, IDH_FPROP_GEN_MOUNTEDPROP,
|
|
IDD_FILETYPE_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
|
|
IDC_DRV_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
|
|
0, 0
|
|
};
|
|
|
|
const DWORD aFolderGeneralHelpIds[] = {
|
|
IDD_LINE_1, NO_HELP,
|
|
IDD_LINE_2, NO_HELP,
|
|
IDD_LINE_3, NO_HELP,
|
|
IDD_ITEMICON, IDH_FPROP_GEN_ICON,
|
|
IDD_NAMEEDIT, IDH_FPROP_GEN_NAME,
|
|
IDC_CHANGEFILETYPE, IDH_FPROP_GEN_CHANGE,
|
|
IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
|
|
IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
|
|
IDD_OPENSWITH_TXT, IDH_FPROP_GEN_OPENSWITH,
|
|
IDD_OPENSWITH, IDH_FPROP_GEN_OPENSWITH,
|
|
IDD_LOCATION_TXT, IDH_FPROP_GEN_LOCATION,
|
|
IDD_LOCATION, IDH_FPROP_GEN_LOCATION,
|
|
IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
|
|
IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
|
|
IDD_FILESIZE_COMPRESSED, IDH_FPROP_GEN_COMPRESSED_SIZE,
|
|
IDD_FILESIZE_COMPRESSED_TXT, IDH_FPROP_GEN_COMPRESSED_SIZE,
|
|
IDD_CONTAINS_TXT, IDH_FPROP_FOLDER_CONTAINS,
|
|
IDD_CONTAINS, IDH_FPROP_FOLDER_CONTAINS,
|
|
IDD_CREATED_TXT, IDH_FPROP_GEN_DATE_CREATED,
|
|
IDD_CREATED, IDH_FPROP_GEN_DATE_CREATED,
|
|
IDD_LASTMODIFIED_TXT, IDH_FPROP_GEN_LASTCHANGE,
|
|
IDD_LASTMODIFIED, IDH_FPROP_GEN_LASTCHANGE,
|
|
IDD_LASTACCESSED_TXT, IDH_FPROP_GEN_LASTACCESS,
|
|
IDD_LASTACCESSED, IDH_FPROP_GEN_LASTACCESS,
|
|
IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
|
|
IDD_READONLY, IDH_FPROP_GEN_FOLDER_READONLY,
|
|
IDD_HIDDEN, IDH_FPROP_GEN_HIDDEN,
|
|
IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
|
|
IDC_ADVANCED, IDH_FPROP_GEN_ADVANCED,
|
|
IDC_DRV_PROPERTIES, IDH_FPROP_GEN_MOUNTEDPROP,
|
|
IDD_FILETYPE_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
|
|
IDC_DRV_TARGET, IDH_FPROP_GEN_MOUNTEDTARGET,
|
|
0, 0
|
|
};
|
|
|
|
const DWORD aMultiPropHelpIds[] = {
|
|
IDD_LINE_1, NO_HELP,
|
|
IDD_LINE_2, NO_HELP,
|
|
IDD_ITEMICON, IDH_FPROP_GEN_ICON,
|
|
IDD_CONTAINS, IDH_MULTPROP_NAME,
|
|
IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
|
|
IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
|
|
IDD_LOCATION_TXT, IDH_FPROP_GEN_LOCATION,
|
|
IDD_LOCATION, IDH_FPROP_GEN_LOCATION,
|
|
IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
|
|
IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
|
|
IDD_FILESIZE_COMPRESSED, IDH_FPROP_GEN_COMPRESSED_SIZE,
|
|
IDD_FILESIZE_COMPRESSED_TXT, IDH_FPROP_GEN_COMPRESSED_SIZE,
|
|
IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
|
|
IDD_READONLY, IDH_FPROP_GEN_READONLY,
|
|
IDD_HIDDEN, IDH_FPROP_GEN_HIDDEN,
|
|
IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
|
|
IDC_ADVANCED, IDH_FPROP_GEN_ADVANCED,
|
|
0, 0
|
|
};
|
|
|
|
const DWORD aAdvancedHelpIds[] = {
|
|
IDD_ITEMICON, NO_HELP,
|
|
IDC_MANAGEFILES_TXT, NO_HELP,
|
|
IDD_MANAGEFOLDERS_TXT, NO_HELP,
|
|
IDD_ARCHIVE, IDH_FPROP_GEN_ARCHIVE,
|
|
IDD_INDEX, IDH_FPROP_GEN_INDEX,
|
|
IDD_COMPRESS, IDH_FPROP_GEN_COMPRESSED,
|
|
IDD_ENCRYPT, IDH_FPROP_GEN_ENCRYPT,
|
|
0, 0
|
|
};
|
|
|
|
FOLDERCONTENTSINFO* Create_FolderContentsInfo()
|
|
{
|
|
FOLDERCONTENTSINFO *pfci = (FOLDERCONTENTSINFO*)LocalAlloc(LPTR, sizeof(*pfci));
|
|
if (pfci)
|
|
{
|
|
pfci->_cRef = 1;
|
|
}
|
|
return pfci;
|
|
}
|
|
|
|
void Free_FolderContentsInfoMembers(FOLDERCONTENTSINFO* pfci)
|
|
{
|
|
if (pfci->hida)
|
|
{
|
|
GlobalFree(pfci->hida);
|
|
pfci->hida = NULL;
|
|
}
|
|
}
|
|
|
|
LONG AddRef_FolderContentsInfo(FOLDERCONTENTSINFO* pfci)
|
|
{
|
|
ASSERTMSG(pfci != NULL, "AddRef_FolderContentsInfo: caller passed a null pfci");
|
|
if (pfci)
|
|
{
|
|
return InterlockedIncrement(&pfci->_cRef);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LONG Release_FolderContentsInfo(FOLDERCONTENTSINFO* pfci)
|
|
{
|
|
if (pfci)
|
|
{
|
|
if (InterlockedDecrement(&pfci->_cRef))
|
|
{
|
|
return pfci->_cRef;
|
|
}
|
|
Free_FolderContentsInfoMembers(pfci);
|
|
LocalFree(pfci);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void UpdateSizeCount(FILEPROPSHEETPAGE * pfpsp)
|
|
{
|
|
TCHAR szNum[32], szNum1[64];
|
|
LPTSTR pszFmt = ShellConstructMessageString(HINST_THISDLL,
|
|
MAKEINTRESOURCE(pfpsp->pfci->cbSize ? IDS_SIZEANDBYTES : IDS_SIZE),
|
|
ShortSizeFormat64(pfpsp->pfci->cbSize, szNum, ARRAYSIZE(szNum)),
|
|
AddCommas64(pfpsp->pfci->cbSize, szNum1, ARRAYSIZE(szNum1)));
|
|
if (pszFmt)
|
|
{
|
|
SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE, pszFmt);
|
|
LocalFree(pszFmt);
|
|
}
|
|
|
|
pszFmt = ShellConstructMessageString(HINST_THISDLL,
|
|
MAKEINTRESOURCE(pfpsp->pfci->cbActualSize ? IDS_SIZEANDBYTES : IDS_SIZE),
|
|
ShortSizeFormat64(pfpsp->pfci->cbActualSize, szNum, ARRAYSIZE(szNum)),
|
|
AddCommas64(pfpsp->pfci->cbActualSize, szNum1, ARRAYSIZE(szNum1)));
|
|
|
|
if (pszFmt)
|
|
{
|
|
SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE_COMPRESSED, pszFmt);
|
|
LocalFree(pszFmt);
|
|
}
|
|
|
|
pszFmt = ShellConstructMessageString(HINST_THISDLL,
|
|
MAKEINTRESOURCE(IDS_NUMFILES),
|
|
AddCommas(pfpsp->pfci->cFiles, szNum, ARRAYSIZE(szNum)),
|
|
AddCommas(pfpsp->pfci->cFolders, szNum1, ARRAYSIZE(szNum1)));
|
|
if (pszFmt && !pfpsp->fMountedDrive)
|
|
{
|
|
SetDlgItemText(pfpsp->hDlg, IDD_CONTAINS, pszFmt);
|
|
LocalFree(pszFmt);
|
|
}
|
|
}
|
|
|
|
|
|
STDAPI_(BOOL) HIDA_FillFindData(HIDA hida, UINT iItem, LPTSTR pszPath, WIN32_FIND_DATA *pfd, BOOL fReturnCompressedSize)
|
|
{
|
|
BOOL fRet = FALSE; // assume error
|
|
*pszPath = 0; // assume error
|
|
|
|
LPITEMIDLIST pidl = HIDA_ILClone(hida, iItem);
|
|
if (pidl)
|
|
{
|
|
if (SHGetPathFromIDList(pidl, pszPath))
|
|
{
|
|
if (pfd)
|
|
{
|
|
HANDLE h = FindFirstFile(pszPath, pfd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
{
|
|
// error, zero the bits
|
|
ZeroMemory(pfd, sizeof(*pfd));
|
|
}
|
|
else
|
|
{
|
|
FindClose(h);
|
|
// if the user wants the compressed file size, and compression is supported, then go get it
|
|
if (fReturnCompressedSize && (pfd->dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE)))
|
|
{
|
|
pfd->nFileSizeLow = SHGetCompressedFileSize(pszPath, &pfd->nFileSizeHigh);
|
|
}
|
|
}
|
|
}
|
|
fRet = TRUE;
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
|
|
DWORD CALLBACK SizeThreadProc(void *pv)
|
|
{
|
|
FOLDERCONTENTSINFO* pfci = (FOLDERCONTENTSINFO*)pv;
|
|
|
|
pfci->cbSize = 0;
|
|
pfci->cbActualSize = 0;
|
|
pfci->cFiles = 0;
|
|
pfci->cFolders = 0;
|
|
|
|
if (pfci->bContinue && pfci->hDlg)
|
|
{
|
|
// update the dialog every 1/4 second
|
|
SetTimer(pfci->hDlg, IDT_SIZE, 250, NULL);
|
|
}
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
for (UINT iItem = 0; HIDA_FillFindData(pfci->hida, iItem, szPath, &pfci->fd, FALSE) && pfci->bContinue; iItem++)
|
|
{
|
|
if (pfci->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
FolderSize(szPath, pfci);
|
|
|
|
if (pfci->fMultipleFiles)
|
|
{
|
|
// for multiple file/folder properties, count myself
|
|
pfci->cFolders++;
|
|
}
|
|
}
|
|
else
|
|
{ // file selected
|
|
ULARGE_INTEGER ulSize, ulSizeOnDisk;
|
|
DWORD dwClusterSize = PathGetClusterSize(szPath);
|
|
|
|
// if compression is supported, we check to see if the file is sparse or compressed
|
|
if (pfci->fIsCompressionAvailable && (pfci->fd.dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE)))
|
|
{
|
|
ulSizeOnDisk.LowPart = SHGetCompressedFileSize(szPath, &ulSizeOnDisk.HighPart);
|
|
}
|
|
else
|
|
{
|
|
// not compressed or sparse, so just round to the cluster size
|
|
ulSizeOnDisk.LowPart = pfci->fd.nFileSizeLow;
|
|
ulSizeOnDisk.HighPart = pfci->fd.nFileSizeHigh;
|
|
ulSizeOnDisk.QuadPart = ROUND_TO_CLUSTER(ulSizeOnDisk.QuadPart, dwClusterSize);
|
|
}
|
|
|
|
// add the size in
|
|
ulSize.LowPart = pfci->fd.nFileSizeLow;
|
|
ulSize.HighPart = pfci->fd.nFileSizeHigh;
|
|
pfci->cbSize += ulSize.QuadPart;
|
|
|
|
// add the size on disk in
|
|
pfci->cbActualSize += ulSizeOnDisk.QuadPart;
|
|
|
|
// increment the # of files
|
|
pfci->cFiles++;
|
|
}
|
|
|
|
// set this so the progress bar knows how much total work there is to do
|
|
|
|
// ISSUE RAID BUG - 120446 - Need to Guard access to pfci->ulTotalNumberOfBytes.QuadParts
|
|
pfci->ulTotalNumberOfBytes.QuadPart = pfci->cbActualSize;
|
|
} // end of For Loop.
|
|
|
|
|
|
if (pfci->bContinue && pfci->hDlg)
|
|
{
|
|
KillTimer(pfci->hDlg, IDT_SIZE);
|
|
// make sure that there is a WM_TIMER message in the queue so we will get the "final" results
|
|
PostMessage(pfci->hDlg, WM_TIMER, (WPARAM)IDT_SIZE, (LPARAM)NULL);
|
|
}
|
|
|
|
pfci->fIsSizeThreadAlive = FALSE;
|
|
Release_FolderContentsInfo(pfci);
|
|
return 0;
|
|
}
|
|
|
|
DWORD CALLBACK SizeThread_AddRefCallBack(void *pv)
|
|
{
|
|
FOLDERCONTENTSINFO* pfci = (FOLDERCONTENTSINFO *)pv;
|
|
AddRef_FolderContentsInfo(pfci);
|
|
pfci->fIsSizeThreadAlive = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
void CreateSizeThread(FILEPROPSHEETPAGE * pfpsp)
|
|
{
|
|
if (pfpsp->pfci->bContinue)
|
|
{
|
|
if (!pfpsp->pfci->fIsSizeThreadAlive)
|
|
{
|
|
SHCreateThread(SizeThreadProc, pfpsp->pfci, CTF_COINIT, SizeThread_AddRefCallBack);
|
|
}
|
|
else
|
|
{
|
|
// previous size thread still running, so bail
|
|
}
|
|
}
|
|
}
|
|
|
|
void KillSizeThread(FILEPROPSHEETPAGE * pfpsp)
|
|
{
|
|
// signal the thread to stop
|
|
pfpsp->pfci->bContinue = FALSE;
|
|
}
|
|
|
|
|
|
DWORD GetVolumeFlags(LPCTSTR pszPath, OUT OPTIONAL LPTSTR pszFileSys, int cchFileSys)
|
|
{
|
|
TCHAR szRoot[MAX_PATH];
|
|
|
|
/* Is this mounted point, e.g. c:\ or c:\hostfolder\ */
|
|
if (!PathGetMountPointFromPath(pszPath, szRoot, ARRAYSIZE(szRoot)))
|
|
{
|
|
//no
|
|
lstrcpyn(szRoot, pszPath, ARRAYSIZE(szRoot));
|
|
PathStripToRoot(szRoot);
|
|
}
|
|
// GetVolumeInformation requires a trailing backslash. Append one
|
|
PathAddBackslash(szRoot);
|
|
|
|
if (pszFileSys)
|
|
*pszFileSys = 0 ;
|
|
|
|
DWORD dwVolumeFlags;
|
|
if (GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, &dwVolumeFlags, pszFileSys, cchFileSys))
|
|
{
|
|
return dwVolumeFlags;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// This function sets the initial file attributes based on the dwFlagsAND / dwFlagsOR
|
|
// for the multiple file case
|
|
//
|
|
void SetInitialFileAttribs(FILEPROPSHEETPAGE* pfpsp, DWORD dwFlagsAND, DWORD dwFlagsOR)
|
|
{
|
|
DWORD dwTriState = dwFlagsAND ^ dwFlagsOR; // this dword now has all the bits that are in the BST_INDETERMINATE state
|
|
#ifdef DEBUG
|
|
// the pfpsp struct should have been zero inited, make sure that our ATTRIBUTESTATE
|
|
// structs are zero inited
|
|
ATTRIBUTESTATE asTemp = {0};
|
|
ASSERT(memcmp(&pfpsp->asInitial, &asTemp, sizeof(pfpsp->asInitial)) == 0);
|
|
#endif // DEBUG
|
|
|
|
// set the inital state based on the flags
|
|
if (dwTriState & FILE_ATTRIBUTE_READONLY)
|
|
{
|
|
pfpsp->asInitial.fReadOnly = BST_INDETERMINATE;
|
|
}
|
|
else if (dwFlagsAND & FILE_ATTRIBUTE_READONLY)
|
|
{
|
|
pfpsp->asInitial.fReadOnly = BST_CHECKED;
|
|
}
|
|
|
|
if (dwTriState & FILE_ATTRIBUTE_HIDDEN)
|
|
{
|
|
pfpsp->asInitial.fHidden = BST_INDETERMINATE;
|
|
}
|
|
else if (dwFlagsAND & FILE_ATTRIBUTE_HIDDEN)
|
|
{
|
|
pfpsp->asInitial.fHidden = BST_CHECKED;
|
|
}
|
|
|
|
if (dwTriState & FILE_ATTRIBUTE_ARCHIVE)
|
|
{
|
|
pfpsp->asInitial.fArchive = BST_INDETERMINATE;
|
|
}
|
|
else if (dwFlagsAND & FILE_ATTRIBUTE_ARCHIVE)
|
|
{
|
|
pfpsp->asInitial.fArchive = BST_CHECKED;
|
|
}
|
|
|
|
if (dwTriState & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
|
|
{
|
|
pfpsp->asInitial.fIndex = BST_INDETERMINATE;
|
|
}
|
|
else if (!(dwFlagsAND & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
|
|
{
|
|
pfpsp->asInitial.fIndex = BST_CHECKED;
|
|
}
|
|
|
|
if (dwTriState & FILE_ATTRIBUTE_COMPRESSED)
|
|
{
|
|
pfpsp->asInitial.fCompress = BST_INDETERMINATE;
|
|
}
|
|
else if (dwFlagsAND & FILE_ATTRIBUTE_COMPRESSED)
|
|
{
|
|
pfpsp->asInitial.fCompress = BST_CHECKED;
|
|
}
|
|
|
|
if (dwTriState & FILE_ATTRIBUTE_ENCRYPTED)
|
|
{
|
|
pfpsp->asInitial.fEncrypt = BST_INDETERMINATE;
|
|
}
|
|
else if (dwFlagsAND & FILE_ATTRIBUTE_ENCRYPTED)
|
|
{
|
|
pfpsp->asInitial.fEncrypt = BST_CHECKED;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Updates the size fields for single and multiple file property sheets.
|
|
//
|
|
// NOTE: if you have the the WIN32_FIND_DATA already, then pass it for perf
|
|
//
|
|
STDAPI_(void) UpdateSizeField(FILEPROPSHEETPAGE* pfpsp, WIN32_FIND_DATA* pfd)
|
|
{
|
|
WIN32_FIND_DATA wfd;
|
|
|
|
if (pfpsp->pfci->fMultipleFiles)
|
|
{
|
|
// multiple selection case
|
|
// create the size and # of files thread
|
|
CreateSizeThread(pfpsp);
|
|
}
|
|
else
|
|
{
|
|
// if the caller didn't pass pfd, then go get the WIN32_FIND_DATA now
|
|
if (!pfd)
|
|
{
|
|
HANDLE hFind = FindFirstFile(pfpsp->szPath, &wfd);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
// if this failed we should clear out all the values as to not show garbage on the screen.
|
|
ZeroMemory(&wfd, sizeof(wfd));
|
|
}
|
|
else
|
|
{
|
|
FindClose(hFind);
|
|
}
|
|
|
|
pfd = &wfd;
|
|
}
|
|
|
|
if (pfpsp->fMountedDrive)
|
|
{
|
|
// mounted drive case
|
|
SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfd->ftCreationTime);
|
|
}
|
|
else if (pfpsp->fIsDirectory)
|
|
{
|
|
// single folder case, in the UI we call this "Modified"
|
|
// but since NTFS updates ftModified when the contents of the
|
|
// folder changes (FAT does not) we use ftCreationTime as the
|
|
// stable end user notiion of "Modified"
|
|
SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfd->ftCreationTime);
|
|
|
|
// create the size and # of files thread
|
|
CreateSizeThread(pfpsp);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szNum1[MAX_COMMA_AS_K_SIZE];
|
|
TCHAR szNum2[MAX_COMMA_NUMBER_SIZE];
|
|
ULARGE_INTEGER ulSize = { pfd->nFileSizeLow, pfd->nFileSizeHigh };
|
|
DWORD dwClusterSize = PathGetClusterSize(pfpsp->szPath);
|
|
|
|
// fill in the "Size:" field
|
|
LPTSTR pszFmt = ShellConstructMessageString(HINST_THISDLL,
|
|
MAKEINTRESOURCE(ulSize.QuadPart ? IDS_SIZEANDBYTES : IDS_SIZE),
|
|
ShortSizeFormat64(ulSize.QuadPart, szNum1, ARRAYSIZE(szNum1)),
|
|
AddCommas64(ulSize.QuadPart, szNum2, ARRAYSIZE(szNum2)));
|
|
if (pszFmt)
|
|
{
|
|
SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE, pszFmt);
|
|
LocalFree(pszFmt);
|
|
}
|
|
|
|
//
|
|
// fill in the "Size on disk:" field
|
|
//
|
|
if (pfd->dwFileAttributes & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE))
|
|
{
|
|
// the file is compressed or sparse, so for "size on disk" use the compressed size
|
|
ulSize.LowPart = SHGetCompressedFileSize(pfpsp->szPath, &ulSize.HighPart);
|
|
}
|
|
else
|
|
{
|
|
// the file isint comrpessed so just round to the cluster size for the "size on disk"
|
|
ulSize.LowPart = pfd->nFileSizeLow;
|
|
ulSize.HighPart = pfd->nFileSizeHigh;
|
|
ulSize.QuadPart = ROUND_TO_CLUSTER(ulSize.QuadPart, dwClusterSize);
|
|
}
|
|
|
|
pszFmt = ShellConstructMessageString(HINST_THISDLL,
|
|
MAKEINTRESOURCE(ulSize.QuadPart ? IDS_SIZEANDBYTES : IDS_SIZE),
|
|
ShortSizeFormat64(ulSize.QuadPart, szNum1, ARRAYSIZE(szNum1)),
|
|
AddCommas64(ulSize.QuadPart, szNum2, ARRAYSIZE(szNum2)));
|
|
if (pszFmt && !pfpsp->fMountedDrive)
|
|
{
|
|
SetDlgItemText(pfpsp->hDlg, IDD_FILESIZE_COMPRESSED, pszFmt);
|
|
LocalFree(pszFmt);
|
|
}
|
|
|
|
//
|
|
// we always touch the file in the process of getting its info, so the
|
|
// ftLastAccessTime is always TODAY, which makes this field pretty useless...
|
|
|
|
// date and time
|
|
SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfd->ftCreationTime);
|
|
SetDateTimeText(pfpsp->hDlg, IDD_LASTMODIFIED, &pfd->ftLastWriteTime);
|
|
{
|
|
// FAT implementation doesn't support last accessed time (gets the date right, but not the time),
|
|
// so we won't display it
|
|
DWORD dwFlags = FDTF_LONGDATE | FDTF_RELATIVE;
|
|
|
|
if (NULL == StrStrI(pfpsp->szFileSys, TEXT("FAT")))
|
|
dwFlags |= FDTF_LONGTIME; // for non FAT file systems
|
|
|
|
SetDateTimeTextEx(pfpsp->hDlg, IDD_LASTACCESSED, &pfd->ftLastAccessTime, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Descriptions:
|
|
// This function fills fields of the multiple object property sheet.
|
|
//
|
|
BOOL InitMultiplePrsht(FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
SHFILEINFO sfi;
|
|
TCHAR szBuffer[MAX_PATH+1];
|
|
BOOL fMultipleType = FALSE;
|
|
BOOL fSameLocation = TRUE;
|
|
DWORD dwFlagsOR = 0; // start all clear
|
|
DWORD dwFlagsAND = (DWORD)-1; // start all set
|
|
DWORD dwVolumeFlagsAND = (DWORD)-1; // start all set
|
|
|
|
TCHAR szType[MAX_PATH];
|
|
TCHAR szDirPath[MAX_PATH];
|
|
szDirPath[0] = 0;
|
|
szType[0] = 0;
|
|
|
|
// For all the selected files compare their types and get their attribs
|
|
for (int iItem = 0; HIDA_FillFindData(pfpsp->pfci->hida, iItem, szBuffer, NULL, FALSE); iItem++)
|
|
{
|
|
DWORD dwFileAttributes = GetFileAttributes(szBuffer);
|
|
|
|
dwFlagsAND &= dwFileAttributes;
|
|
dwFlagsOR |= dwFileAttributes;
|
|
|
|
// process types only if we haven't already found that there are several types
|
|
if (!fMultipleType)
|
|
{
|
|
SHGetFileInfo((LPTSTR)IDA_GetIDListPtr((LPIDA)GlobalLock(pfpsp->pfci->hida), iItem), 0,
|
|
&sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_TYPENAME);
|
|
|
|
if (szType[0] == 0)
|
|
lstrcpyn(szType, sfi.szTypeName, ARRAYSIZE(szType));
|
|
else
|
|
fMultipleType = lstrcmp(szType, sfi.szTypeName) != 0;
|
|
}
|
|
|
|
dwVolumeFlagsAND &= GetVolumeFlags(szBuffer, pfpsp->szFileSys, ARRAYSIZE(pfpsp->szFileSys));
|
|
// check to see if the files are in the same location
|
|
if (fSameLocation)
|
|
{
|
|
PathRemoveFileSpec(szBuffer);
|
|
|
|
if (szDirPath[0] == 0)
|
|
lstrcpyn(szDirPath, szBuffer, ARRAYSIZE(szDirPath));
|
|
else
|
|
fSameLocation = (lstrcmpi(szDirPath, szBuffer) == 0);
|
|
}
|
|
}
|
|
|
|
if ((dwVolumeFlagsAND & FS_FILE_ENCRYPTION) && !SHRestricted(REST_NOENCRYPTION))
|
|
{
|
|
// all the files are on volumes that support encryption (eg NTFS)
|
|
pfpsp->fIsEncryptionAvailable = TRUE;
|
|
}
|
|
|
|
if (dwVolumeFlagsAND & FS_FILE_COMPRESSION)
|
|
{
|
|
pfpsp->pfci->fIsCompressionAvailable = TRUE;
|
|
}
|
|
|
|
//
|
|
// HACK (reinerf) - we dont have a FS_SUPPORTS_INDEXING so we
|
|
// use the FILE_SUPPORTS_SPARSE_FILES flag, because native index support
|
|
// appeared first on NTFS5 volumes, at the same time sparse file support
|
|
// was implemented.
|
|
//
|
|
if (dwVolumeFlagsAND & FILE_SUPPORTS_SPARSE_FILES)
|
|
{
|
|
// yup, we are on NTFS5 or greater
|
|
pfpsp->fIsIndexAvailable = TRUE;
|
|
}
|
|
|
|
// if any of the files was a directory, then we set this flag
|
|
if (dwFlagsOR & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
pfpsp->fIsDirectory = TRUE;
|
|
}
|
|
|
|
// setup all the flags based on what we found out
|
|
SetInitialFileAttribs(pfpsp, dwFlagsAND, dwFlagsOR);
|
|
|
|
// set the current attributes to the same as the initial
|
|
pfpsp->asCurrent = pfpsp->asInitial;
|
|
|
|
//
|
|
// now setup all the controls on the dialog based on the attribs
|
|
// that we have
|
|
//
|
|
|
|
// check for multiple file types
|
|
if (fMultipleType)
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_MULTIPLETYPES, szBuffer, ARRAYSIZE(szBuffer));
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_ALLOFTYPE, szBuffer, ARRAYSIZE(szBuffer));
|
|
StrCatBuff(szBuffer, szType, ARRAYSIZE(szBuffer));
|
|
}
|
|
SetDlgItemText(pfpsp->hDlg, IDD_FILETYPE, szBuffer);
|
|
|
|
if (fSameLocation)
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_ALLIN, szBuffer, ARRAYSIZE(szBuffer));
|
|
StrCatBuff(szBuffer, szDirPath, ARRAYSIZE(szBuffer));
|
|
lstrcpyn(pfpsp->szPath, szDirPath, ARRAYSIZE(pfpsp->szPath));
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_VARFOLDERS, szBuffer, ARRAYSIZE(szBuffer));
|
|
}
|
|
|
|
//Keep Functionality same as NT4 by avoiding PathCompactPath.
|
|
SetDlgItemTextWithToolTip(pfpsp->hDlg, IDD_LOCATION, szBuffer, &pfpsp->hwndTip);
|
|
|
|
//
|
|
// check the ReadOnly and Hidden checkboxes, they always appear on the general tab
|
|
//
|
|
if (pfpsp->asInitial.fReadOnly == BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(pfpsp->hDlg, IDD_READONLY, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
CheckDlgButton(pfpsp->hDlg, IDD_READONLY, pfpsp->asCurrent.fReadOnly);
|
|
|
|
if (pfpsp->asInitial.fHidden == BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(pfpsp->hDlg, IDD_HIDDEN, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
CheckDlgButton(pfpsp->hDlg, IDD_HIDDEN, pfpsp->asCurrent.fHidden);
|
|
|
|
// to avoid people making SYSTEM files HIDDEN (SYSTEM HIDDEN files are
|
|
// never show to the user) we don't let people make SYSTEM files HIDDEN
|
|
if (dwFlagsOR & FILE_ATTRIBUTE_SYSTEM)
|
|
EnableWindow(GetDlgItem(pfpsp->hDlg, IDD_HIDDEN), FALSE);
|
|
|
|
// Archive is only on the general tab for FAT, otherwise it is under the "Advanced attributes"
|
|
// and FAT volumes dont have the "Advanced attributes" button.
|
|
if (pfpsp->pfci->fIsCompressionAvailable || pfpsp->fIsEncryptionAvailable)
|
|
{
|
|
// if compression is available, then we must be on NTFS
|
|
DestroyWindow(GetDlgItem(pfpsp->hDlg, IDD_ARCHIVE));
|
|
}
|
|
else
|
|
{
|
|
// we are on FAT/FAT32, so get rid of the "Advanced attributes" button, and set the inital Archive state
|
|
DestroyWindow(GetDlgItem(pfpsp->hDlg, IDC_ADVANCED));
|
|
|
|
if (pfpsp->asInitial.fArchive == BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(pfpsp->hDlg, IDD_ARCHIVE, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
CheckDlgButton(pfpsp->hDlg, IDD_ARCHIVE, pfpsp->asCurrent.fArchive);
|
|
}
|
|
|
|
UpdateSizeField(pfpsp, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void Free_DlgDependentFilePropSheetPage(FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
// this frees the members that are dependent on pfpsp->hDlg still
|
|
// being valid
|
|
|
|
if (pfpsp)
|
|
{
|
|
ASSERT(IsWindow(pfpsp->hDlg)); // our window had better still be valid!
|
|
|
|
ReplaceDlgIcon(pfpsp->hDlg, IDD_ITEMICON, NULL);
|
|
|
|
if (pfpsp->pfci && !pfpsp->pfci->fMultipleFiles)
|
|
{
|
|
// single-file specific members
|
|
if (!pfpsp->fIsDirectory)
|
|
{
|
|
// cleanup the typeicon for non-folders
|
|
ReplaceDlgIcon(pfpsp->hDlg, IDD_TYPEICON, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Free_DlgIndepFilePropSheetPage(FILEPROPSHEETPAGE *pfpsp)
|
|
{
|
|
if (pfpsp)
|
|
{
|
|
IAssocStore* pas = (IAssocStore *)pfpsp->pAssocStore;
|
|
if (pas)
|
|
{
|
|
delete pas;
|
|
pfpsp->pAssocStore = NULL;
|
|
}
|
|
|
|
Release_FolderContentsInfo(pfpsp->pfci);
|
|
pfpsp->pfci = NULL;
|
|
|
|
ILFree(pfpsp->pidl);
|
|
pfpsp->pidl = NULL;
|
|
|
|
ILFree(pfpsp->pidlTarget);
|
|
pfpsp->pidlTarget = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Descriptions:
|
|
// Callback for the property sheet code
|
|
//
|
|
UINT CALLBACK FilePrshtCallback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
|
|
{
|
|
if (uMsg == PSPCB_RELEASE)
|
|
{
|
|
FILEPROPSHEETPAGE * pfpsp = (FILEPROPSHEETPAGE *)ppsp;
|
|
|
|
// Careful! pfpsp can be NULL in low memory situations
|
|
if (pfpsp)
|
|
{
|
|
KillSizeThread(pfpsp);
|
|
Free_DlgIndepFilePropSheetPage(pfpsp);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Opens the file for compression. It handles the case where a READONLY
|
|
// file is trying to be compressed or uncompressed. Since read only files
|
|
// cannot be opened for WRITE_DATA, it temporarily resets the file to NOT
|
|
// be READONLY in order to open the file, and then sets it back once the
|
|
// file has been compressed.
|
|
//
|
|
// Taken from WinFile module wffile.c without change. Originally from
|
|
// G. Kimura's compact.c. Now taken from shcompui without change.
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// phFile
|
|
// Address of file handle variable for handle of open file if
|
|
// successful.
|
|
//
|
|
// szFile
|
|
// Name string of file to be opened.
|
|
//
|
|
// RETURNS:
|
|
//
|
|
// TRUE = File successfully opened. Handle in *phFile.
|
|
// FALSE = File couldn't be opened. *phFile == INVALID_HANDLE_VALUE
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL OpenFileForCompress(HANDLE *phFile, LPCTSTR szFile)
|
|
{
|
|
//
|
|
// Try to open the file - READ_DATA | WRITE_DATA.
|
|
//
|
|
if ((*phFile = CreateFile(szFile,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL)) != INVALID_HANDLE_VALUE)
|
|
{
|
|
//
|
|
// Successfully opened the file.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
if (GetLastError() != ERROR_ACCESS_DENIED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Try to open the file - READ_ATTRIBUTES | WRITE_ATTRIBUTES.
|
|
//
|
|
HANDLE hAttr = CreateFile(szFile,
|
|
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
if (hAttr == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// See if the READONLY attribute is set.
|
|
//
|
|
BY_HANDLE_FILE_INFORMATION fi;
|
|
if ((!GetFileInformationByHandle(hAttr, &fi)) ||
|
|
(!(fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)))
|
|
{
|
|
//
|
|
// If the file could not be open for some reason other than that
|
|
// the readonly attribute was set, then fail.
|
|
//
|
|
CloseHandle(hAttr);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Turn OFF the READONLY attribute.
|
|
//
|
|
fi.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
|
|
if (!SetFileAttributes(szFile, fi.dwFileAttributes))
|
|
{
|
|
CloseHandle(hAttr);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Try again to open the file - READ_DATA | WRITE_DATA.
|
|
//
|
|
*phFile = CreateFile(szFile,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
//
|
|
// Close the file handle opened for READ_ATTRIBUTE | WRITE_ATTRIBUTE.
|
|
//
|
|
CloseHandle(hAttr);
|
|
|
|
//
|
|
// Make sure the open succeeded. If it still couldn't be opened with
|
|
// the readonly attribute turned off, then fail.
|
|
//
|
|
if (*phFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Turn the READONLY attribute back ON.
|
|
//
|
|
fi.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
|
|
if (!SetFileAttributes(szFile, fi.dwFileAttributes))
|
|
{
|
|
CloseHandle(*phFile);
|
|
*phFile = INVALID_HANDLE_VALUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Return success. A valid file handle is in *phFile.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
// One half second (500 ms = 0.5s)
|
|
#define ENCRYPT_RETRY_PERIOD 500
|
|
// Retry 4 times (at least 2s)
|
|
#define ENCRYPT_MAX_RETRIES 4
|
|
|
|
//
|
|
// This function encrypts/decrypts a file. If the readonly bit is set, the
|
|
// function will clear it and encrypt/decrypt and then set the RO bit back
|
|
// We will also remove/replace the system bit for known encryptable system
|
|
// files
|
|
//
|
|
// szPath a string that has the full path to the file
|
|
// fCompress TRUE - compress the file
|
|
// FALSE - decompress the file
|
|
//
|
|
//
|
|
// return: TRUE - the file was sucessfully encryped/decryped
|
|
// FALSE - the file could not be encryped/decryped
|
|
//
|
|
STDAPI_(BOOL) SHEncryptFile(LPCTSTR pszPath, BOOL fEncrypt)
|
|
{
|
|
BOOL bRet = fEncrypt ? EncryptFile(pszPath) : DecryptFile(pszPath, 0);
|
|
|
|
if (!bRet)
|
|
{
|
|
DWORD dwLastError = GetLastError();
|
|
DWORD dwAttribs = GetFileAttributes(pszPath);
|
|
|
|
// Check to see if the attributes are blocking the encryption and we can change them
|
|
if (dwAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))
|
|
{
|
|
BOOL fStripAttribs = TRUE;
|
|
if (dwAttribs & FILE_ATTRIBUTE_SYSTEM)
|
|
{
|
|
fStripAttribs = FALSE;
|
|
|
|
// We can only strip attributes if it is a know encryptable system file
|
|
WCHAR szStream[MAX_PATH + 13];
|
|
StrCpyNW(szStream, pszPath, ARRAYSIZE(szStream));
|
|
StrCatW(szStream, TEXT(":encryptable"));
|
|
|
|
HANDLE hStream = CreateFile(szStream, GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL);
|
|
if (hStream != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hStream);
|
|
fStripAttribs = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fStripAttribs)
|
|
{
|
|
if (SetFileAttributes(pszPath, dwAttribs & ~FILE_ATTRIBUTE_READONLY & ~FILE_ATTRIBUTE_SYSTEM))
|
|
{
|
|
int i = 0;
|
|
while (!bRet && i < ENCRYPT_MAX_RETRIES)
|
|
{
|
|
bRet = fEncrypt ? EncryptFile(pszPath) : DecryptFile(pszPath, 0);
|
|
if (!bRet)
|
|
{
|
|
i++;
|
|
Sleep(ENCRYPT_RETRY_PERIOD);
|
|
}
|
|
}
|
|
SetFileAttributes(pszPath, dwAttribs);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we failed after all this, make sure we return the right error code
|
|
if (!bRet)
|
|
{
|
|
ASSERT(dwLastError != ERROR_SUCCESS);
|
|
SetLastError(dwLastError);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// This function compresses/uncompresses a file.
|
|
//
|
|
// szPath a string that has the full path to the file
|
|
// fCompress TRUE - compress the file
|
|
// FALSE - decompress the file
|
|
//
|
|
//
|
|
// return: TRUE - the file was sucessfully compressed/uncompressed
|
|
// FALSE - the file could not be compressed/uncompressed
|
|
//
|
|
BOOL CompressFile(LPCTSTR pzsPath, BOOL fCompress)
|
|
{
|
|
DWORD dwAttribs = GetFileAttributes(pzsPath);
|
|
|
|
if (dwAttribs & FILE_ATTRIBUTE_ENCRYPTED)
|
|
{
|
|
// We will fail to compress/decompress the file if is encryped. We don't want
|
|
// to bother the user w/ error messages in this case (since encryption "takes
|
|
// presidence" over compression), so we just return success.
|
|
return TRUE;
|
|
}
|
|
|
|
HANDLE hFile;
|
|
if (OpenFileForCompress(&hFile, pzsPath))
|
|
{
|
|
USHORT uState = fCompress ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
|
|
ULONG Length;
|
|
BOOL bRet = DeviceIoControl(hFile,
|
|
FSCTL_SET_COMPRESSION,
|
|
&uState,
|
|
sizeof(USHORT),
|
|
NULL,
|
|
0,
|
|
&Length,
|
|
FALSE);
|
|
CloseHandle(hFile);
|
|
return bRet;
|
|
}
|
|
else
|
|
{
|
|
// couldnt get a file handle
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL IsValidFileName(LPCTSTR pszFileName)
|
|
{
|
|
if (!pszFileName || !pszFileName[0])
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LPCTSTR psz = pszFileName;
|
|
do
|
|
{
|
|
// we are only passed the file name, so its ok to use PIVC_LFN_NAME
|
|
if (!PathIsValidChar(*psz, PIVC_LFN_NAME))
|
|
{
|
|
// found a non-legal character
|
|
return FALSE;
|
|
}
|
|
|
|
psz = CharNext(psz);
|
|
}
|
|
while (*psz);
|
|
|
|
// didn't find any illegal characters
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// renames the file, or checks to see if it could be renamed if fCommit == FALSE
|
|
BOOL ApplyRename(FILEPROPSHEETPAGE* pfpsp, BOOL fCommit)
|
|
{
|
|
ASSERT(pfpsp->fRename);
|
|
|
|
TCHAR szNewName[MAX_PATH];
|
|
Edit_GetText(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), szNewName, ARRAYSIZE(szNewName));
|
|
|
|
if (StrCmpC(pfpsp->szInitialName, szNewName) != 0)
|
|
{
|
|
// the name could be changed from C:\foo.txt to C:\FOO.txt, this is
|
|
// technically the same name to PathFileExists, but we should allow it
|
|
// anyway
|
|
BOOL fCaseChange = (lstrcmpi(pfpsp->szInitialName, szNewName) == 0);
|
|
|
|
// get the dir where the file lives
|
|
TCHAR szDir[MAX_PATH];
|
|
lstrcpyn(szDir, pfpsp->szPath, ARRAYSIZE(szDir));
|
|
PathRemoveFileSpec(szDir);
|
|
|
|
// find out the old name with the extension (we cant use pfpsp->szInitialName here,
|
|
// because it might not have had the extension depending on the users view|options settings)
|
|
LPCTSTR pszOldName = PathFindFileName(pfpsp->szPath);
|
|
|
|
if (!pfpsp->fShowExtension)
|
|
{
|
|
// the extension is hidden, so add it to the new path the user typed
|
|
LPCTSTR pszExt = PathFindExtension(pfpsp->szPath);
|
|
if (*pszExt)
|
|
{
|
|
// Note that we can't call PathAddExtension, because it removes the existing extension.
|
|
lstrcatn(szNewName, pszExt, ARRAYSIZE(szNewName));
|
|
}
|
|
}
|
|
|
|
// is this a test or is it the real thing? (test needed so we can put up error UI before we get
|
|
// the PSN_LASTCHANCEAPPLY)
|
|
if (fCommit)
|
|
{
|
|
if (SHRenameFileEx(pfpsp->hDlg, NULL, szDir, pszOldName, szNewName) == ERROR_SUCCESS)
|
|
{
|
|
SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_FLUSH | SHCNF_PATH, pszOldName, szNewName);
|
|
}
|
|
else
|
|
{
|
|
return FALSE; // dont need error ui because SHRenameFile takes care of that for us.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TCHAR szNewPath[MAX_PATH];
|
|
PathCombine(szNewPath, szDir, szNewName);
|
|
|
|
if (!IsValidFileName(szNewName) || (PathFileExists(szNewPath) && !fCaseChange))
|
|
{
|
|
LRESULT lRet = SHRenameFileEx(pfpsp->hDlg, NULL, szDir, pszOldName, szNewName);
|
|
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
// Whoops, I guess we really CAN rename the file (this case can happen if the user
|
|
// tries to add a whole bunch of .'s to the end of a folder name).
|
|
|
|
// Rename it back so we can succeed when we call this fn. again with fCommit = TRUE;
|
|
lRet = SHRenameFileEx(NULL, NULL, szDir, szNewName, pszOldName);
|
|
ASSERT(lRet == ERROR_SUCCESS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// SHRenameFileEx put up the error UI for us, so just return false.
|
|
return FALSE;
|
|
}
|
|
}
|
|
// we dont bother doing anything if the rename succeeded since we only do renames
|
|
// if the dialog is about to close (user hit "ok")
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// this is the dlg proc for Attribute Errors
|
|
//
|
|
// returns
|
|
//
|
|
// IDCANCEL - user clicked abort
|
|
// IDRETRY - user clicked retry
|
|
// IDIGNORE - user clicked ignore
|
|
// IDIGNOREALL - user clikced ignore all
|
|
//
|
|
BOOL_PTR CALLBACK FailedApplyAttribDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
ATTRIBUTEERROR* pae = (ATTRIBUTEERROR*)lParam;
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
lstrcpyn(szPath, pae->pszPath, ARRAYSIZE(szPath));
|
|
|
|
// Modify very long path names so that they fit into the message box.
|
|
// get the size of the text boxes
|
|
RECT rc;
|
|
GetWindowRect(GetDlgItem(hDlg, IDD_NAME), &rc);
|
|
PathCompactPath(NULL, szPath, rc.right - rc.left);
|
|
|
|
SetDlgItemText(hDlg, IDD_NAME, szPath);
|
|
|
|
// Default message if FormatMessage doesn't recognize dwLastError
|
|
TCHAR szTemplate[MAX_PATH];
|
|
LoadString(HINST_THISDLL, IDS_UNKNOWNERROR, szTemplate, ARRAYSIZE(szTemplate));
|
|
TCHAR szErrorMsg[MAX_PATH];
|
|
wnsprintf(szErrorMsg, ARRAYSIZE(szErrorMsg), szTemplate, pae->dwLastError);
|
|
|
|
// Try the system error message
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, pae->dwLastError, 0, szErrorMsg, ARRAYSIZE(szErrorMsg), NULL);
|
|
|
|
SetDlgItemText(hDlg, IDD_ERROR_TXT, szErrorMsg);
|
|
EnableWindow(hDlg, TRUE);
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
UINT uCtrlID = GET_WM_COMMAND_ID(wParam, lParam);
|
|
switch (uCtrlID)
|
|
{
|
|
case IDIGNOREALL: // = 10 (this comes from shell32.rc, the rest come from winuser.h)
|
|
case IDCANCEL: // = 2
|
|
case IDRETRY: // = 4
|
|
case IDIGNORE: // = 5
|
|
EndDialog(hDlg, uCtrlID);
|
|
return TRUE;
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// This function displays the "and error has occured [abort] [retry] [ignore] [ignore all]" message
|
|
// If the user hits abort, then we return FALSE so that our caller knows to abort the operation
|
|
//
|
|
// returns the id of the button pressed (one of: IDIGNOREALL, IDIGNORE, IDCANCEL, IDRETRY)
|
|
//
|
|
int FailedApplyAttribsErrorDlg(HWND hWndParent, ATTRIBUTEERROR* pae)
|
|
{
|
|
// Put up the error message box - ABORT, RETRY, IGNORE, IGNORE ALL.
|
|
int iRet = (int)DialogBoxParam(HINST_THISDLL,
|
|
MAKEINTRESOURCE(DLG_ATTRIBS_ERROR),
|
|
hWndParent,
|
|
FailedApplyAttribDlgProc,
|
|
(LPARAM)pae);
|
|
//
|
|
// if the user hits the ESC key or the little X thingy, then
|
|
// iRet = 0, so we set iRet = IDCANCEL
|
|
//
|
|
if (!iRet)
|
|
{
|
|
iRet = IDCANCEL;
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
//
|
|
// we check to see if this is a known bad file that we skip applying attribs to
|
|
//
|
|
BOOL IsBadAttributeFile(LPCTSTR pszFile, FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
const static LPTSTR s_rgszBadFiles[] = {
|
|
{TEXT("pagefile.sys")},
|
|
{TEXT("hiberfil.sys")},
|
|
{TEXT("ntldr")},
|
|
{TEXT("ntdetect.com")},
|
|
{TEXT("explorer.exe")},
|
|
{TEXT("System Volume Information")},
|
|
{TEXT("cmldr")},
|
|
{TEXT("desktop.ini")},
|
|
{TEXT("ntuser.dat")},
|
|
{TEXT("ntuser.dat.log")},
|
|
{TEXT("ntuser.pol")},
|
|
{TEXT("usrclass.dat")},
|
|
{TEXT("usrclass.dat.log")}};
|
|
|
|
LPTSTR pszFileName = PathFindFileName(pszFile);
|
|
for (int i = 0; i < ARRAYSIZE(s_rgszBadFiles); i++)
|
|
{
|
|
if (lstrcmpi(s_rgszBadFiles[i], pszFileName) == 0)
|
|
{
|
|
// this file matched on of the "bad" files that we dont apply attributes to
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// ok to muck with this file
|
|
return FALSE;
|
|
}
|
|
|
|
// This is the encryption warning callback dlg proc
|
|
|
|
BOOL_PTR CALLBACK EncryptionWarningDlgProc(HWND hDlgWarning, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LPCTSTR pszPath = (LPCTSTR)lParam;
|
|
|
|
SetWindowPtr(hDlgWarning, DWLP_USER, (void*) pszPath);
|
|
|
|
// set the initial state of the radio buttons
|
|
CheckDlgButton(hDlgWarning, IDC_ENCRYPT_PARENTFOLDER, TRUE);
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
if ((LOWORD(wParam) == IDOK) && (IsDlgButtonChecked(hDlgWarning, IDC_ENCRYPT_PARENTFOLDER) == BST_CHECKED))
|
|
{
|
|
LPTSTR pszPath = (LPTSTR) GetWindowPtr(hDlgWarning, DWLP_USER);
|
|
|
|
if (pszPath)
|
|
{
|
|
LPITEMIDLIST pidl = ILCreateFromPath(pszPath);
|
|
|
|
if (pidl)
|
|
{
|
|
SHChangeNotifySuspendResume(TRUE, pidl, TRUE, 0);
|
|
}
|
|
|
|
RetryEncryptParentFolder:
|
|
if (!SHEncryptFile(pszPath, TRUE))
|
|
{
|
|
ATTRIBUTEERROR ae = {pszPath, GetLastError()};
|
|
|
|
if (FailedApplyAttribsErrorDlg(hDlgWarning, &ae) == IDRETRY)
|
|
{
|
|
goto RetryEncryptParentFolder;
|
|
}
|
|
}
|
|
|
|
if (pidl)
|
|
{
|
|
SHChangeNotifySuspendResume(FALSE, pidl, TRUE, 0);
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// we want the MessageBoxCheckExDlgProc have a crack at everything as well,
|
|
// so return false here
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This function warns the user that they are encrypting a file that is not in and encrypted
|
|
// folder. Most editors (MS word included), do a "safe-save" where they rename the file being
|
|
// edited, and then save the new modified version out, and then delete the old original. This
|
|
// causes an encrypted document that is NOT in an encrypted folder to become decrypted so we
|
|
// warn the user here.
|
|
//
|
|
// returns:
|
|
// TRUE - the user hit "ok" (either compress just the file, or the parent folder as well)
|
|
// FALSE - the user hit "cancel"
|
|
//
|
|
int WarnUserAboutDecryptedParentFolder(LPCTSTR pszPath, HWND hWndParent)
|
|
{
|
|
// check for the root case (no parent), or the directory case
|
|
if (PathIsRoot(pszPath) || PathIsDirectory(pszPath))
|
|
return TRUE;
|
|
|
|
int iRet = IDOK; // assume everything is okidokey
|
|
|
|
// first check to see if the parent folder is encrypted
|
|
TCHAR szParentFolder[MAX_PATH];
|
|
lstrcpyn(szParentFolder, pszPath, ARRAYSIZE(szParentFolder));
|
|
PathRemoveFileSpec(szParentFolder);
|
|
|
|
DWORD dwAttribs = GetFileAttributes(szParentFolder);
|
|
if ((dwAttribs != (DWORD)-1) && !(dwAttribs & FILE_ATTRIBUTE_ENCRYPTED) && !PathIsRoot(szParentFolder))
|
|
{
|
|
// the parent folder is NOT encrypted and the parent folder isin't the root, so warn the user
|
|
iRet = SHMessageBoxCheckEx(hWndParent, HINST_THISDLL, MAKEINTRESOURCE(DLG_ENCRYPTWARNING), EncryptionWarningDlgProc,
|
|
(void *)szParentFolder, IDOK, TEXT("EncryptionWarning"));
|
|
}
|
|
|
|
return (iRet == IDOK);
|
|
}
|
|
|
|
//
|
|
// Sets attributes of a file based on the info in pfpsp
|
|
//
|
|
// szFilename - the name of the file to compress
|
|
//
|
|
// pfpsp - the filepropsheetpage info
|
|
//
|
|
// hwndParent - Parent hwnd in case we need to put up some ui
|
|
//
|
|
// pbSomethingChanged - pointer to a bool that says whether or not something actually was
|
|
// changed during the operation.
|
|
// TRUE - we applied at leaset one attribute
|
|
// FALSE - we didnt change anything (either an error or all the attribs already matched)
|
|
//
|
|
// return value: TRUE - the operation was sucessful
|
|
// FALSE - there was an error and the user hit cancel to abort the operation
|
|
//
|
|
//
|
|
// NOTE: the caller of this function must take care of generating the SHChangeNotifies so that
|
|
// we dont end up blindly sending them for every file in a dir (the caller will send
|
|
// one for just that dir). That is why we have the pbSomethingChanged variable.
|
|
//
|
|
STDAPI_(BOOL) ApplyFileAttributes(LPCTSTR pszPath, FILEPROPSHEETPAGE* pfpsp, HWND hwndParent, BOOL* pbSomethingChanged)
|
|
{
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
BOOL bCallSetFileAttributes = FALSE;
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
// assume nothing changed to start with
|
|
*pbSomethingChanged = 0;
|
|
|
|
if ((pfpsp->fRecursive || pfpsp->pfci->fMultipleFiles) && IsBadAttributeFile(pszPath, pfpsp))
|
|
{
|
|
// we are doing a recursive operation or a multiple file operation, so we skip files
|
|
// that we dont want to to mess with because they will ususally give error dialogs
|
|
if (pfpsp->pProgressDlg)
|
|
{
|
|
// since we are skipping this file, we subtract its size from both
|
|
// ulTotal and ulCompleted. This will make sure the progress bar isint
|
|
// messed up by files like pagefile.sys who are huge but get "compressed"
|
|
// in milliseconds.
|
|
ULARGE_INTEGER ulTemp;
|
|
|
|
ulTemp.LowPart = pfpsp->fd.nFileSizeLow;
|
|
ulTemp.HighPart = pfpsp->fd.nFileSizeHigh;
|
|
|
|
// guard against underflow
|
|
if (pfpsp->ulNumberOfBytesDone.QuadPart < ulTemp.QuadPart)
|
|
{
|
|
pfpsp->ulNumberOfBytesDone.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
pfpsp->ulNumberOfBytesDone.QuadPart -= ulTemp.QuadPart;
|
|
}
|
|
|
|
pfpsp->pfci->ulTotalNumberOfBytes.QuadPart -= ulTemp.QuadPart;
|
|
|
|
UpdateProgressBar(pfpsp);
|
|
}
|
|
|
|
// return telling the user everying is okidokey
|
|
return TRUE;
|
|
}
|
|
|
|
RetryApplyAttribs:
|
|
DWORD dwInitialAttributes = GetFileAttributes(pszPath);
|
|
|
|
if (dwInitialAttributes == -1)
|
|
{
|
|
// we were unable to get the file attribues, doh!
|
|
dwLastError = GetLastError();
|
|
goto RaiseErrorMsg;
|
|
}
|
|
|
|
if (pfpsp->pProgressDlg)
|
|
{
|
|
// update the progress dialog file name
|
|
SetProgressDlgPath(pfpsp, pszPath, TRUE);
|
|
}
|
|
|
|
//
|
|
// we only allow attribs that SetFileAttributes can handle
|
|
//
|
|
DWORD dwNewAttributes = (dwInitialAttributes & (FILE_ATTRIBUTE_READONLY |
|
|
FILE_ATTRIBUTE_HIDDEN |
|
|
FILE_ATTRIBUTE_ARCHIVE |
|
|
FILE_ATTRIBUTE_OFFLINE |
|
|
FILE_ATTRIBUTE_SYSTEM |
|
|
FILE_ATTRIBUTE_TEMPORARY |
|
|
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED));
|
|
|
|
BOOL bIsSuperHidden = IS_SYSTEM_HIDDEN(dwInitialAttributes);
|
|
|
|
if (pfpsp->asInitial.fReadOnly != pfpsp->asCurrent.fReadOnly)
|
|
{
|
|
// don't allow changing of folders read only bit, since this is a trigger
|
|
// for shell special folder stuff like thumbnails, etc.
|
|
if (!(dwInitialAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
if (pfpsp->asCurrent.fReadOnly)
|
|
{
|
|
dwNewAttributes |= FILE_ATTRIBUTE_READONLY;
|
|
}
|
|
else
|
|
{
|
|
dwNewAttributes &= ~FILE_ATTRIBUTE_READONLY;
|
|
}
|
|
|
|
bCallSetFileAttributes = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// don't allow setting of hidden on system files, as this will make them disappear for good.
|
|
//
|
|
if (pfpsp->asInitial.fHidden != pfpsp->asCurrent.fHidden && !(dwNewAttributes & FILE_ATTRIBUTE_SYSTEM))
|
|
{
|
|
if (pfpsp->asCurrent.fHidden)
|
|
{
|
|
dwNewAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
|
}
|
|
else
|
|
{
|
|
dwNewAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
|
}
|
|
|
|
bCallSetFileAttributes = TRUE;
|
|
}
|
|
|
|
if (pfpsp->asInitial.fArchive != pfpsp->asCurrent.fArchive)
|
|
{
|
|
if (pfpsp->asCurrent.fArchive)
|
|
{
|
|
dwNewAttributes |= FILE_ATTRIBUTE_ARCHIVE;
|
|
}
|
|
else
|
|
{
|
|
dwNewAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
|
|
}
|
|
|
|
bCallSetFileAttributes = TRUE;
|
|
}
|
|
|
|
if (pfpsp->asInitial.fIndex != pfpsp->asCurrent.fIndex)
|
|
{
|
|
if (pfpsp->asCurrent.fIndex)
|
|
{
|
|
dwNewAttributes &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
|
|
}
|
|
else
|
|
{
|
|
dwNewAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
|
|
}
|
|
|
|
bCallSetFileAttributes = TRUE;
|
|
}
|
|
|
|
// did something change that we need to call SetFileAttributes for?
|
|
if (bCallSetFileAttributes)
|
|
{
|
|
if (SetFileAttributes(pszPath, dwNewAttributes))
|
|
{
|
|
// success! set fSomethingChanged so we know to send out
|
|
// a changenotify
|
|
*pbSomethingChanged = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// get the last error value now so we know why it failed
|
|
dwLastError = GetLastError();
|
|
goto RaiseErrorMsg;
|
|
}
|
|
}
|
|
|
|
// We need to be careful about the order we compress/encrypt in since these
|
|
// operations are mutually exclusive.
|
|
// We therefore do the uncompressing/decrypting first
|
|
if ((pfpsp->asInitial.fCompress != pfpsp->asCurrent.fCompress) &&
|
|
(pfpsp->asCurrent.fCompress == BST_UNCHECKED))
|
|
{
|
|
if (!CompressFile(pszPath, FALSE))
|
|
{
|
|
// get the last error value now so we know why it failed
|
|
dwLastError = GetLastError();
|
|
goto RaiseErrorMsg;
|
|
}
|
|
else
|
|
{
|
|
// success
|
|
*pbSomethingChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
if ((pfpsp->asInitial.fEncrypt != pfpsp->asCurrent.fEncrypt) &&
|
|
(pfpsp->asCurrent.fEncrypt == BST_UNCHECKED))
|
|
{
|
|
BOOL fSucceeded = SHEncryptFile(pszPath, FALSE); // try to decrypt the file
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
// get the last error value now so we know why it failed
|
|
dwLastError = GetLastError();
|
|
|
|
if (ERROR_SHARING_VIOLATION == dwLastError)
|
|
{
|
|
// Encrypt/Decrypt needs exclusive access to the file, this is a problem if we
|
|
// initiate encrypt for a folder from Explorer, then most probably the folder will
|
|
// be opened. We don't do "SHChangeNotifySuspendResume" right away for perf reasons,
|
|
// we wait for it to fail and then we try again. (stephstm)
|
|
|
|
ASSERT(pidl == NULL);
|
|
pidl = ILCreateFromPath(pszPath);
|
|
|
|
if (pidl)
|
|
{
|
|
SHChangeNotifySuspendResume(TRUE, pidl, TRUE, 0);
|
|
}
|
|
|
|
// retry to decrypt after the suspend
|
|
fSucceeded = SHEncryptFile(pszPath, FALSE);
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
// get the last error value now so we know why it failed
|
|
dwLastError = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fSucceeded)
|
|
{
|
|
// success
|
|
*pbSomethingChanged = TRUE;
|
|
dwLastError = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(dwLastError != ERROR_SUCCESS);
|
|
goto RaiseErrorMsg;
|
|
}
|
|
}
|
|
|
|
// now check for encrypt/compress
|
|
if ((pfpsp->asInitial.fCompress != pfpsp->asCurrent.fCompress) &&
|
|
(pfpsp->asCurrent.fCompress == BST_CHECKED))
|
|
{
|
|
if (!CompressFile(pszPath, TRUE))
|
|
{
|
|
// get the last error value now so we know why it failed
|
|
dwLastError = GetLastError();
|
|
goto RaiseErrorMsg;
|
|
}
|
|
else
|
|
{
|
|
// success
|
|
*pbSomethingChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
if ((pfpsp->asInitial.fEncrypt != pfpsp->asCurrent.fEncrypt) &&
|
|
(pfpsp->asCurrent.fEncrypt == BST_CHECKED))
|
|
{
|
|
// only prompt for encrypting the parent folder on non-recursive operations
|
|
if (!pfpsp->fRecursive && !WarnUserAboutDecryptedParentFolder(pszPath, hwndParent))
|
|
{
|
|
// user cancled the operation
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL fSucceeded = SHEncryptFile(pszPath, TRUE); // try to encrypt the file
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
// get the last error value now so we know why it failed
|
|
dwLastError = GetLastError();
|
|
|
|
if (ERROR_SHARING_VIOLATION == dwLastError)
|
|
{
|
|
// Encrypt/Decrypt needs exclusive access to the file, this is a problem if we
|
|
// initiate encrypt for a folder from Explorer, then most probably the folder will
|
|
// be opened. We don't do "SHChangeNotifySuspendResume" right away for perf reasons,
|
|
// we wait for it to fail and then we try again. (stephstm)
|
|
|
|
ASSERT(pidl == NULL);
|
|
pidl = ILCreateFromPath(pszPath);
|
|
|
|
if (pidl)
|
|
{
|
|
SHChangeNotifySuspendResume(TRUE, pidl, TRUE, 0);
|
|
}
|
|
|
|
// retry to encrypt after the suspend
|
|
fSucceeded = SHEncryptFile(pszPath, TRUE);
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
// get the last error value now so we know why it failed
|
|
dwLastError = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fSucceeded)
|
|
{
|
|
// success
|
|
*pbSomethingChanged = TRUE;
|
|
dwLastError = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(dwLastError != ERROR_SUCCESS);
|
|
goto RaiseErrorMsg;
|
|
}
|
|
}
|
|
|
|
RaiseErrorMsg:
|
|
|
|
if (pidl)
|
|
{
|
|
SHChangeNotifySuspendResume(FALSE, pidl, TRUE, 0);
|
|
ILFree(pidl);
|
|
pidl = NULL;
|
|
}
|
|
|
|
// if we are ignoring all errors or we dont have an hwnd to use as a parent,
|
|
// then dont show any error msgs.
|
|
if (pfpsp->fIgnoreAllErrors || !hwndParent)
|
|
{
|
|
dwLastError = ERROR_SUCCESS;
|
|
}
|
|
|
|
// If kernel threw up an error dialog (such as "the disk is write proctected")
|
|
// and the user hit "abort" then return false to avoid a second error dialog
|
|
if (dwLastError == ERROR_REQUEST_ABORTED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// put up the error dlg if necessary, but not for super hidden files
|
|
if (dwLastError != ERROR_SUCCESS)
|
|
{
|
|
// !PathIsRoot is required, since the root path (eg c:\) is superhidden by default even after formatting a drive,
|
|
// why the filesystem thinks that the root should be +s +r after a format is a mystery to me...
|
|
if (bIsSuperHidden && !ShowSuperHidden() && !PathIsRoot(pszPath))
|
|
{
|
|
dwLastError = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ATTRIBUTEERROR ae;
|
|
|
|
ae.pszPath = pszPath;
|
|
ae.dwLastError = dwLastError;
|
|
|
|
int iRet = FailedApplyAttribsErrorDlg(hwndParent, &ae);
|
|
|
|
switch (iRet)
|
|
{
|
|
case IDRETRY:
|
|
// we clear out dwError and try again
|
|
dwLastError = ERROR_SUCCESS;
|
|
goto RetryApplyAttribs;
|
|
break;
|
|
|
|
case IDIGNOREALL:
|
|
pfpsp->fIgnoreAllErrors = TRUE;
|
|
dwLastError = ERROR_SUCCESS;
|
|
break;
|
|
|
|
case IDIGNORE:
|
|
dwLastError = ERROR_SUCCESS;
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update the progress bar
|
|
if (pfpsp->pProgressDlg)
|
|
{
|
|
ULARGE_INTEGER ulTemp;
|
|
|
|
// it is the callers responsibility to make sure that pfpsp->fd is filled with
|
|
// the proper information for the file we are applying attributes to.
|
|
ulTemp.LowPart = pfpsp->fd.nFileSizeLow;
|
|
ulTemp.HighPart = pfpsp->fd.nFileSizeHigh;
|
|
|
|
pfpsp->ulNumberOfBytesDone.QuadPart += ulTemp.QuadPart;
|
|
|
|
UpdateProgressBar(pfpsp);
|
|
}
|
|
|
|
return (dwLastError == ERROR_SUCCESS) ? TRUE : FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the text of a dialog item and attach a tooltip if necessary.
|
|
//
|
|
STDAPI_(void) SetDlgItemTextWithToolTip(HWND hDlg, UINT id, LPCTSTR pszText, HWND *phwnd)
|
|
{
|
|
HWND hwnd = GetDlgItem(hDlg, id);
|
|
if (hwnd)
|
|
{
|
|
SetWindowText(hwnd, pszText);
|
|
RECT rc;
|
|
HDC hDC;
|
|
if (GetClientRect(hwnd, &rc) && (hDC = GetDC(hDlg)) != NULL)
|
|
{
|
|
HFONT hFont = GetWindowFont(hwnd);
|
|
if (hFont)
|
|
{
|
|
// set the dlg font into the DC so we can calc the size
|
|
hFont = (HFONT)SelectObject(hDC, hFont);
|
|
|
|
SIZE size = {0};
|
|
GetTextExtentPoint32(hDC, pszText, lstrlen(pszText), &size);
|
|
// restore the prev. hFont
|
|
SelectObject(hDC, hFont);
|
|
|
|
if (size.cx > rc.right)
|
|
{
|
|
// our text size is bigger than the dlg width, so its clipped
|
|
if (*phwnd == NULL)
|
|
{
|
|
*phwnd = CreateWindow(TOOLTIPS_CLASS,
|
|
c_szNULL,
|
|
WS_POPUP | TTS_NOPREFIX,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
hDlg,
|
|
NULL,
|
|
HINST_THISDLL,
|
|
NULL);
|
|
}
|
|
|
|
if (*phwnd)
|
|
{
|
|
TOOLINFO ti;
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
|
|
ti.hwnd = hDlg;
|
|
ti.uId = (UINT_PTR)hwnd;
|
|
ti.lpszText = (LPTSTR)pszText; // const -> non const
|
|
ti.hinst = HINST_THISDLL;
|
|
SendMessage(*phwnd, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
|
|
}
|
|
}
|
|
}
|
|
ReleaseDC(hDlg, hDC);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UpdateTriStateCheckboxes(FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
// we turn off tristate after applying attibs for those things that were tri-state
|
|
// initially but are not anymore since we sucessfully applied the attributes
|
|
|
|
if (pfpsp->hDlg)
|
|
{
|
|
if (pfpsp->asInitial.fReadOnly == BST_INDETERMINATE && pfpsp->asCurrent.fReadOnly != BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(pfpsp->hDlg, IDD_READONLY, BM_SETSTYLE, BS_AUTOCHECKBOX, 0);
|
|
}
|
|
|
|
if (pfpsp->asInitial.fHidden == BST_INDETERMINATE && pfpsp->asCurrent.fHidden != BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(pfpsp->hDlg, IDD_HIDDEN, BM_SETSTYLE, BS_AUTOCHECKBOX, 0);
|
|
}
|
|
|
|
// Archive is only on the general tab for files on FAT/FAT32 volumes
|
|
if (!pfpsp->pfci->fIsCompressionAvailable && pfpsp->asInitial.fArchive == BST_INDETERMINATE && pfpsp->asCurrent.fArchive != BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(pfpsp->hDlg, IDD_ARCHIVE, BM_SETSTYLE, BS_AUTOCHECKBOX, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This applies the attributes to the selected files (multiple file case)
|
|
//
|
|
// return value:
|
|
// TRUE We sucessfully applied all the attributes
|
|
// FALSE The user hit cancel, and we stoped
|
|
//
|
|
STDAPI_(BOOL) ApplyMultipleFileAttributes(FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
// create the progress dialog. This may fail if out of memory. If it does fail, we will
|
|
// abort the operation because it will also probably fail if out of memory.
|
|
if (CreateAttributeProgressDlg(pfpsp))
|
|
{
|
|
BOOL bSomethingChanged = FALSE;
|
|
|
|
bRet = TRUE;
|
|
|
|
// make sure that HIDA_FillFindDatat returns the compressed size, else our progress est will be way off
|
|
TCHAR szPath[MAX_PATH];
|
|
for (int iItem = 0; HIDA_FillFindData(pfpsp->pfci->hida, iItem, szPath, &pfpsp->fd, TRUE); iItem++)
|
|
{
|
|
if (HasUserCanceledAttributeProgressDlg(pfpsp))
|
|
{
|
|
// the user hit cancel on the progress dlg, so stop
|
|
bRet = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (pfpsp->fRecursive && (pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
// apply attribs to the subfolders
|
|
bRet = ApplyRecursiveFolderAttribs(szPath, pfpsp);
|
|
|
|
// send out a notification for the whole dir, regardless if the user hit cancel since
|
|
// something could have changed
|
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, szPath, NULL);
|
|
}
|
|
else
|
|
{
|
|
HWND hwndParent = NULL;
|
|
|
|
// if we have a progress hwnd, try to use it as our parent. This will fail
|
|
// if the progress dialog isn't being displayed yet.
|
|
IUnknown_GetWindow((IUnknown*)pfpsp->pProgressDlg, &hwndParent);
|
|
|
|
if (!hwndParent)
|
|
{
|
|
// the progress dlg isint here yet, so use the property page hwnd
|
|
hwndParent = GetParent(pfpsp->hDlg);
|
|
}
|
|
|
|
// apply the attribs to this item only
|
|
bRet = ApplyFileAttributes(szPath, pfpsp, hwndParent, &bSomethingChanged);
|
|
|
|
if (bSomethingChanged)
|
|
{
|
|
// something changed, so send out a notification for that file
|
|
DeleteFileThumbnail(szPath);
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, szPath, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// destroy the progress dialog
|
|
DestroyAttributeProgressDlg(pfpsp);
|
|
|
|
if (bRet)
|
|
{
|
|
// since we just sucessfully applied attribs, reset any tri-state checkboxes as necessary
|
|
UpdateTriStateCheckboxes(pfpsp);
|
|
|
|
// the user did NOT hit cancel, so update the prop sheet to reflect the new attribs
|
|
pfpsp->asInitial = pfpsp->asCurrent;
|
|
}
|
|
|
|
// flush any change-notifications we generated
|
|
SHChangeNotifyHandleEvents();
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
STDAPI_(BOOL) ApplySingleFileAttributes(FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
BOOL bSomethingChanged = FALSE;
|
|
|
|
if (!pfpsp->fRecursive)
|
|
{
|
|
bRet = ApplyFileAttributes(pfpsp->szPath, pfpsp, GetParent(pfpsp->hDlg), &bSomethingChanged);
|
|
|
|
if (bSomethingChanged)
|
|
{
|
|
// something changed, so generate a notification for the item
|
|
DeleteFileThumbnail(pfpsp->szPath);
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pfpsp->szPath, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We only should be doing a recursive operation if we have a directory!
|
|
ASSERT(pfpsp->fIsDirectory);
|
|
|
|
// create the progress dialog. This may fail if out of memory. If it does fail, we will
|
|
// abort the operation because it will also probably fail if out of memory.
|
|
if (CreateAttributeProgressDlg(pfpsp))
|
|
{
|
|
// apply attribs to this folder & sub files/folders
|
|
bRet = ApplyRecursiveFolderAttribs(pfpsp->szPath, pfpsp);
|
|
|
|
// HACKHACK: send out a notification for the item so that defview will refresh properly
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pfpsp->szPath, NULL);
|
|
|
|
// send out a notification for the whole dir, regardless of the return value since
|
|
// something could have changed even if the user hit cancel
|
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, pfpsp->szPath, NULL);
|
|
|
|
DestroyAttributeProgressDlg(pfpsp);
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
// since we just sucessfully applied attribs, reset any tri-state checkboxes as necessary
|
|
UpdateTriStateCheckboxes(pfpsp);
|
|
|
|
// the user did NOT hit cancel, so update the prop sheet to reflect the new attribs
|
|
pfpsp->asInitial = pfpsp->asCurrent;
|
|
|
|
// (reinerf) need to update the size fields (eg file was just compressed)
|
|
}
|
|
|
|
// handle any events we may have generated
|
|
SHChangeNotifyHandleEvents();
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// this function sets the string that tells the user what attributes they are about to
|
|
// apply
|
|
//
|
|
BOOL SetAttributePromptText(HWND hDlgRecurse, FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
TCHAR szAttribsToApply[MAX_PATH];
|
|
TCHAR szTemp[MAX_PATH];
|
|
|
|
szAttribsToApply[0] = 0;
|
|
|
|
if (pfpsp->asInitial.fReadOnly != pfpsp->asCurrent.fReadOnly)
|
|
{
|
|
if (pfpsp->asCurrent.fReadOnly)
|
|
EVAL(LoadString(HINST_THISDLL, IDS_READONLY, szTemp, ARRAYSIZE(szTemp)));
|
|
else
|
|
EVAL(LoadString(HINST_THISDLL, IDS_NOTREADONLY, szTemp, ARRAYSIZE(szTemp)));
|
|
|
|
StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
|
|
}
|
|
|
|
if (pfpsp->asInitial.fHidden != pfpsp->asCurrent.fHidden)
|
|
{
|
|
if (pfpsp->asCurrent.fHidden)
|
|
EVAL(LoadString(HINST_THISDLL, IDS_HIDE, szTemp, ARRAYSIZE(szTemp)));
|
|
else
|
|
EVAL(LoadString(HINST_THISDLL, IDS_UNHIDE, szTemp, ARRAYSIZE(szTemp)));
|
|
|
|
StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
|
|
}
|
|
|
|
if (pfpsp->asInitial.fArchive != pfpsp->asCurrent.fArchive)
|
|
{
|
|
if (pfpsp->asCurrent.fArchive)
|
|
EVAL(LoadString(HINST_THISDLL, IDS_ARCHIVE, szTemp, ARRAYSIZE(szTemp)));
|
|
else
|
|
EVAL(LoadString(HINST_THISDLL, IDS_UNARCHIVE, szTemp, ARRAYSIZE(szTemp)));
|
|
|
|
StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
|
|
}
|
|
|
|
if (pfpsp->asInitial.fIndex != pfpsp->asCurrent.fIndex)
|
|
{
|
|
if (pfpsp->asCurrent.fIndex)
|
|
EVAL(LoadString(HINST_THISDLL, IDS_INDEX, szTemp, ARRAYSIZE(szTemp)));
|
|
else
|
|
EVAL(LoadString(HINST_THISDLL, IDS_DISABLEINDEX, szTemp, ARRAYSIZE(szTemp)));
|
|
|
|
StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
|
|
}
|
|
|
|
if (pfpsp->asInitial.fCompress != pfpsp->asCurrent.fCompress)
|
|
{
|
|
if (pfpsp->asCurrent.fCompress)
|
|
EVAL(LoadString(HINST_THISDLL, IDS_COMPRESS, szTemp, ARRAYSIZE(szTemp)));
|
|
else
|
|
EVAL(LoadString(HINST_THISDLL, IDS_UNCOMPRESS, szTemp, ARRAYSIZE(szTemp)));
|
|
|
|
StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
|
|
}
|
|
|
|
if (pfpsp->asInitial.fEncrypt != pfpsp->asCurrent.fEncrypt)
|
|
{
|
|
if (pfpsp->asCurrent.fEncrypt)
|
|
EVAL(LoadString(HINST_THISDLL, IDS_ENCRYPT, szTemp, ARRAYSIZE(szTemp)));
|
|
else
|
|
EVAL(LoadString(HINST_THISDLL, IDS_DECRYPT, szTemp, ARRAYSIZE(szTemp)));
|
|
|
|
StrCatBuff(szAttribsToApply, szTemp, ARRAYSIZE(szAttribsToApply));
|
|
}
|
|
|
|
if (!*szAttribsToApply)
|
|
{
|
|
// nothing changed bail
|
|
return FALSE;
|
|
}
|
|
|
|
// remove the trailing ", "
|
|
int iLength = lstrlen(szAttribsToApply);
|
|
ASSERT(iLength >= 3);
|
|
lstrcpy(&szAttribsToApply[iLength - 2], TEXT("\0"));
|
|
|
|
SetDlgItemText(hDlgRecurse, IDD_ATTRIBSTOAPPLY, szAttribsToApply);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// This dlg proc is for the prompt to ask the user if they want to have their changes apply
|
|
// to only the directories, or all files/folders within the directories.
|
|
//
|
|
BOOL_PTR CALLBACK RecursivePromptDlgProc(HWND hDlgRecurse, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
FILEPROPSHEETPAGE* pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlgRecurse, DWLP_USER);
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hDlgRecurse, DWLP_USER, lParam);
|
|
pfpsp = (FILEPROPSHEETPAGE *)lParam;
|
|
|
|
// set the initial state of the radio button
|
|
CheckDlgButton(hDlgRecurse, IDD_RECURSIVE, TRUE);
|
|
|
|
// set the IDD_ATTRIBSTOAPPLY based on what attribs we are applying
|
|
if (!SetAttributePromptText(hDlgRecurse, pfpsp))
|
|
{
|
|
// we should not get here because we check for the no attribs
|
|
// to apply earlier
|
|
ASSERT(FALSE);
|
|
|
|
EndDialog(hDlgRecurse, TRUE);
|
|
}
|
|
|
|
// load either "this folder" or "the selected items"
|
|
TCHAR szFolderText[MAX_PATH];
|
|
LoadString(HINST_THISDLL, pfpsp->pfci->fMultipleFiles ? IDS_THESELECTEDITEMS : IDS_THISFOLDER, szFolderText, ARRAYSIZE(szFolderText));
|
|
|
|
// set the IDD_RECURSIVE_TXT text to have "this folder" or "the selected items"
|
|
TCHAR szFormatString[MAX_PATH];
|
|
GetDlgItemText(hDlgRecurse, IDD_RECURSIVE_TXT, szFormatString, ARRAYSIZE(szFormatString));
|
|
TCHAR szDlgText[MAX_PATH];
|
|
wsprintf(szDlgText, szFormatString, szFolderText);
|
|
SetDlgItemText(hDlgRecurse, IDD_RECURSIVE_TXT, szDlgText);
|
|
|
|
// set the IDD_NOTRECURSIVE raido button text to have "this folder" or "the selected items"
|
|
GetDlgItemText(hDlgRecurse, IDD_NOTRECURSIVE, szFormatString, ARRAYSIZE(szFormatString));
|
|
wsprintf(szDlgText, szFormatString, szFolderText);
|
|
SetDlgItemText(hDlgRecurse, IDD_NOTRECURSIVE, szDlgText);
|
|
|
|
// set the IDD_RECURSIVE raido button text to have "this folder" or "the selected items"
|
|
GetDlgItemText(hDlgRecurse, IDD_RECURSIVE, szFormatString, ARRAYSIZE(szFormatString));
|
|
wsprintf(szDlgText, szFormatString, szFolderText);
|
|
SetDlgItemText(hDlgRecurse, IDD_RECURSIVE, szDlgText);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
UINT uCtrlID = GET_WM_COMMAND_ID(wParam, lParam);
|
|
switch (uCtrlID)
|
|
{
|
|
case IDOK:
|
|
pfpsp->fRecursive = (IsDlgButtonChecked(hDlgRecurse, IDD_RECURSIVE) == BST_CHECKED);
|
|
// fall through
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hDlgRecurse, (uCtrlID == IDCANCEL) ? FALSE : TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// This wndproc handles the "Advanced Attributes..." button on the general tab for
|
|
//
|
|
// return - FALSE: the user hit cancle
|
|
// TRUE: the user hit ok
|
|
//
|
|
BOOL_PTR CALLBACK AdvancedFileAttribsDlgProc(HWND hDlgAttribs, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
FILEPROPSHEETPAGE* pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlgAttribs, DWLP_USER);
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hDlgAttribs, DWLP_USER, lParam);
|
|
pfpsp = (FILEPROPSHEETPAGE *)lParam;
|
|
|
|
// set the initial state of the checkboxes
|
|
|
|
if (pfpsp->asInitial.fArchive == BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(hDlgAttribs, IDD_ARCHIVE, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
CheckDlgButton(hDlgAttribs, IDD_ARCHIVE, pfpsp->asCurrent.fArchive);
|
|
|
|
if (pfpsp->asInitial.fIndex == BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(hDlgAttribs, IDD_INDEX, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
CheckDlgButton(hDlgAttribs, IDD_INDEX, pfpsp->asCurrent.fIndex);
|
|
|
|
if (pfpsp->asInitial.fCompress == BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(hDlgAttribs, IDD_COMPRESS, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
CheckDlgButton(hDlgAttribs, IDD_COMPRESS, pfpsp->asCurrent.fCompress);
|
|
|
|
if (pfpsp->asInitial.fEncrypt == BST_INDETERMINATE)
|
|
{
|
|
SendDlgItemMessage(hDlgAttribs, IDD_ENCRYPT, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
CheckDlgButton(hDlgAttribs, IDD_ENCRYPT, pfpsp->asCurrent.fEncrypt);
|
|
|
|
// assert that compression and encryption are mutually exclusive
|
|
ASSERT(!((pfpsp->asCurrent.fCompress == BST_CHECKED) && (pfpsp->asCurrent.fEncrypt == BST_CHECKED)));
|
|
|
|
// gray any checkboxs that are not supported by this filesystem
|
|
EnableWindow(GetDlgItem(hDlgAttribs, IDD_INDEX), pfpsp->fIsIndexAvailable);
|
|
EnableWindow(GetDlgItem(hDlgAttribs, IDD_COMPRESS), pfpsp->pfci->fIsCompressionAvailable);
|
|
EnableWindow(GetDlgItem(hDlgAttribs, IDD_ENCRYPT), pfpsp->fIsEncryptionAvailable);
|
|
|
|
if (pfpsp->fIsEncryptionAvailable &&
|
|
pfpsp->asInitial.fEncrypt &&
|
|
!pfpsp->fIsDirectory &&
|
|
!pfpsp->pfci->fMultipleFiles)
|
|
{
|
|
// we only support the Advanced button for the single folder case
|
|
EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), TRUE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), FALSE);
|
|
}
|
|
|
|
// load either "this folder" or "the selected items"
|
|
TCHAR szFolderText[MAX_PATH];
|
|
LoadString(HINST_THISDLL, pfpsp->pfci->fMultipleFiles ? IDS_THESELECTEDITEMS : IDS_THISFOLDER, szFolderText, ARRAYSIZE(szFolderText));
|
|
|
|
// set the IDC_MANAGEFILES_TXT text to have "this folder" or "the selected items"
|
|
TCHAR szFormatString[MAX_PATH];
|
|
GetDlgItemText(hDlgAttribs, IDC_MANAGEFILES_TXT, szFormatString, ARRAYSIZE(szFormatString));
|
|
TCHAR szDlgText[MAX_PATH];
|
|
wsprintf(szDlgText, szFormatString, szFolderText);
|
|
SetDlgItemText(hDlgAttribs, IDC_MANAGEFILES_TXT, szDlgText);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aAdvancedHelpIds);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
if ((int)SendMessage(hDlgAttribs, WM_NCHITTEST, 0, lParam) != HTCLIENT)
|
|
{
|
|
// not in our client area, so don't process it
|
|
return FALSE;
|
|
}
|
|
WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aAdvancedHelpIds);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
UINT uCtrlID = GET_WM_COMMAND_ID(wParam, lParam);
|
|
|
|
switch (uCtrlID)
|
|
{
|
|
case IDD_COMPRESS:
|
|
// encrypt and compress are mutually exclusive
|
|
if (IsDlgButtonChecked(hDlgAttribs, IDD_COMPRESS) == BST_CHECKED)
|
|
{
|
|
// the user checked compress so uncheck the encrypt checkbox
|
|
CheckDlgButton(hDlgAttribs, IDD_ENCRYPT, BST_UNCHECKED);
|
|
}
|
|
break;
|
|
|
|
case IDD_ENCRYPT:
|
|
// encrypt and compress are mutually exclusive
|
|
if (IsDlgButtonChecked(hDlgAttribs, IDD_ENCRYPT) == BST_CHECKED)
|
|
{
|
|
// the user checked encrypt, so uncheck the compression checkbox
|
|
CheckDlgButton(hDlgAttribs, IDD_COMPRESS, BST_UNCHECKED);
|
|
|
|
if (!pfpsp->pfci->fMultipleFiles && pfpsp->asInitial.fEncrypt)
|
|
{
|
|
EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(hDlgAttribs, IDC_ADVANCED), FALSE);
|
|
}
|
|
break;
|
|
|
|
case IDC_ADVANCED:
|
|
ASSERT(pfpsp->fIsEncryptionAvailable && pfpsp->asInitial.fEncrypt && !pfpsp->pfci->fMultipleFiles);
|
|
// bring up the EfsDetail dialog
|
|
EfsDetail(hDlgAttribs, pfpsp->szPath);
|
|
break;
|
|
|
|
case IDOK:
|
|
pfpsp->asCurrent.fArchive = IsDlgButtonChecked(hDlgAttribs, IDD_ARCHIVE);
|
|
if (pfpsp->asCurrent.fArchive == BST_INDETERMINATE)
|
|
{
|
|
// if its indeterminate, it better had been indeterminate to start with
|
|
ASSERT(pfpsp->asInitial.fArchive == BST_INDETERMINATE);
|
|
}
|
|
|
|
pfpsp->asCurrent.fIndex = IsDlgButtonChecked(hDlgAttribs, IDD_INDEX);
|
|
if (pfpsp->asCurrent.fIndex == BST_INDETERMINATE)
|
|
{
|
|
// if its indeterminate, it better had been indeterminate to start with
|
|
ASSERT(pfpsp->asInitial.fIndex == BST_INDETERMINATE);
|
|
}
|
|
|
|
pfpsp->asCurrent.fCompress = IsDlgButtonChecked(hDlgAttribs, IDD_COMPRESS);
|
|
if (pfpsp->asCurrent.fCompress == BST_INDETERMINATE)
|
|
{
|
|
// if its indeterminate, it better had been indeterminate to start with
|
|
ASSERT(pfpsp->asInitial.fCompress == BST_INDETERMINATE);
|
|
}
|
|
|
|
pfpsp->asCurrent.fEncrypt = IsDlgButtonChecked(hDlgAttribs, IDD_ENCRYPT);
|
|
if (pfpsp->asCurrent.fEncrypt == BST_INDETERMINATE)
|
|
{
|
|
// if its indeterminate, it better had been indeterminate to start with
|
|
ASSERT(pfpsp->asInitial.fEncrypt == BST_INDETERMINATE);
|
|
}
|
|
// fall through...
|
|
|
|
case IDCANCEL:
|
|
ReplaceDlgIcon(hDlgAttribs, IDD_ITEMICON, NULL);
|
|
|
|
EndDialog(hDlgAttribs, (uCtrlID == IDCANCEL) ? FALSE : TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Descriptions:
|
|
// This is the dialog procedure for multiple object property sheet.
|
|
//
|
|
BOOL_PTR CALLBACK MultiplePrshtDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
FILEPROPSHEETPAGE * pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
pfpsp = (FILEPROPSHEETPAGE *)lParam;
|
|
pfpsp->hDlg = hDlg;
|
|
pfpsp->pfci->hDlg = hDlg;
|
|
|
|
InitMultiplePrsht(pfpsp);
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(void *)aMultiPropHelpIds);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
if ((int)SendMessage(hDlg, WM_NCHITTEST, 0, lParam) != HTCLIENT)
|
|
{
|
|
// not in our client area, so don't process it
|
|
return FALSE;
|
|
}
|
|
WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aMultiPropHelpIds);
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
UpdateSizeCount(pfpsp);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDD_READONLY:
|
|
case IDD_HIDDEN:
|
|
case IDD_ARCHIVE:
|
|
break;
|
|
|
|
case IDC_ADVANCED:
|
|
// the dialog box returns fase if the user hit cancel, and true if they hit ok,
|
|
// so if they cancelled, return immediately and don't send the PSM_CHANGED message
|
|
// because nothing actually changed
|
|
if (!DialogBoxParam(HINST_THISDLL,
|
|
MAKEINTRESOURCE(pfpsp->fIsDirectory ? DLG_FOLDERATTRIBS : DLG_FILEATTRIBS),
|
|
hDlg,
|
|
AdvancedFileAttribsDlgProc,
|
|
(LPARAM)pfpsp))
|
|
{
|
|
// the user has cancled
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return TRUE;
|
|
}
|
|
|
|
// check to see if we need to enable the Apply button
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
|
|
{
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
Free_DlgDependentFilePropSheetPage(pfpsp);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR *)lParam)->code)
|
|
{
|
|
case PSN_APPLY:
|
|
{
|
|
//
|
|
// Get the final state of the checkboxes
|
|
//
|
|
|
|
pfpsp->asCurrent.fReadOnly = IsDlgButtonChecked(hDlg, IDD_READONLY);
|
|
if (pfpsp->asCurrent.fReadOnly == BST_INDETERMINATE)
|
|
{
|
|
// if its indeterminate, it better had been indeterminate to start with
|
|
ASSERT(pfpsp->asInitial.fReadOnly == BST_INDETERMINATE);
|
|
}
|
|
|
|
pfpsp->asCurrent.fHidden = IsDlgButtonChecked(hDlg, IDD_HIDDEN);
|
|
if (pfpsp->asCurrent.fHidden == BST_INDETERMINATE)
|
|
{
|
|
// if its indeterminate, it better had been indeterminate to start with
|
|
ASSERT(pfpsp->asInitial.fHidden == BST_INDETERMINATE);
|
|
}
|
|
|
|
if (!pfpsp->pfci->fIsCompressionAvailable)
|
|
{
|
|
// at least one of the files is on FAT, so the Archive checkbox is on the general page
|
|
pfpsp->asCurrent.fArchive = IsDlgButtonChecked(hDlg, IDD_ARCHIVE);
|
|
if (pfpsp->asCurrent.fArchive == BST_INDETERMINATE)
|
|
{
|
|
// if its indeterminate, it better had been indeterminate to start with
|
|
ASSERT(pfpsp->asInitial.fArchive == BST_INDETERMINATE);
|
|
}
|
|
}
|
|
|
|
BOOL bRet = TRUE;
|
|
|
|
// check to see if the user actually changed something, if they didnt, then
|
|
// we dont have to apply anything
|
|
if (memcmp(&pfpsp->asInitial, &pfpsp->asCurrent, sizeof(pfpsp->asInitial)) != 0)
|
|
{
|
|
// NOTE: We dont check to see if all the dirs are empty, that would be too expensive.
|
|
// We only do that in the single file case.
|
|
if (pfpsp->fIsDirectory)
|
|
{
|
|
// check to see if the user wants to apply the attribs recursively or not
|
|
bRet = (int)DialogBoxParam(HINST_THISDLL,
|
|
MAKEINTRESOURCE(DLG_ATTRIBS_RECURSIVE),
|
|
hDlg,
|
|
RecursivePromptDlgProc,
|
|
(LPARAM)pfpsp);
|
|
}
|
|
|
|
if (bRet)
|
|
bRet = ApplyMultipleFileAttributes(pfpsp);
|
|
|
|
if (!bRet)
|
|
{
|
|
// the user hit cancel, so we return true to prevent the property sheet form closeing
|
|
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// update the size / last accessed time
|
|
UpdateSizeField(pfpsp, NULL);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// in:
|
|
// hdlg
|
|
// id text control id
|
|
// pftUTC UTC time time to be set
|
|
//
|
|
STDAPI_(void) SetDateTimeText(HWND hdlg, int id, const FILETIME *pftUTC)
|
|
{
|
|
SetDateTimeTextEx(hdlg, id, pftUTC, FDTF_LONGDATE | FDTF_LONGTIME | FDTF_RELATIVE) ;
|
|
}
|
|
|
|
STDAPI_(void) SetDateTimeTextEx(HWND hdlg, int id, const FILETIME *pftUTC, DWORD dwFlags)
|
|
{
|
|
if (!IsNullTime(pftUTC))
|
|
{
|
|
LCID locale = GetUserDefaultLCID();
|
|
|
|
if ((PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_ARABIC) ||
|
|
(PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_HEBREW))
|
|
{
|
|
HWND hWnd = GetDlgItem(hdlg, id);
|
|
DWORD dwExStyle = GetWindowLong(hdlg, GWL_EXSTYLE);
|
|
if ((BOOLIFY(dwExStyle & WS_EX_RTLREADING)) != (BOOLIFY(dwExStyle & RTL_MIRRORED_WINDOW)))
|
|
dwFlags |= FDTF_RTLDATE;
|
|
else
|
|
dwFlags |= FDTF_LTRDATE;
|
|
}
|
|
|
|
TCHAR szBuf[64];
|
|
SHFormatDateTime(pftUTC, &dwFlags, szBuf, SIZECHARS(szBuf));
|
|
SetDlgItemText(hdlg, id, szBuf);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Descriptions:
|
|
// Detects BiDi Calender
|
|
//
|
|
BOOL _IsBiDiCalendar(void)
|
|
{
|
|
TCHAR chCalendar[32];
|
|
BOOL fBiDiCalender = FALSE;
|
|
|
|
//
|
|
// Let's verify the calendar type whether it's gregorian or not.
|
|
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_ICALENDARTYPE,
|
|
(TCHAR *) &chCalendar[0],
|
|
ARRAYSIZE(chCalendar)))
|
|
{
|
|
CALTYPE defCalendar = StrToInt((TCHAR *)&chCalendar[0]);
|
|
LCID locale = GetUserDefaultLCID();
|
|
|
|
if ((defCalendar == CAL_HIJRI) ||
|
|
(defCalendar == CAL_HEBREW) ||
|
|
((defCalendar == CAL_GREGORIAN) &&
|
|
((PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_ARABIC) ||
|
|
(PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_HEBREW))) ||
|
|
(defCalendar == CAL_GREGORIAN_ARABIC) ||
|
|
(defCalendar == CAL_GREGORIAN_XLIT_ENGLISH) ||
|
|
(defCalendar == CAL_GREGORIAN_XLIT_FRENCH))
|
|
{
|
|
fBiDiCalender = TRUE;
|
|
}
|
|
}
|
|
|
|
return fBiDiCalender;
|
|
}
|
|
|
|
// Set the friendly display name into control uId.
|
|
BOOL SetPidlToWindow(HWND hwnd, UINT uId, LPITEMIDLIST pidl)
|
|
{
|
|
BOOL fRes = FALSE;
|
|
LPCITEMIDLIST pidlItem;
|
|
IShellFolder* psf;
|
|
if (SUCCEEDED(SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlItem)))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// SHGDN_FORADDRESSBAR | SHGDN_FORPARSING because we want:
|
|
// c:\winnt\.... and http://weird, but not ::{GUID} or Folder.{GUID}
|
|
if (SUCCEEDED(DisplayNameOf(psf, pidlItem, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath))))
|
|
{
|
|
SetDlgItemText(hwnd, IDD_TARGET, szPath);
|
|
fRes = TRUE;
|
|
}
|
|
psf->Release();
|
|
}
|
|
|
|
return fRes;
|
|
}
|
|
|
|
//
|
|
// Descriptions:
|
|
// This function fills fields of the "general" dialog box (a page of
|
|
// a property sheet) with attributes of the associated file.
|
|
//
|
|
BOOL InitSingleFilePrsht(FILEPROPSHEETPAGE * pfpsp)
|
|
{
|
|
SHFILEINFO sfi;
|
|
// get info about the file.
|
|
SHGetFileInfo(pfpsp->szPath, pfpsp->fd.dwFileAttributes, &sfi, sizeof(sfi),
|
|
SHGFI_ICON|SHGFI_LARGEICON|
|
|
SHGFI_DISPLAYNAME|
|
|
SHGFI_TYPENAME | SHGFI_ADDOVERLAYS);
|
|
|
|
// .ani cursor hack!
|
|
if (lstrcmpi(PathFindExtension(pfpsp->szPath), TEXT(".ani")) == 0)
|
|
{
|
|
HICON hIcon = (HICON)LoadImage(NULL, pfpsp->szPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
|
|
if (hIcon)
|
|
{
|
|
if (sfi.hIcon)
|
|
DestroyIcon(sfi.hIcon);
|
|
|
|
sfi.hIcon = hIcon;
|
|
}
|
|
}
|
|
|
|
// icon
|
|
ReplaceDlgIcon(pfpsp->hDlg, IDD_ITEMICON, sfi.hIcon);
|
|
|
|
// set the initial rename state
|
|
pfpsp->fRename = FALSE;
|
|
|
|
// set the file type
|
|
if (pfpsp->fMountedDrive)
|
|
{
|
|
//Borrow szVolumeGUID
|
|
TCHAR szVolumeGUID[MAX_PATH];
|
|
LoadString(HINST_THISDLL, IDS_MOUNTEDVOLUME, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
|
|
|
|
SetDlgItemText(pfpsp->hDlg, IDD_FILETYPE, szVolumeGUID);
|
|
|
|
//use szVolumeLabel temporarily
|
|
TCHAR szVolumeLabel[MAX_PATH];
|
|
lstrcpyn(szVolumeLabel, pfpsp->szPath, ARRAYSIZE(szVolumeLabel));
|
|
PathAddBackslash(szVolumeLabel);
|
|
GetVolumeNameForVolumeMountPoint(szVolumeLabel, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
|
|
|
|
if (!GetVolumeInformation(szVolumeGUID, szVolumeLabel, ARRAYSIZE(szVolumeLabel),
|
|
NULL, NULL, NULL, pfpsp->szFileSys, ARRAYSIZE(pfpsp->szFileSys)))
|
|
{
|
|
EnableWindow(GetDlgItem(pfpsp->hDlg, IDC_DRV_PROPERTIES), FALSE);
|
|
*szVolumeLabel = 0;
|
|
}
|
|
|
|
if (!(*szVolumeLabel))
|
|
LoadString(HINST_THISDLL, IDS_UNLABELEDVOLUME, szVolumeLabel, ARRAYSIZE(szVolumeLabel));
|
|
|
|
SetDlgItemText(pfpsp->hDlg, IDC_DRV_TARGET, szVolumeLabel);
|
|
}
|
|
else
|
|
{
|
|
SetDlgItemText(pfpsp->hDlg, IDD_FILETYPE, sfi.szTypeName);
|
|
}
|
|
|
|
|
|
// save off the initial short filename, and set the "Name" edit box
|
|
lstrcpyn(pfpsp->szInitialName, sfi.szDisplayName, ARRAYSIZE(pfpsp->szInitialName));
|
|
SetDlgItemText(pfpsp->hDlg, IDD_NAMEEDIT, sfi.szDisplayName);
|
|
|
|
// use a strcmp to see if we are showing the extension
|
|
if (lstrcmpi(sfi.szDisplayName, PathFindFileName(pfpsp->szPath)) == 0)
|
|
{
|
|
// since the strings are the same, we must be showing the extension
|
|
pfpsp->fShowExtension = TRUE;
|
|
}
|
|
|
|
TCHAR szBuffer[MAX_PATH];
|
|
lstrcpyn(szBuffer, pfpsp->szPath, ARRAYSIZE(szBuffer));
|
|
PathRemoveFileSpec(szBuffer);
|
|
|
|
UINT cchMax;
|
|
GetCCHMaxFromPath(pfpsp->szPath, &cchMax, pfpsp->fShowExtension);
|
|
Edit_LimitText(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), cchMax);
|
|
|
|
// apply the limit input code for the item
|
|
pfpsp->pidl = ILCreateFromPath(pfpsp->szPath);
|
|
if (pfpsp->pidl)
|
|
{
|
|
IShellFolder *psf;
|
|
if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pfpsp->pidl, &psf))))
|
|
{
|
|
SHLimitInputEdit(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), psf);
|
|
psf->Release();
|
|
}
|
|
}
|
|
|
|
// Are we a folder shortcut?
|
|
if (pfpsp->fFolderShortcut)
|
|
{
|
|
// Yes; Then we need to populate folder shortcut specific controls.
|
|
if (pfpsp->pidl)
|
|
{
|
|
IShellLink *psl;
|
|
if (SUCCEEDED(SHGetUIObjectFromFullPIDL(pfpsp->pidl, NULL, IID_PPV_ARG(IShellLink, &psl))))
|
|
{
|
|
// Populate the Target
|
|
if (SUCCEEDED(psl->GetIDList(&pfpsp->pidlTarget)))
|
|
{
|
|
if (SetPidlToWindow(pfpsp->hDlg, IDD_TARGET, pfpsp->pidlTarget))
|
|
{
|
|
pfpsp->fValidateEdit = FALSE; // Set this to false because we already have a pidl
|
|
// and don't need to validate.
|
|
}
|
|
}
|
|
|
|
// And description
|
|
TCHAR sz[INFOTIPSIZE];
|
|
if (SUCCEEDED(psl->GetDescription(sz, ARRAYSIZE(sz))))
|
|
{
|
|
SetDlgItemText(pfpsp->hDlg, IDD_COMMENT, sz);
|
|
}
|
|
|
|
psl->Release();
|
|
}
|
|
}
|
|
|
|
SetDateTimeText(pfpsp->hDlg, IDD_CREATED, &pfpsp->fd.ftCreationTime);
|
|
}
|
|
else
|
|
{
|
|
// set the initial attributes
|
|
SetInitialFileAttribs(pfpsp, pfpsp->fd.dwFileAttributes, pfpsp->fd.dwFileAttributes);
|
|
|
|
// special case for folders, we don't apply the read only bit to folders
|
|
// and to indicate that in the UI we make the inital state of the check
|
|
// box tri-state. this allows the read only bit to be applied to files in
|
|
// this folder, but not the folders themselves.
|
|
if (pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
pfpsp->asInitial.fReadOnly = BST_INDETERMINATE;
|
|
SendDlgItemMessage(pfpsp->hDlg, IDD_READONLY, BM_SETSTYLE, BS_AUTO3STATE, 0);
|
|
}
|
|
|
|
// set the current attributes to the same as the initial
|
|
pfpsp->asCurrent = pfpsp->asInitial;
|
|
|
|
CheckDlgButton(pfpsp->hDlg, IDD_READONLY, pfpsp->asInitial.fReadOnly);
|
|
CheckDlgButton(pfpsp->hDlg, IDD_HIDDEN, pfpsp->asInitial.fHidden);
|
|
|
|
// Disable renaming the file if requested
|
|
if (pfpsp->fDisableRename)
|
|
{
|
|
EnableWindow(GetDlgItem(pfpsp->hDlg, IDD_NAMEEDIT), FALSE);
|
|
}
|
|
|
|
if (pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
|
|
{
|
|
// to avoid people making SYSTEM files HIDDEN (superhidden files are
|
|
// not show to the user by default) we don't let people make SYSTEM files HIDDEN
|
|
EnableWindow(GetDlgItem(pfpsp->hDlg, IDD_HIDDEN), FALSE);
|
|
}
|
|
|
|
// Archive is only on the general tab for FAT, otherwise it is under the "Advanced attributes"
|
|
// and FAT volumes dont have the "Advanced attributes" button.
|
|
if (pfpsp->pfci->fIsCompressionAvailable || pfpsp->fIsEncryptionAvailable)
|
|
{
|
|
// if compression/encryption is available, then we must be on NTFS
|
|
DestroyWindow(GetDlgItem(pfpsp->hDlg, IDD_ARCHIVE));
|
|
}
|
|
else
|
|
{
|
|
// we are on FAT/FAT32, so get rid of the "Advanced attributes" button, and set the inital Archive state
|
|
DestroyWindow(GetDlgItem(pfpsp->hDlg, IDC_ADVANCED));
|
|
CheckDlgButton(pfpsp->hDlg, IDD_ARCHIVE, pfpsp->asInitial.fArchive);
|
|
}
|
|
|
|
UpdateSizeField(pfpsp, &pfpsp->fd);
|
|
|
|
if (!(pfpsp->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
// Check to see if the target file is a lnk, because if it is a lnk then
|
|
// we need to display the type information for the target, not the lnk itself.
|
|
if (PathIsShortcut(pfpsp->szPath, pfpsp->fd.dwFileAttributes))
|
|
{
|
|
pfpsp->fIsLink = TRUE;
|
|
}
|
|
if (!(GetFileAttributes(pfpsp->szPath) & FILE_ATTRIBUTE_OFFLINE))
|
|
{
|
|
UpdateOpensWithInfo(pfpsp);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(pfpsp->hDlg, IDC_FT_PROP_CHANGEOPENSWITH), FALSE);
|
|
}
|
|
}
|
|
|
|
// get the full path to the folder that contains this file.
|
|
lstrcpyn(szBuffer, pfpsp->szPath, ARRAYSIZE(szBuffer));
|
|
PathRemoveFileSpec(szBuffer);
|
|
|
|
// Keep Functionality same as NT4 by avoiding PathCompactPath.
|
|
SetDlgItemTextWithToolTip(pfpsp->hDlg, IDD_LOCATION, szBuffer, &pfpsp->hwndTip);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
STDAPI_(BOOL) ShowMountedVolumeProperties(LPCTSTR pszMountedVolume, HWND hwndParent)
|
|
{
|
|
IMountedVolume* pMountedVolume;
|
|
HRESULT hr = SHCoCreateInstance(NULL, &CLSID_MountedVolume, NULL, IID_PPV_ARG(IMountedVolume, &pMountedVolume));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szPathSlash[MAX_PATH];
|
|
lstrcpyn(szPathSlash, pszMountedVolume, ARRAYSIZE(szPathSlash));
|
|
PathAddBackslash(szPathSlash);
|
|
|
|
hr = pMountedVolume->Initialize(szPathSlash);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IDataObject* pDataObj;
|
|
hr = pMountedVolume->QueryInterface(IID_PPV_ARG(IDataObject, &pDataObj));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
PROPSTUFF *pps = (PROPSTUFF *)LocalAlloc(LPTR, sizeof(*pps));
|
|
if (pps)
|
|
{
|
|
pps->lpStartAddress = DrivesPropertiesThreadProc;
|
|
pps->pdtobj = pDataObj;
|
|
|
|
EnableWindow(hwndParent, FALSE);
|
|
|
|
DrivesPropertiesThreadProc(pps);
|
|
|
|
EnableWindow(hwndParent, TRUE);
|
|
|
|
LocalFree(pps);
|
|
}
|
|
|
|
pDataObj->Release();
|
|
}
|
|
pMountedVolume->Release();
|
|
}
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
#ifdef FOLDERSHORTCUT_EDITABLETARGET
|
|
BOOL SetFolderShortcutInfo(HWND hDlg, FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
ASSERT(pfpsp->pidl);
|
|
|
|
BOOL fSuccess = FALSE;
|
|
|
|
IShellLink* psl;
|
|
if (SUCCEEDED(SHGetUIObjectFromFullPIDL(pfpsp->pidl, NULL, IID_PPV_ARG(IShellLink, &psl))))
|
|
{
|
|
TCHAR sz[INFOTIPSIZE];
|
|
Edit_GetText(GetDlgItem(pfpsp->hDlg, IDD_COMMENT), sz, ARRAYSIZE(sz));
|
|
|
|
psl->SetDescription(sz);
|
|
|
|
if (pfpsp->fValidateEdit)
|
|
{
|
|
IShellFolder* psf;
|
|
if (SUCCEEDED(SHGetDesktopFolder(&psf)))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
Edit_GetText(GetDlgItem(pfpsp->hDlg, IDD_TARGET), sz, ARRAYSIZE(sz));
|
|
|
|
if (PathCanonicalize(szPath, sz))
|
|
{
|
|
LPITEMIDLIST pidlDest;
|
|
DWORD dwAttrib = SFGAO_FOLDER | SFGAO_VALIDATE;
|
|
ULONG chEat = 0;
|
|
if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, szPath, &chEat, &pidlDest, &dwAttrib)))
|
|
{
|
|
if ((dwAttrib & SFGAO_FOLDER) == SFGAO_FOLDER)
|
|
{
|
|
ILFree(pfpsp->pidlTarget);
|
|
pfpsp->pidlTarget = pidlDest;
|
|
fSuccess = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ILFree(pidlDest);
|
|
}
|
|
}
|
|
}
|
|
psf->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
if (fSuccess)
|
|
{
|
|
psl->SetIDList(pfpsp->pidlTarget);
|
|
|
|
IPersistFile* ppf;
|
|
if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
|
|
{
|
|
fSuccess = (S_OK == ppf->Save(pfpsp->szPath, TRUE));
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pfpsp->szPath, NULL);
|
|
ppf->Release();
|
|
}
|
|
}
|
|
|
|
psl->Release();
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Descriptions:
|
|
// This is the dialog procedure for the "general" page of a property sheet.
|
|
//
|
|
BOOL_PTR CALLBACK SingleFilePrshtDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
FILEPROPSHEETPAGE* pfpsp = (FILEPROPSHEETPAGE *)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
switch (uMessage)
|
|
{
|
|
case WM_INITDIALOG:
|
|
// REVIEW, we should store more state info here, for example
|
|
// the hIcon being displayed and the FILEINFO pointer, not just
|
|
// the file name ptr
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
pfpsp = (FILEPROPSHEETPAGE *)lParam;
|
|
pfpsp->hDlg = hDlg;
|
|
pfpsp->pfci->hDlg = hDlg;
|
|
|
|
InitSingleFilePrsht(pfpsp);
|
|
|
|
// We set this to signal that we are done processing the WM_INITDIALOG.
|
|
// This is needed because we set the text of the "Name" edit box and unless
|
|
// he knows that this is being set for the first time, he thinks that someone is doing a rename.
|
|
pfpsp->fWMInitFinshed = TRUE;
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if (!pfpsp->fMountedDrive)
|
|
UpdateSizeCount(pfpsp);
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(void *)(pfpsp->fIsDirectory ? aFolderGeneralHelpIds : aFileGeneralHelpIds));
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
if ((int)SendMessage(hDlg, WM_NCHITTEST, 0, lParam) != HTCLIENT)
|
|
{
|
|
// not in our client area, so don't process it
|
|
return FALSE;
|
|
}
|
|
WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)(pfpsp->fIsDirectory ? aFolderGeneralHelpIds : aFileGeneralHelpIds));
|
|
break;
|
|
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDD_READONLY:
|
|
case IDD_HIDDEN:
|
|
case IDD_ARCHIVE:
|
|
break;
|
|
|
|
#ifdef FOLDERSHORTCUT_EDITABLETARGET
|
|
case IDD_TARGET:
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
|
|
{
|
|
// someone typed in the target, enable apply button
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
|
|
|
|
// Do a verification on apply.
|
|
pfpsp->fValidateEdit = TRUE;
|
|
}
|
|
break;
|
|
|
|
case IDD_COMMENT:
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
|
|
{
|
|
// Set the apply.
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
|
|
}
|
|
break;
|
|
#endif
|
|
case IDD_NAMEEDIT:
|
|
// we need to check the pfpsp->fWMInitFinshed to make sure that we are done processing the WM_INITDIALOG,
|
|
// because during init we set the initial IDD_NAMEEDIT text which generates a EN_CHANGE message.
|
|
if ((GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) && !pfpsp->fRename && pfpsp->fWMInitFinshed)
|
|
{
|
|
pfpsp->fRename = TRUE;
|
|
//
|
|
// disable "Apply" even though the edit field has changed (reinerf)
|
|
//
|
|
// We only allow "ok" or "cancel" after the name has changed to be sure we
|
|
// don't rename the file out from under other property sheet extensions that
|
|
// cache the original name away
|
|
PropSheet_DisableApply(GetParent(pfpsp->hDlg));
|
|
}
|
|
break;
|
|
|
|
case IDC_CHANGEFILETYPE:
|
|
{
|
|
// Bring up the "Open With" dialog
|
|
OPENASINFO oai;
|
|
|
|
if (pfpsp->fIsLink && pfpsp->szLinkTarget[0])
|
|
{
|
|
// if we have a link we want to re-associate the link target, NOT .lnk files!
|
|
oai.pcszFile = pfpsp->szLinkTarget;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
LPTSTR pszExt = PathFindExtension(pfpsp->szPath);
|
|
|
|
// reality check...
|
|
ASSERT((lstrcmpi(pszExt, TEXT(".exe")) != 0) &&
|
|
(lstrcmpi(pszExt, TEXT(".lnk")) != 0));
|
|
#endif // DEBUG
|
|
oai.pcszFile = pfpsp->szPath;
|
|
}
|
|
|
|
oai.pcszClass = NULL;
|
|
oai.dwInFlags = (OAIF_REGISTER_EXT | OAIF_FORCE_REGISTRATION); // we want the association to be made
|
|
|
|
if (SUCCEEDED(OpenAsDialog(GetParent(hDlg), &oai)))
|
|
{
|
|
// we changed the association so update the "Opens with:" text. Clear out szLinkTarget to force
|
|
// the update to happen
|
|
pfpsp->szLinkTarget[0] = 0;
|
|
UpdateOpensWithInfo(pfpsp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_ADVANCED:
|
|
// the dialog box returns fase if the user hit cancel, and true if they hit ok,
|
|
// so if they cancelled, return immediately and don't send the PSM_CHANGED message
|
|
// because nothing actually changed
|
|
if (!DialogBoxParam(HINST_THISDLL,
|
|
MAKEINTRESOURCE(pfpsp->fIsDirectory ? DLG_FOLDERATTRIBS : DLG_FILEATTRIBS),
|
|
hDlg,
|
|
AdvancedFileAttribsDlgProc,
|
|
(LPARAM)pfpsp))
|
|
{
|
|
// the user has canceled
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case IDC_DRV_PROPERTIES:
|
|
ASSERT(pfpsp->fMountedDrive);
|
|
ShowMountedVolumeProperties(pfpsp->szPath, hDlg);
|
|
break;
|
|
|
|
#ifdef FOLDERSHORTCUT_EDITABLETARGET
|
|
case IDD_BROWSE:
|
|
{
|
|
// Display the BrowseForFolder dialog.
|
|
|
|
// FEATURE(lamadio): Implement a filter to filter things we can create folder
|
|
// shortcuts to. Not enough time for this rev 6.5.99
|
|
|
|
TCHAR szTitle[MAX_PATH];
|
|
LoadString(HINST_THISDLL, IDS_BROWSEFORFS, szTitle, ARRAYSIZE(szTitle));
|
|
TCHAR szAltPath[MAX_PATH];
|
|
|
|
BROWSEINFO bi = {0};
|
|
bi.hwndOwner = hDlg;
|
|
bi.pidlRoot = NULL;
|
|
bi.pszDisplayName = szAltPath;
|
|
bi.lpszTitle = szTitle;
|
|
bi.ulFlags = BIF_USENEWUI | BIF_EDITBOX;
|
|
LPITEMIDLIST pidlFull = SHBrowseForFolder(&bi);
|
|
if (pidlFull)
|
|
{
|
|
ILFree(pfpsp->pidlTarget);
|
|
pfpsp->pidlTarget = pidlFull;
|
|
|
|
if (SetPidlToWindow(hDlg, IDD_TARGET, pfpsp->pidlTarget))
|
|
{
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
|
|
pfpsp->fValidateEdit = FALSE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return TRUE;
|
|
}
|
|
|
|
// check to see if we need to enable the Apply button
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
|
|
{
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
Free_DlgDependentFilePropSheetPage(pfpsp);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR *)lParam)->code)
|
|
{
|
|
case PSN_APPLY:
|
|
// check to see if we could apply the name change. Note that this
|
|
// does not actually apply the change until PSN_LASTCHANCEAPPLY
|
|
pfpsp->fCanRename = TRUE;
|
|
if (pfpsp->fRename && !ApplyRename(pfpsp, FALSE))
|
|
{
|
|
// can't change the name so don't let the dialog close
|
|
pfpsp->fCanRename = FALSE;
|
|
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
|
|
return TRUE;
|
|
}
|
|
|
|
if (pfpsp->fFolderShortcut)
|
|
{
|
|
#ifdef FOLDERSHORTCUT_EDITABLETARGET
|
|
if (!SetFolderShortcutInfo(hDlg, pfpsp))
|
|
{
|
|
// Display that we could not create because blah, blah, blah
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_FOLDERSHORTCUT_ERR),
|
|
MAKEINTRESOURCE(IDS_FOLDERSHORTCUT_ERR_TITLE),
|
|
MB_OK | MB_ICONSTOP);
|
|
|
|
// Reset the Folder info.
|
|
SetPidlToWindow(hDlg, IDD_TARGET, pfpsp->pidlTarget);
|
|
|
|
// Don't close the dialog.
|
|
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
UINT uReadonlyState = IsDlgButtonChecked(hDlg, IDD_READONLY);
|
|
switch (uReadonlyState)
|
|
{
|
|
case BST_CHECKED:
|
|
pfpsp->asCurrent.fReadOnly = TRUE;
|
|
break;
|
|
|
|
case BST_UNCHECKED:
|
|
pfpsp->asCurrent.fReadOnly = FALSE;
|
|
break;
|
|
|
|
case BST_INDETERMINATE:
|
|
// read-only checkbox is initaially set to BST_INDETERMINATE for folders
|
|
ASSERT(pfpsp->fIsDirectory);
|
|
ASSERT(pfpsp->asInitial.fReadOnly == BST_INDETERMINATE);
|
|
pfpsp->asCurrent.fReadOnly = BST_INDETERMINATE;
|
|
break;
|
|
}
|
|
|
|
pfpsp->asCurrent.fHidden = (IsDlgButtonChecked(hDlg, IDD_HIDDEN) == BST_CHECKED);
|
|
|
|
// Archive is on the general page for FAT volumes
|
|
if (!pfpsp->pfci->fIsCompressionAvailable)
|
|
{
|
|
pfpsp->asCurrent.fArchive = (IsDlgButtonChecked(hDlg, IDD_ARCHIVE) == BST_CHECKED);
|
|
}
|
|
|
|
// check to see if the user actually changed something, if they didnt, then
|
|
// we dont have to apply anything
|
|
if (memcmp(&pfpsp->asInitial, &pfpsp->asCurrent, sizeof(pfpsp->asInitial)) != 0)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
// Check to see if the user wants to apply the attribs recursively or not. If the
|
|
// directory is empty, dont bother to ask, since there is nothing to recurse into
|
|
if (pfpsp->fIsDirectory && !PathIsDirectoryEmpty(pfpsp->szPath))
|
|
{
|
|
bRet = (int)DialogBoxParam(HINST_THISDLL,
|
|
MAKEINTRESOURCE(DLG_ATTRIBS_RECURSIVE),
|
|
hDlg,
|
|
RecursivePromptDlgProc,
|
|
(LPARAM)pfpsp);
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
bRet = ApplySingleFileAttributes(pfpsp);
|
|
}
|
|
|
|
if (!bRet)
|
|
{
|
|
// the user hit cancel, so we return true to prevent the property sheet from closing
|
|
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// update the size / last accessed time
|
|
UpdateSizeField(pfpsp, NULL);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PSN_SETACTIVE:
|
|
if (pfpsp->fIsLink)
|
|
{
|
|
// If this is a link, each time we get set active we need to check to see
|
|
// if the user applied changes on the link tab that would affect the
|
|
// "Opens With:" info.
|
|
UpdateOpensWithInfo(pfpsp);
|
|
}
|
|
break;
|
|
|
|
case PSN_QUERYINITIALFOCUS:
|
|
// Special hack: We do not want initial focus on the "Rename" or "Change" controls, since
|
|
// if the user hit something by accident they would start renaming/modifying the assoc. So
|
|
// we set the focus to the "Read-only" control since it is present on all dialogs that use
|
|
// this wndproc (file, folder, and mounted drive)
|
|
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hDlg, IDD_READONLY));
|
|
return TRUE;
|
|
|
|
case PSN_LASTCHANCEAPPLY:
|
|
//
|
|
// HACKHACK (reinerf)
|
|
//
|
|
// I hacked PSN_LASTCHANCEAPPLY into the prsht code so we can get a notification after
|
|
// every other app has applied, then we can go and do the rename.
|
|
//
|
|
// strangely, PSN_LASTCHANCEAPPLY is called even if PSN_APPY returns TRUE.
|
|
//
|
|
// we can now safely rename the file, since all the other tabs have
|
|
// applied their stuff.
|
|
if (pfpsp->fRename && pfpsp->fCanRename)
|
|
{
|
|
// dont bother to check the return value since this is the last-chance,
|
|
// so the dialog is ending shortly after this
|
|
ApplyRename(pfpsp, TRUE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// This function consists of code that does some
|
|
// Initialization for the file property sheet dialog.
|
|
|
|
STDAPI InitCommonPrsht(FILEPROPSHEETPAGE * pfpsp)
|
|
{
|
|
pfpsp->psp.dwSize = sizeof(FILEPROPSHEETPAGE); // extra data
|
|
pfpsp->psp.dwFlags = PSP_USECALLBACK;
|
|
pfpsp->psp.hInstance = HINST_THISDLL;
|
|
pfpsp->psp.pfnCallback = NULL; //FilePrshtCallback;
|
|
pfpsp->pfci->bContinue = TRUE;
|
|
|
|
// Do basic init for file system props
|
|
if (HIDA_GetCount(pfpsp->pfci->hida) == 1) // single file?
|
|
{
|
|
// get most of the data we will need (the date/time stuff is not filled in)
|
|
if (HIDA_FillFindData(pfpsp->pfci->hida, 0, pfpsp->szPath, &(pfpsp->pfci->fd), FALSE))
|
|
{
|
|
pfpsp->fd = pfpsp->pfci->fd;
|
|
LPITEMIDLIST pidl = HIDA_ILClone(pfpsp->pfci->hida, 0);
|
|
if (pidl)
|
|
{
|
|
// disable renaming here.
|
|
DWORD dwAttrs = SFGAO_CANRENAME;
|
|
if (SUCCEEDED(SHGetNameAndFlags(pidl, 0, NULL, 0, &dwAttrs)) && !(dwAttrs & SFGAO_CANRENAME))
|
|
{
|
|
pfpsp->fDisableRename = TRUE;
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
|
|
if (pfpsp->pfci->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
pfpsp->fIsDirectory = TRUE;
|
|
// check for HostFolder folder mounting a volume)
|
|
//GetVolumeNameFromMountPoint Succeeds then the give path is a mount point
|
|
|
|
|
|
//Make sure the path ends with a backslash. otherwise the following api wont work
|
|
TCHAR szPathSlash[MAX_PATH];
|
|
lstrcpyn(szPathSlash, pfpsp->szPath, ARRAYSIZE(szPathSlash));
|
|
PathAddBackslash(szPathSlash);
|
|
|
|
// Is this a mounted volume at this folder?
|
|
// this fct will return FALSE if not on NT5 and higher
|
|
TCHAR szVolumeName[MAX_PATH];
|
|
if (GetVolumeNameForVolumeMountPoint(szPathSlash, szVolumeName, ARRAYSIZE(szVolumeName)))
|
|
{
|
|
// Yes; show the Mounted Drive Propertysheet instead of normal
|
|
// folder property sheet
|
|
// fpsp.fMountedDrive also means NT5 or higher, because this fct will fail otherwise
|
|
pfpsp->fMountedDrive = TRUE;
|
|
}
|
|
|
|
// check to see if it's a folder shortcut
|
|
if (!(pfpsp->fMountedDrive))
|
|
{
|
|
// Folder and a shortcut? Must be a folder shortcut!
|
|
if (PathIsShortcut(pfpsp->szPath, pfpsp->pfci->fd.dwFileAttributes))
|
|
{
|
|
pfpsp->fFolderShortcut = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
DWORD dwVolumeFlags = GetVolumeFlags(pfpsp->szPath,
|
|
pfpsp->szFileSys,
|
|
ARRAYSIZE(pfpsp->szFileSys));
|
|
|
|
// test for file-based compression.
|
|
if (dwVolumeFlags & FS_FILE_COMPRESSION)
|
|
{
|
|
// filesystem supports compression
|
|
pfpsp->pfci->fIsCompressionAvailable = TRUE;
|
|
}
|
|
|
|
// test for file-based encryption.
|
|
if ((dwVolumeFlags & FS_FILE_ENCRYPTION) && !SHRestricted(REST_NOENCRYPTION))
|
|
{
|
|
// filesystem supports encryption
|
|
pfpsp->fIsEncryptionAvailable = TRUE;
|
|
}
|
|
|
|
//
|
|
// HACKHACK (reinerf) - we dont have a FS_SUPPORTS_INDEXING so we
|
|
// use the FILE_SUPPORTS_SPARSE_FILES flag, because native index support
|
|
// appeared first on NTFS5 volumes, at the same time sparse file support
|
|
// was implemented.
|
|
//
|
|
if (dwVolumeFlags & FILE_SUPPORTS_SPARSE_FILES)
|
|
{
|
|
// yup, we are on NTFS5 or greater
|
|
pfpsp->fIsIndexAvailable = TRUE;
|
|
}
|
|
|
|
// check to see if we have a .exe and we need to prompt for user logon
|
|
pfpsp->fIsExe = PathIsBinaryExe(pfpsp->szPath);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we have multiple files
|
|
pfpsp->pfci->fMultipleFiles = TRUE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Descriptions:
|
|
// This function creates a property sheet object for the "general" page
|
|
// which shows file system attributes.
|
|
//
|
|
// Arguments:
|
|
// hDrop -- specifies the file(s)
|
|
// pfnAddPage -- Specifies the callback function.
|
|
// lParam -- Specifies the lParam to be passed to the callback.
|
|
//
|
|
// Returns:
|
|
// TRUE if it added any pages
|
|
//
|
|
// History:
|
|
// 12-31-92 SatoNa Created
|
|
//
|
|
STDAPI FileSystem_AddPages(IDataObject *pdtobj, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
|
|
{
|
|
FILEPROPSHEETPAGE fpsp = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
fpsp.pfci = Create_FolderContentsInfo();
|
|
if (fpsp.pfci)
|
|
{
|
|
hr = DataObj_CopyHIDA(pdtobj, &fpsp.pfci->hida);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InitCommonPrsht(&fpsp);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fpsp.psp.pfnCallback = FilePrshtCallback;
|
|
|
|
UINT uRes;
|
|
if (!fpsp.pfci->fMultipleFiles)
|
|
{
|
|
fpsp.psp.pfnDlgProc = SingleFilePrshtDlgProc;
|
|
if (fpsp.fIsDirectory)
|
|
{
|
|
if (fpsp.fMountedDrive)
|
|
{
|
|
uRes = DLG_MOUNTEDDRV_GENERAL;
|
|
}
|
|
else if (fpsp.fFolderShortcut)
|
|
{
|
|
uRes = DLG_FOLDERSHORTCUTPROP;
|
|
}
|
|
else
|
|
{
|
|
uRes = DLG_FOLDERPROP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Files
|
|
uRes = DLG_FILEPROP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Multiple Files / Folders.
|
|
fpsp.psp.pfnDlgProc = MultiplePrshtDlgProc;
|
|
uRes = DLG_FILEMULTPROP;
|
|
}
|
|
fpsp.psp.pszTemplate = MAKEINTRESOURCE(uRes);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HPROPSHEETPAGE hpage = CreatePropertySheetPage(&fpsp.psp);
|
|
if (hpage)
|
|
{
|
|
if (pfnAddPage(hpage, lParam))
|
|
{
|
|
hr = S_OK;
|
|
if (!fpsp.pfci->fMultipleFiles)
|
|
{
|
|
if (AddLinkPage(fpsp.szPath, pfnAddPage, lParam))
|
|
{
|
|
// set second page default!
|
|
hr = ResultFromShort(2);
|
|
}
|
|
AddVersionPage(fpsp.szPath, pfnAddPage, lParam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DestroyPropertySheetPage(hpage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Free_DlgIndepFilePropSheetPage(&fpsp);
|
|
}
|
|
|
|
return hr;
|
|
}
|