You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
704 lines
22 KiB
704 lines
22 KiB
#include "shellprv.h"
|
|
#include "propsht.h"
|
|
#include "sencrypt.h"
|
|
#include "datautil.h"
|
|
|
|
#define IDM_ENCRYPT 0
|
|
#define IDM_DECRYPT 1
|
|
#define BOOL_UNINIT 5
|
|
|
|
// Local fns to this .cpp file
|
|
STDAPI CEncryptionContextMenuHandler_CreateInstance(IUnknown *punk, REFIID riid, void **ppv);
|
|
BOOL InitSinglePrshtNoDlg(FILEPROPSHEETPAGE * pfpsp);
|
|
BOOL InitMultiplePrshtNoDlg(FILEPROPSHEETPAGE* pfpsp);
|
|
|
|
// Class definition
|
|
class CEncryptionContextMenu : public IShellExtInit, public IContextMenu
|
|
{
|
|
public:
|
|
CEncryptionContextMenu();
|
|
HRESULT Init_FolderContentsInfo();
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IContextMenu
|
|
STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
|
|
STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici);
|
|
STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
|
|
|
|
// IShellExtInit
|
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
|
|
|
|
private:
|
|
virtual ~CEncryptionContextMenu();
|
|
static DWORD CALLBACK EncryptThreadProc(void *pv) { return ((CEncryptionContextMenu *) pv)->_Encrypt(); };
|
|
DWORD _Encrypt();
|
|
BOOL _InitPrsht(FILEPROPSHEETPAGE * pfpsp);
|
|
BOOL _AreFilesEncryptable(IDataObject *pdtobj);
|
|
|
|
LONG _cRef; // Reference count
|
|
UINT _uFileCount; // number of files selected
|
|
HWND _hwnd; // Window that we're working over
|
|
BOOL _fEncrypt; // If true, do encrypt; if false, do decrypt
|
|
FILEPROPSHEETPAGE _fpsp; // Prop sheet page to be filled in and run through properties funcs
|
|
BOOL _fEncryptAllowed; // True iff we are allowed to encrypt
|
|
IDataObject *_pdtobj; // Our data object. Keep in orig. thread
|
|
TCHAR _szPath[MAX_PATH]; // Path of first thing clicked on
|
|
};
|
|
|
|
|
|
// Constructor & Destructor
|
|
CEncryptionContextMenu::CEncryptionContextMenu() : _cRef(1)
|
|
{
|
|
DllAddRef();
|
|
|
|
_fEncryptAllowed = FALSE; // compute this at ::Initialize() time
|
|
|
|
_uFileCount = 0;
|
|
_hwnd = 0;
|
|
_fEncrypt = FALSE;
|
|
_pdtobj = NULL;
|
|
ZeroMemory(&_fpsp, sizeof(_fpsp));
|
|
}
|
|
|
|
CEncryptionContextMenu::~CEncryptionContextMenu()
|
|
{
|
|
ATOMICRELEASE(_pdtobj);
|
|
if (_fpsp.pfci)
|
|
{
|
|
Release_FolderContentsInfo(_fpsp.pfci);
|
|
}
|
|
DllRelease();
|
|
}
|
|
|
|
HRESULT CEncryptionContextMenu::Init_FolderContentsInfo()
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
_fpsp.pfci = Create_FolderContentsInfo();
|
|
if (_fpsp.pfci)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// IUnknown implementation. Standard stuff, nothing fancy.
|
|
HRESULT CEncryptionContextMenu::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CEncryptionContextMenu, IShellExtInit),
|
|
QITABENT(CEncryptionContextMenu, IContextMenu),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
ULONG CEncryptionContextMenu::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CEncryptionContextMenu::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
// IShellExtInit implementation
|
|
|
|
STDMETHODIMP CEncryptionContextMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
// registry key that enables/disables this menu
|
|
BOOL fEnableEncryptMenu = SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
|
|
TEXT("EncryptionContextMenu"), 0, 0);
|
|
|
|
if (fEnableEncryptMenu && !SHRestricted(REST_NOENCRYPTION) && !_fEncryptAllowed)
|
|
{
|
|
_fEncryptAllowed = _AreFilesEncryptable(pdtobj);
|
|
if (_fEncryptAllowed)
|
|
{
|
|
IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Checks the data object to see if we can encrypt here.
|
|
BOOL CEncryptionContextMenu::_AreFilesEncryptable(IDataObject *pdtobj)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
|
|
STGMEDIUM medium;
|
|
FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
if (SUCCEEDED(pdtobj->GetData(&fe, &medium)))
|
|
{
|
|
// Get the file name from the CF_HDROP.
|
|
_uFileCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0);
|
|
if (_uFileCount)
|
|
{
|
|
if (DragQueryFile((HDROP)medium.hGlobal, 0, _szPath, ARRAYSIZE(_szPath)))
|
|
{
|
|
TCHAR szFileSys[MAX_PATH];
|
|
fSuccess = (FS_FILE_ENCRYPTION & GetVolumeFlags(_szPath, szFileSys, ARRAYSIZE(szFileSys)));
|
|
}
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return fSuccess;
|
|
}
|
|
|
|
// IContextMenuHandler impelementation
|
|
STDMETHODIMP CEncryptionContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu,
|
|
UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if ((uFlags & CMF_DEFAULTONLY) || !_fEncryptAllowed)
|
|
{
|
|
hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(1)); //this menu only allows the defaults or we can't encrypt
|
|
}
|
|
else
|
|
{
|
|
TCHAR szEncryptMsg[128], szDecryptMsg[128];
|
|
|
|
// If only one item is selected, display enc or dec as appropriate
|
|
if (_uFileCount == 1)
|
|
{
|
|
DWORD dwAttribs = GetFileAttributes(_szPath);
|
|
if (dwAttribs != (DWORD)-1)
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_ECM_ENCRYPT, szEncryptMsg, ARRAYSIZE(szEncryptMsg));
|
|
if (!(dwAttribs & FILE_ATTRIBUTE_ENCRYPTED))
|
|
{
|
|
if (InsertMenu(hmenu,
|
|
indexMenu,
|
|
MF_STRING | MF_BYPOSITION,
|
|
idCmdFirst + IDM_ENCRYPT,
|
|
szEncryptMsg))
|
|
{
|
|
hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_ENCRYPT + 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_ECM_DECRYPT, szDecryptMsg, ARRAYSIZE(szDecryptMsg));
|
|
if (InsertMenu(hmenu,
|
|
indexMenu,
|
|
MF_STRING | MF_BYPOSITION,
|
|
idCmdFirst + IDM_ENCRYPT + 1,
|
|
szDecryptMsg))
|
|
{
|
|
hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DECRYPT + 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (idCmdLast - idCmdFirst >= 2)
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_ECM_ENCRYPT, szEncryptMsg, ARRAYSIZE(szDecryptMsg));
|
|
LoadString(HINST_THISDLL, IDS_ECM_DECRYPT, szDecryptMsg, ARRAYSIZE(szDecryptMsg));
|
|
|
|
// If more than one item is selected, display both enc and dec
|
|
if (InsertMenu(hmenu,
|
|
indexMenu,
|
|
MF_STRING | MF_BYPOSITION,
|
|
idCmdFirst + IDM_ENCRYPT,
|
|
szEncryptMsg))
|
|
{
|
|
if (InsertMenu(hmenu,
|
|
indexMenu + 1,
|
|
MF_STRING | MF_BYPOSITION,
|
|
idCmdFirst + IDM_DECRYPT,
|
|
szDecryptMsg))
|
|
{
|
|
hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DECRYPT + 1));
|
|
}
|
|
else
|
|
{
|
|
// If you can't add both, add neither
|
|
RemoveMenu(hmenu, indexMenu, MF_BYPOSITION);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
const ICIVERBTOIDMAP c_IDMap[] =
|
|
{
|
|
{ L"encrypt", "encrypt", IDM_ENCRYPT, IDM_ENCRYPT, },
|
|
{ L"decrypt", "decrypt", IDM_DECRYPT, IDM_DECRYPT, },
|
|
};
|
|
|
|
STDMETHODIMP CEncryptionContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
|
{
|
|
UINT uID;
|
|
HRESULT hr = E_FAIL;
|
|
if (_fEncryptAllowed)
|
|
{
|
|
hr = SHMapICIVerbToCmdID(pici, c_IDMap, ARRAYSIZE(c_IDMap), &uID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
switch (uID)
|
|
{
|
|
case IDM_ENCRYPT:
|
|
case IDM_DECRYPT:
|
|
_fEncrypt = (IDM_ENCRYPT == uID);
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG(0, "Should never get commands we didn't put on the menu...");
|
|
break;
|
|
}
|
|
|
|
_hwnd = pici->hwnd; // The handle to the explorer window that called us.
|
|
|
|
ASSERT(NULL == _fpsp.pfci->hida);
|
|
|
|
hr = DataObj_CopyHIDA(_pdtobj, &_fpsp.pfci->hida);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
AddRef(); // Give our background thread a ref
|
|
|
|
// Start the new thread here
|
|
if (SHCreateThread(EncryptThreadProc, this, CTF_COINIT | CTF_FREELIBANDEXIT, NULL))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Release(); // thread create failed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we succeeded or not, we give up our data here
|
|
ATOMICRELEASE(_pdtobj);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CEncryptionContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags,
|
|
UINT *pRes, LPSTR pszName, UINT uMaxNameLen)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
// Note that because we can be specifically asked for
|
|
// UNICODE or ansi strings, we have to be ready to load all strings in
|
|
// either version.
|
|
|
|
if (idCommand == IDM_ENCRYPT ||
|
|
idCommand == IDM_DECRYPT)
|
|
{
|
|
switch(uFlags)
|
|
{
|
|
case GCS_HELPTEXTA:
|
|
if (idCommand == IDM_ENCRYPT)
|
|
{
|
|
LoadStringA(HINST_THISDLL, IDS_ECM_ENCRYPT_HELP, pszName, uMaxNameLen);
|
|
}
|
|
else
|
|
{
|
|
LoadStringA(HINST_THISDLL, IDS_ECM_DECRYPT_HELP, pszName, uMaxNameLen);
|
|
}
|
|
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case GCS_HELPTEXTW:
|
|
if (idCommand == IDM_ENCRYPT)
|
|
{
|
|
LoadStringW(HINST_THISDLL, IDS_ECM_ENCRYPT_HELP, (LPWSTR)pszName, uMaxNameLen);
|
|
}
|
|
else
|
|
{
|
|
LoadStringW(HINST_THISDLL, IDS_ECM_DECRYPT_HELP, (LPWSTR)pszName, uMaxNameLen);
|
|
}
|
|
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case GCS_VERBA:
|
|
case GCS_VERBW:
|
|
hr = SHMapCmdIDToVerb(idCommand, c_IDMap, ARRAYSIZE(c_IDMap), pszName, uMaxNameLen, GCS_VERBW == uFlags);
|
|
break;
|
|
|
|
default:
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
STDAPI CEncryptionContextMenuHandler_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppv = NULL;
|
|
CEncryptionContextMenu *pdocp = new CEncryptionContextMenu();
|
|
if (pdocp)
|
|
{
|
|
hr = pdocp->Init_FolderContentsInfo();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pdocp->QueryInterface(riid, ppv);
|
|
}
|
|
pdocp->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Multithread code
|
|
|
|
// Proc for thread that does the encryption and manages the progress dialong.
|
|
// The passed parameter is a ptr hwnd to be made modal for the context menu
|
|
DWORD CEncryptionContextMenu::_Encrypt(void)
|
|
{
|
|
// Transfer ownership in case someone reenters our thread creator
|
|
// Init the property sheet
|
|
BOOL fSuccess = _InitPrsht(&_fpsp);
|
|
if (fSuccess)
|
|
{
|
|
// Set encryption opts, turn off compression
|
|
if (_fEncrypt)
|
|
{
|
|
_fpsp.asCurrent.fCompress = FALSE;
|
|
_fpsp.asCurrent.fEncrypt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
_fpsp.asCurrent.fEncrypt = FALSE;
|
|
}
|
|
|
|
// See if the user wants to do this recursive-style
|
|
if (_fpsp.fIsDirectory)
|
|
{
|
|
// check to see if the user wants to apply the attribs recursively or not
|
|
fSuccess = (int)DialogBoxParam(HINST_THISDLL,
|
|
MAKEINTRESOURCE(DLG_ATTRIBS_RECURSIVE),
|
|
_hwnd, RecursivePromptDlgProc, (LPARAM)&_fpsp);
|
|
}
|
|
|
|
// Apply encryption, remember to turn off compression
|
|
if (fSuccess)
|
|
{
|
|
if (_fpsp.pfci->fMultipleFiles || _fpsp.fRecursive)
|
|
{
|
|
ApplyMultipleFileAttributes(&_fpsp);
|
|
}
|
|
else
|
|
{
|
|
ApplySingleFileAttributesNoDlg(&_fpsp, _hwnd);
|
|
}
|
|
}
|
|
}
|
|
// As far as I can tell, nothing in fpsp must be freed
|
|
// But, we give up the storage medium here
|
|
Release(); // Release our ref
|
|
return fSuccess; // Must give a ret value
|
|
}
|
|
|
|
// Helper to init the passed prsht
|
|
BOOL CEncryptionContextMenu::_InitPrsht(FILEPROPSHEETPAGE * pfpsp)
|
|
{
|
|
// Init the propsht properly
|
|
BOOL fSuccess = S_OK == InitCommonPrsht(pfpsp);
|
|
if (fSuccess)
|
|
{
|
|
if (_uFileCount == 1)
|
|
{
|
|
fSuccess = InitSinglePrshtNoDlg(pfpsp);
|
|
}
|
|
else if (_uFileCount > 1)
|
|
{
|
|
fSuccess = InitMultiplePrshtNoDlg(pfpsp);
|
|
}
|
|
}
|
|
return fSuccess;
|
|
}
|
|
|
|
//
|
|
// Descriptions:
|
|
// This function fills fields of the multiple object property sheet,
|
|
// without getting the current state from the dialog.
|
|
//
|
|
BOOL InitMultiplePrshtNoDlg(FILEPROPSHEETPAGE* pfpsp)
|
|
{
|
|
SHFILEINFO sfi;
|
|
TCHAR szBuffer[MAX_PATH+1];
|
|
TCHAR szType[MAX_PATH] = {0};
|
|
TCHAR szDirPath[MAX_PATH] = {0};
|
|
int iItem;
|
|
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
|
|
|
|
// For all the selected files compare their types and get their attribs
|
|
for (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] == TEXT('\0'))
|
|
StringCchCopy(szType, ARRAYSIZE(szType), sfi.szTypeName);
|
|
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] == TEXT('\0'))
|
|
StrCpyN(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;
|
|
}
|
|
|
|
//
|
|
// 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 (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;
|
|
|
|
if (fSameLocation)
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_ALLIN, szBuffer, ARRAYSIZE(szBuffer));
|
|
StrCatBuff(szBuffer, szDirPath, ARRAYSIZE(szBuffer));
|
|
StrCpyN(pfpsp->szPath, szDirPath, ARRAYSIZE(pfpsp->szPath));
|
|
}
|
|
|
|
UpdateSizeField(pfpsp, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Descriptions:
|
|
// This function fills fields of the "general" dialog box (a page of
|
|
// a property sheet) with attributes of the associated file. Doesn't
|
|
// make calss to hDlg
|
|
//
|
|
BOOL InitSinglePrshtNoDlg(FILEPROPSHEETPAGE * pfpsp)
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
SHFILEINFO sfi;
|
|
|
|
// fd is filled in with info from the pidl, but this
|
|
// does not contain all the date/time information so hit the disk here.
|
|
HANDLE hfind = FindFirstFile(pfpsp->szPath, &pfpsp->fd);
|
|
ASSERT(hfind != INVALID_HANDLE_VALUE);
|
|
if (hfind == INVALID_HANDLE_VALUE)
|
|
{
|
|
// if this failed we should clear out some values as to not show garbage on the screen.
|
|
ZeroMemory(&pfpsp->fd, sizeof(pfpsp->fd));
|
|
}
|
|
else
|
|
{
|
|
FindClose(hfind);
|
|
}
|
|
|
|
// 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 (StrCmpI(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;
|
|
}
|
|
}
|
|
|
|
// set the initial rename state
|
|
pfpsp->fRename = FALSE;
|
|
|
|
// set the file type
|
|
if (pfpsp->fMountedDrive)
|
|
{
|
|
TCHAR szVolumeGUID[MAX_PATH];
|
|
TCHAR szVolumeLabel[MAX_PATH];
|
|
|
|
//Borrow szVolumeGUID
|
|
LoadString(HINST_THISDLL, IDS_MOUNTEDVOLUME, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
|
|
|
|
//use szVolumeLabel temporarily
|
|
StringCchCopy(szVolumeLabel, ARRAYSIZE(szVolumeLabel), pfpsp->szPath);
|
|
PathAddBackslash(szVolumeLabel);
|
|
GetVolumeNameForVolumeMountPoint(szVolumeLabel, szVolumeGUID, ARRAYSIZE(szVolumeGUID));
|
|
|
|
if (!GetVolumeInformation(szVolumeGUID, szVolumeLabel, ARRAYSIZE(szVolumeLabel),
|
|
NULL, NULL, NULL, pfpsp->szFileSys, ARRAYSIZE(pfpsp->szFileSys)))
|
|
{
|
|
*szVolumeLabel = 0;
|
|
}
|
|
|
|
if (!(*szVolumeLabel))
|
|
LoadString(HINST_THISDLL, IDS_UNLABELEDVOLUME, szVolumeLabel, ARRAYSIZE(szVolumeLabel));
|
|
}
|
|
|
|
// save off the initial short filename, and set the "Name" edit box
|
|
StringCchCopy(pfpsp->szInitialName, ARRAYSIZE(pfpsp->szInitialName), 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;
|
|
}
|
|
|
|
StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), pfpsp->szPath);
|
|
PathRemoveFileSpec(szBuffer);
|
|
|
|
// Are we a folder shortcut?
|
|
if (!pfpsp->fFolderShortcut)
|
|
{
|
|
// set the initial attributes
|
|
SetInitialFileAttribs(pfpsp, pfpsp->fd.dwFileAttributes, pfpsp->fd.dwFileAttributes);
|
|
|
|
// set the current attributes to the same as the initial
|
|
pfpsp->asCurrent = pfpsp->asInitial;
|
|
|
|
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
|
|
{
|
|
pfpsp->fIsDirectory = TRUE;
|
|
}
|
|
|
|
// get the full path to the folder that contains this file.
|
|
StrCpyN(szBuffer, pfpsp->szPath, ARRAYSIZE(szBuffer));
|
|
PathRemoveFileSpec(szBuffer);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
STDAPI_(BOOL) ApplySingleFileAttributesNoDlg(FILEPROPSHEETPAGE* pfpsp, HWND hwnd)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
BOOL bSomethingChanged = FALSE;
|
|
|
|
if (!pfpsp->fRecursive)
|
|
{
|
|
bRet = ApplyFileAttributes(pfpsp->szPath, pfpsp, hwnd, &bSomethingChanged);
|
|
|
|
if (bSomethingChanged)
|
|
{
|
|
// something changed, so generate a notification for the item
|
|
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);
|
|
|
|
CreateAttributeProgressDlg(pfpsp);
|
|
|
|
// apply attribs to this folder & sub files/folders
|
|
bRet = ApplyRecursiveFolderAttribs(pfpsp->szPath, pfpsp);
|
|
|
|
// 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// handle any events we may have generated
|
|
SHChangeNotifyHandleEvents();
|
|
|
|
return bRet;
|
|
}
|