#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 #include // MAX_GUID_STRING_LEN #include "ole2dup.h" #include "category.h" #define EXCLUDE_COMPPROPSHEET #include "unicpp\dcomp.h" #undef EXCLUDE_COMPPROPSHEET #include "enumidlist.h" #include #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