#include "shellprv.h" #include "filefldr.h" #include #include #include "idlcomm.h" #include "pidl.h" #include "views.h" #include "ids.h" #include "shitemid.h" #include "datautil.h" #include "prop.h" #include "basefvcb.h" #include "brutil.h" #include "enumuicommand.h" #include "enumidlist.h" #include "wia.h" #include "shimgvw.h" #include "cdburn.h" #include "foldertypes.h" #include "htmlhelp.h" #include "buytasks.h" #include // for MD5DIGESTLEN const SHOP_INFO c_BuySampleMusic = { L"BuyURL", L"http://go.microsoft.com/fwlink/?LinkId=730&clcid={SUB_CLCID}", FALSE}; const SHOP_INFO c_BuyMusic = { L"MusicBuyURL", L"http://go.microsoft.com/fwlink/?LinkId=493&clcid={SUB_CLCID}", TRUE}; const SHOP_INFO c_BuySamplePictures = { L"BuyURL", L"http://go.microsoft.com/fwlink/?LinkId=625&clcid={SUB_CLCID}", TRUE}; class CFSFolderViewCB : public CBaseShellFolderViewCB { public: CFSFolderViewCB(CFSFolder *pfsf); STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); STDMETHODIMP SetSite(IUnknown* pUnkSite); private: ~CFSFolderViewCB(); HRESULT OnSize(DWORD pv, UINT cx, UINT cy); HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane); HRESULT OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax); HRESULT OnWindowCreated(DWORD pv, HWND wP); HRESULT OnInsertDeleteItem(int iMul, LPCITEMIDLIST wP); HRESULT OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP); HRESULT OnUpdateStatusBar(DWORD pv, BOOL wP); HRESULT OnRefresh(DWORD pv, BOOL fPreRefresh); HRESULT OnSelectAll(DWORD pv); HRESULT OnGetWorkingDir(DWORD pv, UINT wP, LPTSTR lP); HRESULT OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST* rgpidl); HRESULT OnGetViewData(DWORD pv, UINT uViewMode, SFVM_VIEW_DATA* pvi); HRESULT OnGetWebViewTemplate(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_TEMPLATE_DATA* pvit); 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 OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme); HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE*lP); HRESULT OnGetCustomViewInfo(DWORD pv, SFVM_CUSTOMVIEWINFO_DATA* pData); HRESULT OnSupportsIdentity(DWORD pv); HRESULT OnQueryReuseExtView(DWORD pv, BOOL *pfReuseAllowed); HRESULT OnGetNotify(DWORD pv, LPITEMIDLIST*wP, LONG*lP); HRESULT OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings); BOOL _CollectDefaultFolderState(); PERCEIVED _GetFolderPerceivedType(LPCIDFOLDER pidf); HRESULT _GetStringForFolderType(int iType, LPWSTR pszFolderType, UINT cchBuf); BOOL _IsBarricadedFolder(); UINT _cItems; FSSELCHANGEINFO _fssci; CFSFolder* _pfsf; BOOL _fStatusInitialized; TRIBIT _fHasWIADevices; IPreview3 * _pPreview; HRESULT _GetPreview3(IPreview3** ppPreview3); HRESULT _GetShoppingBrowsePidl(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, const SHOP_INFO *pShopInfo, LPITEMIDLIST *ppidl); HRESULT _GetShoppingURL(const SHOP_INFO *pShopInfo, LPTSTR pszURL, DWORD cchURL); HRESULT _DataObjectFromItemsOrFolder(IShellItemArray *psiItemArray, IDataObject **ppdto); public: // webview task implementations: static HRESULT _HasWiaDevices(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _HasItems(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanBuyPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanPlayMusic(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanPlayVideos(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _CanSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); static HRESULT _OnCommonDocumentsHelp(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnPlayMusic(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnPlayVideos(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnShopForMusicOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnShopForPicturesOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnGetFromCamera(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnSlideShow(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _OnSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); static HRESULT _CanPlay(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState, int fDATAOBJCB); static HRESULT _OnPlay(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, int fDATAOBJCB); }; #define FS_EVENTS (SHCNE_DISKEVENTS | SHCNE_ASSOCCHANGED | SHCNE_NETSHARE | SHCNE_NETUNSHARE) CFSFolderViewCB::CFSFolderViewCB(CFSFolder *pfsf) : CBaseShellFolderViewCB(pfsf->_pidl, FS_EVENTS), _pfsf(pfsf) { _pfsf->AddRef(); ZeroMemory(&_fssci, sizeof(_fssci)); // _fssci.szDrive[0] == '\0' means "unknown" / "not available" _fssci.cbFree = -1; // this field uses -1 to mean // "unknown" / "not available" _pPreview = NULL; ASSERT(!_fStatusInitialized); } CFSFolderViewCB::~CFSFolderViewCB() { if (_pPreview) { IUnknown_SetSite(_pPreview, NULL); _pPreview->Release(); } _pfsf->Release(); } STDMETHODIMP CFSFolderViewCB::SetSite(IUnknown* punkSite) { if (_pPreview) { IUnknown_SetSite(_pPreview, punkSite); } return CBaseShellFolderViewCB::SetSite(punkSite); } HRESULT CFSFolderViewCB::OnSize(DWORD pv, UINT cx, UINT cy) { ResizeStatus(_punkSite, cx); return S_OK; } HRESULT CFSFolderViewCB::OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane) { if (PANE_ZONE == dwPaneID) *pdwPane = 2; return S_OK; } HRESULT CFSFolderViewCB::OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax) { TCHAR szName[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(_pfsf, pidlItem, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName)))) { _pfsf->GetMaxLength(szName, (int *)pcchMax); } return S_OK; } HRESULT CFSFolderViewCB::OnWindowCreated(DWORD pv, HWND wP) { if (SUCCEEDED(_pfsf->_GetPath(_fssci.szDrive, ARRAYSIZE(_fssci.szDrive)))) { _fssci.cbFree = -1; // not known yet if (!_fStatusInitialized) { InitializeStatus(_punkSite); _fStatusInitialized = TRUE; } return S_OK; } return E_FAIL; } HRESULT CFSFolderViewCB::OnInsertDeleteItem(int iMul, LPCITEMIDLIST wP) { ViewInsertDeleteItem(_pfsf, &_fssci, wP, iMul); // Tell the FSFolder that it needs to update the extended columns // when we get an insert item. This will cause the next call to // IColumnProvider::GetItemData to flush it's row-wise cache. if (1 == iMul) { _pfsf->_bUpdateExtendedCols = TRUE; } return S_OK; } HRESULT CFSFolderViewCB::OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP) { ViewSelChange(_pfsf, lP, &_fssci); return S_OK; } HRESULT CFSFolderViewCB::OnUpdateStatusBar(DWORD pv, BOOL wP) { if (!_fStatusInitialized) { InitializeStatus(_punkSite); _fStatusInitialized = TRUE; } // if initializing, force refresh of disk free space if (wP) _fssci.cbFree = -1; return ViewUpdateStatusBar(_punkSite, _pidl, &_fssci); } HRESULT CFSFolderViewCB::OnRefresh(DWORD pv, BOOL fPreRefresh) { // pre refresh... if (fPreRefresh) { _fHasWIADevices = TRIBIT_UNDEFINED; // so we re-query } else { _fssci.cHiddenFiles = _pfsf->_cHiddenFiles; _fssci.cbSize = _pfsf->_cbSize; } return S_OK; } HRESULT CFSFolderViewCB::OnSelectAll(DWORD pv) { HRESULT hr = S_OK; if (_fssci.cHiddenFiles > 0) { if (ShellMessageBox(HINST_THISDLL, _hwndMain, MAKEINTRESOURCE(IDS_SELECTALLBUTHIDDEN), MAKEINTRESOURCE(IDS_SELECTALL), MB_OKCANCEL | MB_SETFOREGROUND | MB_ICONWARNING, _fssci.cHiddenFiles) == IDCANCEL) { hr = S_FALSE; } } return hr; } HRESULT CFSFolderViewCB::OnGetWorkingDir(DWORD pv, UINT wP, LPTSTR lP) { return _pfsf->_GetPath(lP, MAX_PATH); // assumed buffer size! possible overflow. } HRESULT CFSFolderViewCB::_HasWiaDevices(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; if (TRIBIT_UNDEFINED == pThis->_fHasWIADevices && fOkToBeSlow) { pThis->_fHasWIADevices = TRIBIT_FALSE; // strings stolen from stiregi.h // REGSTR_PATH_SOFT_STI, REGSTR_VAL_WIA_PRESEN if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\StillImage"), TEXT("WIADevicePresent"), NULL, NULL, NULL)) { IWiaDevMgr* pwia; if (SUCCEEDED(CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_NO_FAILURE_LOG, IID_PPV_ARG(IWiaDevMgr, &pwia)))) { IEnumWIA_DEV_INFO* penum; if (S_OK == pwia->EnumDeviceInfo(0, &penum)) { ULONG cItems; if ((S_OK == penum->GetCount(&cItems)) && cItems > 0) { pThis->_fHasWIADevices = TRIBIT_TRUE; } penum->Release(); } pwia->Release(); } } } *puisState = (TRIBIT_TRUE == pThis->_fHasWIADevices) ? UIS_ENABLED : UIS_HIDDEN; return TRIBIT_UNDEFINED == pThis->_fHasWIADevices ? E_PENDING : S_OK; } HRESULT CFSFolderViewCB::_HasItems(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; *puisState = UIS_ENABLED; if (!psiItemArray) { // empty folders don't want this task *puisState = UIS_DISABLED; IFolderView* pfv; IDataObject *pdo; if (pThis->_punkSite && SUCCEEDED(pThis->_punkSite->QueryInterface(IID_PPV_ARG(IFolderView, &pfv)))) { if (SUCCEEDED(pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IDataObject, &pdo)))) { *puisState = UIS_ENABLED; pdo->Release(); } pfv->Release(); } } return S_OK; } // Image options #define IMAGEOPTION_CANROTATE 0x00000001 #define IMAGEOPTION_CANWALLPAPER 0x00000002 HRESULT CFSFolderViewCB::_CanWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { *puisState = UIS_DISABLED; IDataObject *pdo; if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)))) { LPITEMIDLIST pidl; if (SUCCEEDED(PidlFromDataObject(pdo, &pidl))) // could get this dircetly from ShellItemArray { IAssociationArray *paa; if (SUCCEEDED(SHGetUIObjectOf(pidl, NULL, IID_PPV_ARG(IAssociationArray, &paa)))) { DWORD dwFlags, cb = sizeof(dwFlags); if (SUCCEEDED(paa->QueryDword(ASSOCELEM_MASK_QUERYNORMAL, AQN_NAMED_VALUE, L"ImageOptionFlags", &dwFlags)) && (dwFlags & IMAGEOPTION_CANWALLPAPER)) { *puisState = UIS_ENABLED; } paa->Release(); } ILFree(pidl); } pdo->Release(); } return S_OK; } enum { DATAOBJCB_IMAGE = 0x1, DATAOBJCB_MUSIC = 0x2, DATAOBJCB_VIDEO = 0x4, DATAOBJCB_ONLYCHECKEXISTENCE = 0x80000000 }; class CDataObjectCallback : public INamespaceWalkCB { public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // INamespaceWalkCB STDMETHODIMP FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHODIMP InitializeProgressDialog(LPWSTR *ppszTitle, LPWSTR *ppszCancel); CDataObjectCallback(DWORD dwFlags); BOOL Found(); private: DWORD _dwFlags; BOOL _fAlreadyFound; }; STDMETHODIMP_(ULONG) CDataObjectCallback::AddRef() { return 3; } STDMETHODIMP_(ULONG) CDataObjectCallback::Release() { return 2; } CDataObjectCallback::CDataObjectCallback(DWORD dwFlags) { _dwFlags = dwFlags; _fAlreadyFound = FALSE; } BOOL CDataObjectCallback::Found() { return _fAlreadyFound; } STDMETHODIMP CDataObjectCallback::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDataObjectCallback, INamespaceWalkCB), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP CDataObjectCallback::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl) { // a slight misuse of the walker -- we bail out early if we know we've already found // what we're looking for if ((_dwFlags & DATAOBJCB_ONLYCHECKEXISTENCE) && _fAlreadyFound) return E_FAIL; PERCEIVED gen = GetPerceivedType(psf, pidl); if ((_dwFlags & DATAOBJCB_IMAGE) && (gen == GEN_IMAGE) || (_dwFlags & DATAOBJCB_MUSIC) && (gen == GEN_AUDIO) || (_dwFlags & DATAOBJCB_VIDEO) && (gen == GEN_VIDEO)) { if (_dwFlags & DATAOBJCB_ONLYCHECKEXISTENCE) { _fAlreadyFound = TRUE; } return S_OK; } return S_FALSE; } STDMETHODIMP CDataObjectCallback::EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { if ((_dwFlags & DATAOBJCB_ONLYCHECKEXISTENCE) && _fAlreadyFound) return E_FAIL; return S_OK; } STDMETHODIMP CDataObjectCallback::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { return S_OK; } STDMETHODIMP CDataObjectCallback::InitializeProgressDialog(LPWSTR *ppszTitle, LPWSTR *ppszCancel) { *ppszCancel = NULL; // use default TCHAR szMsg[128]; LoadString(HINST_THISDLL, IDS_WALK_PROGRESS_TITLE, szMsg, ARRAYSIZE(szMsg)); return SHStrDup(szMsg, ppszTitle); } HRESULT InvokeVerbsOnItems(HWND hwndOwner, const LPCSTR rgszVerbs[], UINT cVerbs, LPITEMIDLIST *ppidls, UINT cItems) { IContextMenu *pcm; HRESULT hr = SHGetUIObjectFromFullPIDL(ppidls[0], NULL, IID_PPV_ARG(IContextMenu, &pcm)); if (SUCCEEDED(hr)) { ITEMIDLIST id = {0}; IDataObject *pdtobj; hr = SHCreateFileDataObject(&id, cItems, (LPCITEMIDLIST *)ppidls, NULL, &pdtobj); if (SUCCEEDED(hr)) { IShellExtInit *psei; hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei)); if (SUCCEEDED(hr)) { psei->Initialize(NULL, pdtobj, NULL); psei->Release(); } pdtobj->Release(); } hr = SHInvokeCommandsOnContextMenu(hwndOwner, NULL, pcm, 0, rgszVerbs, cVerbs); pcm->Release(); } return hr; } HRESULT PlayFromUnk(IUnknown *punk, HWND hwndOwner, int fDATAOBJCB) { INamespaceWalk *pnsw; HRESULT hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { CDataObjectCallback cb(fDATAOBJCB); hr = pnsw->Walk(punk, NSWF_NONE_IMPLIES_ALL | NSWF_ONE_IMPLIES_ALL | NSWF_SHOW_PROGRESS | NSWF_FLAG_VIEWORDER, 10, &cb); if (SUCCEEDED(hr)) { UINT cItems; LPITEMIDLIST *ppidls; hr = pnsw->GetIDArrayResult(&cItems, &ppidls); if (SUCCEEDED(hr)) { if (cItems) { const LPCSTR c_rgszVerbs[] = { "Play", "Open" }; hr = InvokeVerbsOnItems(hwndOwner, c_rgszVerbs, ARRAYSIZE(c_rgszVerbs), ppidls, cItems); } else { ShellMessageBox( HINST_THISDLL, hwndOwner, MAKEINTRESOURCE(IDS_PLAYABLEFILENOTFOUND), NULL, MB_OK | MB_ICONERROR); hr = S_FALSE; } FreeIDListArray(ppidls, cItems); } } pnsw->Release(); } return hr; } HRESULT CFSFolderViewCB::_OnCommonDocumentsHelp(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { SHELLEXECUTEINFO sei = { 0 }; sei.cbSize = sizeof(sei); sei.fMask = 0; sei.hwnd = ((CFSFolderViewCB*)(void*)pv)->_hwndMain; sei.nShow = SW_SHOWNORMAL; sei.lpFile = L"hcp://services/subsite?node=TopLevelBucket_2/Networking_and_the_Web&topic=MS-ITS%3A%25HELP_LOCATION%25%5Cfilefold.chm%3A%3A/using_shared_documents_folder.htm&select=TopLevelBucket_2/Networking_and_the_Web/Sharing_files__printers__and_other_resources"; return ShellExecuteEx(&sei) ? S_OK : E_FAIL; } HRESULT CFSFolderViewCB::_CanOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; // TODO: Use fOkToBeSlow (with a return of E_PENDING) to allow walk to // occur on a background task thread (for performance reasons). However, // it doesn't work at present because it's completely specialized for WIA // stuff, and it will not be trivial to adapt to the general case. Thus, // we make assumptions as best we can in determining the state for now. *puisState = UIS_DISABLED; if (SHRestricted(REST_NOONLINEPRINTSWIZARD)) { // bail out early with UIS_HIDDEN, we dont show the verb return S_OK; } IDataObject *pdo = NULL; HRESULT hr = psiItemArray ? psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)) : S_OK; if (SUCCEEDED(hr)) { if (pThis->_fssci.nItems > 0) // Files selected. Determine if any images... { INamespaceWalk *pnsw; hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { CDataObjectCallback cb(DATAOBJCB_IMAGE | DATAOBJCB_ONLYCHECKEXISTENCE); pnsw->Walk(psiItemArray ? pdo : pThis->_punkSite, NSWF_NONE_IMPLIES_ALL | NSWF_DONT_ACCUMULATE_RESULT, 0, &cb); if (cb.Found()) { *puisState = UIS_ENABLED; } pnsw->Release(); } } else { *puisState = UIS_ENABLED; // No files selected. Assume image files exist. hr = S_OK; // Note we "assume" for the TODO perf reason above. } ATOMICRELEASE(pdo); } return hr; } HRESULT CFSFolderViewCB::_CanPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; HRESULT hr; // TODO: Use fOkToBeSlow (with a return of E_PENDING) to allow walk to // occur on a background task thread (for performance reasons). However, // it doesn't work at present because it's completely specialized for WIA // stuff, and it will not be trivial to adapt to the general case. Thus, // we make assumptions as best we can in determining the state for now. if (psiItemArray) { *puisState = UIS_DISABLED; IDataObject *pdo; hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)); if (SUCCEEDED(hr)) { INamespaceWalk *pnsw; hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { CDataObjectCallback cb(DATAOBJCB_IMAGE | DATAOBJCB_ONLYCHECKEXISTENCE); pnsw->Walk(pdo, NSWF_DONT_ACCUMULATE_RESULT, 0, &cb); if (cb.Found()) { *puisState = UIS_ENABLED; } pnsw->Release(); } pdo->Release(); } } else { *puisState = UIS_ENABLED; // No files selected. Assume image files exist. hr = S_OK; // Note we "assume" for the TODO perf reason above. } return hr; } HRESULT CFSFolderViewCB::_CanBuyPictures(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; *puisState = UIS_DISABLED; // If there is a BuyURL in the desktop.ini, then we'll show the buy pictures task. WCHAR szIniPath[MAX_PATH]; if (pThis->_pfsf->_CheckDefaultIni(NULL, szIniPath, ARRAYSIZE(szIniPath)) && PathFileExistsAndAttributes(szIniPath, NULL)) { WCHAR szURLArguments[MAX_PATH]; if (GetPrivateProfileString(L".ShellClassInfo", c_BuySamplePictures.szURLKey, L"", szURLArguments, ARRAYSIZE(szURLArguments), szIniPath)) { // Note: // String validation does not occur here (by design). This is // simply an "existance" check. Validation will occur only if // the user actually clicks on this task and we need to execute. // Yes - there's something. *puisState = UIS_ENABLED; } } return S_OK; } HRESULT CFSFolderViewCB::_CanPlayMusic(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { return _CanPlay(pv, psiItemArray, fOkToBeSlow, puisState, DATAOBJCB_MUSIC | DATAOBJCB_VIDEO); } HRESULT CFSFolderViewCB::_CanPlayVideos(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { return _CanPlay(pv, psiItemArray, fOkToBeSlow, puisState, DATAOBJCB_VIDEO); } HRESULT CFSFolderViewCB::_CanPlay(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState, int fDATAOBJCB) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; *puisState = UIS_DISABLED; // TODO: Use fOkToBeSlow (with a return of E_PENDING) to allow walk to // occur on a background task thread (for performance reasons). However, // it doesn't work at present because it's completely specialized for WIA // stuff, and it will not be trivial to adapt to the general case. Thus, // we make assumptions as best we can in determining the state for now. IDataObject *pdo = NULL; HRESULT hr = psiItemArray ? psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)) : S_OK; if (SUCCEEDED(hr)) { RIPMSG(!psiItemArray || pdo, "CFSFolderViewCB::_CanPlay - BindToHandler returned S_OK but NULL pdo"); RIPMSG(psiItemArray || pThis->_punkSite, "CFSFolderViewCB::_CanPlay - no _punkSite!"); if (pThis->_fssci.cFiles > 0) { if (pThis->_fssci.nItems > 0) // Files selected. Determine if any playable... { INamespaceWalk *pnsw; hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw)); if (SUCCEEDED(hr)) { CDataObjectCallback cb(fDATAOBJCB | DATAOBJCB_ONLYCHECKEXISTENCE); pnsw->Walk(psiItemArray ? pdo : pThis->_punkSite, NSWF_DONT_ACCUMULATE_RESULT, 4, &cb); if (cb.Found()) { *puisState = UIS_ENABLED; } pnsw->Release(); } } else *puisState = UIS_ENABLED; // No files selected. Assume playable files exist. } // Note we "assume" for the TODO perf reason above. ATOMICRELEASE(pdo); } return hr; } HRESULT CFSFolderViewCB::_OnPlayMusic(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { return _OnPlay(pv, psiItemArray, pbc, DATAOBJCB_MUSIC | DATAOBJCB_VIDEO); } HRESULT CFSFolderViewCB::_OnPlayVideos(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { return _OnPlay(pv, psiItemArray, pbc, DATAOBJCB_VIDEO); } HRESULT CFSFolderViewCB::_OnPlay(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, int fDATAOBJCB) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; HRESULT hr; if (psiItemArray) { IDataObject *pdo; hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)); if (SUCCEEDED(hr)) { hr = PlayFromUnk(pdo, pThis->_hwndMain, fDATAOBJCB); pdo->Release(); } } else { hr = PlayFromUnk(pThis->_punkSite, pThis->_hwndMain, fDATAOBJCB); } return hr; } HRESULT CFSFolderViewCB::_GetShoppingURL(const SHOP_INFO *pShopInfo, LPTSTR pszURL, DWORD cchURL) { HRESULT hr = URLSubstitution(pShopInfo->szURLPrefix, pszURL, cchURL, URLSUB_CLCID); if (SUCCEEDED(hr)) { WCHAR szIniPath[MAX_PATH]; // If we can't just use the fwlink with no arguments, then assume failure. hr = pShopInfo->bUseDefault ? S_OK : E_FAIL; if (_pfsf->_CheckDefaultIni(NULL, szIniPath, ARRAYSIZE(szIniPath)) && PathFileExistsAndAttributes(szIniPath, NULL)) { WCHAR szURLArguments[MAX_PATH]; if (GetPrivateProfileString(L".ShellClassInfo", pShopInfo->szURLKey, L"", szURLArguments, ARRAYSIZE(szURLArguments), szIniPath)) { // Note: // All URL's are read from hard-coded strings in the code // base, and are of the form: // // http://go.microsoft.com/fwlink/?LinkId=730&clcid={SUB_CLCID} // // The desktop.ini simply offers an avenue to add additional // arguments onto the end of the URL to refine the redirect. // We do not validate these arguments here, because it is // assumed the fwlink service is robust enough to handle bad // input. If it wasn't, than anyone could type a bad fwlink // URL in their address bar and wreck havoc on the fwlink // service. StringCchCat(pszURL, cchURL, L"&"); StringCchCat(pszURL, cchURL, szURLArguments); // Got some arguments - we're definitely ok. hr = S_OK; } } } return hr; } HRESULT CFSFolderViewCB::_GetShoppingBrowsePidl(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc, const SHOP_INFO *pShopInfo, LPITEMIDLIST *ppidl) { WCHAR wszShoppingURL[MAX_URL_STRING]; HRESULT hr = _GetShoppingURL(pShopInfo, wszShoppingURL, ARRAYSIZE(wszShoppingURL)); if (SUCCEEDED(hr)) { IShellFolder *psfDesktop; hr = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hr)) { hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszShoppingURL, NULL, ppidl, NULL); psfDesktop->Release(); } } return hr; } HRESULT CFSFolderViewCB::_OnShopForMusicOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; LPITEMIDLIST pidl; // See if there is a sample music BuyURL // (do this check first, because the regular music buy URL should always succeed) HRESULT hr = pThis->_GetShoppingBrowsePidl(pv, psiItemArray, pbc, &c_BuySampleMusic, &pidl); if (SUCCEEDED(hr)) { hr = pThis->_BrowseObject(pidl, SBSP_NEWBROWSER); ILFree(pidl); } else { // Nope - look for the regular music buy URL hr = pThis->_GetShoppingBrowsePidl(pv, psiItemArray, pbc, &c_BuyMusic, &pidl); if (SUCCEEDED(hr)) { hr = pThis->_BrowseObject(pidl, SBSP_NEWBROWSER); ILFree(pidl); } } return hr; } HRESULT CFSFolderViewCB::_OnShopForPicturesOnline(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; WCHAR wszShoppingURL[MAX_URL_STRING]; HRESULT hr = pThis->_GetShoppingURL(&c_BuySamplePictures, wszShoppingURL, ARRAYSIZE(wszShoppingURL)); if (SUCCEEDED(hr)) { HINSTANCE hinstRet = ShellExecute(NULL, NULL, wszShoppingURL, NULL, NULL, SW_SHOWNORMAL); hr = ((UINT_PTR)hinstRet) <= 32 ? E_FAIL : S_OK; } return hr; } HRESULT CFSFolderViewCB::_DataObjectFromItemsOrFolder(IShellItemArray *psiItemArray, IDataObject **ppdto) { *ppdto = NULL; HRESULT hr; if (psiItemArray) { // Something selected -- work with selected items. hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, ppdto)); } else { // Nothing selected -- imply folder selected. hr = SHGetUIObjectOf(_pidl, NULL, IID_PPV_ARG(IDataObject, ppdto)); } return hr; } HRESULT CFSFolderViewCB::_CanSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; *puisState = UIS_DISABLED; IDataObject *pdo; HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo); if (SUCCEEDED(hr)) { // todo: use fOkToBeSlow to get off the UI thread -- right now it wont work because // its specialized just for the WIA stuff and things that have global state ICDBurn *pcdb; if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ICDBurn, &pcdb)))) { // media player will get invoked, so we only worry about if the system has a // recordable drive at all -- whether the shell burning is enabled or not doesnt matter BOOL fHasRecorder; if (SUCCEEDED(pcdb->HasRecordableDrive(&fHasRecorder)) && fHasRecorder) { IUnknown *punk; // if this probe works, we can get something thats good to go and itll burn cds. if (SUCCEEDED(CDBurn_GetExtensionObject(CDBE_TYPE_MUSIC, pdo, IID_PPV_ARG(IUnknown, &punk)))) { *puisState = UIS_ENABLED; punk->Release(); } } pcdb->Release(); } pdo->Release(); } return hr; } HRESULT CFSFolderViewCB::_OnSendToAudioCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; IDataObject *pdo; HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo); if (SUCCEEDED(hr)) { IDropTarget *pdt; hr = CDBurn_GetExtensionObject(CDBE_TYPE_MUSIC, pdo, IID_PPV_ARG(IDropTarget, &pdt)); if (SUCCEEDED(hr)) { hr = SHSimulateDrop(pdt, pdo, 0, NULL, NULL); pdt->Release(); } pdo->Release(); } return hr; } HRESULT CFSFolderViewCB::_CanSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { *puisState = UIS_DISABLED; WCHAR szDrive[4]; if (SUCCEEDED(CDBurn_GetRecorderDriveLetter(szDrive, ARRAYSIZE(szDrive)))) { // if this succeeds, shell cd burning is enabled. *puisState = UIS_ENABLED; } return S_OK; } HRESULT CFSFolderViewCB::_OnSendToCD(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; IDataObject *pdo; HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo); if (SUCCEEDED(hr)) { WCHAR szDrive[4]; hr = CDBurn_GetRecorderDriveLetter(szDrive, ARRAYSIZE(szDrive)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = SHILCreateFromPath(szDrive, &pidl, NULL); if (SUCCEEDED(hr)) { IDropTarget *pdt; hr = SHGetUIObjectOf(pidl, NULL, IID_PPV_ARG(IDropTarget, &pdt)); if (SUCCEEDED(hr)) { hr = SHSimulateDropWithSite(pdt, pdo, 0, NULL, NULL, pThis->_punkSite); pdt->Release(); } ILFree(pidl); } } pdo->Release(); } return hr; } HRESULT CFSFolderViewCB::_OnGetFromCamera(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc) { SHELLEXECUTEINFO sei = {0}; sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_DOENVSUBST; sei.hwnd = ((CFSFolderViewCB*)(void*)pv)->_hwndMain; sei.lpFile = TEXT("%SystemRoot%\\System32\\wiaacmgr.exe"); sei.lpParameters = TEXT("/SelectDevice"); sei.nShow = SW_SHOWNORMAL; return ShellExecuteEx(&sei) ? S_OK : E_FAIL; } HRESULT CFSFolderViewCB::_GetPreview3(IPreview3** ppPreview3) { HRESULT hr = E_FAIL; *ppPreview3 = NULL; if (!_pPreview) { hr = CoCreateInstance(CLSID_Preview, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPreview3, &_pPreview)); if (SUCCEEDED(hr)) { IUnknown_SetSite(_pPreview, _punkSite); } } if (_pPreview) { *ppPreview3 = _pPreview; _pPreview->AddRef(); hr = S_OK; } return hr; } HRESULT CFSFolderViewCB::_OnSlideShow(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; IPreview3* pPreview3; HRESULT hr = pThis->_GetPreview3(&pPreview3); if (SUCCEEDED(hr)) { hr = pPreview3->SlideShow(); pPreview3->Release(); } return hr; } HRESULT CFSFolderViewCB::_OnWallpaper(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; HRESULT hr = E_FAIL; IDataObject *pdo; if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo)))) { IPreview3* pPreview3; if (SUCCEEDED(pThis->_GetPreview3(&pPreview3))) { TCHAR szPath[MAX_PATH]; if (SUCCEEDED(PathFromDataObject(pdo, szPath, ARRAYSIZE(szPath)))) { hr = pPreview3->SetWallpaper(szPath); } pPreview3->Release(); } pdo->Release(); } return hr; } HRESULT CFSFolderViewCB::_OnOrderPrints(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; IDataObject *pdo; HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo); if (SUCCEEDED(hr)) { hr = SHSimulateDropOnClsid(CLSID_InternetPrintOrdering, pThis->_punkSite, pdo); pdo->Release(); } return hr; } HRESULT CFSFolderViewCB::_OnPrintPictures(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFSFolderViewCB* pThis = (CFSFolderViewCB*)(void*)pv; IDataObject *pdo; HRESULT hr = pThis->_DataObjectFromItemsOrFolder(psiItemArray, &pdo); if (SUCCEEDED(hr)) { hr = SHSimulateDropOnClsid(CLSID_PrintPhotosDropTarget, pThis->_punkSite, pdo); pdo->Release(); } return hr; } const WVTASKITEM c_CommonDocumentsSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_COMMONDOCUMENTS, IDS_HEADER_COMMONDOCUMENTS_TT); const WVTASKITEM c_CommonDocumentsSpecialTaskList[] = { WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_COMMONDOCUMENTSHELP, IDS_TASK_COMMONDOCUMENTSHELP_TT, IDI_TASK_HELP, NULL, CFSFolderViewCB::_OnCommonDocumentsHelp), }; const LPCTSTR c_DocumentsOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) }; const WVTASKITEM c_MusicSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_MUSIC, IDS_HEADER_MUSIC_TT); const WVTASKITEM c_MusicSpecialTaskList[] = { WVTI_ENTRY_ALL_TITLE(UICID_PlayMusic, L"shell32.dll", IDS_TASK_PLAYALL, IDS_TASK_PLAYALL, IDS_TASK_PLAY, IDS_TASK_PLAY, IDS_TASK_PLAY_TT, IDI_TASK_PLAY_MUSIC, CFSFolderViewCB::_CanPlayMusic, CFSFolderViewCB::_OnPlayMusic), WVTI_ENTRY_ALL(UICID_ShopForMusicOnline, L"shell32.dll", IDS_TASK_SHOPFORMUSICONLINE, IDS_TASK_SHOPFORMUSICONLINE_TT, IDI_TASK_BUY_MUSIC, NULL, CFSFolderViewCB::_OnShopForMusicOnline), WVTI_ENTRY_ALL_TITLE(GUID_NULL, L"shell32.dll", IDS_TASK_COPYTOAUDIOCDALL, IDS_TASK_COPYTOAUDIOCD, IDS_TASK_COPYTOAUDIOCD, IDS_TASK_COPYTOAUDIOCD, IDS_TASK_COPYTOAUDIOCD_TT, IDI_TASK_SENDTOAUDIOCD, CFSFolderViewCB::_CanSendToAudioCD, CFSFolderViewCB::_OnSendToAudioCD), }; const LPCTSTR c_MusicOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_MYMUSIC), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) }; const LPCTSTR c_MyMusicOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_COMMON_MUSIC), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) }; const WVTASKITEM c_PicturesSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_PICTURES, IDS_HEADER_PICTURES_TT); const WVTASKITEM c_PicturesSpecialTaskList[] = { WVTI_ENTRY_ALL(UICID_GetFromCamera, L"shell32.dll", IDS_TASK_GETFROMCAMERA, IDS_TASK_GETFROMCAMERA_TT, IDI_TASK_GETFROMCAMERA, CFSFolderViewCB::_HasWiaDevices, CFSFolderViewCB::_OnGetFromCamera), WVTI_ENTRY_ALL(UICID_SlideShow, L"shell32.dll", IDS_TASK_SLIDESHOW, IDS_TASK_SLIDESHOW_TT, IDI_TASK_SLIDESHOW, CFSFolderViewCB::_HasItems, CFSFolderViewCB::_OnSlideShow), WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_ORDERPRINTS, IDS_TASK_ORDERPRINTS_TT, IDI_TASK_ORDERPRINTS, CFSFolderViewCB::_CanOrderPrints, CFSFolderViewCB::_OnOrderPrints), WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_PRINT_PICTURE_FOLDER, IDS_TASK_PRINT_PICTURE, IDS_TASK_PRINT_PICTURE_FOLDER, IDS_TASK_PRINT_PICTURES, IDS_TASK_PRINT_PICTURES_TT, IDI_TASK_PRINTPICTURES, CFSFolderViewCB::_CanPrintPictures, CFSFolderViewCB::_OnPrintPictures), WVTI_ENTRY_FILE(UICID_SetAsWallpaper,L"shell32.dll",IDS_TASK_SETASWALLPAPER, IDS_TASK_SETASWALLPAPER_TT, IDI_TASK_SETASWALLPAPER,CFSFolderViewCB::_CanWallpaper, CFSFolderViewCB::_OnWallpaper), WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_COPYTOCDALL, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD_TT, IDI_TASK_SENDTOCD, CFSFolderViewCB::_CanSendToCD, CFSFolderViewCB::_OnSendToCD), // Note: temporarily using IDI_ORDERPRINTS for the following task: WVTI_ENTRY_ALL(UICID_ShopForPicturesOnline, L"shell32.dll", IDS_TASK_SHOPFORPICTURESONLINE, IDS_TASK_SHOPFORPICTURESONLINE_TT, IDI_TASK_ORDERPRINTS, CFSFolderViewCB::_CanBuyPictures, CFSFolderViewCB::_OnShopForPicturesOnline), }; const LPCTSTR c_PicturesOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_MYPICTURES), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) }; const LPCTSTR c_MyPicturesOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_COMMON_PICTURES), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) }; const WVTASKITEM c_VideosSpecialTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_VIDEOS, IDS_HEADER_VIDEOS_TT); const WVTASKITEM c_VideosSpecialTaskList[] = { WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_PLAYALL, IDS_TASK_PLAYALL, IDS_TASK_PLAY, IDS_TASK_PLAY, IDS_TASK_PLAY_VIDEOS_TT, IDI_TASK_PLAY_MUSIC, CFSFolderViewCB::_CanPlayVideos, CFSFolderViewCB::_OnPlayVideos), WVTI_ENTRY_ALL(UICID_GetFromCamera, L"shell32.dll", IDS_TASK_GETFROMCAMERA, IDS_TASK_GETFROMCAMERA_TT, IDI_TASK_GETFROMCAMERA, CFSFolderViewCB::_HasWiaDevices, CFSFolderViewCB::_OnGetFromCamera), WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_COPYTOCDALL, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD, IDS_TASK_COPYTOCD_TT, IDI_TASK_SENDTOCD, CFSFolderViewCB::_CanSendToCD, CFSFolderViewCB::_OnSendToCD) }; const LPCTSTR c_VideosOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_MYVIDEO), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) }; const LPCTSTR c_MyVideosOtherPlaces[] = { MAKEINTRESOURCE(CSIDL_COMMON_VIDEO), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) }; typedef struct { const WVTASKITEM *pwvIntroText; const WVTASKITEM *pwvSpecialHeader; const WVTASKITEM *pwvSpecialTaskList; UINT cSpecialTaskList; const WVTASKITEM *pwvFolderHeader; const WVTASKITEM *pwvFolderTaskList; UINT cFolderTaskList; const LPCTSTR *pdwOtherPlacesList; UINT cOtherPlacesList; LPCWSTR pszThemeInfo; } WVCONTENT_DATA; #define WVCONTENT_DEFVIEWDEFAULT(op) { NULL, NULL, NULL, 0, NULL, NULL, 0, (op), ARRAYSIZE(op), NULL } #define WVCONTENT_FOLDER(fh, ft, op) { NULL, NULL, NULL, 0, &(fh), (ft), ARRAYSIZE(ft), (op), ARRAYSIZE(op), NULL } #define WVCONTENT_SPECIAL(sh, st, op, th) { NULL, &(sh), (st), ARRAYSIZE(st), NULL, NULL, 0, (op), ARRAYSIZE(op), (th) } const WVCONTENT_DATA c_wvContent[] = { WVCONTENT_DEFVIEWDEFAULT(c_DocumentsOtherPlaces), // FVCBFT_DOCUMENTS WVCONTENT_DEFVIEWDEFAULT(c_DocumentsOtherPlaces), // FVCBFT_MYDOCUMENTS WVCONTENT_SPECIAL(c_PicturesSpecialTaskHeader, c_PicturesSpecialTaskList, c_PicturesOtherPlaces, L"picture"),// FVCBFT_PICTURES WVCONTENT_SPECIAL(c_PicturesSpecialTaskHeader, c_PicturesSpecialTaskList, c_MyPicturesOtherPlaces, L"picture"),// FVCBFT_MYPICTURES WVCONTENT_SPECIAL(c_PicturesSpecialTaskHeader, c_PicturesSpecialTaskList, c_PicturesOtherPlaces, L"picture"),// FVCBFT_PHOTOALBUM WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MusicOtherPlaces, L"music"), // FVCBFT_MUSIC WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MyMusicOtherPlaces, L"music"), // FVCBFT_MYMUSIC WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MusicOtherPlaces, L"music"), // FVCBFT_MUSICARTIST WVCONTENT_SPECIAL(c_MusicSpecialTaskHeader, c_MusicSpecialTaskList, c_MusicOtherPlaces, L"music"), // FVCBFT_MUSICALBUM WVCONTENT_SPECIAL(c_VideosSpecialTaskHeader, c_VideosSpecialTaskList, c_VideosOtherPlaces, L"video"), // FVCBFT_VIDEOS WVCONTENT_SPECIAL(c_VideosSpecialTaskHeader, c_VideosSpecialTaskList, c_MyVideosOtherPlaces, L"video"), // FVCBFT_MYVIDEOS WVCONTENT_SPECIAL(c_VideosSpecialTaskHeader, c_VideosSpecialTaskList, c_VideosOtherPlaces, L"video"), // FVCBFT_VIDEOALBUM WVCONTENT_DEFVIEWDEFAULT(c_DocumentsOtherPlaces),// stub, it should not be used as legacy htts wont have DUI view. // FVCBFT_USELEGACYHTT WVCONTENT_SPECIAL(c_CommonDocumentsSpecialTaskHeader, c_CommonDocumentsSpecialTaskList, c_DocumentsOtherPlaces, NULL), // FVCBFT_COMMONDOCUMENTS }; // This structure describes what a Folder Type can control: // typedef struct { BOOL fIncludeThumbstrip; FOLDERVIEWMODE fvmFew; FOLDERVIEWMODE fvmMid; FOLDERVIEWMODE fvmMany; const SHCOLUMNID* pscidSort; int iSortDirection; } FVCBFOLDERTYPEDATA; // Here are all the Folder Types we know about: const FVCBFOLDERTYPEDATA c_rgFolderType[] = { // flmstrip // <25 // 25..49 //50... //sort by //sort dir { FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_DOCUMENTS { FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_MYDOCUMENTS { TRUE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_PICTURES { TRUE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_MYPICTURES { TRUE, FVM_THUMBSTRIP,FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_PHOTOALBUM { FALSE, FVM_TILE, FVM_TILE, FVM_DETAILS, &SCID_NAME, 1}, // FVCBFT_MUSIC { FALSE, FVM_THUMBNAIL, FVM_TILE, FVM_LIST, &SCID_NAME, 1}, // FVCBFT_MYMUSIC { FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, -1}, // FVCBFT_MUSICARTIST { FALSE, FVM_TILE, FVM_TILE, FVM_DETAILS, &SCID_NAME, 1}, // FVCBFT_MUSICALBUM, SCID_MUSIC_Track is the same as SCID_NAME { FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_VIDEOS { FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_MYVIDEOS { FALSE, FVM_THUMBNAIL, FVM_THUMBNAIL, FVM_THUMBNAIL, &SCID_NAME, 1}, // FVCBFT_VIDEOALBUM { FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_USELEGACYHTT, only for listview state to look like FVCBFT_DOCUMENTS { FALSE, FVM_TILE, FVM_TILE, FVM_ICON, &SCID_NAME, 1}, // FVCBFT_COMMONDOCUMENTS }; // This is used to sniff the Folder Type based on folder location: typedef struct { UINT csidl; FVCBFOLDERTYPE ft; DWORD dwFlags; } FVCBDATA; #define FVCBDF_SUBFOLDERS_ONLY 1 #define FVCBDF_THISFOLDER_ONLY 2 const FVCBDATA c_rgFolderState[] = { {CSIDL_COMMON_PICTURES, FVCBFT_PHOTOALBUM, FVCBDF_SUBFOLDERS_ONLY}, {CSIDL_MYPICTURES, FVCBFT_PHOTOALBUM, FVCBDF_SUBFOLDERS_ONLY}, {CSIDL_COMMON_PICTURES, FVCBFT_PICTURES, FVCBDF_THISFOLDER_ONLY}, {CSIDL_MYPICTURES, FVCBFT_MYPICTURES, FVCBDF_THISFOLDER_ONLY}, {CSIDL_COMMON_MUSIC, FVCBFT_MUSIC, FVCBDF_THISFOLDER_ONLY}, {CSIDL_MYMUSIC, FVCBFT_MYMUSIC, FVCBDF_THISFOLDER_ONLY}, {CSIDL_MYMUSIC, FVCBFT_MUSICARTIST, FVCBDF_SUBFOLDERS_ONLY}, {CSIDL_COMMON_VIDEO, FVCBFT_VIDEOS, 0}, {CSIDL_MYVIDEO, FVCBFT_MYVIDEOS, 0}, {CSIDL_COMMON_DOCUMENTS,FVCBFT_COMMONDOCUMENTS, FVCBDF_THISFOLDER_ONLY}, {CSIDL_PERSONAL, FVCBFT_MYDOCUMENTS, FVCBDF_THISFOLDER_ONLY}, }; // these are special folders that used to be web view folders. we override the "support legacy" for this list: const UINT c_rgFolderStateNoLegacy[] = { CSIDL_WINDOWS, CSIDL_SYSTEM, CSIDL_PROGRAM_FILES, }; // This is used to map desktop.ini's folder type into our Folder Type const struct { LPCWSTR pszType; FVCBFOLDERTYPE ft; } c_rgPropBagFolderType[] = { {STR_TYPE_DOCUMENTS, FVCBFT_DOCUMENTS}, {STR_TYPE_MYDOCUMENTS, FVCBFT_MYDOCUMENTS}, {STR_TYPE_PICTURES, FVCBFT_PICTURES}, {STR_TYPE_MYPICTURES, FVCBFT_MYPICTURES}, {STR_TYPE_PHOTOALBUM, FVCBFT_PHOTOALBUM}, {STR_TYPE_MUSIC, FVCBFT_MUSIC}, {STR_TYPE_MYMUSIC, FVCBFT_MYMUSIC}, {STR_TYPE_MUSICARTIST, FVCBFT_MUSICARTIST}, {STR_TYPE_MUSICALBUM, FVCBFT_MUSICALBUM}, {STR_TYPE_VIDEOS, FVCBFT_VIDEOS}, {STR_TYPE_MYVIDEOS, FVCBFT_MYVIDEOS}, {STR_TYPE_VIDEOALBUM, FVCBFT_VIDEOALBUM}, {STR_TYPE_USELEGACYHTT, FVCBFT_USELEGACYHTT}, {STR_TYPE_COMMONDOCUMENTS, FVCBFT_COMMONDOCUMENTS}, }; const struct { PERCEIVED gen; FVCBFOLDERTYPE ft; } c_rgSniffType[] = { {GEN_AUDIO, FVCBFT_MUSIC}, {GEN_IMAGE, FVCBFT_PHOTOALBUM}, {GEN_VIDEO, FVCBFT_VIDEOS}, }; HRESULT _GetFolderTypeForString(LPCWSTR pszFolderType, FVCBFOLDERTYPE *piType) { HRESULT hr = E_FAIL; for (int i = 0; i < ARRAYSIZE(c_rgPropBagFolderType); i++) { if (!StrCmpI(c_rgPropBagFolderType[i].pszType, pszFolderType)) { *piType = c_rgPropBagFolderType[i].ft; hr = S_OK; break; } } return hr; } HRESULT CFSFolderViewCB::_GetStringForFolderType(int iType, LPWSTR pszFolderType, UINT cchBuf) { HRESULT hr = E_FAIL; for (int i = 0; i < ARRAYSIZE(c_rgPropBagFolderType); i++) { if (c_rgPropBagFolderType[i].ft == iType) { hr = StringCchCopy(pszFolderType, cchBuf, c_rgPropBagFolderType[i].pszType); break; } } return hr; } extern HRESULT GetTemplateInfoFromHandle(HANDLE h, UCHAR * pKey, DWORD *pdwSize); FVCBFOLDERTYPE _GetFolderType(LPCWSTR pszPath, LPCITEMIDLIST pidl, BOOL fIsSystemFolder) { // Assume we don't find a match FVCBFOLDERTYPE nFolderType = FVCBFT_NOTSPECIFIED; WCHAR szFolderType[MAX_PATH]; szFolderType[0] = 0; // peruser is first if (FVCBFT_NOTSPECIFIED == nFolderType) { IPropertyBag *ppb; if (SUCCEEDED(SHGetViewStatePropertyBag(pidl, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb)))) { SHPropertyBag_ReadStr(ppb, L"FolderType", szFolderType, ARRAYSIZE(szFolderType)); if (szFolderType[0]) _GetFolderTypeForString(szFolderType, &nFolderType); ppb->Release(); } } // next, alluser if ((FVCBFT_NOTSPECIFIED == nFolderType) && fIsSystemFolder) { GetFolderString(pszPath, NULL, szFolderType, ARRAYSIZE(szFolderType), TEXT("FolderType")); if (szFolderType[0]) { _GetFolderTypeForString(szFolderType, &nFolderType); } } // Check the location of this folder is next // if (FVCBFT_NOTSPECIFIED == nFolderType) { for (int i = 0; i < ARRAYSIZE(c_rgFolderState); i++) { if (FVCBDF_THISFOLDER_ONLY & c_rgFolderState[i].dwFlags) { if (PathIsOneOf(pszPath, &(c_rgFolderState[i].csidl), 1)) { nFolderType = c_rgFolderState[i].ft; break; } } else if (FVCBDF_SUBFOLDERS_ONLY & c_rgFolderState[i].dwFlags) { if (PathIsDirectChildOf(MAKEINTRESOURCE(c_rgFolderState[i].csidl), pszPath)) { nFolderType = c_rgFolderState[i].ft; break; } } else if (PathIsEqualOrSubFolder(MAKEINTRESOURCE(c_rgFolderState[i].csidl), pszPath)) { nFolderType = c_rgFolderState[i].ft; break; } } } // Upgrade old webviews to their DUI equivalents, if we can if (FVCBFT_NOTSPECIFIED == nFolderType && fIsSystemFolder && SHRestricted(REST_ALLOWLEGACYWEBVIEW)) { // Don't check for legacy webview on our special folders if (!PathIsOneOf(pszPath, c_rgFolderStateNoLegacy, ARRAYSIZE(c_rgFolderStateNoLegacy))) { SFVM_WEBVIEW_TEMPLATE_DATA wvData; if (SUCCEEDED(DefaultGetWebViewTemplateFromPath(pszPath, &wvData))) { if (StrStrI(wvData.szWebView, L"ImgView.htt")) { nFolderType = FVCBFT_PHOTOALBUM; } else if (StrStrI(wvData.szWebView, L"classic.htt") || StrStrI(wvData.szWebView, L"default.htt") || StrStrI(wvData.szWebView, L"standard.htt")) { // map all of these to "documents", since DUI should take care // of what the old templates did automatically nFolderType = FVCBFT_DOCUMENTS; } else if (StrStrI(wvData.szWebView, L"folder.htt")) { LPTSTR pszFilePrefix = StrStrI(wvData.szWebView, L"file://"); HANDLE hfile = CreateFileWrapW( pszFilePrefix && (&pszFilePrefix[6] < &wvData.szWebView[MAX_PATH - 1]) ? &pszFilePrefix[7] : wvData.szWebView, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE != hfile) { DWORD dwSize; UCHAR pKey[MD5DIGESTLEN]; if (SUCCEEDED(GetTemplateInfoFromHandle(hfile, pKey, &dwSize))) { static const struct { UCHAR pKey[MD5DIGESTLEN]; FVCBFOLDERTYPE nFolderType; } c_paLegacyKeyMap[] = { { { 0xf6, 0xad, 0x42, 0xbd, 0xfa, 0x92, 0xb6, 0x61, 0x08, 0x13, 0xd3, 0x71, 0x32, 0x18, 0x85, 0xc7 }, FVCBFT_DOCUMENTS }, // Win98 Gold Program Files { { 0x80, 0xea, 0xcb, 0xc7, 0x85, 0x1e, 0xbb, 0x99, 0x12, 0x7b, 0x9d, 0xc7, 0x80, 0xa6, 0x55, 0x2f }, FVCBFT_DOCUMENTS }, // Win98 Gold System //{ { 0x80, 0xea, 0xcb, 0xc7, 0x85, 0x1e, 0xbb, 0x99, 0x12, 0x7b, 0x9d, 0xc7, 0x80, 0xa6, 0x55, 0x2f }, FVCBFT_DOCUMENTS }, // Win98 Gold Windows { { 0x13, 0x0b, 0xe7, 0xaa, 0x42, 0x6f, 0x9c, 0x2e, 0xab, 0x6b, 0x90, 0x77, 0xce, 0x2d, 0xd1, 0x04 }, FVCBFT_DOCUMENTS }, // Win98 Gold - folder.htt //{ { 0xf6, 0xad, 0x42, 0xbd, 0xfa, 0x92, 0xb6, 0x61, 0x08, 0x13, 0xd3, 0x71, 0x32, 0x18, 0x85, 0xc7 }, FVCBFT_DOCUMENTS }, // Win98 SE Program Files { { 0xc4, 0xab, 0x8f, 0x60, 0xf8, 0xfc, 0x5d, 0x07, 0x9e, 0x16, 0xd8, 0xea, 0x12, 0x2c, 0xad, 0x5c }, FVCBFT_DOCUMENTS }, // Win98 SE System //{ { 0xc4, 0xab, 0x8f, 0x60, 0xf8, 0xfc, 0x5d, 0x07, 0x9e, 0x16, 0xd8, 0xea, 0x12, 0x2c, 0xad, 0x5c }, FVCBFT_DOCUMENTS }, // Win98 SE Windows //{ { 0x13, 0x0b, 0xe7, 0xaa, 0x42, 0x6f, 0x9c, 0x2e, 0xab, 0x6b, 0x90, 0x77, 0xce, 0x2d, 0xd1, 0x04 }, FVCBFT_DOCUMENTS }, // Win98 SE - folder.htt { { 0xef, 0xd0, 0x3e, 0x9e, 0xd8, 0x5e, 0xf3, 0xc5, 0x7e, 0x40, 0xbd, 0x8e, 0x52, 0xbc, 0x9c, 0x67 }, FVCBFT_DOCUMENTS }, // WinME Program Files { { 0x49, 0xdb, 0x25, 0x79, 0x7a, 0x5c, 0xb2, 0x8a, 0xe2, 0x57, 0x59, 0xde, 0x2b, 0xd2, 0xa6, 0x70 }, FVCBFT_DOCUMENTS }, // WinME System //{ { 0x49, 0xdb, 0x25, 0x79, 0x7a, 0x5c, 0xb2, 0x8a, 0xe2, 0x57, 0x59, 0xde, 0x2b, 0xd2, 0xa6, 0x70 }, FVCBFT_DOCUMENTS }, // WinME Windows { { 0x2b, 0xcd, 0xc3, 0x11, 0x72, 0x28, 0x34, 0x46, 0xfa, 0x88, 0x31, 0x34, 0xfc, 0xee, 0x7a, 0x3b }, FVCBFT_DOCUMENTS }, // WinME - classic.htt { { 0x68, 0x20, 0xa0, 0xa1, 0x6c, 0xba, 0xbf, 0x67, 0x80, 0xfe, 0x1e, 0x70, 0xdf, 0xcb, 0xd6, 0x34 }, FVCBFT_DOCUMENTS }, // WinME - folder.htt { { 0x5e, 0x18, 0xaf, 0x48, 0xb1, 0x9f, 0xb8, 0x12, 0x58, 0x64, 0x4a, 0xa2, 0xf5, 0x12, 0x0f, 0x01 }, FVCBFT_PHOTOALBUM }, // WinME - imgview.htt { { 0x33, 0x94, 0x21, 0x3b, 0x17, 0x31, 0x2b, 0xeb, 0xac, 0x93, 0x84, 0x13, 0xb8, 0x1f, 0x95, 0x24 }, FVCBFT_DOCUMENTS }, // WinME - standard.htt { { 0x47, 0x03, 0x19, 0xf8, 0x0c, 0x20, 0xc4, 0x4f, 0x10, 0xfd, 0x63, 0xf1, 0x2d, 0x2d, 0x0a, 0xcb }, FVCBFT_DOCUMENTS }, // WinME - starter.htt { { 0x60, 0x7d, 0xea, 0xa5, 0xaf, 0x5e, 0xbb, 0x9b, 0x10, 0x18, 0xf9, 0x59, 0x9e, 0x43, 0x89, 0x62 }, FVCBFT_DOCUMENTS }, // Win2k Program Files { { 0x1c, 0xa6, 0x22, 0xd4, 0x4a, 0x31, 0x57, 0x93, 0xa7, 0x26, 0x68, 0x3c, 0x87, 0x95, 0x8c, 0xce }, FVCBFT_DOCUMENTS }, // Win2k System32 //{ { 0x1c, 0xa6, 0x22, 0xd4, 0x4a, 0x31, 0x57, 0x93, 0xa7, 0x26, 0x68, 0x3c, 0x87, 0x95, 0x8c, 0xce }, FVCBFT_DOCUMENTS }, // Win2k Windows (WinNT) { { 0x03, 0x43, 0x48, 0xed, 0xe4, 0x9f, 0xd6, 0xc0, 0x58, 0xf7, 0x72, 0x3f, 0x1b, 0xd0, 0xa7, 0x10 }, FVCBFT_DOCUMENTS }, // Win2k - classic.htt { { 0xa8, 0x84, 0xf9, 0x37, 0x84, 0x10, 0xde, 0x7c, 0x0b, 0x34, 0x90, 0x37, 0x23, 0x9e, 0x54, 0x35 }, FVCBFT_DOCUMENTS }, // Win2k - folder.htt { { 0x75, 0x1f, 0xcf, 0xca, 0xdd, 0xc7, 0x1d, 0xc7, 0xe1, 0xaf, 0x0c, 0x3e, 0x1e, 0xae, 0x18, 0x51 }, FVCBFT_PHOTOALBUM }, // Win2k - imgview.htt { { 0xcc, 0x3f, 0x15, 0xce, 0x4b, 0xfa, 0x36, 0xdf, 0x9b, 0xd8, 0x24, 0x82, 0x3a, 0x9c, 0x0b, 0xa7 }, FVCBFT_DOCUMENTS }, // Win2k - standard.htt { { 0x6c, 0xd1, 0xbf, 0xcf, 0xf9, 0x24, 0x24, 0x24, 0x22, 0xfa, 0x1a, 0x8d, 0xd2, 0x1a, 0x41, 0x73 }, FVCBFT_DOCUMENTS }, // Win2k - starter.htt }; static const size_t c_nLegacyKeys = ARRAYSIZE(c_paLegacyKeyMap); for (size_t i = 0; i < c_nLegacyKeys; i++) { if (0 == memcmp(pKey, c_paLegacyKeyMap[i].pKey, sizeof(UCHAR) * MD5DIGESTLEN)) { // It's a known legacy folder.htt. nFolderType = c_paLegacyKeyMap[i].nFolderType; break; } } } CloseHandle(hfile); } // If we can't say it's a known legacy folder.htt... if (FVCBFT_NOTSPECIFIED == nFolderType) { // ...don't map it to a DUI folder type (preserve customizations). nFolderType = FVCBFT_USELEGACYHTT; } } else { nFolderType = FVCBFT_USELEGACYHTT; } } } } return nFolderType; } BOOL CFSFolderViewCB::_IsBarricadedFolder() { BOOL bResult = FALSE; TCHAR szPath[MAX_PATH]; if (SUCCEEDED(_pfsf->_GetPath(szPath, ARRAYSIZE(szPath)))) { const UINT uiFolders[] = {CSIDL_PROGRAM_FILES, CSIDL_WINDOWS, CSIDL_SYSTEM}; if (PathIsOneOf(szPath, uiFolders, ARRAYSIZE(uiFolders))) bResult = TRUE; else { TCHAR szSystemDrive[4]; ExpandEnvironmentStrings(TEXT("%SystemDrive%\\"), szSystemDrive, ARRAYSIZE(szSystemDrive)); if (!lstrcmpi(szPath, szSystemDrive)) bResult = TRUE; } } return bResult; } static const struct { FVCBFOLDERTYPE type; PCWSTR pszClass; PERCEIVED gen;} c_rgDirectoryClasses[] = { {FVCBFT_PICTURES, L"Directory.Image", GEN_IMAGE}, {FVCBFT_MYPICTURES, L"Directory.Image", GEN_IMAGE}, {FVCBFT_PHOTOALBUM, L"Directory.Image", GEN_IMAGE}, {FVCBFT_MUSIC, L"Directory.Audio", GEN_AUDIO}, {FVCBFT_MYMUSIC, L"Directory.Audio", GEN_AUDIO}, {FVCBFT_MUSICARTIST, L"Directory.Audio", GEN_AUDIO}, {FVCBFT_MUSICALBUM, L"Directory.Audio", GEN_AUDIO}, {FVCBFT_VIDEOS, L"Directory.Video", GEN_VIDEO}, {FVCBFT_MYVIDEOS, L"Directory.Video", GEN_VIDEO}, {FVCBFT_VIDEOALBUM, L"Directory.Video", GEN_VIDEO}, }; LPCWSTR _GetDirectoryClass(LPCWSTR pszPath, LPCITEMIDLIST pidl, BOOL fIsSystemFolder) { FVCBFOLDERTYPE type = _GetFolderType(pszPath, pidl, fIsSystemFolder); if (type != FVCBFT_NOTSPECIFIED) { for (int i = 0; i < ARRAYSIZE(c_rgDirectoryClasses); i++) { if (c_rgDirectoryClasses[i].type == type) return c_rgDirectoryClasses[i].pszClass; } } return NULL; } PERCEIVED CFSFolderViewCB::_GetFolderPerceivedType(LPCIDFOLDER pidf) { PERCEIVED gen = GEN_FOLDER; WCHAR szPath[MAX_PATH]; if (SUCCEEDED(_pfsf->_GetPathForItem(pidf, szPath, ARRAYSIZE(szPath)))) { LPITEMIDLIST pidl = ILCombine(_pfsf->_GetIDList(), (LPCITEMIDLIST)pidf); if (pidl) { FVCBFOLDERTYPE type = _GetFolderType(szPath, pidl, CFSFolder::_IsSystemFolder(pidf)); if (type != -1) { for (int i = 0; i < ARRAYSIZE(c_rgDirectoryClasses); i++) { if (c_rgDirectoryClasses[i].type == type) { gen = c_rgDirectoryClasses[i].gen; break; } } } ILFree(pidl); } } return gen; } HRESULT CFSFolderViewCB::OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST* rgpidl) { // Remember the count of items _cItems = celt; FVCBFOLDERTYPE nFolderType = FVCBFT_NOTSPECIFIED; WCHAR szHere[MAX_PATH]; if (SUCCEEDED(_pfsf->_GetPath(szHere, ARRAYSIZE(szHere)))) { nFolderType = _GetFolderType(szHere, _pfsf->_GetIDList(), _pfsf->_CheckDefaultIni(NULL, NULL, 0)); } if (FVCBFT_NOTSPECIFIED == nFolderType) { if (_IsBarricadedFolder()) { nFolderType = FVCBFT_DOCUMENTS; } } // Our location didn't do the trick, so look at the enumerated contents if (FVCBFT_NOTSPECIFIED == nFolderType && celt > 0) { DWORD dwExtCount[ARRAYSIZE(c_rgSniffType)] = {0}; // look at each pidl -> what type is it // // But don't look at too many pidls or we really slow down folder // creation time. If we can't figure it out in the first 100, give up. // DWORD dwTotalCount = 0; for (UINT n = 0; n < celt && dwTotalCount < 100; n++) { LPCIDFOLDER pidf = CFSFolder_IsValidID(rgpidl[n]); ASSERT(pidf); CFileSysItemString fsi(pidf); PERCEIVED gen = fsi.PerceivedType(); if (gen == GEN_FOLDER) { gen = _GetFolderPerceivedType(pidf); } for (int i = 0; i < ARRAYSIZE(c_rgSniffType); i++) { if (c_rgSniffType[i].gen == gen) { dwExtCount[i]++; break; } } if (gen != GEN_FOLDER) dwTotalCount++; } // if we found files we determine the overall folder type if (dwTotalCount > 0) { DWORD dwSixtyPercent = MulDiv(dwTotalCount, 3, 5); for (int i = 0; i < ARRAYSIZE(c_rgSniffType); i++) { if (dwExtCount[i] >= dwSixtyPercent) { nFolderType = c_rgSniffType[i].ft; break; } } } } // if at this point we've already decided on a folder type, then it either came from sniffing // or the folder location and we can safely persist that out. // if celt != 0 then we've sniffed it and we dont want to sniff again, so persist that out. // otherwise we're in a random folder with 0 elements and we'll sniff it next time. BOOL fCommit = (FVCBFT_NOTSPECIFIED != nFolderType) || (celt != 0); // Last resort, assume we're a document folder: if (FVCBFT_NOTSPECIFIED == nFolderType) { nFolderType = FVCBFT_DOCUMENTS; } // store what we found out back into the bag. IPropertyBag *ppb; if (fCommit && SUCCEEDED(SHGetViewStatePropertyBag(_pfsf->_GetIDList(), VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb)))) { WCHAR szFolderType[MAX_PATH]; if (SUCCEEDED(_GetStringForFolderType(nFolderType, szFolderType, ARRAYSIZE(szFolderType)))) { SHPropertyBag_WriteStr(ppb, PROPSTR_FOLDERTYPE, szFolderType); } ppb->Release(); } _pfsf->_nFolderType = nFolderType; return S_OK; } HRESULT CFSFolderViewCB::OnGetViewData(DWORD pv, UINT uViewMode, SFVM_VIEW_DATA* pvi) { // Normally whatever defview wants is good for us pvi->dwOptions = SFVMQVI_NORMAL; // If our sniff type likes THUMBSTRIP, then override defview // if (FVM_THUMBSTRIP == uViewMode) { if (c_rgFolderType[_pfsf->_nFolderType].fIncludeThumbstrip) { pvi->dwOptions = SFVMQVI_INCLUDE; } } return S_OK; } HRESULT CFSFolderViewCB::OnGetWebViewTemplate(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_TEMPLATE_DATA* pvit) { HRESULT hr = E_FAIL; if (FVCBFT_USELEGACYHTT == _pfsf->_nFolderType) { TCHAR szHere[MAX_PATH]; if (SUCCEEDED(_pfsf->_GetPath(szHere, ARRAYSIZE(szHere))) && _pfsf->_CheckDefaultIni(NULL, NULL, 0)) { hr = DefaultGetWebViewTemplateFromPath(szHere, pvit); } } return hr; } // Note: defview provides this implementation, this is only for testing // so the WIA guys can override defview's behavior (and as a way for us // to force DUI in the presence of HTML content) // HRESULT CFSFolderViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData) { HRESULT hr = E_FAIL; if (FVCBFT_USELEGACYHTT != _pfsf->_nFolderType) { ZeroMemory(pData, sizeof(*pData)); pData->dwLayout = SFVMWVL_NORMAL | SFVMWVL_FILES; if (FVM_THUMBSTRIP == uViewMode) { pData->dwLayout = SFVMWVL_PREVIEW | SFVMWVL_FILES; // duiview will do a release on this pointer when the control is destroyed _GetPreview3((IPreview3 **)&pData->punkPreview); } // RAID 242382 // If we have an image folder, we want to unconditionally hide DefView's // default "Print this file" folder task since we will supply a context // appropriate "Print pictures" special task. // // RAID 359567 // If we have a music folder, we want to unconditionally hide DefView's // default "Publish this file" folder task. Not sure the rationale // behind this, but perhaps they don't want us to be seen as a Napster. // // Note: // This is a HACK added for Whistler, which should be removed in Blackcomb. // switch (_pfsf->_nFolderType) { case FVCBFT_PICTURES: case FVCBFT_MYPICTURES: case FVCBFT_PHOTOALBUM: case FVCBFT_VIDEOS: case FVCBFT_MYVIDEOS: case FVCBFT_VIDEOALBUM: pData->dwLayout |= SFVMWVL_NOPRINT; break; case FVCBFT_MUSIC: case FVCBFT_MYMUSIC: case FVCBFT_MUSICARTIST: case FVCBFT_MUSICALBUM: pData->dwLayout |= SFVMWVL_NOPUBLISH; break; } hr = S_OK; } return hr; } HRESULT CFSFolderViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData) { ZeroMemory(pData, sizeof(*pData)); // Check if the folder we are currently over is one of the blockaded folders. if (_IsBarricadedFolder()) { pData->dwFlags = SFVMWVF_BARRICADE; } if (c_wvContent[_pfsf->_nFolderType].pwvIntroText) Create_IUIElement(c_wvContent[_pfsf->_nFolderType].pwvIntroText, &(pData->pIntroText)); if (c_wvContent[_pfsf->_nFolderType].pwvSpecialHeader && c_wvContent[_pfsf->_nFolderType].pwvSpecialTaskList) Create_IUIElement(c_wvContent[_pfsf->_nFolderType].pwvSpecialHeader, &(pData->pSpecialTaskHeader)); if (c_wvContent[_pfsf->_nFolderType].pwvFolderHeader && c_wvContent[_pfsf->_nFolderType].pwvFolderTaskList) Create_IUIElement(c_wvContent[_pfsf->_nFolderType].pwvFolderHeader, &(pData->pFolderTaskHeader)); if (c_wvContent[_pfsf->_nFolderType].pdwOtherPlacesList) CreateIEnumIDListOnCSIDLs(_pfsf->_pidl, (LPCTSTR *)c_wvContent[_pfsf->_nFolderType].pdwOtherPlacesList, c_wvContent[_pfsf->_nFolderType].cOtherPlacesList, &(pData->penumOtherPlaces)); return S_OK; } HRESULT CFSFolderViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks) { ZeroMemory(pTasks, sizeof(*pTasks)); if (c_wvContent[_pfsf->_nFolderType].pwvSpecialHeader && c_wvContent[_pfsf->_nFolderType].pwvSpecialTaskList) { Create_IEnumUICommand((IUnknown*)(void*)this, c_wvContent[_pfsf->_nFolderType].pwvSpecialTaskList, c_wvContent[_pfsf->_nFolderType].cSpecialTaskList, &pTasks->penumSpecialTasks); } if (c_wvContent[_pfsf->_nFolderType].pwvFolderHeader && c_wvContent[_pfsf->_nFolderType].pwvFolderTaskList) { Create_IEnumUICommand((IUnknown*)(void*)this, c_wvContent[_pfsf->_nFolderType].pwvFolderTaskList, c_wvContent[_pfsf->_nFolderType].cFolderTaskList, &pTasks->penumFolderTasks); } return S_OK; } HRESULT CFSFolderViewCB::OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme) { ZeroMemory(pTheme, sizeof(*pTheme)); pTheme->pszThemeID = c_wvContent[_pfsf->_nFolderType].pszThemeInfo; return S_OK; } HRESULT CFSFolderViewCB::OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pfvm) { HRESULT hr = E_FAIL; IPropertyBag* pPB; if (SUCCEEDED(SHGetViewStatePropertyBag(_pfsf->_GetIDList(), VS_BAGSTR_EXPLORER, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &pPB)))) { SHELLVIEWID vidDefault; if (SUCCEEDED(SHPropertyBag_ReadGUID(pPB, L"ExtShellFolderViews\\Default", &vidDefault))) { hr = ViewModeFromSVID(&vidDefault, pfvm); } pPB->Release(); } if (FAILED(hr)) { if (IsOS(OS_SERVERADMINUI)) *pfvm = FVM_DETAILS; // Server Admin always gets DETAILS else if (_cItems < DEFVIEW_FVM_FEW_CUTOFF) *pfvm = c_rgFolderType[_pfsf->_nFolderType].fvmFew; else if (_cItems < DEFVIEW_FVM_MANY_CUTOFF) *pfvm = c_rgFolderType[_pfsf->_nFolderType].fvmMid; else *pfvm = c_rgFolderType[_pfsf->_nFolderType].fvmMany; hr = S_OK; } return hr; } HRESULT CFSFolderViewCB::OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings) { HRESULT hr = OnDefViewMode(pv, &pSettings->fvm); if (SUCCEEDED(hr)) { pSettings->fGroupView = (_cItems >= 100) && !IsEqualSCID(SCID_NAME, *c_rgFolderType[_pfsf->_nFolderType].pscidSort); pSettings->iSortDirection = c_rgFolderType[_pfsf->_nFolderType].iSortDirection; if (pSettings->fvm == FVM_THUMBNAIL || pSettings->fvm == FVM_THUMBSTRIP || pSettings->fvm == FVM_TILE) pSettings->fFlags = FWF_AUTOARRANGE; if (FAILED(_pfsf->_MapSCIDToColumn(c_rgFolderType[_pfsf->_nFolderType].pscidSort, &pSettings->uSortCol))) pSettings->uSortCol = 0; } return hr; } HRESULT CFSFolderViewCB::OnGetCustomViewInfo(DWORD pv, SFVM_CUSTOMVIEWINFO_DATA* pData) { HRESULT hr = E_FAIL; TCHAR szIniFile[MAX_PATH]; if (_pfsf->_CheckDefaultIni(NULL, szIniFile, ARRAYSIZE(szIniFile))) { if (PathFileExistsAndAttributes(szIniFile, NULL)) { // Read the custom colors // const LPCTSTR c_szCustomColors[CRID_COLORCOUNT] = { TEXT("IconArea_TextBackground"), TEXT("IconArea_Text") }; for (int i = 0; i < CRID_COLORCOUNT; i++) { pData->crCustomColors[i] = GetPrivateProfileInt(TEXT("{BE098140-A513-11D0-A3A4-00C04FD706EC}"), c_szCustomColors[i], CLR_MYINVALID, szIniFile); } // Read the background image TCHAR szTemp1[MAX_PATH]; if (0 < GetPrivateProfileString(TEXT("{BE098140-A513-11D0-A3A4-00C04FD706EC}") /* VID_FolderState */, TEXT("IconArea_Image"), TEXT(""), szTemp1, ARRAYSIZE(szTemp1), szIniFile)) { TCHAR szTemp2[MAX_PATH]; SHExpandEnvironmentStrings(szTemp1, szTemp2, ARRAYSIZE(szTemp2)); // expand the env vars if any if (SUCCEEDED(_pfsf->_GetPath(szTemp1, ARRAYSIZE(szTemp1)))) { if (PathCombine(szTemp2, szTemp1, szTemp2)) { if (FAILED(StringCchCopy(pData->szIconAreaImage, ARRAYSIZE(pData->szIconAreaImage), szTemp2))) { pData->szIconAreaImage[0] = NULL; } } } } // Success if we have any real data hr = (*(pData->szIconAreaImage) || pData->crCustomColors[0]!=CLR_MYINVALID || pData->crCustomColors[1]!=CLR_MYINVALID) ? S_OK : E_FAIL; } } return hr; } const CLSID *c_rgFilePages[] = { &CLSID_FileTypes, &CLSID_OfflineFilesOptions }; // add optional pages to Explore/Options. HRESULT SFVCB_OnAddPropertyPages(DWORD pv, SFVM_PROPPAGE_DATA *ppagedata) { for (int i = 0; i < ARRAYSIZE(c_rgFilePages); i++) { IShellPropSheetExt * pspse; HRESULT hr = SHCoCreateInstance(NULL, c_rgFilePages[i], NULL, IID_PPV_ARG(IShellPropSheetExt, &pspse)); if (SUCCEEDED(hr)) { pspse->AddPages(ppagedata->pfn, ppagedata->lParam); pspse->Release(); } } return S_OK; } HRESULT CFSFolderViewCB::OnGetNotify(DWORD pv, LPITEMIDLIST*wP, LONG*lP) { if (IsExplorerModeBrowser(_punkSite)) _lEvents |= SHCNE_FREESPACE; // need free space info here too return E_FAIL; // return failure to let base guy do the rest } STDMETHODIMP CFSFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(0, SFVM_GETCCHMAX, OnGetCCHMax); HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated); HANDLE_MSG(1 , SFVM_INSERTITEM, OnInsertDeleteItem); HANDLE_MSG(-1, SFVM_DELETEITEM, OnInsertDeleteItem); HANDLE_MSG(0, SFVM_SELCHANGE, OnSelChange); HANDLE_MSG(0, SFVM_UPDATESTATUSBAR, OnUpdateStatusBar); HANDLE_MSG(0, SFVM_REFRESH, OnRefresh); HANDLE_MSG(0, SFVM_SELECTALL, OnSelectAll); HANDLE_MSG(0, SFVM_GETWORKINGDIR, OnGetWorkingDir); HANDLE_MSG(0, SFVM_ENUMERATEDITEMS, OnEnumeratedItems); HANDLE_MSG(0, SFVM_GETVIEWDATA, OnGetViewData); HANDLE_MSG(0, SFVM_GETWEBVIEW_TEMPLATE, OnGetWebViewTemplate); HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout); HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent); HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks); HANDLE_MSG(0, SFVM_GETWEBVIEWTHEME, OnGetWebViewTheme); HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode); HANDLE_MSG(0, SFVM_GETCUSTOMVIEWINFO, OnGetCustomViewInfo); HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages); HANDLE_MSG(0, SFVM_SIZE, OnSize); HANDLE_MSG(0, SFVM_GETPANE, OnGetPane); HANDLE_MSG(0, SFVM_GETNOTIFY, OnGetNotify); HANDLE_MSG(0, SFVM_GETDEFERREDVIEWSETTINGS, OnGetDeferredViewSettings); default: return E_FAIL; } return S_OK; } STDAPI CFSFolderCallback_Create(CFSFolder *pfsf, IShellFolderViewCB **ppsfvcb) { *ppsfvcb = new CFSFolderViewCB(pfsf); return *ppsfvcb ? S_OK : E_OUTOFMEMORY; }