Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2648 lines
81 KiB

#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