#include "shellprv.h" #include "common.h" #include "resource.h" #include "dpastuff.h" #include "bands.h" #include "isfband.h" #include "legacy.h" #include "uemapp.h" #define SUPERCLASS CISFBand #define DM_PERSIST DM_TRACE // trace IPS::Load, ::Save, etc. class CQuickLinks : public CISFBand { public: // *** IUnknown *** virtual STDMETHODIMP_(ULONG) AddRef(void) { return CISFBand::AddRef(); }; virtual STDMETHODIMP_(ULONG) Release(void){ return CISFBand::Release(); }; virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); // *** IPersistStream methods *** virtual STDMETHODIMP GetClassID(CLSID *pClassID); virtual STDMETHODIMP Load(IStream *pStm); virtual STDMETHODIMP Save(IStream *pstm, BOOL fClearDirty); // *** IDockingWindow methods (override) *** virtual STDMETHODIMP ShowDW(BOOL fShow); virtual STDMETHODIMP CloseDW(DWORD dw) { return CISFBand::CloseDW(dw); }; // *** IObjectWithSite methods *** virtual STDMETHODIMP SetSite(IUnknown* punkSite) { return CISFBand::SetSite(punkSite); }; // *** IOleCommandTarget *** virtual STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut); // *** IDeskBand methods *** virtual STDMETHODIMP GetBandInfo(DWORD dwBandID, DWORD fViewMode, DESKBANDINFO* pdbi); // *** IDelegateDropTarget *** virtual HRESULT OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect); protected: CQuickLinks(); virtual ~CQuickLinks(); HRESULT _GetTitleW(LPWSTR pwzTitle, DWORD cchSize); HRESULT _InternalInit(void); virtual HRESULT _LoadOrderStream(); virtual HRESULT _SaveOrderStream(); virtual BOOL _AllowDropOnTitle() { return TRUE; }; virtual HRESULT _GetIEnumIDList(DWORD dwEnumFlags, IEnumIDList **ppenum); private: BITBOOL _fIsInited :1; BITBOOL _fSingleLine :1; friend HRESULT CQuickLinks_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv); // for ctor }; #define MAX_QL_SITES 5 // Number of Sites on the quick link bar #define SZ_REGKEY_SPECIALFOLDERS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders") HRESULT SHGetSpecialFolderPathEx(LPTSTR pszPath, DWORD cchSize, DWORD dwCSIDL, LPCTSTR pszFolderName) { HRESULT hr = S_OK; if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL, 0, pszPath))) return hr; cchSize *= sizeof(TCHAR); // Count of chars to count of bytes. if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, SZ_REGKEY_SPECIALFOLDERS, pszFolderName, NULL, pszPath, &cchSize)) hr = E_FAIL; TraceMsg(TF_BAND|TF_GENERAL, "CQuickLinks SHGetSpecialFolderPath(CSIDL_FAVORITES), Failed so getting Fav dir from registry. Path=%s; hr=%#8lx", pszPath, hr); return hr; } #define LINKS_FOLDERNAME_KEY TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar") #define LINKS_FOLDERNAME_VALUE TEXT("LinksFolderName") // _GetTitleW and QuickLinks_GetFolder call this. // if we ever go back to ANSI days we'll get a build break // right now we are saving some space by not having A version that's not used void QuickLinks_GetName(LPTSTR pszName, DWORD cchSize, BOOL bSetup) { DWORD cb = cchSize * SIZEOF(TCHAR); // try to get the name of the folder from the registry (in case of upgrade to a different // language we cannot use the resource) if (SHGetValue(HKEY_CURRENT_USER, LINKS_FOLDERNAME_KEY, LINKS_FOLDERNAME_VALUE, NULL, (void *)pszName, &cb) != ERROR_SUCCESS) { // no luck, try the HKLM if we are doing per user registration, maybe setup stored the old links folder name there cb = cchSize * SIZEOF(TCHAR); if (!bSetup || SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion"), TEXT("LinkFolderName"), NULL, (void *)pszName, &cb) != ERROR_SUCCESS) { // if everything else fails load it from the resource LoadString(HINST_THISDLL, IDS_QLINKS, pszName, cchSize); } } } HRESULT QuickLinks_GetFolder(LPTSTR pszPath, DWORD cchSize, BOOL bSetup = FALSE) { TCHAR szQuickLinks[MAX_PATH]; if (SUCCEEDED(SHGetSpecialFolderPathEx(pszPath, cchSize, CSIDL_FAVORITES, TEXT("Favorites")))) { QuickLinks_GetName(szQuickLinks, ARRAYSIZE(szQuickLinks), bSetup); PathCombine(pszPath, pszPath, szQuickLinks); return S_OK; } return E_FAIL; } // Length of the text under each quick links #define MAX_QL_TEXT_LENGTH 256 #define MAX_QL_WIDTH 92 #define QL_BUFFER (MAX_QL_TEXT_LENGTH + MAX_URL_STRING + MAX_TOOLTIP_STRING + 3) // // Load strings needed for quick links // // returns TRUE/FALSE if it was user specified (false used to load the default // urls, but now we leave that to branding dll BOOL QLLoadLinkName(HUSKEY hUSKey, int i, LPTSTR pszTitle, UINT cchTitle, LPTSTR pszURL, UINT cchURL) { CHAR szScratch[QL_BUFFER]; DWORD dwcbData = SIZEOF(szScratch); CHAR * pszTemp; TCHAR szRegValues[5]; // In IE3, links did not have its own folder. Instead, links were stored in the registry as a // set of binary streams of ANSI strings. wnsprintf(szRegValues, ARRAYSIZE(szRegValues), TEXT("%d"), i+1); if (hUSKey && (ERROR_SUCCESS == SHRegQueryUSValue(hUSKey, szRegValues, NULL, (LPBYTE)szScratch, &dwcbData, FALSE, NULL, 0))) { int nNULLs = 0; pszTemp = szScratch; DWORD j; for (j = 0; j < dwcbData; j++) { #ifdef MAINWIN // Because of the limitations of the MainWin registry, we'll put '*' instead of '\0'. if (*pszTemp == TEXT('*')) *pszTemp = '\0'; #endif nNULLs += (UINT)(*pszTemp++ == TEXT('\0')); } // make sure we have 3 strings with a double NULL at the end if (nNULLs > 3) { pszTemp = szScratch; SHAnsiToTChar(pszTemp, pszTitle, cchTitle); pszTemp += lstrlenA(pszTemp) + 1; SHAnsiToTChar(pszTemp, pszURL, cchURL); return TRUE; } } return FALSE; } void ImportQuickLinks() { TCHAR szQuickLinksDir[MAX_PATH]; if (FAILED(QuickLinks_GetFolder(szQuickLinksDir, ARRAYSIZE(szQuickLinksDir), TRUE))) return; // need to write the folder name to the registry so we can use it w/ different plug ui languages LPTSTR pszQLinks; DWORD cb; PathRemoveBackslash(szQuickLinksDir); pszQLinks = PathFindFileName(szQuickLinksDir); if (pszQLinks) { cb = (lstrlen(pszQLinks)+1) * sizeof(TCHAR); SHSetValue(HKEY_CURRENT_USER, LINKS_FOLDERNAME_KEY, LINKS_FOLDERNAME_VALUE, REG_SZ, (void *)pszQLinks, cb); } if (!PathFileExists(szQuickLinksDir) && CreateDirectory(szQuickLinksDir, NULL)) { HUSKEY hUSKey = NULL; SHRegOpenUSKey(TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar\\Links"), KEY_READ, NULL, &hUSKey, FALSE); for (int i = 0; i < MAX_QL_SITES; i++) { // this was user specified... convert it LPITEMIDLIST pidl; TCHAR szURLTemp[MAX_URL_STRING]; TCHAR szTitle[MAX_QL_TEXT_LENGTH]; if (QLLoadLinkName(hUSKey, i, szTitle, ARRAYSIZE(szTitle), szURLTemp, ARRAYSIZE(szURLTemp))) { WCHAR szURL[MAX_URL_STRING]; if (SUCCEEDED(URLSubstitution(szURLTemp, szURL, ARRAYSIZE(szURL), URLSUB_ALL)) && SUCCEEDED(IECreateFromPath(szURL, &pidl))) { CreateShortcutInDir(pidl, szTitle, szQuickLinksDir, NULL, FALSE); ILFree(pidl); } } } // we found the key, there's something to migrate if (hUSKey) SHRegCloseUSKey(hUSKey); // all converted, delete the key SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar\\Links")); } else { // ie4 -> ieX upgrade // create a value in hkcu\sw\ms\ie\toolbar to indicate that we should preserve the order from the links stream // not the one from the favorites\links that we are using for links starting w/ ie5 BOOL bVal = TRUE; // we don't care if this fails. if it does we'll just just favorites\links order stream SHSetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar"), TEXT("SaveLinksOrder"), REG_BINARY, (DWORD *)&bVal, SIZEOF(bVal)); } } HRESULT CQuickLinks::_InternalInit(void) { if (!_fIsInited && !_psf) { LPITEMIDLIST pidlQLinks; TCHAR szPath[MAX_PATH]; QuickLinks_GetFolder(szPath, ARRAYSIZE(szPath)); if (!PathFileExists(szPath)) CreateDirectory(szPath, NULL); if (SUCCEEDED(IECreateFromPath(szPath, &pidlQLinks))) { InitializeSFB(NULL, pidlQLinks); ILFree(pidlQLinks); } } _fIsInited = TRUE; return S_OK; } CQuickLinks::CQuickLinks() : SUPERCLASS() { #ifdef DEBUG if (IsFlagSet(g_dwBreakFlags, BF_ONAPIENTER)) { TraceMsg(TF_ALWAYS, "Stopping in CQuickLinks ctor"); DEBUG_BREAK; } #endif ASSERT(!_fIsInited); _fCascadeFolder = TRUE; _fVariableWidth = TRUE; _pguidUEMGroup = &UEMIID_BROWSER; } CQuickLinks::~CQuickLinks() { } HRESULT CQuickLinks_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { // aggregation checking is handled in class factory HRESULT hr = E_OUTOFMEMORY; CQuickLinks *pObj = new CQuickLinks(); if (pObj) { hr = pObj->QueryInterface(riid, ppv); pObj->Release(); } return hr; } HRESULT CQuickLinks::_LoadOrderStream() { HRESULT hr = E_FAIL; if (_pidl && _psf) { IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, _pidl, REG_SUBKEY_FAVORITESA, STGM_READ); if (pstm) { OrderList_Destroy(&_hdpaOrder); hr = OrderList_LoadFromStream(pstm, &_hdpaOrder, _psf); pstm->Release(); } } return hr; } HRESULT CQuickLinks::_SaveOrderStream() { HRESULT hr = E_FAIL; if (_pidl && (_hdpa || _hdpaOrder)) { IStream* pstm = OpenPidlOrderStream((LPCITEMIDLIST)CSIDL_FAVORITES, _pidl, REG_SUBKEY_FAVORITESA, STGM_CREATE | STGM_WRITE); if (pstm) { hr = OrderList_SaveToStream(pstm, (_hdpa ? _hdpa : _hdpaOrder), _psf); pstm->Release(); } } if (SUCCEEDED(hr)) hr = SUPERCLASS::_SaveOrderStream(); return hr; } HRESULT CQuickLinks::_GetIEnumIDList(DWORD dwEnumFlags, IEnumIDList **ppenum) { HRESULT hres; ASSERT(_psf); // Pass in a NULL hwnd so the enumerator does not show any UI while // we're filling a band. hres = IShellFolder_EnumObjects(_psf, NULL, dwEnumFlags, ppenum); // we could have failed because our folder does not exist // that can happen if someone delted/renamed links while there is // stream in the registry that saves the pidl - we get the pidl and // bind to it (bind does not hit the disk so it succeeds even though // the file does not exist if (FAILED(hres) && hres != E_OUTOFMEMORY) { TCHAR szPath[MAX_PATH]; ASSERT(_pidl); if (SHGetPathFromIDList(_pidl, szPath) && !PathFileExists(szPath)) { LPITEMIDLIST pidlQLinks; QuickLinks_GetFolder(szPath, ARRAYSIZE(szPath)); if (!PathFileExists(szPath)) CreateDirectory(szPath, NULL); if (SUCCEEDED(IECreateFromPath(szPath, &pidlQLinks))) { if (SUCCEEDED(InitializeSFB(NULL, pidlQLinks))) { hres = _psf->EnumObjects(NULL, dwEnumFlags, ppenum); } ILFree(pidlQLinks); } } } return hres; } //*** CQuickLinks::IPersistStream HRESULT CQuickLinks::Load(IStream *pstm) { HRESULT hr = S_OK; hr = SUPERCLASS::Load(pstm); // This forces a refresh _fIsInited = FALSE; ATOMICRELEASE(_psf); _InternalInit(); // if we are on our first run through (i.e. this reg key exists) // we load the order stream from our old location (used in ie4) and avoid overwriting it w/ favorites stream // so user can have their custom order preserved on upgrade (they are more likely to customize links bar // order then favorites/links so we picked that one) if (SHGetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar"), TEXT("SaveLinksOrder"), NULL, NULL, NULL) == ERROR_SUCCESS) { SHDeleteValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar"), TEXT("SaveLinksOrder")); // must persist old order stream in the new location (fav/links) _SaveOrderStream(); } else { _LoadOrderStream(); } return hr; } HRESULT CQuickLinks::Save(IStream *pstm, BOOL fClearDirty) { HRESULT hr = SUPERCLASS::Save(pstm, fClearDirty); _SaveOrderStream(); return hr; } HRESULT CQuickLinks::GetClassID(CLSID *pClassID) { *pClassID = CLSID_QuickLinks; return S_OK; } // *** IUnknown Interface *** HRESULT CQuickLinks::QueryInterface(REFIID riid, void **ppvObj) { return SUPERCLASS::QueryInterface(riid, ppvObj); } // command target STDMETHODIMP CQuickLinks::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { HRESULT hres = S_FALSE; if (pguidCmdGroup) { if (IsEqualGUID(*pguidCmdGroup, CLSID_QuickLinks)) { switch (nCmdID) { case QLCMD_SINGLELINE: _fSingleLine = (nCmdexecopt == 1); return S_OK; } } else if (IsEqualGUID(*pguidCmdGroup, CGID_ISFBand)) { switch(nCmdID) { case ISFBID_SETORDERSTREAM: hres = SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); _SaveOrderStream(); break; } } } if (hres == S_FALSE) hres = SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); return hres; } // *** IDockingWindow Interface *** HRESULT CQuickLinks::ShowDW(BOOL fShow) { if (fShow) _InternalInit(); return SUPERCLASS::ShowDW(fShow); } HRESULT CQuickLinks::_GetTitleW(LPWSTR pwzTitle, DWORD cchSize) { QuickLinks_GetName(pwzTitle, cchSize, FALSE); return S_OK; } HRESULT CQuickLinks::GetBandInfo(DWORD dwBandID, DWORD fViewMode, DESKBANDINFO* pdbi) { HRESULT hres = SUPERCLASS::GetBandInfo(dwBandID, fViewMode, pdbi); if (_hwndTB && _fSingleLine) { LRESULT lButtonSize = SendMessage(_hwndTB, TB_GETBUTTONSIZE, 0, 0L); pdbi->ptMinSize.y = HIWORD(lButtonSize); pdbi->dwModeFlags &= ~DBIMF_VARIABLEHEIGHT; } return hres; } HRESULT CQuickLinks::OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect) { HRESULT hr; BOOL fIsSafe = TRUE; // if we are not the source of the drop and the Links folder does not exist we need to create it if (_iDragSource == -1) { TCHAR szPath[MAX_PATH]; QuickLinks_GetFolder(szPath, ARRAYSIZE(szPath)); if (!PathFileExists(szPath)) CreateDirectory(szPath, NULL); LPITEMIDLIST pidl; if (SUCCEEDED(SHPidlFromDataObject(pdtobj, &pidl, NULL, 0))) { fIsSafe = IEIsLinkSafe(_hwnd, pidl, ILS_LINK); ILFree(pidl); } } if (fIsSafe) { hr = SUPERCLASS::OnDropDDT(pdt, pdtobj, pgrfKeyState, pt, pdwEffect); } else { hr = S_FALSE; } return hr; }