2649 lines
81 KiB
C++
2649 lines
81 KiB
C++
#include "shellprv.h"
|
|
#include "caggunk.h"
|
|
#include "views.h"
|
|
#include "drives.h"
|
|
#include "netview.h"
|
|
#include "propsht.h"
|
|
#include "infotip.h"
|
|
#include "mtpt.h"
|
|
#include "prop.h"
|
|
#include "defcm.h"
|
|
|
|
#include "basefvcb.h"
|
|
#include "fstreex.h"
|
|
#include "ovrlaymn.h"
|
|
|
|
#include "shitemid.h"
|
|
#include "clsobj.h"
|
|
|
|
#include "deskfldr.h"
|
|
#include "datautil.h"
|
|
|
|
#include <ntddcdrm.h>
|
|
#include <cfgmgr32.h> // MAX_GUID_STRING_LEN
|
|
#include "ole2dup.h"
|
|
|
|
#include "category.h"
|
|
#define EXCLUDE_COMPPROPSHEET
|
|
#include "unicpp\dcomp.h"
|
|
#undef EXCLUDE_COMPPROPSHEET
|
|
|
|
#include "enumidlist.h"
|
|
#include <enumt.h>
|
|
|
|
#define ShowDriveInfo(_iDrive) (!IsRemovableDrive(_iDrive))
|
|
|
|
#define CDRIVES_REGITEM_CONTROL 0
|
|
#define IDLIST_DRIVES ((LPCITEMIDLIST)&c_idlDrives)
|
|
|
|
// These are the sort order for items in MyComputer
|
|
#define CONTROLS_SORT_INDEX 30
|
|
#define CDRIVES_REGITEM_CONTROL 0
|
|
|
|
REQREGITEM g_asDrivesReqItems[] =
|
|
{
|
|
{ &CLSID_ControlPanel, IDS_CONTROLPANEL, c_szShell32Dll, -IDI_CPLFLD, CONTROLS_SORT_INDEX, SFGAO_FOLDER | SFGAO_HASSUBFOLDER, NULL},
|
|
};
|
|
|
|
|
|
STDAPI CDriveExtractImage_Create(LPCIDDRIVE pidd, REFIID riid, void **ppvObj);
|
|
|
|
|
|
class CDrivesViewCallback;
|
|
class CDrivesFolderEnum;
|
|
|
|
class CDrivesBackgroundMenuCB : public IContextMenuCB
|
|
{
|
|
public:
|
|
CDrivesBackgroundMenuCB(LPITEMIDLIST pidlFolder);
|
|
~CDrivesBackgroundMenuCB();
|
|
// IUnknown
|
|
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
|
|
STDMETHOD_(ULONG,AddRef)();
|
|
STDMETHOD_(ULONG,Release)();
|
|
// IContextMenuCB
|
|
STDMETHOD(CallBack) (IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
private:
|
|
STDMETHOD(_GetHelpText) (UINT offset, BOOL bWide, LPARAM lParam, UINT cch);
|
|
LPITEMIDLIST _pidlFolder;
|
|
LONG _cRef;
|
|
};
|
|
|
|
class CDrivesFolder : public CAggregatedUnknown, IShellFolder2, IPersistFolder2, IShellIconOverlay
|
|
{
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj)
|
|
{ return CAggregatedUnknown::QueryInterface(riid, ppvObj); };
|
|
STDMETHODIMP_(ULONG) AddRef(void)
|
|
{ return CAggregatedUnknown::AddRef(); };
|
|
STDMETHODIMP_(ULONG) Release(void)
|
|
{ return CAggregatedUnknown::Release(); };
|
|
|
|
// IShellFolder
|
|
STDMETHODIMP ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPOLESTR pszDisplayName,
|
|
ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
|
|
STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList);
|
|
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppvOut);
|
|
STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppvObj);
|
|
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void** ppvOut);
|
|
STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
|
|
STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl,
|
|
REFIID riid, UINT* prgfInOut, void** ppvOut);
|
|
STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
|
|
STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags,
|
|
LPITEMIDLIST* ppidlOut);
|
|
|
|
// IShellFolder2
|
|
STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
|
|
STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
|
|
STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay);
|
|
STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState);
|
|
STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv);
|
|
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails);
|
|
STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid);
|
|
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID* pClassID);
|
|
|
|
// IPersistFolder
|
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
|
|
|
|
// IPersistFolder2
|
|
STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
|
|
|
|
// IShellIconOverlay
|
|
STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int* pIndex);
|
|
STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int* pIconIndex);
|
|
|
|
STDMETHODIMP GetMaxNameLength(LPCITEMIDLIST pidlItem, UINT *pcchMax);
|
|
|
|
protected:
|
|
CDrivesFolder(IUnknown* punkOuter);
|
|
~CDrivesFolder();
|
|
|
|
// used by the CAggregatedUnknown stuff
|
|
HRESULT v_InternalQueryInterface(REFIID riid, void **ppvObj);
|
|
BOOL v_HandleDelete(PLONG pcRef);
|
|
|
|
STDMETHODIMP CompareItemIDs(LPCIDDRIVE pidd1, LPCIDDRIVE pidd2);
|
|
static BOOL _GetFreeSpace(LPCIDDRIVE pidd, ULONGLONG *pSize, ULONGLONG *pFree);
|
|
static HRESULT _OnChangeNotify(LPARAM lNotification, LPCITEMIDLIST *ppidl);
|
|
static HRESULT _GetCLSIDFromPidl(LPCIDDRIVE pidd, CLSID *pclsid);
|
|
static HRESULT _CheckDriveType(int iDrive, LPCTSTR pszCLSID);
|
|
static HRESULT _FindExtCLSID(int iDrive, CLSID *pclsid);
|
|
static HRESULT _FillIDDrive(DRIVE_IDLIST *piddl, int iDrive, BOOL fNoCLSID, IBindCtx* pbc);
|
|
static LPCIDDRIVE _IsValidID(LPCITEMIDLIST pidl);
|
|
static HRESULT _GetDisplayNameStrRet(LPCIDDRIVE pidd, STRRET *pStrRet);
|
|
static HRESULT _GetDisplayName(LPCIDDRIVE pidd, LPTSTR pszName, UINT cchMax);
|
|
static HRESULT _CreateFSFolderObj(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv);
|
|
static HRESULT _CreateFSFolder(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv);
|
|
static HRESULT _GetEditTextStrRet(LPCIDDRIVE pidd, STRRET *pstr);
|
|
static BOOL _IsReg(LPCIDDRIVE pidd) { return pidd->bFlags == SHID_COMPUTER_REGITEM; }
|
|
static HRESULT _GetIconOverlayInfo(LPCIDDRIVE pidd, int *pIndex, DWORD dwFlags);
|
|
|
|
static CDrivesFolder* _spThis;
|
|
|
|
private:
|
|
friend HRESULT CDrives_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv);
|
|
friend void CDrives_Terminate(void);
|
|
|
|
friend CDrivesViewCallback;
|
|
friend class CDrivesFolderEnum;
|
|
|
|
IUnknown* _punkReg;
|
|
};
|
|
|
|
#define DRIVES_EVENTS \
|
|
SHCNE_DRIVEADD | \
|
|
SHCNE_DRIVEREMOVED | \
|
|
SHCNE_MEDIAINSERTED | \
|
|
SHCNE_MEDIAREMOVED | \
|
|
SHCNE_NETSHARE | \
|
|
SHCNE_NETUNSHARE | \
|
|
SHCNE_CREATE | \
|
|
SHCNE_DELETE | \
|
|
SHCNE_RENAMEITEM | \
|
|
SHCNE_RENAMEFOLDER | \
|
|
SHCNE_UPDATEITEM
|
|
|
|
|
|
// return S_OK if non NULL CLSID copied out
|
|
|
|
HRESULT CDrivesFolder::_GetCLSIDFromPidl(LPCIDDRIVE pidd, CLSID *pclsid)
|
|
{
|
|
*pclsid = CLSID_NULL;
|
|
|
|
if ((pidd->cb >= sizeof(IDDRIVE)) &&
|
|
((pidd->wSig & IDDRIVE_ORDINAL_MASK) == IDDRIVE_ORDINAL_DRIVEEXT) &&
|
|
(pidd->wSig & IDDRIVE_FLAGS_DRIVEEXT_HASCLSID))
|
|
{
|
|
*pclsid = pidd->clsid;
|
|
return S_OK;
|
|
}
|
|
return S_FALSE; // does not have a CLSID
|
|
}
|
|
|
|
HRESULT CDrivesFolder::GetMaxNameLength(LPCITEMIDLIST pidlItem, UINT *pcchMax)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
LPCIDDRIVE pidd = _IsValidID(pidlItem);
|
|
if (pidd)
|
|
{
|
|
if (pidd->bFlags == SHID_COMPUTER_REGITEM)
|
|
{
|
|
// this is bogus, we are handling stuff for regfldr
|
|
*pcchMax = MAX_REGITEMCCH;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
if (pMtPt)
|
|
{
|
|
TCHAR szLabel[MAX_LABEL_NTFS + 1];
|
|
hr = pMtPt->GetLabel(szLabel, ARRAYSIZE(szLabel));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pMtPt->IsNTFS())
|
|
*pcchMax = MAX_LABEL_NTFS;
|
|
else
|
|
*pcchMax = MAX_LABEL_FAT;
|
|
}
|
|
pMtPt->Release();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
class CDrivesViewCallback : public CBaseShellFolderViewCB, public IFolderFilter
|
|
{
|
|
public:
|
|
CDrivesViewCallback(CDrivesFolder *pfolder);
|
|
|
|
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
|
STDMETHODIMP_(ULONG) AddRef(void) { return CBaseShellFolderViewCB::AddRef(); };
|
|
STDMETHODIMP_(ULONG) Release(void) { return CBaseShellFolderViewCB::Release(); };
|
|
|
|
// IFolderFilter
|
|
STDMETHODIMP ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem);
|
|
STDMETHODIMP GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags);
|
|
|
|
private:
|
|
~CDrivesViewCallback();
|
|
|
|
HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnInsertItem(DWORD pv, LPCITEMIDLIST wP)
|
|
{
|
|
LPIDDRIVE pidd = (LPIDDRIVE)wP;
|
|
if (pidd && pidd->bFlags != SHID_COMPUTER_REGITEM)
|
|
{
|
|
// clear the size info
|
|
pidd->qwSize = pidd->qwFree = 0;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnWindowCreated(DWORD pv, HWND wP)
|
|
{
|
|
InitializeStatus(_punkSite);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnSize(DWORD pv, UINT cx, UINT cy)
|
|
{
|
|
ResizeStatus(_punkSite, cx);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane)
|
|
{
|
|
if (PANE_ZONE == dwPaneID)
|
|
*pdwPane = 2;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pfvm)
|
|
{
|
|
*pfvm = FVM_TILE;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
|
|
HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
|
|
HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
|
|
|
|
HRESULT OnUpdateStatusBar(DWORD pv, BOOL fIniting)
|
|
{
|
|
// Ask DefView to set the default text but not initialize
|
|
// since we did the initialization in our OnSize handler.
|
|
return SFVUSB_INITED;
|
|
}
|
|
|
|
HRESULT OnFSNotify(DWORD pv, LPCITEMIDLIST*wP, LPARAM lP)
|
|
{
|
|
return CDrivesFolder::_OnChangeNotify(lP, wP);
|
|
}
|
|
|
|
HRESULT OnBACKGROUNDENUM(DWORD pv)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax)
|
|
{
|
|
return _pfolder->GetMaxNameLength(pidlItem, pcchMax);
|
|
}
|
|
|
|
CDrivesFolder *_pfolder;
|
|
LONG _cRef;
|
|
|
|
public:
|
|
// Web View Task implementations
|
|
static HRESULT _CanEject(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
static HRESULT _CanChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
static HRESULT _CanSysProperties(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
static HRESULT _CanAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
static HRESULT _OnSystemProperties(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
static HRESULT _OnAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
static HRESULT _OnChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
static HRESULT _OnEject(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
};
|
|
|
|
CDrivesViewCallback::CDrivesViewCallback(CDrivesFolder *pfolder) :
|
|
CBaseShellFolderViewCB((LPCITEMIDLIST)&c_idlDrives, DRIVES_EVENTS), _pfolder(pfolder), _cRef(1)
|
|
{
|
|
_pfolder->AddRef();
|
|
}
|
|
|
|
CDrivesViewCallback::~CDrivesViewCallback()
|
|
{
|
|
_pfolder->Release();
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrivesViewCallback::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
|
|
HANDLE_MSG(0, SFVM_INSERTITEM, OnInsertItem);
|
|
HANDLE_MSG(0, SFVM_UPDATESTATUSBAR, OnUpdateStatusBar);
|
|
HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNotify);
|
|
HANDLE_MSG(0, SFVM_BACKGROUNDENUM, OnBACKGROUNDENUM);
|
|
HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
|
|
HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
|
|
HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
|
|
HANDLE_MSG(0, SFVM_SIZE, OnSize);
|
|
HANDLE_MSG(0, SFVM_GETPANE, OnGetPane);
|
|
HANDLE_MSG(0, SFVM_GETCCHMAX, OnGetCCHMax);
|
|
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr = CBaseShellFolderViewCB::QueryInterface(riid, ppv);
|
|
if (FAILED(hr))
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CDrivesViewCallback, IFolderFilter),
|
|
{ 0 },
|
|
};
|
|
hr = QISearch(this, qit, riid, ppv);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesViewCallback::ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
|
|
{
|
|
HRESULT hr = S_OK; //Assume that this item should be shown!
|
|
|
|
if (SHRestricted(REST_NOMYCOMPUTERICON)) // this policy means hide my computer everywhere AND hide the contents if the user is sneaky and gets in anyway
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
IShellFolder2 *psf2;
|
|
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
|
|
{
|
|
// Get the GUID in the pidl, which requires IShellFolder2.
|
|
CLSID guidItem;
|
|
if (SUCCEEDED(GetItemCLSID(psf2, pidlItem, &guidItem)))
|
|
{
|
|
//Convert the guid to a string
|
|
TCHAR szGuidValue[MAX_GUID_STRING_LEN];
|
|
|
|
SHStringFromGUID(guidItem, szGuidValue, ARRAYSIZE(szGuidValue));
|
|
|
|
//See if this item is turned off in the registry.
|
|
if (SHRegGetBoolUSValue(REGSTR_PATH_HIDDEN_MYCOMP_ICONS, szGuidValue, FALSE, /* default */FALSE))
|
|
hr = S_FALSE; //They want to hide it; So, return S_FALSE.
|
|
}
|
|
psf2->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesViewCallback::GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
|
|
{
|
|
ZeroMemory(pData, sizeof(*pData));
|
|
pData->dwLayout = SFVMWVL_DETAILS;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDrivesViewCallback::_CanAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
*puisState = (SHRestricted(REST_ARP_NOARP)) ? UIS_DISABLED : UIS_ENABLED;
|
|
return S_OK;
|
|
}
|
|
|
|
// Note:
|
|
// This method is NOT designed to handle multi-select cases. If you enhance
|
|
// the task list and wish to multi-eject (?why?), make sure you fix this up!
|
|
//
|
|
HRESULT CDrivesViewCallback::_CanEject(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
*puisState = UIS_DISABLED;
|
|
IDataObject *pdo;
|
|
|
|
// should just use the ShellItemArray directly
|
|
|
|
if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo))))
|
|
{
|
|
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdo, &medium);
|
|
if (pida)
|
|
{
|
|
ASSERT(pida->cidl == 1); // Only allow eject if a single item is selected.
|
|
|
|
LPCIDDRIVE pidd = CDrivesFolder::_IsValidID(IDA_GetIDListPtr(pida, 0));
|
|
if (pidd)
|
|
{
|
|
CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
if (pmtpt)
|
|
{
|
|
if (pmtpt->IsEjectable())
|
|
*puisState = UIS_ENABLED;
|
|
pmtpt->Release();
|
|
}
|
|
}
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
|
|
pdo->Release();
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::_CanSysProperties(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
*puisState = SHRestricted(REST_MYCOMPNOPROP) ? UIS_DISABLED : UIS_ENABLED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::_OnSystemProperties(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
|
|
|
|
return SHInvokeCommandOnPidl(pThis->_hwndMain, NULL, pThis->_pidl, 0, "properties");
|
|
}
|
|
HRESULT CDrivesViewCallback::_OnAddRemovePrograms(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
BOOL fRet = SHRunControlPanel(L"appwiz.cpl", NULL);
|
|
|
|
return (fRet) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::_CanChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
*puisState = SHRestricted(REST_NOCONTROLPANEL) ? UIS_DISABLED : UIS_ENABLED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::_OnChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
|
|
|
|
IShellBrowser* psb;
|
|
HRESULT hr = IUnknown_QueryService(pThis->_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
hr = SHGetFolderLocation(NULL, CSIDL_CONTROLS, NULL, 0, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psb->BrowseObject(pidl, 0);
|
|
ILFree(pidl);
|
|
}
|
|
psb->Release();
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::_OnEject(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
|
|
IDataObject *pdo;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo))))
|
|
{
|
|
hr = SHInvokeCommandOnDataObject(pThis->_hwndMain, NULL, pdo, 0, "eject");
|
|
pdo->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
const WVTASKITEM c_MyComputerTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_MYCOMPUTER, IDS_HEADER_MYCOMPUTER_TT);
|
|
const WVTASKITEM c_MyComputerTaskList[] =
|
|
{
|
|
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_MYCOMPUTER_SYSTEMPROPERTIES, IDS_TASK_MYCOMPUTER_SYSTEMPROPERTIES_TT, IDI_TASK_PROPERTIES,CDrivesViewCallback::_CanSysProperties, CDrivesViewCallback::_OnSystemProperties),
|
|
WVTI_ENTRY_ALL(UICID_AddRemovePrograms, L"shell32.dll", IDS_TASK_ARP, IDS_TASK_ARP_TT, IDI_CPCAT_ARP, CDrivesViewCallback::_CanAddRemovePrograms, CDrivesViewCallback::_OnAddRemovePrograms),
|
|
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_CHANGESETTINGS, IDS_TASK_CHANGESETTINGS_TT, IDI_CPLFLD, CDrivesViewCallback::_CanChangeSettings,CDrivesViewCallback::_OnChangeSettings),
|
|
WVTI_ENTRY_TITLE(CLSID_NULL, L"shell32.dll", 0, IDS_TASK_EJECTDISK, 0, IDS_TASK_EJECTDISK_TT, IDI_STEJECT, CDrivesViewCallback::_CanEject, CDrivesViewCallback::_OnEject),
|
|
};
|
|
|
|
HRESULT CDrivesViewCallback::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
|
|
{
|
|
ZeroMemory(pData, sizeof(*pData));
|
|
|
|
Create_IUIElement(&c_MyComputerTaskHeader, &(pData->pFolderTaskHeader));
|
|
|
|
// My Computer wants a different order than the default,
|
|
// and it doesn't want to expose "Desktop" as a place to go
|
|
LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_NETWORK), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_CONTROLS) };
|
|
CreateIEnumIDListOnCSIDLs(NULL, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &(pData->penumOtherPlaces));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDrivesViewCallback::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
|
|
{
|
|
ZeroMemory(pTasks, sizeof(*pTasks));
|
|
|
|
Create_IEnumUICommand((IUnknown*)(void*)this, c_MyComputerTaskList, ARRAYSIZE(c_MyComputerTaskList), &pTasks->penumFolderTasks);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDAPI_(IShellFolderViewCB*) CDrives_CreateSFVCB(CDrivesFolder *pfolder)
|
|
{
|
|
return new CDrivesViewCallback(pfolder);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwDrivesMask;
|
|
int nLastFoundDrive;
|
|
DWORD dwRestricted;
|
|
DWORD dwSavedErrorMode;
|
|
DWORD grfFlags;
|
|
} EnumDrives;
|
|
|
|
typedef enum
|
|
{
|
|
DRIVES_ICOL_NAME = 0,
|
|
DRIVES_ICOL_TYPE,
|
|
DRIVES_ICOL_CAPACITY,
|
|
DRIVES_ICOL_FREE,
|
|
DRIVES_ICOL_FILESYSTEM,
|
|
DRIVES_ICOL_COMMENT,
|
|
};
|
|
|
|
const COLUMN_INFO c_drives_cols[] =
|
|
{
|
|
DEFINE_COL_STR_ENTRY(SCID_NAME, 20, IDS_NAME_COL),
|
|
DEFINE_COL_STR_ENTRY(SCID_TYPE, 25, IDS_TYPE_COL),
|
|
DEFINE_COL_SIZE_ENTRY(SCID_CAPACITY, IDS_DRIVES_CAPACITY),
|
|
DEFINE_COL_SIZE_ENTRY(SCID_FREESPACE, IDS_DRIVES_FREE),
|
|
DEFINE_COL_STR_MENU_ENTRY(SCID_FILESYSTEM, 15, IDS_DRIVES_FILESYSTEM),
|
|
DEFINE_COL_STR_ENTRY(SCID_Comment, 20, IDS_EXCOL_COMMENT),
|
|
};
|
|
|
|
CDrivesFolder* CDrivesFolder::_spThis = NULL;
|
|
|
|
HRESULT CDrives_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
|
|
{
|
|
HRESULT hr;
|
|
ASSERT(NULL != ppv);
|
|
|
|
ENTERCRITICAL;
|
|
if (NULL != CDrivesFolder::_spThis)
|
|
{
|
|
hr = CDrivesFolder::_spThis->QueryInterface(riid, ppv);
|
|
LEAVECRITICAL;
|
|
}
|
|
else
|
|
{
|
|
LEAVECRITICAL;
|
|
CDrivesFolder* pDF = new CDrivesFolder(punkOuter);
|
|
if (NULL != pDF)
|
|
{
|
|
ASSERT(NULL == pDF->_punkReg);
|
|
|
|
if (SHRestricted(REST_NOCONTROLPANEL) || SHRestricted(REST_NOSETFOLDERS))
|
|
g_asDrivesReqItems[CDRIVES_REGITEM_CONTROL].dwAttributes |= SFGAO_NONENUMERATED;
|
|
|
|
REGITEMSINFO sDrivesRegInfo =
|
|
{
|
|
REGSTR_PATH_EXPLORER TEXT("\\MyComputer\\NameSpace"),
|
|
NULL,
|
|
TEXT(':'),
|
|
SHID_COMPUTER_REGITEM,
|
|
-1,
|
|
SFGAO_CANLINK,
|
|
ARRAYSIZE(g_asDrivesReqItems),
|
|
g_asDrivesReqItems,
|
|
RIISA_ORIGINAL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
CRegFolder_CreateInstance(&sDrivesRegInfo,
|
|
(IUnknown*)(IShellFolder2*) pDF,
|
|
IID_PPV_ARG(IUnknown, &pDF->_punkReg));
|
|
|
|
if (SHInterlockedCompareExchange((void**) &CDrivesFolder::_spThis, pDF, NULL))
|
|
{
|
|
// Someone else snuck in and initialized a CDrivesFolder first,
|
|
// so release our object and then recurse so we should get the other instance
|
|
pDF->Release();
|
|
hr = CDrives_CreateInstance(punkOuter, riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
hr = pDF->QueryInterface(riid, ppv);
|
|
|
|
// release the self-reference, but keep _spThis intact
|
|
// (it will be reset to NULL in the destructor)
|
|
pDF->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
*ppv = NULL;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// This should only be called during process detach
|
|
void CDrives_Terminate(void)
|
|
{
|
|
if (NULL != CDrivesFolder::_spThis)
|
|
{
|
|
delete CDrivesFolder::_spThis;
|
|
}
|
|
}
|
|
|
|
CDrivesFolder::CDrivesFolder(IUnknown* punkOuter) :
|
|
CAggregatedUnknown (punkOuter),
|
|
_punkReg (NULL)
|
|
{
|
|
DllAddRef();
|
|
}
|
|
|
|
CDrivesFolder::~CDrivesFolder()
|
|
{
|
|
SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), &_punkReg);
|
|
SHInterlockedCompareExchange((void**) &CDrivesFolder::_spThis, NULL, this);
|
|
DllRelease();
|
|
}
|
|
|
|
HRESULT CDrivesFolder::v_InternalQueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CDrivesFolder, IShellFolder2), // IID_IShellFolder2
|
|
QITABENTMULTI(CDrivesFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
|
|
QITABENT(CDrivesFolder, IPersistFolder2), // IID_IPersistFolder2
|
|
QITABENTMULTI(CDrivesFolder, IPersistFolder, IPersistFolder2), // IID_IPersistFolder
|
|
QITABENTMULTI(CDrivesFolder, IPersist, IPersistFolder2), // IID_IPersist
|
|
QITABENTMULTI2(CDrivesFolder, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject
|
|
QITABENT(CDrivesFolder, IShellIconOverlay), // IID_IShellIconOverlay
|
|
{ 0 },
|
|
};
|
|
HRESULT hr;
|
|
if (_punkReg && RegGetsFirstShot(riid))
|
|
{
|
|
hr = _punkReg->QueryInterface(riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
hr = QISearch(this, qit, riid, ppv);
|
|
if ((E_NOINTERFACE == hr) && _punkReg)
|
|
{
|
|
hr = _punkReg->QueryInterface(riid, ppv);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
BOOL CDrivesFolder::v_HandleDelete(PLONG pcRef)
|
|
{
|
|
ASSERT(NULL != pcRef);
|
|
ENTERCRITICAL;
|
|
|
|
//
|
|
// The same bad thing can happen here as in
|
|
// CNetRootFolder::v_HandleDelete. See that function for gory details.
|
|
//
|
|
if (this == _spThis && 0 == *pcRef)
|
|
{
|
|
*pcRef = 1000; // protect against cached pointers bumping us up then down
|
|
delete this;
|
|
}
|
|
LEAVECRITICAL;
|
|
// return TRUE to indicate that we've implemented this function
|
|
// (regardless of whether or not this object was actually deleted)
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HRESULT CDrivesFolder::_GetDisplayName(LPCIDDRIVE pidd, LPTSTR pszName, UINT cchMax)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
if (pMtPt)
|
|
{
|
|
hr = pMtPt->GetDisplayName(pszName, cchMax);
|
|
pMtPt->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDrivesFolder::_GetDisplayNameStrRet(LPCIDDRIVE pidd, STRRET *pStrRet)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
if (pMtPt)
|
|
{
|
|
TCHAR szName[MAX_DISPLAYNAME];
|
|
|
|
hr = pMtPt->GetDisplayName(szName, ARRAYSIZE(szName));
|
|
if (SUCCEEDED(hr))
|
|
hr = StringToStrRet(szName, pStrRet);
|
|
|
|
pMtPt->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
#define REGKEY_DRIVE_FOLDEREXT L"Drive\\shellex\\FolderExtensions"
|
|
|
|
HRESULT CDrivesFolder::_CheckDriveType(int iDrive, LPCTSTR pszCLSID)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
TCHAR szKey[MAX_PATH];
|
|
StrCpyN(szKey, REGKEY_DRIVE_FOLDEREXT L"\\", ARRAYSIZE(szKey));
|
|
StrCatBuff(szKey, pszCLSID, ARRAYSIZE(szKey));
|
|
|
|
DWORD dwDriveMask;
|
|
DWORD cb = sizeof(DWORD);
|
|
if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, szKey, L"DriveMask", NULL, &dwDriveMask, &cb))
|
|
{
|
|
TCHAR szDrive[4];
|
|
if (PathBuildRoot(szDrive, iDrive))
|
|
{
|
|
int iType = GetDriveType(szDrive);
|
|
// its possible that we're asked to parse a drive that's no longer mounted,
|
|
// so GetDriveType will fail with DRIVE_NO_ROOT_DIR.
|
|
// in that case, pass it on down to the handler anyway.
|
|
// let's say it's the handler's job to remember the last drive it matched on.
|
|
if ((DRIVE_NO_ROOT_DIR == iType) || ((1 << iType) & dwDriveMask))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDrivesFolder::_FindExtCLSID(int iDrive, CLSID *pclsid)
|
|
{
|
|
*pclsid = CLSID_NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
HKEY hk;
|
|
if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, REGKEY_DRIVE_FOLDEREXT, &hk))
|
|
{
|
|
TCHAR szCLSID[MAX_GUID_STRING_LEN];
|
|
for (int i = 0; FAILED(hr) && (ERROR_SUCCESS == RegEnumKey(hk, i, szCLSID, ARRAYSIZE(szCLSID))); i++)
|
|
{
|
|
IDriveFolderExt *pdfe;
|
|
if (SUCCEEDED(_CheckDriveType(iDrive, szCLSID)) &&
|
|
SUCCEEDED(SHExtCoCreateInstance(szCLSID, NULL, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe))))
|
|
{
|
|
if (SUCCEEDED(pdfe->DriveMatches(iDrive)))
|
|
{
|
|
SHCLSIDFromString(szCLSID, pclsid);
|
|
}
|
|
pdfe->Release();
|
|
}
|
|
|
|
// if we successfully matched one, break out.
|
|
if (!IsEqualCLSID(*pclsid, CLSID_NULL))
|
|
hr = S_OK;
|
|
}
|
|
RegCloseKey(hk);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// this is called from parse and enum, both times passing a stack var into piddl.
|
|
// we reset the cb manually and then the callers will do ILClone to allocate the correct amount
|
|
// of memory.
|
|
HRESULT CDrivesFolder::_FillIDDrive(DRIVE_IDLIST *piddl, int iDrive, BOOL fNoCLSID, IBindCtx* pbc)
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
|
BOOL fDoIt = FALSE;
|
|
|
|
ZeroMemory(piddl, sizeof(*piddl));
|
|
|
|
PathBuildRootA(piddl->idd.cName, iDrive);
|
|
|
|
if (S_OK == SHIsFileSysBindCtx(pbc, NULL))
|
|
{
|
|
fDoIt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (BindCtx_GetMode(pbc, 0) & STGM_CREATE)
|
|
{
|
|
fDoIt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
CMountPoint* pmtpt = CMountPoint::GetMountPoint(iDrive, FALSE);
|
|
|
|
if (pmtpt)
|
|
{
|
|
fDoIt = TRUE;
|
|
pmtpt->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fDoIt)
|
|
{
|
|
// start the cb as the IDDRIVE less the clsid at the end
|
|
// this is so that in the usual case when we dont have a clsid, the pidl will look
|
|
// just like all our pidls on win2k.
|
|
piddl->idd.cb = FIELD_OFFSET(IDDRIVE, clsid);
|
|
piddl->idd.bFlags = SHID_COMPUTER_MISC;
|
|
|
|
if (!fNoCLSID)
|
|
{
|
|
CLSID clsid;
|
|
|
|
if (SUCCEEDED(_FindExtCLSID(iDrive, &clsid)))
|
|
{
|
|
piddl->idd.clsid = clsid;
|
|
// boost the cb to include the whole thing
|
|
piddl->idd.cb = sizeof(IDDRIVE);
|
|
// mark the flags of the pidl to say "hey im a drive extension with a clsid"
|
|
piddl->idd.wSig = IDDRIVE_ORDINAL_DRIVEEXT | IDDRIVE_FLAGS_DRIVEEXT_HASCLSID;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
ASSERT(piddl->cbNext == 0);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPOLESTR pwzDisplayName,
|
|
ULONG* pchEaten, LPITEMIDLIST* ppidlOut, ULONG* pdwAttributes)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
if (ppidlOut)
|
|
{
|
|
*ppidlOut = NULL; // assume error
|
|
|
|
if (pwzDisplayName && pwzDisplayName[0] &&
|
|
pwzDisplayName[1] == TEXT(':') && pwzDisplayName[2] == TEXT('\\'))
|
|
{
|
|
DRIVE_IDLIST idlDrive;
|
|
|
|
if (InRange(*pwzDisplayName, 'a', 'z') ||
|
|
InRange(*pwzDisplayName, 'A', 'Z'))
|
|
{
|
|
hr = _FillIDDrive(&idlDrive, DRIVEID(pwzDisplayName), SHSkipJunctionBinding(pbc, NULL), pbc);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Check if there are any subdirs
|
|
if (pwzDisplayName[3])
|
|
{
|
|
IShellFolder *psfDrive;
|
|
hr = BindToObject((LPITEMIDLIST)&idlDrive, pbc, IID_PPV_ARG(IShellFolder, &psfDrive));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ULONG chEaten;
|
|
LPITEMIDLIST pidlDir;
|
|
hr = psfDrive->ParseDisplayName(hwnd, pbc, pwzDisplayName + 3,
|
|
&chEaten, &pidlDir, pdwAttributes);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHILCombine((LPCITEMIDLIST)&idlDrive, pidlDir, ppidlOut);
|
|
SHFree(pidlDir);
|
|
}
|
|
psfDrive->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = SHILClone((LPITEMIDLIST)&idlDrive, ppidlOut);
|
|
if (pdwAttributes && *pdwAttributes)
|
|
GetAttributesOf(1, (LPCITEMIDLIST *)ppidlOut, pdwAttributes);
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
else if (0 == StrCmpNI(TEXT("\\\\?\\Volume{"), pwzDisplayName, ARRAYSIZE(TEXT("\\\\?\\Volume{"))))
|
|
{
|
|
//check if dealing with mounteddrive
|
|
//something like: "\\?\Volume{9e2df3f5-c7f1-11d1-84d5-000000000000}\" (without quotes)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
TraceMsg(TF_WARNING, "CDrivesFolder::ParseDisplayName(), hr:%x %ls", hr, pwzDisplayName);
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL IsShareable(int iDrive)
|
|
{
|
|
return !IsRemoteDrive(iDrive);
|
|
}
|
|
|
|
class CDrivesFolderEnum : public CEnumIDListBase
|
|
{
|
|
public:
|
|
// IEnumIDList
|
|
STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
|
|
|
|
private:
|
|
CDrivesFolderEnum(CDrivesFolder *psf, DWORD grfFlags);
|
|
~CDrivesFolderEnum();
|
|
friend HRESULT Create_DrivesFolderEnum(CDrivesFolder* psf, DWORD grfFlags, IEnumIDList** ppenum);
|
|
|
|
CDrivesFolder *_pdsf; // CDrivesFolder object we're enumerating
|
|
DWORD _dwDrivesMask;
|
|
int _nLastFoundDrive;
|
|
DWORD _dwRestricted;
|
|
DWORD _dwSavedErrorMode;
|
|
DWORD _grfFlags;
|
|
};
|
|
|
|
CDrivesFolderEnum::CDrivesFolderEnum(CDrivesFolder *pdsf, DWORD grfFlags) : CEnumIDListBase()
|
|
{
|
|
_pdsf = pdsf;
|
|
_pdsf->AddRef();
|
|
|
|
_dwDrivesMask = CMountPoint::GetDrivesMask();
|
|
_nLastFoundDrive = -1;
|
|
_dwRestricted = SHRestricted(REST_NODRIVES);
|
|
_dwSavedErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
_grfFlags = grfFlags;
|
|
}
|
|
|
|
HRESULT Create_DrivesFolderEnum(CDrivesFolder *psf, DWORD grfFlags, IEnumIDList** ppenum)
|
|
{
|
|
HRESULT hr;
|
|
CDrivesFolderEnum* p= new CDrivesFolderEnum(psf, grfFlags);
|
|
if (p)
|
|
{
|
|
hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
|
|
p->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
*ppenum = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
CDrivesFolderEnum::~CDrivesFolderEnum()
|
|
{
|
|
_pdsf->Release(); // release the "this" ptr we have
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolderEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
|
|
{
|
|
HRESULT hr = S_FALSE; // assume "no more element"
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
for (int iDrive = _nLastFoundDrive + 1; iDrive < 26; iDrive++)
|
|
{
|
|
if (_dwRestricted & (1 << iDrive))
|
|
{
|
|
TraceMsg(DM_TRACE, "s.cd_ecb: Drive %d restricted.", iDrive);
|
|
}
|
|
else if ((_dwDrivesMask & (1 << iDrive)) || IsUnavailableNetDrive(iDrive))
|
|
{
|
|
if (!(SHCONTF_SHAREABLE & _grfFlags) || IsShareable(iDrive))
|
|
{
|
|
DRIVE_IDLIST iddrive;
|
|
hr = _pdsf->_FillIDDrive(&iddrive, iDrive, FALSE, NULL);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHILClone((LPITEMIDLIST)&iddrive, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CMountPoint* pmtpt = CMountPoint::GetMountPoint(iDrive, FALSE);
|
|
if (pmtpt)
|
|
{
|
|
pmtpt->ChangeNotifyRegisterAlias();
|
|
pmtpt->Release();
|
|
}
|
|
|
|
_nLastFoundDrive = iDrive;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppidl = pidl;
|
|
if (pceltFetched)
|
|
*pceltFetched = (S_OK == hr) ? 1 : 0;
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenum)
|
|
{
|
|
return Create_DrivesFolderEnum(this, grfFlags, ppenum);
|
|
}
|
|
|
|
LPCIDDRIVE CDrivesFolder::_IsValidID(LPCITEMIDLIST pidl)
|
|
{
|
|
if (pidl && (SIL_GetType(pidl) & SHID_GROUPMASK) == SHID_COMPUTER)
|
|
return (LPCIDDRIVE)pidl;
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT CDrivesFolder::_CreateFSFolderObj(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv)
|
|
{
|
|
PERSIST_FOLDER_TARGET_INFO pfti = {0};
|
|
|
|
pfti.pidlTargetFolder = (LPITEMIDLIST)pidlDrive;
|
|
SHAnsiToUnicode(pidd->cName, pfti.szTargetParsingName, ARRAYSIZE(pfti.szTargetParsingName));
|
|
pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system?
|
|
pfti.csidl = -1;
|
|
|
|
return CFSFolder_CreateFolder(NULL, pbc, pidlDrive, &pfti, riid, ppv);
|
|
}
|
|
|
|
|
|
HRESULT CDrivesFolder::_CreateFSFolder(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
CLSID clsid;
|
|
if (S_OK == _GetCLSIDFromPidl(pidd, &clsid) && (!SHSkipJunctionBinding(pbc, NULL)))
|
|
{
|
|
IDriveFolderExt *pdfe;
|
|
// SHExtCoCreateInstance since this shell extension needs to go through approval
|
|
hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pdfe->Bind(pidlDrive, pbc, riid, ppv);
|
|
pdfe->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = _CreateFSFolderObj(pbc, pidlDrive, pidd, riid, ppv);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrivesFolder::BindToObject(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppv = NULL;
|
|
|
|
LPCIDDRIVE pidd = _IsValidID(pidl);
|
|
if (pidd)
|
|
{
|
|
LPCITEMIDLIST pidlNext = _ILNext(pidl);
|
|
LPITEMIDLIST pidlDrive = ILCombineParentAndFirst(IDLIST_DRIVES, pidl, pidlNext);
|
|
if (pidlDrive)
|
|
{
|
|
// we only try ask for the riid at the end of the pidl binding.
|
|
if (ILIsEmpty(pidlNext))
|
|
{
|
|
hr = _CreateFSFolder(pbc, pidlDrive, pidd, riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
// now we need to get the subfolder from which to grab our goodies
|
|
IShellFolder *psfDrive;
|
|
hr = _CreateFSFolder(pbc, pidlDrive, pidd, IID_PPV_ARG(IShellFolder, &psfDrive));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// this means that there is more to bind to, we must pass it on...
|
|
hr = psfDrive->BindToObject(pidlNext, pbc, riid, ppv);
|
|
psfDrive->Release();
|
|
}
|
|
}
|
|
ILFree(pidlDrive);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
TraceMsg(TF_WARNING, "CDrivesFolder::BindToObject(), bad PIDL %s", DumpPidl(pidl));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::BindToStorage(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppv)
|
|
{
|
|
return BindToObject(pidl, pbc, riid, ppv);
|
|
}
|
|
|
|
BOOL CDrivesFolder::_GetFreeSpace(LPCIDDRIVE pidd, ULONGLONG *pSize, ULONGLONG *pFree)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
CLSID clsid;
|
|
if (S_OK == _GetCLSIDFromPidl(pidd, &clsid))
|
|
{
|
|
IDriveFolderExt *pdfe;
|
|
// SHExtCoCreateInstance since this shell extension needs to go through approval
|
|
if (SUCCEEDED(SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe))))
|
|
{
|
|
bRet = SUCCEEDED(pdfe->GetSpace(pSize, pFree));
|
|
pdfe->Release();
|
|
}
|
|
}
|
|
|
|
if (!bRet && !_IsReg(pidd) && ShowDriveInfo(DRIVEID(pidd->cName)))
|
|
{
|
|
if (pidd->qwSize || pidd->qwFree)
|
|
{
|
|
*pSize = pidd->qwSize; // cache hit
|
|
*pFree = pidd->qwFree;
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
int iDrive = DRIVEID(pidd->cName);
|
|
// Don't wake up sleeping net connections
|
|
if (!IsRemoteDrive(iDrive) || !IsDisconnectedNetDrive(iDrive))
|
|
{
|
|
// Call our helper function Who understands
|
|
// OSR2 and NT as well as old W95...
|
|
ULARGE_INTEGER qwFreeUser, qwTotal, qwTotalFree;
|
|
bRet = SHGetDiskFreeSpaceExA(pidd->cName, &qwFreeUser, &qwTotal, &qwTotalFree);
|
|
if (bRet)
|
|
{
|
|
*pSize = qwTotal.QuadPart;
|
|
*pFree = qwFreeUser.QuadPart;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
LPCIDDRIVE pidd1 = _IsValidID(pidl1);
|
|
LPCIDDRIVE pidd2 = _IsValidID(pidl2);
|
|
|
|
if (!pidd1 || !pidd2)
|
|
{
|
|
TraceMsg(TF_WARNING, "CDrivesFolder::CompareIDs(), bad(s) pidl11:%s, pidl2:%s", DumpPidl(pidl1), DumpPidl(pidl2));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// For any column other than DRIVES_ICOL_NAME, we force an
|
|
// all-fields comparison to break ties.
|
|
if ((iCol & SHCIDS_COLUMNMASK) != DRIVES_ICOL_NAME)
|
|
iCol |= SHCIDS_ALLFIELDS;
|
|
|
|
HRESULT hr;
|
|
switch (iCol & SHCIDS_COLUMNMASK)
|
|
{
|
|
default: // If asking for unknown column, just use name
|
|
case DRIVES_ICOL_NAME:
|
|
hr = ResultFromShort(StrCmpICA(pidd1->cName, pidd2->cName));
|
|
break;
|
|
|
|
case DRIVES_ICOL_TYPE:
|
|
{
|
|
TCHAR szName1[80], szName2[80];
|
|
|
|
if (SHID_COMPUTER_REGITEM != pidd1->bFlags)
|
|
{
|
|
CMountPoint::GetTypeString(DRIVEID(pidd1->cName), szName1, ARRAYSIZE(szName1));
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szName1, ARRAYSIZE(szName1));
|
|
}
|
|
|
|
if (SHID_COMPUTER_REGITEM != pidd1->bFlags)
|
|
{
|
|
CMountPoint::GetTypeString(DRIVEID(pidd2->cName), szName2, ARRAYSIZE(szName2));
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szName2, ARRAYSIZE(szName2));
|
|
}
|
|
|
|
hr = ResultFromShort(ustrcmpi(szName1, szName2));
|
|
break;
|
|
}
|
|
|
|
case DRIVES_ICOL_CAPACITY:
|
|
case DRIVES_ICOL_FREE:
|
|
{
|
|
ULONGLONG qwSize1, qwFree1;
|
|
ULONGLONG qwSize2, qwFree2;
|
|
|
|
BOOL fGotInfo1 = _GetFreeSpace(pidd1, &qwSize1, &qwFree1);
|
|
BOOL fGotInfo2 = _GetFreeSpace(pidd2, &qwSize2, &qwFree2);
|
|
|
|
if (fGotInfo1 && fGotInfo2)
|
|
{
|
|
ULONGLONG i1, i2; // this is a "guess" at the disk size and free space
|
|
|
|
if ((iCol & SHCIDS_COLUMNMASK) == DRIVES_ICOL_CAPACITY)
|
|
{
|
|
i1 = qwSize1;
|
|
i2 = qwSize2;
|
|
}
|
|
else
|
|
{
|
|
i1 = qwFree1;
|
|
i2 = qwFree2;
|
|
}
|
|
|
|
if (i1 == i2)
|
|
hr = ResultFromShort(0);
|
|
else if (i1 < i2)
|
|
hr = ResultFromShort(-1);
|
|
else
|
|
hr = ResultFromShort(1);
|
|
}
|
|
else if (!fGotInfo1 && !fGotInfo2)
|
|
{
|
|
hr = ResultFromShort(0);
|
|
}
|
|
else
|
|
{
|
|
hr = ResultFromShort(fGotInfo1 - fGotInfo2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 == HRESULT_CODE(hr))
|
|
{
|
|
// check if clsids are equivalent, if they're different then we're done.
|
|
// duh... this should be checked AFTER the other checks so sort order is preserved.
|
|
CLSID clsid1, clsid2;
|
|
_GetCLSIDFromPidl(pidd1, &clsid1);
|
|
_GetCLSIDFromPidl(pidd2, &clsid2);
|
|
hr = ResultFromShort(memcmp(&clsid1, &clsid2, sizeof(CLSID)));
|
|
}
|
|
|
|
// if they were the same so far, and we forcing an all-fields
|
|
// comparison, then use the all-fields comparison to break ties.
|
|
if ((0 == HRESULT_CODE(hr)) && (iCol & SHCIDS_ALLFIELDS))
|
|
{
|
|
hr = CompareItemIDs(pidd1, pidd2);
|
|
}
|
|
|
|
// If the items are still the same, then ask ILCompareRelIDs
|
|
// to walk recursively to the next ids.
|
|
if (0 == HRESULT_CODE(hr))
|
|
{
|
|
hr = ILCompareRelIDs(SAFECAST(this, IShellFolder *), pidl1, pidl2, iCol);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI CDrivesDropTarget_Create(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt);
|
|
|
|
STDMETHODIMP CDrivesFolder::CreateViewObject(HWND hwnd, REFIID riid, void** ppv)
|
|
{
|
|
// We should not get here unless we have initialized properly
|
|
HRESULT hr = E_NOINTERFACE;
|
|
*ppv = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IShellView))
|
|
{
|
|
SFV_CREATE sSFV;
|
|
|
|
sSFV.cbSize = sizeof(sSFV);
|
|
sSFV.psvOuter = NULL;
|
|
sSFV.psfvcb = CDrives_CreateSFVCB(this);
|
|
|
|
QueryInterface(IID_PPV_ARG(IShellFolder, &sSFV.pshf)); // in case we are agregated
|
|
|
|
hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
|
|
|
|
if (sSFV.pshf)
|
|
sSFV.pshf->Release();
|
|
|
|
if (sSFV.psfvcb)
|
|
sSFV.psfvcb->Release();
|
|
}
|
|
else if (IsEqualIID(riid, IID_IDropTarget))
|
|
{
|
|
hr = CDrivesDropTarget_Create(hwnd, IDLIST_DRIVES, (IDropTarget **)ppv);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IContextMenu))
|
|
{
|
|
LPITEMIDLIST pidlFolder;
|
|
if (SUCCEEDED(GetCurFolder(&pidlFolder)))
|
|
{
|
|
IContextMenuCB *pcmcb = new CDrivesBackgroundMenuCB(pidlFolder);
|
|
if (pcmcb)
|
|
{
|
|
hr = CDefFolderMenu_Create2Ex(IDLIST_DRIVES, hwnd, 0, NULL, SAFECAST(this, IShellFolder*), pcmcb,
|
|
0, NULL, (IContextMenu **)ppv);
|
|
pcmcb->Release();
|
|
}
|
|
ILFree(pidlFolder);
|
|
}
|
|
}
|
|
else if (IsEqualIID(riid, IID_ICategoryProvider))
|
|
{
|
|
HKEY hk = NULL;
|
|
|
|
BEGIN_CATEGORY_LIST(s_DriveCategories)
|
|
CATEGORY_ENTRY_SCIDMAP(SCID_CAPACITY, CLSID_DriveSizeCategorizer)
|
|
CATEGORY_ENTRY_SCIDMAP(SCID_TYPE, CLSID_DriveTypeCategorizer)
|
|
CATEGORY_ENTRY_SCIDMAP(SCID_FREESPACE, CLSID_FreeSpaceCategorizer)
|
|
END_CATEGORY_LIST()
|
|
|
|
RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Drive\\shellex\\Category"), &hk);
|
|
hr = CCategoryProvider_Create(&CLSID_DetailCategorizer, &SCID_TYPE, hk, s_DriveCategories, this, riid, ppv);
|
|
if (hk)
|
|
RegCloseKey(hk);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut)
|
|
{
|
|
UINT rgfOut = SFGAO_HASSUBFOLDER | SFGAO_CANLINK | SFGAO_CANCOPY |
|
|
SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_FOLDER | SFGAO_STORAGE |
|
|
SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR;
|
|
|
|
if (cidl == 0)
|
|
{
|
|
// We are getting the attributes for the "MyComputer" folder itself.
|
|
rgfOut = (*prgfInOut & g_asDesktopReqItems[CDESKTOP_REGITEM_DRIVES].dwAttributes);
|
|
}
|
|
else if (cidl == 1)
|
|
{
|
|
TCHAR szDrive[MAX_PATH];
|
|
LPCIDDRIVE pidd = _IsValidID(apidl[0]);
|
|
|
|
if (!pidd)
|
|
return E_INVALIDARG;
|
|
|
|
CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
|
|
if (pmtpt)
|
|
{
|
|
SHAnsiToTChar(pidd->cName, szDrive, ARRAYSIZE(szDrive));
|
|
|
|
if (*prgfInOut & SFGAO_VALIDATE)
|
|
{
|
|
// (tybeam) todo: make this extensible to validate through the clsid object
|
|
// ill do this when i break everything out into IDriveFolderExt or whatever
|
|
CLSID clsid;
|
|
if (S_OK != _GetCLSIDFromPidl(pidd, &clsid))
|
|
{
|
|
DWORD dwAttribs;
|
|
if (!PathFileExistsAndAttributes(szDrive, &dwAttribs))
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
// If caller wants compression status, we need to ask the filesystem
|
|
|
|
if (*prgfInOut & SFGAO_COMPRESSED)
|
|
{
|
|
// Don't wake up sleeping net connections
|
|
if (!pmtpt->IsRemote() || !pmtpt->IsDisconnectedNetDrive())
|
|
{
|
|
if (pmtpt->IsCompressed())
|
|
{
|
|
rgfOut |= SFGAO_COMPRESSED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*prgfInOut & SFGAO_SHARE)
|
|
{
|
|
if (!pmtpt->IsRemote())
|
|
{
|
|
if (IsShared(szDrive, FALSE))
|
|
rgfOut |= SFGAO_SHARE;
|
|
}
|
|
|
|
}
|
|
|
|
if ((*prgfInOut & SFGAO_REMOVABLE) &&
|
|
(pmtpt->IsStrictRemovable() || pmtpt->IsFloppy() ||
|
|
pmtpt->IsCDROM()))
|
|
{
|
|
rgfOut |= SFGAO_REMOVABLE;
|
|
}
|
|
|
|
// we need to also handle the SFGAO_READONLY bit.
|
|
if (*prgfInOut & SFGAO_READONLY)
|
|
{
|
|
DWORD dwAttributes = pmtpt->GetAttributes();
|
|
|
|
if (dwAttributes != -1 && dwAttributes & FILE_ATTRIBUTE_READONLY)
|
|
rgfOut |= SFGAO_READONLY;
|
|
}
|
|
|
|
// Should we add the write protect stuff and readonly?
|
|
if ((*prgfInOut & SFGAO_CANRENAME) &&
|
|
(pmtpt->IsStrictRemovable() || pmtpt->IsFloppy() ||
|
|
pmtpt->IsFixedDisk() || pmtpt->IsRemote()) ||
|
|
pmtpt->IsDVDRAMMedia())
|
|
{
|
|
rgfOut |= SFGAO_CANRENAME;
|
|
}
|
|
|
|
// Is a restriction causing this drive to not be enumerated?
|
|
if (*prgfInOut & SFGAO_NONENUMERATED)
|
|
{
|
|
DWORD dwRestricted = SHRestricted(REST_NODRIVES);
|
|
if (dwRestricted)
|
|
{
|
|
if (((1 << DRIVEID(pidd->cName)) & dwRestricted))
|
|
{
|
|
rgfOut |= SFGAO_NONENUMERATED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We want to allow moving volumes for bulk copy from some media
|
|
// such as dragging pictures from a compact flash to the my pictures
|
|
// folder.
|
|
if (*prgfInOut & SFGAO_CANMOVE)
|
|
{
|
|
if (pmtpt->IsStrictRemovable() || pmtpt->IsFloppy())
|
|
rgfOut |= SFGAO_CANMOVE;
|
|
}
|
|
|
|
pmtpt->Release();
|
|
}
|
|
}
|
|
|
|
*prgfInOut = rgfOut;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDrivesFolder::_GetEditTextStrRet(LPCIDDRIVE pidd, STRRET *pstr)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
if (pMtPt)
|
|
{
|
|
TCHAR szEdit[MAX_PATH];
|
|
hr = pMtPt->GetLabel(szEdit, ARRAYSIZE(szEdit));
|
|
if (SUCCEEDED(hr))
|
|
hr = StringToStrRet(szEdit, pstr);
|
|
pMtPt->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET* pStrRet)
|
|
{
|
|
HRESULT hr;
|
|
LPCIDDRIVE pidd = _IsValidID(pidl);
|
|
if (pidd)
|
|
{
|
|
TCHAR szDrive[ARRAYSIZE(pidd->cName)];
|
|
LPCITEMIDLIST pidlNext = _ILNext(pidl); // Check if pidl contains more than one ID
|
|
|
|
SHAnsiToTChar(pidd->cName, szDrive, ARRAYSIZE(szDrive));
|
|
|
|
if (ILIsEmpty(pidlNext))
|
|
{
|
|
if (uFlags & SHGDN_FORPARSING)
|
|
{
|
|
hr = StringToStrRet(szDrive, pStrRet);
|
|
}
|
|
else if (uFlags & SHGDN_FOREDITING)
|
|
{
|
|
hr = _GetEditTextStrRet(pidd, pStrRet);
|
|
}
|
|
else
|
|
hr = _GetDisplayNameStrRet(pidd, pStrRet);
|
|
}
|
|
else
|
|
{
|
|
LPITEMIDLIST pidlDrive = ILCombineParentAndFirst(IDLIST_DRIVES, pidl, pidlNext);
|
|
if (pidlDrive)
|
|
{
|
|
// now we need to get the subfolder from which to grab our goodies
|
|
IShellFolder *psfDrive;
|
|
hr = _CreateFSFolder(NULL, pidlDrive, pidd, IID_PPV_ARG(IShellFolder, &psfDrive));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfDrive->GetDisplayNameOf(pidlNext, uFlags, pStrRet);
|
|
psfDrive->Release();
|
|
}
|
|
ILFree(pidlDrive);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
TraceMsg(TF_WARNING, "CDrivesFolder::GetDisplayNameOf() bad PIDL %s", DumpPidl(pidl));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
|
|
LPCWSTR pszName, DWORD dwReserved, LPITEMIDLIST* ppidlOut)
|
|
{
|
|
if (ppidlOut)
|
|
*ppidlOut = NULL;
|
|
|
|
HRESULT hr = E_INVALIDARG;
|
|
LPCIDDRIVE pidd = _IsValidID(pidl);
|
|
if (pidd)
|
|
{
|
|
hr = SetDriveLabel(hwnd, NULL, DRIVEID(pidd->cName), pszName);
|
|
if (SUCCEEDED(hr) && ppidlOut)
|
|
{
|
|
*ppidlOut = ILClone(pidl);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
class CDriveAssocEnumData : public CEnumAssociationElements
|
|
{
|
|
public:
|
|
CDriveAssocEnumData(int iDrive) : _iDrive(iDrive) {}
|
|
|
|
private:
|
|
virtual BOOL _Next(IAssociationElement **ppae);
|
|
|
|
int _iDrive;
|
|
DWORD _dwChecked;
|
|
};
|
|
|
|
enum
|
|
{
|
|
DAED_CHECK_KEY = 0x0001,
|
|
DAED_CHECK_CDORDVD = 0x0002,
|
|
DAED_CHECK_TYPE = 0x0004,
|
|
};
|
|
|
|
BOOL CDriveAssocEnumData::_Next(IAssociationElement **ppae)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CMountPoint* pmtpt = CMountPoint::GetMountPoint(_iDrive);
|
|
if (pmtpt)
|
|
{
|
|
if (!(_dwChecked & DAED_CHECK_KEY))
|
|
{
|
|
HKEY hk = pmtpt->GetRegKey();
|
|
if (hk)
|
|
{
|
|
hr = AssocElemCreateForKey(&CLSID_AssocShellElement, hk, ppae);
|
|
RegCloseKey(hk);
|
|
}
|
|
_dwChecked |= DAED_CHECK_KEY;
|
|
}
|
|
|
|
if (FAILED(hr) && !(_dwChecked & DAED_CHECK_CDORDVD))
|
|
{
|
|
PCWSTR psz = NULL;
|
|
if (pmtpt->IsAudioCD())
|
|
psz = L"AudioCD";
|
|
else if (pmtpt->IsDVD())
|
|
psz = L"DVD";
|
|
|
|
if (psz)
|
|
{
|
|
hr = AssocElemCreateForClass(&CLSID_AssocProgidElement, psz, ppae);
|
|
}
|
|
_dwChecked |= DAED_CHECK_CDORDVD;
|
|
}
|
|
|
|
if (FAILED(hr) && !(_dwChecked & DAED_CHECK_TYPE))
|
|
{
|
|
hr = pmtpt->GetAssocSystemElement(ppae);
|
|
_dwChecked |= DAED_CHECK_TYPE;
|
|
}
|
|
|
|
pmtpt->Release();
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
STDAPI_(BOOL) TBCContainsObject(LPCWSTR pszKey)
|
|
{
|
|
IUnknown *punk;
|
|
if (SUCCEEDED(TBCGetObjectParam(pszKey, IID_PPV_ARG(IUnknown, &punk))))
|
|
{
|
|
punk->Release();
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
HRESULT CDrives_AssocCreate(PCSTR pszName, REFIID riid, void **ppv)
|
|
{
|
|
*ppv = NULL;
|
|
IAssociationArrayInitialize *paai;
|
|
HRESULT hr = ::AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IAssociationArrayInitialize, &paai));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = paai->InitClassElements(ASSOCELEM_BASEIS_FOLDER, L"Drive");
|
|
if (SUCCEEDED(hr) && pszName && !TBCContainsObject(L"ShellExec SHGetAssociations"))
|
|
{
|
|
IEnumAssociationElements *penum = new CDriveAssocEnumData(DRIVEID(pszName));
|
|
if (penum)
|
|
{
|
|
paai->InsertElements(ASSOCELEM_DATA, penum);
|
|
penum->Release();
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = paai->QueryInterface(riid, ppv);
|
|
|
|
paai->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI_(DWORD) CDrives_GetKeys(PCSTR pszName, HKEY *rgk, UINT ck)
|
|
{
|
|
IAssociationArray *paa;
|
|
HRESULT hr = CDrives_AssocCreate(pszName, IID_PPV_ARG(IAssociationArray, &paa));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ck = SHGetAssocKeysEx(paa, -1, rgk, ck);
|
|
paa->Release();
|
|
}
|
|
else
|
|
ck = 0;
|
|
return ck;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
|
|
REFIID riid, UINT* prgfInOut, void** ppv)
|
|
{
|
|
HRESULT hr;
|
|
LPCIDDRIVE pidd = (cidl && apidl) ? _IsValidID(apidl[0]) : NULL;
|
|
|
|
*ppv = NULL;
|
|
|
|
if (!pidd)
|
|
return E_INVALIDARG;
|
|
|
|
if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW) && pidd)
|
|
{
|
|
WCHAR szDrive[MAX_PATH];
|
|
|
|
SHAnsiToUnicode(pidd->cName, szDrive, ARRAYSIZE(szDrive));
|
|
|
|
hr = SHCreateDrvExtIcon(szDrive, riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
if (IsEqualIID(riid, IID_IContextMenu))
|
|
{
|
|
HKEY rgk[MAX_ASSOC_KEYS];
|
|
DWORD ck = CDrives_GetKeys(pidd->cName, rgk, ARRAYSIZE(rgk));
|
|
hr = CDefFolderMenu_Create2(IDLIST_DRIVES, hwnd, cidl, apidl,
|
|
SAFECAST(this, IShellFolder *), CDrives_DFMCallBack, ck, rgk, (IContextMenu **)ppv);
|
|
|
|
SHRegCloseKeys(rgk, ck);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IDataObject))
|
|
{
|
|
hr = SHCreateFileDataObject(IDLIST_DRIVES, cidl, apidl, NULL, (IDataObject **)ppv);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IDropTarget))
|
|
{
|
|
IShellFolder *psfT;
|
|
hr = BindToObject((LPCITEMIDLIST)pidd, NULL, IID_PPV_ARG(IShellFolder, &psfT));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfT->CreateViewObject(hwnd, IID_IDropTarget, ppv);
|
|
psfT->Release();
|
|
}
|
|
}
|
|
else if (IsEqualIID(riid, IID_IQueryInfo))
|
|
{
|
|
// REVIEW: Shouldn't we use IQA to determine the "prop" string dynamically??? (ZekeL / BuzzR)
|
|
hr = CreateInfoTipFromItem(SAFECAST(this, IShellFolder2 *), (LPCITEMIDLIST)pidd, L"prop:FreeSpace;Capacity", riid, ppv);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IQueryAssociations)
|
|
|| IsEqualIID(riid, IID_IAssociationArray))
|
|
{
|
|
hr = CDrives_AssocCreate(pidd->cName, riid, ppv);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IExtractImage))
|
|
{
|
|
CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
|
|
hr = E_NOINTERFACE;
|
|
|
|
if (pmtpt)
|
|
{
|
|
if (pmtpt->IsFixedDisk() || pmtpt->IsRAMDisk())
|
|
{
|
|
hr = CDriveExtractImage_Create(pidd, riid, ppv);
|
|
}
|
|
|
|
pmtpt->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetDefaultSearchGUID(GUID *pGuid)
|
|
{
|
|
return DefaultSearchGUID(pGuid);
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::EnumSearches(IEnumExtraSearch **ppenum)
|
|
{
|
|
*ppenum = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD* pdwState)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (iColumn < ARRAYSIZE(c_drives_cols))
|
|
{
|
|
*pdwState = c_drives_cols[iColumn].csFlags;
|
|
if (iColumn == DRIVES_ICOL_COMMENT)
|
|
{
|
|
*pdwState |= SHCOLSTATE_SLOW; // It takes a long time to extract the comment from drives
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
LPCIDDRIVE pidd = _IsValidID(pidl);
|
|
if (pidd)
|
|
{
|
|
if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID))
|
|
{
|
|
SHDESCRIPTIONID did;
|
|
CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
|
|
if (pmtpt)
|
|
{
|
|
did.dwDescriptionId = pmtpt->GetShellDescriptionID();
|
|
|
|
pmtpt->Release();
|
|
}
|
|
else
|
|
{
|
|
did.dwDescriptionId = SHDID_COMPUTER_OTHER;
|
|
}
|
|
|
|
did.clsid = CLSID_NULL;
|
|
hr = InitVariantFromBuffer(pv, &did, sizeof(did));
|
|
}
|
|
else if (IsEqualSCID(*pscid, SCID_DetailsProperties))
|
|
{
|
|
// DUI webview properties
|
|
// shouldnt we use IQA??? - ZekeL
|
|
hr = InitVariantFromStr(pv, TEXT("prop:Name;Type;FileSystem;FreeSpace;Capacity"));
|
|
}
|
|
else
|
|
{
|
|
int iCol = FindSCID(c_drives_cols, ARRAYSIZE(c_drives_cols), pscid);
|
|
if (iCol >= 0)
|
|
{
|
|
switch (iCol)
|
|
{
|
|
case DRIVES_ICOL_CAPACITY:
|
|
case DRIVES_ICOL_FREE:
|
|
{
|
|
ULONGLONG ullSize, ullFree;
|
|
hr = E_FAIL;
|
|
if (_GetFreeSpace(pidd, &ullSize, &ullFree))
|
|
{
|
|
pv->vt = VT_UI8;
|
|
pv->ullVal = iCol == DRIVES_ICOL_CAPACITY ? ullSize : ullFree;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
SHELLDETAILS sd;
|
|
|
|
hr = GetDetailsOf(pidl, iCol, &sd);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InitVariantFromStrRet(&sd.str, pidl, pv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
|
|
{
|
|
TCHAR szTemp[INFOTIPSIZE];
|
|
szTemp[0] = 0;
|
|
|
|
pDetails->str.uType = STRRET_CSTR;
|
|
pDetails->str.cStr[0] = 0;
|
|
|
|
if (!pidl)
|
|
{
|
|
return GetDetailsOfInfo(c_drives_cols, ARRAYSIZE(c_drives_cols), iColumn, pDetails);
|
|
}
|
|
|
|
LPCIDDRIVE pidd = _IsValidID(pidl);
|
|
ASSERTMSG(pidd != NULL, "someone passed us a bad pidl");
|
|
if (!pidd)
|
|
return E_FAIL; // protect faulting code below
|
|
|
|
switch (iColumn)
|
|
{
|
|
case DRIVES_ICOL_NAME:
|
|
_GetDisplayName(pidd, szTemp, ARRAYSIZE(szTemp));
|
|
break;
|
|
|
|
case DRIVES_ICOL_TYPE:
|
|
CMountPoint::GetTypeString(DRIVEID(pidd->cName), szTemp, ARRAYSIZE(szTemp));
|
|
break;
|
|
|
|
case DRIVES_ICOL_COMMENT:
|
|
GetDriveComment(DRIVEID(pidd->cName), szTemp, ARRAYSIZE(szTemp));
|
|
break;
|
|
|
|
case DRIVES_ICOL_CAPACITY:
|
|
case DRIVES_ICOL_FREE:
|
|
{
|
|
ULONGLONG ullSize, ullFree;
|
|
|
|
if (_GetFreeSpace(pidd, &ullSize, &ullFree))
|
|
{
|
|
StrFormatByteSize64((iColumn == DRIVES_ICOL_CAPACITY) ? ullSize : ullFree, szTemp, ARRAYSIZE(szTemp));
|
|
}
|
|
}
|
|
break;
|
|
case DRIVES_ICOL_FILESYSTEM:
|
|
{
|
|
CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
|
|
if (pMtPt)
|
|
{
|
|
WCHAR szFileSysName[MAX_FILESYSNAME];
|
|
// GetFileSystemName hits the disk for floppies so disable it.
|
|
// since this is a perf win for defview but disables some functionality, it means
|
|
// do NOT rely on the namespace for getting filesystem information, go direct to
|
|
// the mountpoint instead. if filefldr ever supports SCID_FILESYSTEM like
|
|
// SCID_FREESPACE then this should be munged around.
|
|
if (!pMtPt->IsFloppy() && pMtPt->GetFileSystemName(szFileSysName, ARRAYSIZE(szFileSysName)))
|
|
{
|
|
StrCpyN(szTemp, szFileSysName, min(ARRAYSIZE(szTemp),ARRAYSIZE(szFileSysName)));
|
|
}
|
|
pMtPt->Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
return StringToStrRet(szTemp, &pDetails->str);
|
|
}
|
|
|
|
HRESULT CDrivesFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid)
|
|
{
|
|
return MapColumnToSCIDImpl(c_drives_cols, ARRAYSIZE(c_drives_cols), iColumn, pscid);
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetClassID(CLSID* pCLSID)
|
|
{
|
|
*pCLSID = CLSID_MyComputer;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
|
|
{
|
|
// Only allow the Drives root on the desktop
|
|
ASSERT(AssertIsIDListInNameSpace(pidl, &CLSID_MyComputer) && ILIsEmpty(_ILNext(pidl)));
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetCurFolder(LPITEMIDLIST *ppidl)
|
|
{
|
|
return GetCurFolderImpl(IDLIST_DRIVES, ppidl);
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::_GetIconOverlayInfo(LPCIDDRIVE pidd, int *pIndex, DWORD dwFlags)
|
|
{
|
|
IShellIconOverlayManager *psiom;
|
|
HRESULT hr = GetIconOverlayManager(&psiom);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR wszDrive[10];
|
|
SHAnsiToUnicode(pidd->cName, wszDrive, ARRAYSIZE(wszDrive));
|
|
if (IsShared(wszDrive, FALSE))
|
|
{
|
|
hr = psiom->GetReservedOverlayInfo(wszDrive, 0, pIndex, SIOM_OVERLAYINDEX, SIOM_RESERVED_SHARED);
|
|
}
|
|
else
|
|
{
|
|
hr = psiom->GetFileOverlayInfo(wszDrive, 0, pIndex, dwFlags);
|
|
}
|
|
psiom->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPCIDDRIVE pidd = _IsValidID(pidl);
|
|
if (pidd)
|
|
{
|
|
hr = _GetIconOverlayInfo(pidd, pIndex, SIOM_OVERLAYINDEX);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIndex)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPCIDDRIVE pidd = _IsValidID(pidl);
|
|
if (pidd)
|
|
{
|
|
hr = _GetIconOverlayInfo(pidd, pIndex, SIOM_ICONINDEX);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesFolder::CompareItemIDs(LPCIDDRIVE pidd1, LPCIDDRIVE pidd2)
|
|
{
|
|
// Compare the drive letter for sorting purpose.
|
|
int iRes = StrCmpICA(pidd1->cName, pidd2->cName); // don't need local goo
|
|
|
|
// then, compare pidl sizes
|
|
if (iRes == 0)
|
|
{
|
|
iRes = pidd1->cb - pidd2->cb;
|
|
}
|
|
|
|
// still equal, compare clsids if both pidls are big and have them
|
|
if ((iRes == 0) && (pidd1->cb >= sizeof(IDDRIVE)))
|
|
{
|
|
iRes = memcmp(&pidd1->clsid, &pidd2->clsid, sizeof(CLSID));
|
|
}
|
|
|
|
// still equal, compare on bFlags
|
|
if (iRes == 0)
|
|
{
|
|
iRes = pidd1->bFlags - pidd2->bFlags;
|
|
}
|
|
return ResultFromShort(iRes);
|
|
}
|
|
|
|
HRESULT CDrivesFolder::_OnChangeNotify(LPARAM lNotification, LPCITEMIDLIST *ppidl)
|
|
{
|
|
// Get to the last part of this id list...
|
|
if ((lNotification != SHCNE_DRIVEADD) || (ppidl == NULL) || (*ppidl == NULL))
|
|
return S_OK;
|
|
|
|
DWORD dwRestricted = SHRestricted(REST_NODRIVES);
|
|
if (dwRestricted == 0)
|
|
return S_OK; // no drives restricted... (majority case)
|
|
|
|
LPCIDDRIVE pidd = (LPCIDDRIVE)ILFindLastID(*ppidl);
|
|
|
|
if (((1 << DRIVEID(pidd->cName)) & dwRestricted))
|
|
{
|
|
TraceMsg(DM_TRACE, "Drive not added due to restrictions or Drivespace says it should be hidden");
|
|
return S_FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
CDrivesBackgroundMenuCB::CDrivesBackgroundMenuCB(LPITEMIDLIST pidlFolder) : _cRef(1)
|
|
{
|
|
_pidlFolder = ILClone(pidlFolder);
|
|
}
|
|
|
|
CDrivesBackgroundMenuCB::~CDrivesBackgroundMenuCB()
|
|
{
|
|
ILFree(_pidlFolder);
|
|
}
|
|
|
|
STDMETHODIMP CDrivesBackgroundMenuCB::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CDrivesBackgroundMenuCB, IContextMenuCB),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDrivesBackgroundMenuCB::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDrivesBackgroundMenuCB::Release()
|
|
{
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP CDrivesBackgroundMenuCB::_GetHelpText(UINT offset, BOOL bWide, LPARAM lParam, UINT cch)
|
|
{
|
|
UINT idRes = IDS_MH_FSIDM_FIRST + offset;
|
|
if (bWide)
|
|
LoadStringW(HINST_THISDLL, idRes, (LPWSTR)lParam, cch);
|
|
else
|
|
LoadStringA(HINST_THISDLL, idRes, (LPSTR)lParam, cch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrivesBackgroundMenuCB::CallBack (IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case DFM_MERGECONTEXTMENU_BOTTOM:
|
|
if (!(wParam & (CMF_VERBSONLY | CMF_DVFILE)))
|
|
{
|
|
DWORD dwAttr = SFGAO_HASPROPSHEET;
|
|
if (FAILED(SHGetAttributesOf(_pidlFolder, &dwAttr)) ||
|
|
SFGAO_HASPROPSHEET & dwAttr)
|
|
{
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_PROPERTIES_BG, 0, (LPQCMINFO)lParam);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
case DFM_GETHELPTEXTW:
|
|
hr = _GetHelpText(LOWORD(wParam), uMsg == DFM_GETHELPTEXTW, lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
switch (wParam)
|
|
{
|
|
case FSIDM_PROPERTIESBG:
|
|
SHRunControlPanel(TEXT("SYSDM.CPL"), hwndOwner);
|
|
break;
|
|
|
|
default:
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
class CDriveExtractImage : public IExtractImage,
|
|
public IPersist
|
|
{
|
|
public:
|
|
CDriveExtractImage();
|
|
|
|
STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
|
|
STDMETHOD_(ULONG, AddRef)();
|
|
STDMETHOD_(ULONG, Release)();
|
|
|
|
// IExtractImage/IExtractLogo
|
|
STDMETHOD (GetLocation)(LPWSTR pszPathBuffer,
|
|
DWORD cch,
|
|
DWORD * pdwPriority,
|
|
const SIZE * prgSize,
|
|
DWORD dwRecClrDepth,
|
|
DWORD *pdwFlags);
|
|
STDMETHOD (Extract)(HBITMAP * phBmpThumbnail);
|
|
|
|
// IPersist
|
|
STDMETHOD(GetClassID)(LPCLSID lpClassID);
|
|
|
|
STDMETHOD(Init)(LPCIDDRIVE pidd);
|
|
private:
|
|
~CDriveExtractImage();
|
|
|
|
long _cRef;
|
|
SIZE _size;
|
|
TCHAR _szPath[4];
|
|
|
|
DWORDLONG _dwlFreeSpace;
|
|
DWORDLONG _dwlUsedSpace;
|
|
DWORDLONG _dwlTotalSpace;
|
|
DWORD _dwUsedSpacePer1000; // amount of used space /1000
|
|
|
|
// root drive
|
|
enum
|
|
{
|
|
PIE_USEDCOLOR = 0,
|
|
PIE_FREECOLOR,
|
|
PIE_USEDSHADOW,
|
|
PIE_FREESHADOW,
|
|
PIE_NUM // keep track of number of PIE_ values
|
|
};
|
|
COLORREF _acrChartColors[PIE_NUM]; // color scheme
|
|
|
|
STDMETHOD(_CreateRenderingDC)(HDC* phdc, HBITMAP* phBmpThumbnail, HBITMAP* phbmpOld, int cx, int cy);
|
|
void _DestroyRenderingDC(HDC hdc, HBITMAP hbmpOld);
|
|
STDMETHOD(_RenderToDC)(HDC hdc);
|
|
|
|
STDMETHOD(ComputeFreeSpace)(LPTSTR pszFileName);
|
|
DWORD IntSqrt(DWORD dwNum);
|
|
STDMETHOD(Draw3dPie)(HDC hdc, LPRECT lprc, DWORD dwPer1000, const COLORREF *lpColors);
|
|
};
|
|
|
|
STDAPI CDriveExtractImage_Create(LPCIDDRIVE pidd, REFIID riid, void **ppvObj)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
#if 0
|
|
CDriveExtractImage * pObj = new CDriveExtractImage;
|
|
if (pObj)
|
|
{
|
|
hr = pObj->Init(pidd);
|
|
if (SUCCEEDED(hr))
|
|
hr = pObj->QueryInterface(riid, ppvObj);
|
|
pObj->Release();
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
CDriveExtractImage::CDriveExtractImage() : _cRef (1)
|
|
{
|
|
}
|
|
|
|
CDriveExtractImage::~CDriveExtractImage()
|
|
{
|
|
}
|
|
|
|
STDMETHODIMP CDriveExtractImage::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CDriveExtractImage, IExtractImage),
|
|
QITABENTMULTI2(CDriveExtractImage, IID_IExtractLogo, IExtractImage),
|
|
QITABENT(CDriveExtractImage, IPersist),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDriveExtractImage::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDriveExtractImage::Release()
|
|
{
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP CDriveExtractImage::GetLocation(LPWSTR pszPathBuffer, DWORD cch,
|
|
DWORD * pdwPriority, const SIZE * prgSize,
|
|
DWORD dwRecClrDepth, DWORD *pdwFlags)
|
|
{
|
|
// Sets the size of the thumbnail
|
|
_size = *prgSize;
|
|
|
|
SHTCharToUnicode(_szPath, pszPathBuffer, sizeof(_szPath));
|
|
|
|
*pdwFlags &= ~IEIFLAG_CACHE;
|
|
*pdwFlags |= IEIFLAG_ASYNC | IEIFLAG_NOBORDER;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDriveExtractImage::_CreateRenderingDC(HDC* phdc, HBITMAP* phBmpThumbnail, HBITMAP* phbmpOld, int cx, int cy)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
HDC hdc = GetDC(NULL);
|
|
|
|
if (hdc)
|
|
{
|
|
*phdc = CreateCompatibleDC(hdc);
|
|
if (*phdc)
|
|
{
|
|
*phBmpThumbnail = CreateCompatibleBitmap (hdc, cx, cy);
|
|
if (*phBmpThumbnail)
|
|
{
|
|
*phbmpOld = (HBITMAP) SelectObject(*phdc, *phBmpThumbnail);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CDriveExtractImage::_DestroyRenderingDC(HDC hdc, HBITMAP hbmpOld) // Unselects the bitmap, and deletes the Dc
|
|
{
|
|
if (hbmpOld)
|
|
SelectObject (hdc, hbmpOld);
|
|
DeleteDC(hdc);
|
|
}
|
|
|
|
STDMETHODIMP CDriveExtractImage::_RenderToDC(HDC hdc) // Does a generic render of child pidl
|
|
{
|
|
RECT rc = { 0, 0, (long)_size.cx, (long)_size.cy };
|
|
|
|
HBRUSH hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
|
|
if (hbr)
|
|
{
|
|
FillRect (hdc, (const RECT*) &rc, hbr);
|
|
DeleteObject(hbr);
|
|
}
|
|
|
|
if (SUCCEEDED(ComputeFreeSpace(_szPath)))
|
|
{
|
|
_acrChartColors[PIE_USEDCOLOR] = GetSysColor(COLOR_3DFACE);
|
|
_acrChartColors[PIE_FREECOLOR] = GetSysColor(COLOR_3DHILIGHT);
|
|
_acrChartColors[PIE_USEDSHADOW] = GetSysColor(COLOR_3DSHADOW);
|
|
_acrChartColors[PIE_FREESHADOW] = GetSysColor(COLOR_3DFACE);
|
|
|
|
rc.top += 10;
|
|
rc.left += 10;
|
|
rc.bottom -= 10;
|
|
rc.right -= 10;
|
|
|
|
Draw3dPie(hdc, &rc, _dwUsedSpacePer1000, _acrChartColors);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDriveExtractImage::Extract(HBITMAP * phBmpThumbnail)
|
|
{
|
|
HDC hdc;
|
|
HBITMAP hbmpOld;
|
|
|
|
// Creates the rendering DC based on the screen
|
|
HRESULT hr = _CreateRenderingDC(&hdc, phBmpThumbnail, &hbmpOld, _size.cx, _size.cy);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Does a generic render of child pidl
|
|
hr = _RenderToDC(hdc);
|
|
|
|
// Unselects the bitmap, restores the old bitmap and deletes the DC
|
|
_DestroyRenderingDC(hdc, hbmpOld);
|
|
}
|
|
|
|
if (FAILED(hr) && *phBmpThumbnail)
|
|
{
|
|
DeleteObject(*phBmpThumbnail);
|
|
*phBmpThumbnail = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDriveExtractImage::GetClassID(CLSID *pClassID)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDriveExtractImage::Init(LPCIDDRIVE pidd)
|
|
{
|
|
SHAnsiToTChar(pidd->cName, _szPath, ARRAYSIZE(_szPath));
|
|
return S_OK;
|
|
}
|
|
|
|
// Pie Chart functions
|
|
STDMETHODIMP CDriveExtractImage::ComputeFreeSpace(LPTSTR pszFileName)
|
|
{
|
|
ULARGE_INTEGER qwFreeCaller; // use this for free space -- this will take into account disk quotas and such on NT
|
|
ULARGE_INTEGER qwTotal;
|
|
ULARGE_INTEGER qwFree; // unused
|
|
|
|
// Compute free & total space and check for valid results
|
|
// if have a fn pointer call SHGetDiskFreeSpaceA
|
|
if (SHGetDiskFreeSpaceEx(pszFileName, &qwFreeCaller, &qwTotal, &qwFree))
|
|
{
|
|
_dwlFreeSpace = qwFreeCaller.QuadPart;
|
|
_dwlTotalSpace = qwTotal.QuadPart;
|
|
_dwlUsedSpace = _dwlTotalSpace - _dwlFreeSpace;
|
|
|
|
|
|
if (EVAL(_dwlTotalSpace > 0 && _dwlFreeSpace <= _dwlTotalSpace))
|
|
{
|
|
// some special cases require interesting treatment
|
|
if (_dwlTotalSpace == 0 || _dwlFreeSpace == _dwlTotalSpace)
|
|
{
|
|
_dwUsedSpacePer1000 = 0;
|
|
}
|
|
else if (_dwlFreeSpace == 0)
|
|
{
|
|
_dwUsedSpacePer1000 = 1000;
|
|
}
|
|
else
|
|
{
|
|
// not completely full or empty
|
|
_dwUsedSpacePer1000 = (DWORD)(_dwlUsedSpace * 1000 / _dwlTotalSpace);
|
|
|
|
// Trick: if user has extremely little free space, the user expects to still see
|
|
// a tiny free slice -- not a full drive. Similarly for almost free drive.
|
|
if (_dwUsedSpacePer1000 == 0)
|
|
{
|
|
_dwUsedSpacePer1000 = 1;
|
|
}
|
|
else if (_dwUsedSpacePer1000 == 1000)
|
|
{
|
|
_dwUsedSpacePer1000 = 999;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
DWORD CDriveExtractImage::IntSqrt(DWORD dwNum)
|
|
{
|
|
// This code came from "drawpie.c"
|
|
DWORD dwSqrt = 0;
|
|
DWORD dwRemain = 0;
|
|
DWORD dwTry = 0;
|
|
|
|
for (int i=0; i<16; ++i)
|
|
{
|
|
dwRemain = (dwRemain<<2) | (dwNum>>30);
|
|
dwSqrt <<= 1;
|
|
dwTry = dwSqrt*2 + 1;
|
|
|
|
if (dwRemain >= dwTry)
|
|
{
|
|
dwRemain -= dwTry;
|
|
dwSqrt |= 0x01;
|
|
}
|
|
dwNum <<= 2;
|
|
}
|
|
return dwSqrt;
|
|
} // IntSqrt
|
|
|
|
|
|
STDMETHODIMP CDriveExtractImage::Draw3dPie(HDC hdc, LPRECT lprc, DWORD dwPer1000, const COLORREF *lpColors)
|
|
{
|
|
ASSERT(lprc != NULL && lpColors != NULL);
|
|
|
|
enum
|
|
{
|
|
COLOR_UP = 0,
|
|
COLOR_DN,
|
|
COLOR_UPSHADOW,
|
|
COLOR_DNSHADOW,
|
|
COLOR_NUM // #of entries
|
|
};
|
|
|
|
// The majority of this code came from "drawpie.c"
|
|
const LONG c_lShadowScale = 6; // ratio of shadow depth to height
|
|
const LONG c_lAspectRatio = 2; // ratio of width : height of ellipse
|
|
|
|
// We make sure that the aspect ratio of the pie-chart is always preserved
|
|
// regardless of the shape of the given rectangle
|
|
// Stabilize the aspect ratio now...
|
|
LONG lHeight = lprc->bottom - lprc->top;
|
|
LONG lWidth = lprc->right - lprc->left;
|
|
LONG lTargetHeight = (lHeight * c_lAspectRatio <= lWidth? lHeight: lWidth / c_lAspectRatio);
|
|
LONG lTargetWidth = lTargetHeight * c_lAspectRatio; // need to adjust because w/c * c isn't always == w
|
|
|
|
// Shrink the rectangle on both sides to the correct size
|
|
lprc->top += (lHeight - lTargetHeight) / 2;
|
|
lprc->bottom = lprc->top + lTargetHeight;
|
|
lprc->left += (lWidth - lTargetWidth) / 2;
|
|
lprc->right = lprc->left + lTargetWidth;
|
|
|
|
// Compute a shadow depth based on height of the image
|
|
LONG lShadowDepth = lTargetHeight / c_lShadowScale;
|
|
|
|
// check dwPer1000 to ensure within bounds
|
|
if (dwPer1000 > 1000)
|
|
dwPer1000 = 1000;
|
|
|
|
// Now the drawing function
|
|
int cx, cy, rx, ry, x, y;
|
|
int uQPctX10;
|
|
RECT rcItem;
|
|
HRGN hEllRect, hEllipticRgn, hRectRgn;
|
|
HBRUSH hBrush, hOldBrush;
|
|
HPEN hPen, hOldPen;
|
|
|
|
rcItem = *lprc;
|
|
rcItem.left = lprc->left;
|
|
rcItem.top = lprc->top;
|
|
rcItem.right = lprc->right - rcItem.left;
|
|
rcItem.bottom = lprc->bottom - rcItem.top - lShadowDepth;
|
|
|
|
rx = rcItem.right / 2;
|
|
cx = rcItem.left + rx - 1;
|
|
ry = rcItem.bottom / 2;
|
|
cy = rcItem.top + ry - 1;
|
|
if (rx<=10 || ry<=10)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
rcItem.right = rcItem.left+2*rx;
|
|
rcItem.bottom = rcItem.top+2*ry;
|
|
|
|
/* Translate to first quadrant of a Cartesian system
|
|
*/
|
|
uQPctX10 = (dwPer1000 % 500) - 250;
|
|
if (uQPctX10 < 0)
|
|
{
|
|
uQPctX10 = -uQPctX10;
|
|
}
|
|
|
|
/* Calc x and y. I am trying to make the area be the right percentage.
|
|
** I don't know how to calculate the area of a pie slice exactly, so I
|
|
** approximate it by using the triangle area instead.
|
|
*/
|
|
|
|
// NOTE-- *** in response to the above comment ***
|
|
// Calculating the area of a pie slice exactly is actually very
|
|
// easy by conceptually rescaling into a circle but the complications
|
|
// introduced by having to work in fixed-point arithmetic makes it
|
|
// unworthwhile to code this-- CemP
|
|
|
|
if (uQPctX10 < 120)
|
|
{
|
|
x = IntSqrt(((DWORD)rx*(DWORD)rx*(DWORD)uQPctX10*(DWORD)uQPctX10)
|
|
/((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
|
|
|
|
y = IntSqrt(((DWORD)rx*(DWORD)rx-(DWORD)x*(DWORD)x)*(DWORD)ry*(DWORD)ry/((DWORD)rx*(DWORD)rx));
|
|
}
|
|
else
|
|
{
|
|
y = IntSqrt((DWORD)ry*(DWORD)ry*(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)
|
|
/((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
|
|
|
|
x = IntSqrt(((DWORD)ry*(DWORD)ry-(DWORD)y*(DWORD)y)*(DWORD)rx*(DWORD)rx/((DWORD)ry*(DWORD)ry));
|
|
}
|
|
|
|
/* Switch on the actual quadrant
|
|
*/
|
|
switch (dwPer1000 / 250)
|
|
{
|
|
case 1:
|
|
y = -y;
|
|
break;
|
|
|
|
case 2:
|
|
break;
|
|
|
|
case 3:
|
|
x = -x;
|
|
break;
|
|
|
|
default: // case 0 and case 4
|
|
x = -x;
|
|
y = -y;
|
|
break;
|
|
}
|
|
|
|
/* Now adjust for the center.
|
|
*/
|
|
x += cx;
|
|
y += cy;
|
|
|
|
//
|
|
// Hack to get around bug in NTGDI
|
|
|
|
x = x < 0 ? 0 : x;
|
|
|
|
/* Draw the shadows using regions (to reduce flicker).
|
|
*/
|
|
hEllipticRgn = CreateEllipticRgnIndirect(&rcItem);
|
|
OffsetRgn(hEllipticRgn, 0, lShadowDepth);
|
|
hEllRect = CreateRectRgn(rcItem.left, cy, rcItem.right, cy+lShadowDepth);
|
|
hRectRgn = CreateRectRgn(0, 0, 0, 0);
|
|
CombineRgn(hRectRgn, hEllipticRgn, hEllRect, RGN_OR);
|
|
OffsetRgn(hEllipticRgn, 0, -(int)lShadowDepth);
|
|
CombineRgn(hEllRect, hRectRgn, hEllipticRgn, RGN_DIFF);
|
|
|
|
/* Always draw the whole area in the free shadow/
|
|
*/
|
|
hBrush = CreateSolidBrush(lpColors[COLOR_DNSHADOW]);
|
|
if (hBrush)
|
|
{
|
|
FillRgn(hdc, hEllRect, hBrush);
|
|
DeleteObject(hBrush);
|
|
}
|
|
|
|
/* Draw the used shadow only if the disk is at least half used.
|
|
*/
|
|
if (dwPer1000>500 && (hBrush = CreateSolidBrush(lpColors[COLOR_UPSHADOW]))!=NULL)
|
|
{
|
|
DeleteObject(hRectRgn);
|
|
hRectRgn = CreateRectRgn(x, cy, rcItem.right, lprc->bottom);
|
|
CombineRgn(hEllipticRgn, hEllRect, hRectRgn, RGN_AND);
|
|
FillRgn(hdc, hEllipticRgn, hBrush);
|
|
DeleteObject(hBrush);
|
|
}
|
|
|
|
DeleteObject(hRectRgn);
|
|
DeleteObject(hEllipticRgn);
|
|
DeleteObject(hEllRect);
|
|
|
|
hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
|
|
hOldPen = (HPEN__*) SelectObject(hdc, hPen);
|
|
|
|
// if per1000 is 0 or 1000, draw full elipse, otherwise, also draw a pie section.
|
|
// we might have a situation where per1000 isn't 0 or 1000 but y == cy due to approx error,
|
|
// so make sure to draw the ellipse the correct color, and draw a line (with Pie()) to
|
|
// indicate not completely full or empty pie.
|
|
hBrush = CreateSolidBrush(lpColors[dwPer1000 < 500 && y == cy && x < cx? COLOR_DN: COLOR_UP]);
|
|
hOldBrush = (HBRUSH__*) SelectObject(hdc, hBrush);
|
|
|
|
Ellipse(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
|
|
SelectObject(hdc, hOldBrush);
|
|
DeleteObject(hBrush);
|
|
|
|
if (dwPer1000 != 0 && dwPer1000 != 1000)
|
|
{
|
|
// display small sub-section of ellipse for smaller part
|
|
hBrush = CreateSolidBrush(lpColors[COLOR_DN]);
|
|
hOldBrush = (HBRUSH__*) SelectObject(hdc, hBrush);
|
|
|
|
Pie(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
|
|
rcItem.left, cy, x, y);
|
|
SelectObject(hdc, hOldBrush);
|
|
DeleteObject(hBrush);
|
|
}
|
|
|
|
Arc(hdc, rcItem.left, rcItem.top+lShadowDepth, rcItem.right - 1, rcItem.bottom+lShadowDepth - 1,
|
|
rcItem.left, cy+lShadowDepth, rcItem.right, cy+lShadowDepth-1);
|
|
MoveToEx(hdc, rcItem.left, cy, NULL);
|
|
LineTo(hdc, rcItem.left, cy+lShadowDepth);
|
|
MoveToEx(hdc, rcItem.right-1, cy, NULL);
|
|
LineTo(hdc, rcItem.right-1, cy+lShadowDepth);
|
|
if (dwPer1000 > 500 && dwPer1000 < 1000)
|
|
{
|
|
MoveToEx(hdc, x, y, NULL);
|
|
LineTo(hdc, x, y+lShadowDepth);
|
|
}
|
|
SelectObject(hdc, hOldPen);
|
|
DeleteObject(hPen);
|
|
|
|
return S_OK; // Everything worked fine
|
|
} // Draw3dPie
|