|
|
#include "priv.h"
#include "sccls.h"
#include "dochost.h"
#include "resource.h"
#include "stdenum.h"
#include <idhidden.h>
#include "shdocfl.h"
#include <vdate.h>
#include <mluisupp.h>
#ifdef UNIX
#include "unixstuff.h"
#endif
HRESULT CDocObjectView_Create(IShellView** ppv, IShellFolder* psf, LPCITEMIDLIST pidl);
#define DM_STARTUP 0
#define DM_CDOFPDN 0 // CDocObjectFolder::ParseDisplayName
class CDocObjectFolder : public IShellFolder2, public IPersistFolder2, public IBrowserFrameOptions { public: CDocObjectFolder(LPCITEMIDLIST pidlRoot = NULL);
// IUnknown
STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void);
// IShellFolder
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut); STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj); STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject (HWND hwnd, REFIID riid, void **ppvOut); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwnd, 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(LPGUID pGuid); STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum); STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { return E_NOTIMPL; }; STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState) { return E_NOTIMPL; }; STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { return E_NOTIMPL; }; STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails){ return E_NOTIMPL; }; STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid) { return E_NOTIMPL; };
// IPersistFolder
STDMETHODIMP GetClassID(LPCLSID pClassID); STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
// IPersistFolder2
STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl); // IBrowserFrameOptions
STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions);
protected:
~CDocObjectFolder();
LONG _cRef; LPITEMIDLIST _pidlRoot; };
//========================================================================
// CDocObjectFolder members
//========================================================================
CDocObjectFolder::CDocObjectFolder(LPCITEMIDLIST pidlRoot) : _cRef(1), _pidlRoot(NULL) { TraceMsg(TF_SHDLIFE, "ctor CDocObjectFolder %x", this);
DllAddRef();
if (pidlRoot) _pidlRoot = ILClone(pidlRoot); }
CDocObjectFolder::~CDocObjectFolder() { TraceMsg(TF_SHDLIFE, "dtor CDocObjectFolder %x", this);
if (_pidlRoot) ILFree(_pidlRoot);
DllRelease(); }
HRESULT CDocObjectFolder::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENTMULTI(CDocObjectFolder, IShellFolder, IShellFolder2), QITABENT(CDocObjectFolder, IShellFolder2), QITABENTMULTI(CDocObjectFolder, IPersistFolder, IPersistFolder2), QITABENT(CDocObjectFolder, IPersistFolder2), QITABENT(CDocObjectFolder, IBrowserFrameOptions), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CDocObjectFolder::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CDocObjectFolder::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CDocObjectFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes) { AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL")); *ppidl = NULL; return E_UNEXPECTED; } HRESULT CDocObjectFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList) { *ppenumIDList = NULL; return E_UNEXPECTED; }
HRESULT CDocObjectFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut) { AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL")); *ppvOut = NULL; return E_UNEXPECTED; }
HRESULT CDocObjectFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj) { AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL")); *ppvObj = NULL; return E_UNEXPECTED; }
HRESULT CDocObjectFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL")); return E_UNEXPECTED; }
HRESULT CDocObjectFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppvOut) { HRESULT hres = E_FAIL;
if (IsEqualIID(riid, IID_IShellView)) { hres = CDocObjectView_Create((IShellView**)ppvOut, this, _pidlRoot); } else { hres = E_NOINTERFACE; *ppvOut = NULL; } return hres; }
HRESULT CDocObjectFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, void **ppvOut) { AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL")); *ppvOut = NULL; return E_UNEXPECTED; }
HRESULT CDocObjectFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut) { // we should never have any children.
ASSERT(cidl == 0); if (cidl != 0) return E_UNEXPECTED; if (*rgfInOut) { // they want to know about the document itself
ASSERT(_pidlRoot); return SHGetAttributesOf(_pidlRoot, rgfInOut); }
return S_OK; }
HRESULT CDocObjectFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName) { AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL")); return E_UNEXPECTED; }
HRESULT CDocObjectFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST *ppidlOut) { return E_UNEXPECTED; }
HRESULT CDocObjectFolder::GetDefaultSearchGUID(GUID *pGuid) { *pGuid = SRCID_SWebSearch; return S_OK; }
HRESULT CDocObjectFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum) { *ppenum = NULL; return E_NOTIMPL; }
HRESULT CDocObjectFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_CDocObjectFolder; return S_OK; }
HRESULT CDocObjectFolder::Initialize(LPCITEMIDLIST pidl) { if (_pidlRoot) { ILFree(_pidlRoot); _pidlRoot = NULL; }
if (pidl) _pidlRoot = ILClone(pidl);
return S_OK; }
HRESULT CDocObjectFolder::GetCurFolder(LPITEMIDLIST* ppidl) { return SHILClone(_pidlRoot, ppidl); }
// IBrowserFrameOptions
#define BASE_OPTIONS \
(BFO_BROWSER_PERSIST_SETTINGS | BFO_RENAME_FOLDER_OPTIONS_TOINTERNET | \ BFO_PREFER_IEPROCESS | BFO_ENABLE_HYPERLINK_TRACKING | \ BFO_USE_IE_LOGOBANDING | BFO_ADD_IE_TOCAPTIONBAR | BFO_GO_HOME_PAGE | \ BFO_USE_IE_TOOLBAR | BFO_NO_PARENT_FOLDER_SUPPORT | BFO_NO_REOPEN_NEXT_RESTART | \ BFO_SHOW_NAVIGATION_CANCELLED)
// IBrowserFrameOptions
HRESULT CDocObjectFolder::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions) { // We are hosting a DocObj?
BOOL fIsFileURL = FALSE;
// Is this under the Internet Name Space? Yes for
// HTTP and FTP owned by the IE name space. MSIEFTP
// pidls are passed straight to that folder.
// This function will return FALSE for non-IE stuff
// but we will then want to check if it's a file system
// thing that wants to act like a web page because it's
// MIME TYPE or other association is associated with the web.
if (!IsURLChild(_pidlRoot, TRUE)) { // Since IsURLChild() returned FALSE, this must be in the file system.
// This case will happen with:
// C:\foo.htm
// http://www.yahoo.com/
// http://bryanst/resume.doc
// http://bryanst/notes.txt
// <Start Page> [I couldn't find a case that hit CInternetFolder]
// C:\foo.doc (use the addressbar to repro)
fIsFileURL = TRUE; }
*pdwOptions = dwMask & BASE_OPTIONS; if (!fIsFileURL) { // Add the Offline Support when we aren't in the file system.
*pdwOptions |= dwMask & (BFO_USE_IE_OFFLINE_SUPPORT | BFO_USE_DIALUP_REF); } return S_OK; }
STDAPI CDocObjectFolder_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi) { CDocObjectFolder *psf = new CDocObjectFolder; if (psf) { *ppunk = SAFECAST(psf, IShellFolder *); return S_OK; } return E_OUTOFMEMORY; }
class CInternetFolder : CDocObjectFolder { public: CInternetFolder(LPCITEMIDLIST pidlRoot = NULL) ;
// IUnknown
STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void);
// IShellFolder
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes); STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList); STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut); STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj); STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwnd, 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);
// IPersistFolder
STDMETHODIMP GetClassID(CLSID *pClassID);
// IBrowserFrameOptions
STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions);
protected: ~CInternetFolder();
HRESULT _CreateProtocolHandler(LPCSTR pszProtocol, IBindCtx * pbc, IShellFolder **ppsf); HRESULT _CreateProtocolHandlerFromPidl(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsf); HRESULT _GetAttributesOfProtocol(LPCSTR pszProtocol, LPCITEMIDLIST *apidl, UINT cpidl, ULONG *rgfInOut); HRESULT _FaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IUnknown * punkSite); HRESULT _ConditionallyFaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IBindCtx * pbc); HRESULT _AssocCreate(LPCITEMIDLIST pidl, REFIID riid, void * *ppv); HRESULT _GetScheme(LPCITEMIDLIST pidl, LPWSTR pszOut, DWORD cchOut); HRESULT _GetUIObjectFromShortcut(LPCITEMIDLIST pidl, REFIID riid, void **ppvOut); HRESULT _GetTitle(LPCWSTR pszUrl, STRRET *pstr); HRESULT _InitHistoryStg(IUrlHistoryStg **pphist);
IUrlHistoryStg *_phist; };
CInternetFolder::CInternetFolder(LPCITEMIDLIST pidlRoot) : CDocObjectFolder(pidlRoot) { TraceMsg(TF_URLNAMESPACE, "[%X] ctor CInternetFolder", this); ASSERT(NULL == _phist); }
CInternetFolder::~CInternetFolder() { ATOMICRELEASE(_phist); TraceMsg(TF_URLNAMESPACE, "[%X] dtor CInternetFolder", this); }
HRESULT CInternetFolder::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENTMULTI(CInternetFolder, IShellFolder, IShellFolder2), QITABENT(CInternetFolder, IShellFolder2), QITABENTMULTI(CDocObjectFolder, IPersistFolder, IPersistFolder2), QITABENT(CDocObjectFolder, IPersistFolder2), QITABENT(CInternetFolder, IBrowserFrameOptions), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CInternetFolder::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CInternetFolder::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
typedef struct tagURLID { ITEMIDLIST idl; // cb and SHID
BYTE bType; // URLID
UINT uiCP; // Code Page
WCHAR achUrl[1]; // variable size string
} URLID;
#define SHID_INTERNET 0x60
#define SHID_INTERNET_SITE 0x61 // IE name space item
#define URLID_URLBASEA 0x00
/////// URLID_LOCATION 0x01 // LEGACY IE3/4 used for Frag IDs
/////// URLID_FTPFOLDER 0x02 // LEGACY used by a pre-release FTP Folder dll
#define URLID_PROTOCOL 0x03 // this is actually a delegated protocol
#define URLID_URLBASEW 0x80 //
// URLIDF_UNICODE 0x80 // URLID_ is actually of UNICODE type
#ifdef UNICODE
#define URLID_URLBASE URLID_URLBASEW
#else
#define URLID_URLBASE URLID_URLBASEA
#endif
typedef const UNALIGNED URLID *PCURLID; typedef UNALIGNED URLID *PURLID;
#define PDID_SIG MAKEWORD(SHID_INTERNET_SITE, URLID_PROTOCOL)
inline PCDELEGATEITEMID _IsValidDelegateID(LPCITEMIDLIST pidl) { PCDELEGATEITEMID pdi = (PCDELEGATEITEMID)pidl; ASSERT(pdi);
if ((pdi->cbSize >= (SIZEOF(PDELEGATEITEMID)-1)) && (pdi->wOuter == PDID_SIG)) return pdi;
return NULL; } LPCSTR _PidlToDelegateProtocol(LPCITEMIDLIST pidl) { PCDELEGATEITEMID pdi = _IsValidDelegateID(pidl); if (pdi) return (LPCSTR)&(pdi->rgb[pdi->cbInner]);
return NULL; }
inline PCURLID _IsValidUrlID(LPCITEMIDLIST pidl) { PCURLID purlid = (PCURLID)pidl; ASSERT(purlid);
// 98/12/22 #263932 vtan: ANSI and Unicode URLs are both valid. Use function
// _ExtractURL to extract the URL from the PIDL as a Unicode string.
if (purlid->idl.mkid.cb >= SIZEOF(URLID) && (purlid->idl.mkid.abID[0] == SHID_INTERNET_SITE) && (purlid->bType == URLID_URLBASEA || purlid->bType == URLID_URLBASEW || _IsValidDelegateID(pidl))) return purlid;
return NULL; }
// 98/12/22 #263932 vtan: IE4 stores the PIDL in a stream as an ANSI
// string. IE5 stores the PIDL in a stream as a Unicode string. This
// functions reads the string (ANSI or Unicode) and converts it to
// an internal Unicode string which is what will be written to the stream.
void _ExtractURL (PCURLID pURLID, LPWSTR wszURL, int iCharCount) { if (pURLID->bType == URLID_URLBASEA) { char aszURL[MAX_URL_STRING];
#ifdef UNIX
ualstrcpynA(aszURL, (const char*)(pURLID->achUrl), sizeof(aszURL)); #else
ualstrcpynA(aszURL, reinterpret_cast<const char*>(pURLID->achUrl), sizeof(aszURL)); #endif
SHAnsiToUnicode(aszURL, wszURL, iCharCount); } else if (pURLID->bType == URLID_URLBASEW) { ualstrcpynW(wszURL, pURLID->achUrl, iCharCount); } }
// 99/01/04 vtan: Added the following to help compare URLIDs which
// can be AA/UU/AU/UA and perform the correct comparison.
int _CompareURL (PCURLID pURLID1, PCURLID pURLID2) { int iResult; if ((pURLID1->bType == URLID_URLBASEA) && (pURLID2->bType == URLID_URLBASEA)) { #ifdef UNIX
iResult = ualstrcmpA((const char*)(pURLID1->achUrl), (const char*)(pURLID2->achUrl)); #else
iResult = ualstrcmpA(reinterpret_cast<const char*>(pURLID1->achUrl), reinterpret_cast<const char*>(pURLID2->achUrl)); #endif
} else if ((pURLID1->bType == URLID_URLBASEW) && (pURLID2->bType == URLID_URLBASEW)) { iResult = ualstrcmpW(pURLID1->achUrl, pURLID2->achUrl); } else { PCURLID pCompareURLID; WCHAR wszURL[MAX_URL_STRING]; // AU/UA comparison. To be efficient only convert the ANSI URLID
// to Unicode and perform the comparison in Unicode.
if (pURLID1->bType == URLID_URLBASEA) { pCompareURLID = pURLID2; _ExtractURL(pURLID1, wszURL, SIZECHARS(wszURL)); } else { pCompareURLID = pURLID1; _ExtractURL(pURLID2, wszURL, SIZECHARS(wszURL)); } iResult = ualstrcmpW(pCompareURLID->achUrl, wszURL); } return iResult; }
IShellFolder* g_psfInternet = NULL;
STDAPI CDelegateMalloc_Create(void *pv, UINT cbSize, WORD wOuter, IMalloc **ppmalloc); //
// this might modify pszName if it's not a fully qualified url!
BOOL _ValidateURL(LPTSTR pszName, DWORD dwFlags) { //
// WARNING: In order to allow URL extensions, we assume all strings
// which contains ":" in it is a valid string.
// Assumptions are:
//
// (1) CDesktop::ParseDisplayName parse file system strings first.
// (2) URL moniker will return an error correctly if URL is not valid.
// (3) someone else (the desktop folder) handles shell: URLs
// they should not be used directly by the browser
//
return SUCCEEDED(IURLQualify(pszName, dwFlags, pszName, NULL, NULL)) && (-1 != GetUrlScheme(pszName)); HRESULT hr = IURLQualify(pszName, dwFlags, pszName, NULL, NULL); DWORD nScheme = GetUrlScheme(pszName); return SUCCEEDED(hr) && (-1 != nScheme) && (URL_SCHEME_SHELL != nScheme); }
LPITEMIDLIST IEILAppendFragment(LPITEMIDLIST pidl, LPCWSTR pszFragment) { // WARNING: See IE5 bug #'s 86951 and 36497 for more details.
// In a nutshell, we're rolling back the change for 36497 because
// the change caused many more problems with customers than
// the behavior we had in IE4.
//
// Because we're not ensuring that
// the fragment is prefixed with a '#', there may be
// cases where the URL in the address bar looks wrong,
// as well as cases where a hyperlink to a different doc
// or HTML page may fail if it contains a fragment.
return ILAppendHiddenStringW(pidl, IDLHID_URLFRAGMENT, pszFragment); }
// browser only uglyness... we need to construct a desktop relative "regitem" pidl for
// the internet since browser only shell does not support psf->ParseDisplayName("::{guid}", &pidl)
// this uses the same layout as REGITEMs so we have PIDL compatibility with integrated mode
// this ensures that a shortcut to the IE icon made in browser only mode works in integrated
#ifndef NOPRAGMAS
#pragma pack(1)
#endif
typedef struct { WORD cb; BYTE bFlags; BYTE bReserved; // This is to get DWORD alignment
CLSID clsid; } IDITEM; // IDREGITEM
typedef struct { IDITEM idri; USHORT cbNext; } IDLITEM; // IDLREGITEM
#ifndef NOPRAGMAS
#pragma pack()
#endif
// stolen from shell32\shitemid.h
#define SHID_ROOT_REGITEM 0x1f // MyDocuments, Internet, etc
const IDLITEM c_idlInetRoot = { {SIZEOF(IDITEM), SHID_ROOT_REGITEM, 0, { 0x871C5380, 0x42A0, 0x1069, 0xA2,0xEA,0x08,0x00,0x2B,0x30,0x30,0x9D },/* CLSID_Internet */ }, 0, };
LPCITEMIDLIST c_pidlURLRoot = (LPCITEMIDLIST)&c_idlInetRoot;
// it must be an absolute pidl with a root regitem id at the front
// if we're a rooted explorer, this is always false
// this means we're definitely in nashville, so we shouldn't have a split
// world
PCURLID _FindUrlChild(LPCITEMIDLIST pidl, BOOL fIncludeHome = FALSE) { if ((pidl == NULL) || (pidl->mkid.cb != sizeof(IDITEM)) || (pidl->mkid.abID[0] != SHID_ROOT_REGITEM)) { return NULL; }
//
// the clsid in the pidl must be our internet folder's
//
if (!IsEqualGUID(((IDITEM*)pidl)->clsid, CLSID_Internet)) { ASSERT(!IsEqualGUID(((IDITEM*)pidl)->clsid, CLSID_CURLFolder)); return NULL; }
// go to the child...
pidl = _ILNext(pidl); //
// if it is a pidl to the internet root then it is the IE3 Home Page
//
if (fIncludeHome && ILIsEmpty(pidl)) return (PCURLID)pidl;
//
// otherwise it is our child if it is a site object
//
return _IsValidUrlID(pidl); }
STDAPI_(BOOL) IsURLChild(LPCITEMIDLIST pidl, BOOL fIncludeHome) { return (NULL != _FindUrlChild(pidl, fIncludeHome)); }
BOOL IEILGetFragment(LPCITEMIDLIST pidl, LPWSTR pszFragment, DWORD cchFragment) { return ILGetHiddenStringW(pidl, IDLHID_URLFRAGMENT, pszFragment, cchFragment); }
UINT IEILGetCP(LPCITEMIDLIST pidl) { PCURLID purlid = _FindUrlChild((pidl)); if (purlid) { if (!_IsValidDelegateID((LPCITEMIDLIST)purlid)) return purlid->uiCP; } return CP_ACP; }
LPITEMIDLIST _UrlIdCreate(UINT uiCP, LPCTSTR pszUrl) { //
// the URLID has a variable sized string
// member. but we put the arbitrary limit
// of MAX_URL_STRING because that is what
// we use everywhere else. we could just remove the
// limit however.
//
USHORT cb = (USHORT)SIZEOF(URLID) - (USHORT)CbFromCch(1); USHORT cchUrl = lstrlen(pszUrl) + 1; cchUrl = (USHORT)min(cchUrl, MAX_URL_STRING); cb += CbFromCch(cchUrl);
PURLID purlid = (PURLID)IEILCreate(cb + SIZEOF(USHORT));
if (purlid) { // everything is actually aligned right now...
purlid->idl.mkid.cb = cb; purlid->idl.mkid.abID[0] = SHID_INTERNET_SITE; purlid->bType = URLID_URLBASE; purlid->uiCP = uiCP; ualstrcpyn(purlid->achUrl, pszUrl, cchUrl); }
return (LPITEMIDLIST) purlid; } LPITEMIDLIST UrlToPidl(UINT uiCP, LPCTSTR pszUrl, LPCWSTR pszFragment) { LPITEMIDLIST pidlRet; LPCTSTR pszAttachedFrag = UrlGetLocation(pszUrl); TCHAR szURLBuf[MAX_URL_STRING];
// deal with URL's that still include the location (as in ParseDisplayName)
if (pszAttachedFrag) { StrCpyN(szURLBuf, pszUrl, (int)(pszAttachedFrag-pszUrl+1)); pszUrl = szURLBuf;
// prefer the passed in fragment to the attached one
if (!pszFragment) pszFragment = pszAttachedFrag; }
ASSERT(pszUrl); pidlRet = _UrlIdCreate(uiCP, pszUrl);
if (pidlRet && pszFragment && *pszFragment) pidlRet = IEILAppendFragment(pidlRet, pszFragment);
return pidlRet; }
typedef struct { LPCSTR pszProtocol; const CLSID * pCLSID; } FAULTIN_URLHANDERS;
// TODO: If there are other URL Handlers, add them here.
const FAULTIN_URLHANDERS c_FaultInUrlHandlers[] = { {"ftp", &CLSID_FTPShellExtension} };
HRESULT CInternetFolder::_FaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IUnknown * punkSite) { HRESULT hr = S_OK; if (pszProtocol) { for (int nIndex = 0; nIndex < ARRAYSIZE(c_FaultInUrlHandlers); nIndex++) { if (!StrCmpIA(pszProtocol, c_FaultInUrlHandlers[nIndex].pszProtocol)) { // Only fault in the feature if we are navigating to an FTP directory.
if ((0 == nIndex) && !UrlIs(pszUrl, URLIS_DIRECTORY)) { // It's not an ftp directory, so skip it.
continue; }
// FTP has a URL Shell Extension handler that is optionally
// installed. Fault it in now if it's needed.
uCLSSPEC ucs; QUERYCONTEXT qc = { 0 }; HWND hwnd = NULL;
ucs.tyspec = TYSPEC_CLSID; ucs.tagged_union.clsid = *c_FaultInUrlHandlers[nIndex].pCLSID;
IUnknown_GetWindow(punkSite, &hwnd); if (EVAL(hwnd)) { // Make it modal while the dialog is being displayed.
IUnknown_EnableModless(punkSite, FALSE); FaultInIEFeature(hwnd, &ucs, &qc, 0); IUnknown_EnableModless(punkSite, TRUE); } break; // pidl can only have 1 procotol, so we don't need to check the other protocol.
} } }
return hr; // We don't care if it didn't make it.
}
HRESULT CInternetFolder::_ConditionallyFaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IBindCtx * pbc) { HRESULT hr = S_OK;
// Faulting in the feature will probably require UI, so we need to assure that the caller
// will allow this.
if (pbc) { IUnknown * punkSite = NULL;
pbc->GetObjectParam(STR_DISPLAY_UI_DURING_BINDING, &punkSite); if (punkSite) { hr = _FaultInUrlHandler(pszProtocol, pszUrl, punkSite); punkSite->Release(); } }
ASSERT(SUCCEEDED(hr)); return S_OK; // We don't care if it didn't make it.
}
// returns:
// success S_OK
// failure FAILED(hres)
HRESULT CInternetFolder::_CreateProtocolHandler(LPCSTR pszProtocol, IBindCtx * pbc, IShellFolder **ppsf) { HRESULT hres; CHAR szCLSID[GUIDSTR_MAX]; DWORD cbSize = SIZEOF(szCLSID);
*ppsf = NULL;
if (pszProtocol && SHGetValueA(HKEY_CLASSES_ROOT, pszProtocol, "ShellFolder", NULL, &szCLSID, &cbSize) == ERROR_SUCCESS) { CLSID clsid; IShellFolder *psf;
GUIDFromStringA(szCLSID, &clsid); if (!SHSkipJunction(pbc, &clsid)) { hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hres)) { // IPersistFolder is optional
IPersistFolder *ppf; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf)))) { ppf->Initialize(_pidlRoot); ppf->Release(); }
IDelegateFolder *pdf; hres = psf->QueryInterface(IID_PPV_ARG(IDelegateFolder, &pdf)); if (SUCCEEDED(hres)) { // REVIEW: we could cache the malloc on a per protocol basis
// to avoid creating these over and over
IMalloc *pmalloc; hres = CDelegateMalloc_Create((void*)pszProtocol, (lstrlenA(pszProtocol) + 1), PDID_SIG, &pmalloc); if (SUCCEEDED(hres)) { hres = pdf->SetItemAlloc(pmalloc); pmalloc->Release(); } pdf->Release(); }
if (SUCCEEDED(hres)) { hres = S_OK; // force all success codes to S_OK
*ppsf = psf; } else psf->Release(); } } else hres = HRESULT_FROM_WIN32(ERROR_CANCELLED); } else hres = E_FAIL;
return hres; }
// returns:
// S_FALSE if it is not a delegate protocol PIDL
// hres of the bind opteration to the delegate protocol handler
HRESULT CInternetFolder::_CreateProtocolHandlerFromPidl(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsf) { LPCSTR pszProtocol = _PidlToDelegateProtocol(pidl); if (pszProtocol) { HRESULT hres = _CreateProtocolHandler(pszProtocol, pbc, ppsf); ASSERT(hres != S_FALSE); // enforce the return value comment
return hres; }
*ppsf = NULL; return S_FALSE; // not a protocal PIDL
}
BOOL _GetUrlProtocol(LPCTSTR pszUrl, LPSTR pszProtocol, DWORD cchProtocol) { TCHAR sz[MAX_PATH]; DWORD cch = SIZECHARS(sz); if (SUCCEEDED(UrlGetPart(pszUrl, sz, &cch, URL_PART_SCHEME, 0))) { SHTCharToAnsi(sz, pszProtocol, cchProtocol); return TRUE; }
return FALSE; }
UINT CodePageFromBindCtx(LPBC pbc) { UINT uiCP = CP_ACP; IDwnCodePage *pDwnCP; if (pbc && SUCCEEDED(pbc->QueryInterface(IID_PPV_ARG(IDwnCodePage, &pDwnCP)))) { uiCP = pDwnCP->GetCodePage(); pDwnCP->Release(); } return uiCP; }
#define STR_PARSE_INTERNET_DONT_ESCAPE_SPACES L"Parse Internet Dont Escape Spaces"
HRESULT CInternetFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes) { HRESULT hres = E_FAIL;
TCHAR szName[MAX_URL_STRING]; UINT uiCP = CodePageFromBindCtx(pbc);
StrCpyN(szName, pwszDisplayName, ARRAYSIZE(szName)); if (!PathIsFilePath(szName)) { if (_ValidateURL(szName, 0) || ShouldShellExecURL(szName)) { CHAR szProtocol[MAX_PATH]; DWORD cchName = ARRAYSIZE(szName); IShellFolder *psfHandler; BOOL fProtocolExists;
// if we're down here, then the szName was really a url so try to encode it.
// turn spaces to %20, unless we are being called from shellexec
// in which case we allow spaces in the URL
if (!BindCtx_ContainsObject(pbc, STR_PARSE_INTERNET_DONT_ESCAPE_SPACES)) UrlEscape(szName, szName, &cchName, URL_ESCAPE_SPACES_ONLY);
fProtocolExists = _GetUrlProtocol(szName, szProtocol, ARRAYSIZE(szProtocol)); _ConditionallyFaultInUrlHandler(szProtocol, szName, pbc);
if (fProtocolExists && _CreateProtocolHandler(szProtocol, pbc, &psfHandler) == S_OK) { TraceMsg(TF_PIDLWRAP, "Asking \"%s\" handler to parse %s (%08X) into a pidl", szProtocol, szName, szName); hres = psfHandler->ParseDisplayName(hwnd, pbc, pwszDisplayName, pchEaten, ppidl, pdwAttributes); TraceMsg(TF_PIDLWRAP, "the result is %08X, the pidl is %08X", hres, *ppidl); psfHandler->Release(); TraceMsg(TF_URLNAMESPACE, "CODF::PDN(%s) called psfHandler and returning %x", szName, hres); } else { *ppidl = UrlToPidl(uiCP, szName, NULL); if (*ppidl) { if (pdwAttributes) hres = _GetAttributesOfProtocol(NULL, (LPCITEMIDLIST *)ppidl, 1, pdwAttributes); else hres = S_OK; } else hres = E_OUTOFMEMORY;
TraceMsg(TF_URLNAMESPACE, "CODF::PDN(%s) called UrlToPidl and returning %x", szName, hres); } } else { TraceMsg(DM_CDOFPDN, "CDOF::PDN(%s) returning E_FAIL because of (%s) is FALSE", szName, TEXT("(_ValidateURL(szName) || ShouldShellExecURL( szName ))")); } }
return hres; }
class CInternetFolderDummyEnum : public IEnumIDList { public: CInternetFolderDummyEnum(); // *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID,void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void);
// *** IEnumIDList methods ***
STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt) {return E_NOTIMPL;} STDMETHODIMP Reset(void){return E_NOTIMPL;} STDMETHODIMP Clone(LPENUMIDLIST *ppenum){return E_NOTIMPL;}
protected: ~CInternetFolderDummyEnum() {;} long _cRef; };
CInternetFolderDummyEnum::CInternetFolderDummyEnum() : _cRef(1) { }
HRESULT CInternetFolderDummyEnum::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CInternetFolderDummyEnum, IEnumIDList), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CInternetFolderDummyEnum::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CInternetFolderDummyEnum::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CInternetFolderDummyEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) { pceltFetched = 0; return S_FALSE; }
HRESULT CInternetFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList) { CInternetFolderDummyEnum *pdummy = new CInternetFolderDummyEnum();
if (pdummy) { *ppenumIDList = (IEnumIDList *)pdummy; return S_OK; } return E_OUTOFMEMORY; }
HRESULT CInternetFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut) { IShellFolder *psfHandler = NULL; BOOL fUseDefault = TRUE; *ppvOut = NULL;
if (!_IsValidUrlID(pidl)) return E_INVALIDARG; HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, pbc, &psfHandler); if (hres == S_OK) { // NOTE: We allow Shell Extensions to take over URL handling on a per
// URL basis. We entered the _CreateProtocolHandlerFromPidl()
// block of code above because
// a shell extension is registered to take over handling this
// URL. The above call to IShellFolder::BindToObject() just failed,
// so we need to fall back and handle it in the traditional way.
// This can be used by Shell Extensions, like the Ftp ShellExt, to
// let the browser (us) handle URLs that are either inaccessible because of
// the proxy or allow the browser to handle it so the traditional code
// will: 1) download the item(s), 2) sniff the data for the type, 3)
// use the suggested MIME type from the server or in the web page, 4)
// check the file for type extension mappings, 5)
// check any downloaded file for security certificates, and 6) display
// Open/Save dialogs.
hres = psfHandler->BindToObject(pidl, pbc, riid, ppvOut);
// the handler will return ERROR_CANCELLED if it wants default behavior
if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hres) fUseDefault = FALSE; }
if (fUseDefault) { STRRET strRet;
if (psfHandler) { // we had a delegated folder that failed, need a normal pidl
hres = psfHandler->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet); } else hres = GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet);
TCHAR szUrl[MAX_URL_STRING]; if (SUCCEEDED(hres) && SUCCEEDED(hres = StrRetToBuf(&strRet, pidl, szUrl, ARRAYSIZE(szUrl)))) { if (IsEqualIID(IID_IMoniker, riid)) { hres = MonikerFromURL(szUrl, (IMoniker **)ppvOut); } else // DEFAULT
{ // create a ShellFolder for the caller
hres = E_OUTOFMEMORY; LPITEMIDLIST pidlT = NULL;
// if we are using a handler but it returned cancelled,
// then we need to recreate the pidl for ourselves to use
// otherwise we just use the one that was passed in,
// which we assume was the one we created.
if (psfHandler) { pidlT = UrlToPidl(CP_ACP, szUrl, NULL); pidl = pidlT; }
if (pidl) { LPITEMIDLIST pidlFull = ILCombine(_pidlRoot, pidl);
if (pidlFull) { CDocObjectFolder *psf = new CDocObjectFolder(pidlFull); if (psf) { hres = psf->QueryInterface(riid, ppvOut); psf->Release(); } ILFree(pidlFull); }
ILFree(pidlT); } } } }
if (psfHandler) psfHandler->Release(); return hres; }
HRESULT CInternetFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj) { IShellFolder *psfHandler;
*ppvObj = NULL;
if (!_IsValidUrlID(pidl)) return E_INVALIDARG;
HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, pbc, &psfHandler); if (hres != S_FALSE) { if (SUCCEEDED(hres)) { hres = psfHandler->BindToStorage(pidl, pbc, riid, ppvObj); psfHandler->Release(); } return hres; } *ppvObj = NULL; return E_NOINTERFACE; }
int CALLBACK CompareDelegateProtocols(void *pv1, void *pv2, LPARAM lParam) { LPCSTR psz1 = _PidlToDelegateProtocol((LPCITEMIDLIST)pv1); LPCSTR psz2 = _PidlToDelegateProtocol((LPCITEMIDLIST)pv2);
if (psz1 && psz2) { int iRet = StrCmpA(psz1, psz2); if (0 == iRet && lParam) *((LPCSTR *)lParam) = psz1; } else if (psz1) { return 1; } else if (psz2) { return -1; } return 0; }
HRESULT CInternetFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { int iRet;
ASSERT(!ILIsEmpty(pidl1) && !ILIsEmpty(pidl2));
// Check for protocol pidls.
LPCSTR psz = NULL; iRet = CompareDelegateProtocols((void *)pidl1, (void *)pidl2, (LPARAM)&psz); if (iRet) return ResultFromShort(iRet);
if (psz) { IShellFolder *psfHandler; if (_CreateProtocolHandler(psz, NULL, &psfHandler) == S_OK) { iRet = psfHandler->CompareIDs(lParam, pidl1, pidl2); psfHandler->Release(); return ResultFromShort(iRet); } }
// we only have one layer of children
ASSERT(ILIsEmpty(_ILNext(pidl1))); ASSERT(ILIsEmpty(_ILNext(pidl2)));
PCURLID purlid1 = _IsValidUrlID(pidl1);
if (purlid1) { PCURLID purlid2 = _IsValidUrlID(pidl2);
if (purlid2) { iRet = _CompareURL(purlid1, purlid2); } else { iRet = -1; } } else { iRet = 1; }
return ResultFromShort(iRet); }
HRESULT CInternetFolder::_GetAttributesOfProtocol(LPCSTR pszProtocol, LPCITEMIDLIST *apidl, UINT cpidl, ULONG *rgfInOut) { HRESULT hres;
ASSERT(cpidl); if (pszProtocol) { //
// We have a protocol. Find the protocol handler
// and pass it the bundle of pidls.
//
IShellFolder *psfHandler; hres = _CreateProtocolHandler(pszProtocol, NULL, &psfHandler); if (hres == S_OK) { hres = psfHandler->GetAttributesOf(cpidl, apidl, rgfInOut); psfHandler->Release(); } } else if (_IsValidUrlID(apidl[0])) { ULONG uOut = SFGAO_CANLINK | SFGAO_BROWSABLE | SFGAO_STREAM; *rgfInOut &= uOut; hres = S_OK; } else hres = E_INVALIDARG;
return hres; }
HRESULT CInternetFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut) { if (*rgfInOut) { //
// Internet folder case.
//
LPCSTR pszProtocol;
if (cidl == 0) { //
// They are asking about the Internet Folder itself.
//
*rgfInOut &= SFGAO_FOLDER | SFGAO_CANLINK | SFGAO_STREAM; } else if (cidl == 1) { //
// Often we are asked about only one child,
// so we optimize that case.
//
pszProtocol = _PidlToDelegateProtocol(apidl[0]);
_GetAttributesOfProtocol(pszProtocol, apidl, cidl, rgfInOut); } else { //
// They are asking about multiple internet children.
// These children may have different protocols,
// so we have to find the GetAttributesOf handler for
// each group of protocols in the list.
//
LPCITEMIDLIST pidlBase; UINT i, cpidlGroup;
// Create a list of pidls sorted by protocol.
HDPA hdpa = DPA_Create(100); if (!hdpa) return E_OUTOFMEMORY;
for (i = 0; i < cidl; i++) { DPA_AppendPtr(hdpa, (void *)apidl[i]); } DPA_Sort(hdpa, CompareDelegateProtocols, NULL);
//
// Call GetAttributesOf on each protocol group.
// A group
// starts at pidlBase
// contains cpidlGroup pidls
// has a protocol of pszProtocol
//
pidlBase = (LPCITEMIDLIST)DPA_FastGetPtr(hdpa, 0); pszProtocol = NULL; cpidlGroup = 0; for (i = 0; *rgfInOut && (i < cidl); i++) { LPCITEMIDLIST pidlNew = (LPCITEMIDLIST)DPA_FastGetPtr(hdpa, i); LPCSTR pszProtocolNew = _PidlToDelegateProtocol(pidlNew); if (pszProtocolNew) { // See if we have a new protocol.
if (!pszProtocol || StrCmpA(pszProtocol, pszProtocolNew)) { // We have a new protocol, time to process
// the last batch pidls.
_GetAttributesOfProtocol(pszProtocol, &pidlBase, cpidlGroup, rgfInOut);
pidlBase = pidlNew; pszProtocol = pszProtocolNew; cpidlGroup = 0; } } cpidlGroup++; } if (*rgfInOut) { ASSERT(cpidlGroup); _GetAttributesOfProtocol(pszProtocol, &pidlBase, cpidlGroup, rgfInOut); }
DPA_Destroy(hdpa); hdpa = NULL; } }
return S_OK; }
BOOL GetCommonProtocol(LPCITEMIDLIST *apidl, UINT cpidl, LPCSTR *ppszProtocol) { UINT ipidl; LPCSTR pszProtocol; LPCSTR pszProtocolNext;
*ppszProtocol = NULL;
if (cpidl == 0) { return TRUE; // No pidls - no protocols, but they do all match!
}
//
// Grab the protocol of the first pidl, and use it to compare
// against the rest of the pidls.
//
pszProtocol = _PidlToDelegateProtocol(apidl[0]);
for (ipidl=1; ipidl<cpidl; ipidl++) {
pszProtocolNext = _PidlToDelegateProtocol(apidl[ipidl]);
//
// Check if the protocols are different.
//
if ((pszProtocol != pszProtocolNext) && ((pszProtocol == NULL) || (pszProtocolNext == NULL) || (StrCmpA(pszProtocol, pszProtocolNext) != 0))) { return FALSE; } }
*ppszProtocol = pszProtocol; return TRUE; }
HRESULT _CombineHidden(LPCITEMIDLIST pidl, DWORD dwIEFlags, LPWSTR pszName, DWORD cchName) { HRESULT hres = S_OK; //
// need to correctly append the fragment and query to the base
// if pszName is a DOSPATH, it will be converted to a
// file: URL so that it can accomadate the location
//
WCHAR sz[MAX_URL_STRING]; DWORD cch = cchName;
if (ILGetHiddenStringW(pidl, IDLHID_URLQUERY, sz, SIZECHARS(sz))) hres = UrlCombineW(pszName, sz, pszName, &cch, 0); if (!(dwIEFlags & IEGDN_NOFRAGMENT) && IEILGetFragment(pidl, sz, SIZECHARS(sz))) { hres = UrlCombineW(pszName, sz, pszName, &cchName, 0); }
// else
// BUBBUG - should we return just the fragment in some case?
return hres; }
HRESULT CInternetFolder::_GetUIObjectFromShortcut(LPCITEMIDLIST pidl, REFIID riid, void **ppvOut) { HRESULT hres = E_NOINTERFACE; STRRET str; TCHAR sz[MAX_URL_STRING];
if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)) && SUCCEEDED(StrRetToBuf(&str, pidl, sz, ARRAYSIZE(sz))) && SUCCEEDED(_CombineHidden(pidl, 0, sz, ARRAYSIZE(sz)))) { IUniformResourceLocator *purl; hres = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUniformResourceLocator, &purl)); if (SUCCEEDED(hres)) { hres = purl->SetURL(sz, 0); if (SUCCEEDED(hres)) { IShellLink * psl; if (SUCCEEDED(purl->QueryInterface(IID_PPV_ARG(IShellLink, &psl)))) { if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_INFOLDER, &str)) && SUCCEEDED(StrRetToBuf(&str, pidl, sz, ARRAYSIZE(sz)))) { PathRenameExtension(sz, TEXT(".url")); psl->SetDescription(sz); } psl->Release(); } hres = purl->QueryInterface(riid, ppvOut); } purl->Release(); } }
return hres; }
HRESULT CInternetFolder::_GetScheme(LPCITEMIDLIST pidl, LPWSTR pszOut, DWORD cchOut) { STRRET str; LPCSTR pszProtocol = _PidlToDelegateProtocol(pidl);
if (pszProtocol) { SHAnsiToUnicode(pszProtocol, pszOut, cchOut); return S_OK; } else if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str))) { WCHAR sz[MAX_URL_STRING]; if (SUCCEEDED(StrRetToBufW(&str, pidl, sz, ARRAYSIZE(sz)))) { return UrlGetPartW(sz, pszOut, &cchOut, URL_PART_SCHEME, 0); } } return E_FAIL; } HRESULT CInternetFolder::_AssocCreate(LPCITEMIDLIST pidl, REFIID riid, void * *ppv) { *ppv = NULL;
IQueryAssociations *pqa; HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { WCHAR szScheme[MAX_PATH]; _GetScheme(pidl, szScheme, SIZECHARS(szScheme));
hr = pqa->Init(0, szScheme, NULL, NULL);
if (SUCCEEDED(hr)) hr = pqa->QueryInterface(riid, ppv);
pqa->Release(); }
return hr; }
HRESULT CInternetFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, void **ppvOut) { HRESULT hres = E_NOINTERFACE; LPCSTR pszProtocol; *ppvOut = NULL;
if (apidl[0] && GetCommonProtocol(apidl, cidl, &pszProtocol) && pszProtocol) { IShellFolder *psfHandler; hres = _CreateProtocolHandlerFromPidl(apidl[0], NULL, &psfHandler); if (hres != S_FALSE) { if (SUCCEEDED(hres)) { hres = psfHandler->GetUIObjectOf(hwnd, 1, apidl, riid, prgfInOut, ppvOut); psfHandler->Release(); } return hres; } } else if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW) || IsEqualIID(riid, IID_IContextMenu) || IsEqualIID(riid, IID_IQueryInfo) || IsEqualIID(riid, IID_IDataObject)) { // WARNING - we only support this for one at a time.
if (cidl == 1) { hres = _GetUIObjectFromShortcut(apidl[0], riid, ppvOut); } } else if (IsEqualIID(riid, IID_IQueryAssociations)) { // WARNING - we only support this for one at a time.
if (cidl == 1) { hres = _AssocCreate(apidl[0], riid, ppvOut); } }
return hres; }
HRESULT CInternetFolder::_InitHistoryStg(IUrlHistoryStg **pphist) { HRESULT hr; if (!_phist) { hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUrlHistoryStg, &_phist)); } if (_phist) { *pphist = _phist; _phist->AddRef(); return S_OK; }
return hr; }
HRESULT CInternetFolder::_GetTitle(LPCWSTR pszUrl, STRRET *pstr) { ASSERT(pszUrl);
IUrlHistoryStg *phist;
HRESULT hr = _InitHistoryStg(&phist);
if (SUCCEEDED(hr)) { ASSERT(phist); STATURL stat = {0}; hr = phist->QueryUrl(pszUrl, STATURL_QUERYFLAG_NOURL, &stat);
if (SUCCEEDED(hr) && stat.pwcsTitle) { hr = StringToStrRet(stat.pwcsTitle, pstr); CoTaskMemFree(stat.pwcsTitle); } else hr = E_FAIL;
phist->Release(); }
return hr; }
HRESULT CInternetFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pstr) { IShellFolder *psfHandler; HRESULT hr = _CreateProtocolHandlerFromPidl(pidl, NULL, &psfHandler); if (hr != S_FALSE) { if (SUCCEEDED(hr)) { hr = psfHandler->GetDisplayNameOf(pidl, uFlags, pstr); psfHandler->Release(); } return hr; }
// FEATURE ZEKEL - should i handle more SHGDN flags here?? - Zekel - 24-NOV-98
PCURLID purlid = _IsValidUrlID(pidl); if (purlid) { WCHAR sz[MAX_URL_STRING];
_ExtractURL(purlid, sz, SIZECHARS(sz));
if (SHGDN_NORMAL != uFlags) hr = StringToStrRet(sz, pstr); else { hr = _GetTitle(sz, pstr);
// fallback to the URL if necessary
if (FAILED(hr)) hr = StringToStrRet(sz, pstr); } } else hr = E_INVALIDARG;
return hr; }
HRESULT CInternetFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST * ppidlOut) { IShellFolder *psfHandler; HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, NULL, &psfHandler); if (hres != S_FALSE) { if (SUCCEEDED(hres)) { hres = psfHandler->SetNameOf(hwnd, pidl, pszName, uFlags, ppidlOut); psfHandler->Release(); } return hres; }
return E_FAIL; }
HRESULT CInternetFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_Internet; return S_OK; }
// IBrowserFrameOptions
HRESULT CInternetFolder::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions) { // The only case I know of that we hit this code is when you select "Internet Explorer" in the
// Folder Browser Band.
HRESULT hr = E_INVALIDARG;
if (pdwOptions) { // CInternetFolder should only be used for the "Internet Explorer" pidl that
// points to the Start Page, so find the start page and substitute it during
// navigation.
*pdwOptions |= dwMask & (BFO_SUBSTITUE_INTERNET_START_PAGE | BASE_OPTIONS); hr = S_OK; }
return hr; }
#ifdef DEBUG
extern void remove_from_memlist(void *pv); #endif
STDAPI CInternetFolder_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi) { CInternetFolder *psf = new CInternetFolder; if (psf) { //
// HACK:
//
// SHELL32 caches c_sfInetRoot in a static DATA section
// and never release it. It caches an instance of CInternetFolder
// and never release it. Therefore, we are removing this object
// from the to-be-memleak-detected list to avoid a false alarm
// assuming that we don't realy leak this object.
// Please don't copy it to another place unless you are really
// sure that it's OK not to detect leaks in that scenario.
// (SatoNa)
//
HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IUnknown, ppunk)); psf->Release(); return hr; } return E_OUTOFMEMORY; }
STDAPI MonikerFromURL(LPCWSTR wszPath, IMoniker** ppmk) { HRESULT hres = CreateURLMoniker(NULL, wszPath, ppmk); #ifndef UNIX
if (FAILED(hres)) #else
// BUG BUG :
// IEUNIX : We use to crash on UNIX if we give a very long invalid url
// in address bar or as home page in inetcpl. We used to crash inside
// MkParseDisplayName.
if (FAILED(hres) && lstrlenW(wszPath) < MAX_PATH) #endif
{ IBindCtx* pbc; hres = CreateBindCtx(0, &pbc); if (SUCCEEDED(hres)) { // Fall back to a system (file) moniker
ULONG cchEaten = 0; hres = MkParseDisplayName(pbc, wszPath, &cchEaten, ppmk); pbc->Release(); } }
return hres; }
STDAPI MonikerFromString(LPCTSTR szPath, IMoniker** ppmk) { return MonikerFromURL(szPath, ppmk); }
#if 0
// alternate way to do the below in a more "pure" way on integrated mode
HRESULT InitPSFInternet() { if (g_psfInternet) return S_OK;
IShellFolder *psfTemp; IShellFolder *psfDesktop; HRESULT hres = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hres)) { ULONG cchEaten; LPITEMIDLIST pidl; WCHAR wszInternet[] = L"::{871C5380-42A0-1069-A2EA-08002B30309D}"; // CLSID_Internet
hres = psfDesktop->ParseDisplayName(NULL, NULL, wszInternet, &cchEaten, &pidl, NULL); if (SUCCEEDED(hres)) { hres = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTemp)); SHFree(pidl); } psfDesktop->Release(); }
if (SHInterlockedCompareExchange((void **)&g_psfInternet, psfTemp, 0)) psfTemp->Release(); // race to the exchange, free dup copy
return hres; } #endif
HRESULT InitPSFInternet() { if (g_psfInternet) return S_OK;
IShellFolder *psfTemp; HRESULT hres = CoCreateInstance(CLSID_CURLFolder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolder, &psfTemp)); if (SUCCEEDED(hres)) { IPersistFolder* ppsf; hres = psfTemp->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppsf)); if (SUCCEEDED(hres)) { hres = ppsf->Initialize(c_pidlURLRoot); if (SUCCEEDED(hres)) { if (SHInterlockedCompareExchange((void **)&g_psfInternet, psfTemp, 0) == 0) psfTemp->AddRef(); // global now holds ref
} ppsf->Release(); } psfTemp->Release(); } return hres; }
HRESULT _GetInternetRoot(IShellFolder **ppsfRoot) { HRESULT hr = InitPSFInternet(); *ppsfRoot = NULL;
if (SUCCEEDED(hr)) { g_psfInternet->AddRef(); *ppsfRoot = g_psfInternet; } return hr; }
HRESULT _GetRoot(LPCITEMIDLIST pidl, BOOL fIsUrl, IShellFolder **ppsfRoot) { HRESULT hr; *ppsfRoot = NULL; if (fIsUrl) { ASSERT(IsURLChild(pidl, TRUE)); TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using the Internet", pidl); hr = _GetInternetRoot(ppsfRoot); } else { ASSERT(ILIsRooted(pidl)); TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using Rooted", pidl);
CLSID clsid;
ILRootedGetClsid(pidl, &clsid);
if (IsEqualGUID(clsid, CLSID_ShellDesktop)) { hr = SHBindToObject(NULL, IID_IShellFolder, ILRootedFindIDList(pidl), (void **)ppsfRoot); } else { IShellFolder *psf; hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { LPCITEMIDLIST pidlRoot = ILRootedFindIDList(pidl); if (!pidlRoot) pidlRoot = &s_idlNULL;
IPersistFolder* ppf; hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf)); if (SUCCEEDED(hr)) { hr = ppf->Initialize(pidlRoot); ppf->Release(); }
// hand over the reference
*ppsfRoot = psf; } } }
return hr; }
STDAPI_(BOOL) IEILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fIgnoreHidden) { UINT cb = ILGetSize(pidl1);
if (cb != ILGetSize(pidl2) || 0 != memcmp(pidl1, pidl2, cb)) { // THEY are binarily different
BOOL fRet = FALSE; BOOL fWebOnly = FALSE;
if (IsURLChild(pidl1, TRUE) || IsURLChild(pidl2, TRUE)) fWebOnly = TRUE; if ((IsURLChild(pidl1, FALSE) && IsURLChild(pidl2, FALSE)) || (ILIsRooted(pidl1) && ILIsEqualRoot(pidl1, pidl2))) { IShellFolder *psf; if (SUCCEEDED(_GetRoot(pidl1, fWebOnly, &psf))) { if (0 == psf->CompareIDs(0, _ILNext(pidl1), _ILNext(pidl2))) fRet = TRUE;
psf->Release(); } } if (!fRet && !fWebOnly) { #undef ILIsEqual
fRet = ILIsEqual(pidl1, pidl2); } if (fRet && !fIgnoreHidden) { fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_URLFRAGMENT));
if (fRet) fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_URLQUERY));
if (fRet) fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_NAVIGATEMARKER)); } return fRet; } return TRUE; }
// pszName must be MAX_URL_STRING
STDAPI IEGetDisplayName(LPCITEMIDLIST pidl, LPWSTR pszName, UINT uFlags) { return IEGetNameAndFlagsEx(pidl, uFlags, 0, pszName, MAX_URL_STRING, NULL); }
#ifdef UNIX
//IEUNIX : No support for internal and external names in hp and solaris linker.
extern "C" STDAPI IEGetDisplayNameW(LPCITEMIDLIST pidl, LPWSTR pszName, UINT uFlags) { return IEGetDisplayName(pidl, pszName, uFlags); } #endif
HRESULT _GetInternetFolderName(LPWSTR pszName, DWORD cchName) { LPCTSTR pszKey; DWORD cbSize = CbFromCch(cchName);
if (4 > GetUIVersion()) pszKey = TEXT("CLSID\\{FBF23B42-E3F0-101B-8488-00AA003E56F8}"); else pszKey = TEXT("CLSID\\{871C5380-42A0-1069-A2EA-08002B30309D}");
if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, pszKey, NULL, NULL, pszName, &cbSize) && *pszName) return S_OK;
if (MLLoadString(IDS_REG_THEINTERNET, pszName, cchName) && *pszName) return S_OK;
return E_UNEXPECTED; }
STDAPI IEGetNameAndFlagsEx(LPCITEMIDLIST pidl, UINT uSHFlags, DWORD dwIEFlags, LPWSTR pszName, DWORD cchName, DWORD *prgfInOutAttrs) { HRESULT hres = E_FAIL;
if (pszName) { VDATEINPUTBUF(pszName, TCHAR, cchName); *pszName = 0; }
// for support of NON-integrated builds, and
// to expedite handling of URLs while browsing
if (IsURLChild(pidl, FALSE)) { hres = InitPSFInternet(); if (SUCCEEDED(hres)) { if (pszName) { STRRET str; hres = g_psfInternet->GetDisplayNameOf(_ILNext(pidl), uSHFlags, &str); if (SUCCEEDED(hres)) { StrRetToBufW(&str, pidl, pszName, cchName); } }
if (prgfInOutAttrs) hres = IEGetAttributesOf(pidl, prgfInOutAttrs); } } else if (GetUIVersion() <= 4 && IsURLChild(pidl, TRUE)) { //
// we need to support requests for the Internet SFs
// Friendly name. on NT5 we will always have something
// even when the SF is hidden. but on older versions
// of the shell, it was possible to delete the folder
// by just removing the icon from the desktop
//
if (pszName) hres = _GetInternetFolderName(pszName, cchName);
if (prgfInOutAttrs) hres = IEGetAttributesOf(pidl, prgfInOutAttrs); } else if (ILIsRooted(pidl)) { IShellFolder *psf; LPCITEMIDLIST pidlChild; hres = IEBindToParentFolder(pidl, &psf, &pidlChild); if (SUCCEEDED(hres)) { if (pszName) { STRRET str; hres = IShellFolder_GetDisplayNameOf(psf, pidlChild, uSHFlags, &str, 0); if (SUCCEEDED(hres)) { hres = StrRetToBufW(&str, pidlChild, pszName, cchName); } }
if (prgfInOutAttrs) hres = psf->GetAttributesOf(ILIsEmpty(pidlChild) ? 0 : 1, &pidlChild, prgfInOutAttrs);
psf->Release(); } } else hres = SHGetNameAndFlags(pidl, uSHFlags, pszName, cchName, prgfInOutAttrs);
if (SUCCEEDED(hres) && pszName && (uSHFlags & SHGDN_FORPARSING)) { hres = _CombineHidden(pidl, dwIEFlags, pszName, cchName); }
TraceMsg(TF_URLNAMESPACE, "IEGDN(%s) returning %x", pszName, hres); return hres; }
STDAPI IEGetNameAndFlags(LPCITEMIDLIST pidl, UINT uFlags, LPWSTR pszName, DWORD cchName, DWORD *prgfInOutAttrs) { return IEGetNameAndFlagsEx(pidl, uFlags, 0, pszName, cchName, prgfInOutAttrs); }
BOOL _ClassIsBrowsable(LPCTSTR pszClass) { BOOL fRet = FALSE; HKEY hk; if (SUCCEEDED(AssocQueryKey(0, ASSOCKEY_CLASS, pszClass, NULL, &hk))) { fRet = (NOERROR == RegQueryValueEx(hk, TEXT("DocObject"), NULL, NULL, NULL, NULL) || NOERROR == RegQueryValueEx(hk, TEXT("BrowseInPlace"), NULL, NULL, NULL, NULL));
RegCloseKey(hk); }
return fRet; }
BOOL _MimeIsBrowsable(LPCTSTR pszExt) { BOOL fRet = FALSE; TCHAR sz[MAX_PATH]; DWORD dwSize = ARRAYSIZE(sz);
if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_CONTENTTYPE, pszExt, NULL, sz, &dwSize))) { TCHAR szKey[MAX_PATH]; dwSize = SIZEOF(sz);
// Get the CLSID for the handler of this content type.
wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("MIME\\Database\\Content Type\\%s"), sz);
// reuse sz for the clsid
if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, szKey, TEXT("CLSID"), NULL, (void *) sz, &dwSize)) { fRet = _ClassIsBrowsable(sz); } } return fRet; } BOOL _StorageIsBrowsable(LPCTSTR pszPath) { BOOL fRet = FALSE; //
// If the file is STILL not browsable, try to open it as a structured storage
// and check its CLSID.
//
IStorage *pStg = NULL;
if (StgOpenStorage(pszPath, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &pStg ) == S_OK && pStg) { STATSTG statstg; if (pStg->Stat( &statstg, STATFLAG_NONAME ) == S_OK) { TCHAR szClsid[GUIDSTR_MAX]; SHStringFromGUIDW(statstg.clsid, szClsid, SIZECHARS(szClsid)); fRet = _ClassIsBrowsable(szClsid); } pStg->Release(); }
return fRet; }
BOOL _IEIsBrowsable(LPCITEMIDLIST pidl) { TCHAR szPath[MAX_PATH]; BOOL fRet = FALSE; if (SUCCEEDED(SHGetPathFromIDList(pidl, szPath))) { // Don't change the order of the following OR'd conditions because
// we want the HTML test to go first. Also, the NT5 shell will
// do the ClassIsBrowsable check for us, so we should avoid repeating
// that check.
if (PathIsHTMLFile(szPath) || _ClassIsBrowsable(szPath) || _MimeIsBrowsable(PathFindExtension(szPath)) || _StorageIsBrowsable(szPath)) fRet = TRUE; } return fRet; }
HRESULT _IEGetAttributesOf(LPCITEMIDLIST pidl, DWORD* pdwAttribs, BOOL fAllowExtraChecks) { HRESULT hres = E_FAIL; DWORD dwAttribs = *pdwAttribs; BOOL fExtraCheckForBrowsable = FALSE;
//
// REARCHITECT - Check if we need to execute an additional logic - ZekeL - 7-JAN-99
// to see if it's browsable or not. this is necessary on shell32s from NT4/win95/IE4
// both NT4/win95 have no notion of SFGAO_BROWSABLE, and even though
// IE4 does, it doesnt handle it correctly for UNICODE file names.
// We are just as thorough (more) in our private check, so just defer to it.
//
// 78777: Even if we are on NT5, IE can browse things that Shell thinks is not
// browsable, for example, .htm files when Netscape is the default browser.
// So we should do the extra check on every platform.
if (fAllowExtraChecks && (dwAttribs & SFGAO_BROWSABLE)) { dwAttribs |= SFGAO_FILESYSTEM | SFGAO_FOLDER; fExtraCheckForBrowsable = TRUE; }
IShellFolder* psfParent; LPCITEMIDLIST pidlChild; if (ILIsEmpty(pidl)) { hres = SHGetDesktopFolder(&psfParent); pidlChild = pidl; } else if (ILIsRooted(pidl) && ILIsEmpty(_ILNext(pidl))) { //
// when getting the attributes of the root itself, we
// decide its better to just limit the attribs to
// some easily supported subset. we used to always
// fail, but that is a little extreme.
//
// we could also try to get the attributes from HKCR\CLSID\{clsid}\shellfolder\attributes
//
*pdwAttribs &= (SFGAO_FOLDER); return S_OK; } else { if (GetUIVersion() < 4 && IsURLChild(pidl, TRUE)) { IShellFolder *psfRoot; //
// if we are Browser Only, and this is the
// internet folder itself that we are interested
// in, then we need to bind to it by hand
// and query it with cidl = 0
//
hres = _GetInternetRoot(&psfRoot);
if (SUCCEEDED(hres)) { hres = SHBindToFolderIDListParent(psfRoot, _ILNext(pidl), IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild); psfRoot->Release(); } } else if (ILIsRooted(pidl)) hres = IEBindToParentFolder(pidl, &psfParent, &pidlChild); else hres = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild); }
if (SUCCEEDED(hres)) { ASSERT(psfParent); hres = psfParent->GetAttributesOf(ILIsEmpty(pidlChild) ? 0 : 1, &pidlChild, &dwAttribs);
if (FAILED(hres)) TraceMsg(TF_WARNING, "IEGetAttribs psfParent->GetAttr failed %x", hres);
psfParent->Release(); } else TraceMsg(TF_WARNING, "IEGetAttribs BindTOParent failed %x", hres);
//
// This is the extra logic we need to execute if this is a browser
// only mode to get the right "browsable" attribute flag to DocObjects.
//
if (fExtraCheckForBrowsable && !(dwAttribs & SFGAO_BROWSABLE)) { if ((dwAttribs & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM) { if (_IEIsBrowsable(pidl)) dwAttribs |= SFGAO_BROWSABLE; } }
*pdwAttribs &= dwAttribs; return hres; }
HRESULT IEGetAttributesOf(LPCITEMIDLIST pidl, DWORD* pdwAttribs) { return _IEGetAttributesOf(pidl, pdwAttribs, TRUE); }
// BRYANST: 7/22/97 - NT Bug #188099
// shell32.dll in IE4 SI and only in that version had a bug if pbc was passed
// to IShellFolder::BindToObject() (fstreex.c!FSBindToFSFolder), it would fail
// to bind to Shell Extensions that extended file system folders, such as:
// the history folder, the occache, etc. We work around this by passing a NULL pbc
// if the destination is an IE4 shell32.dll and it will go thru FSBindToFSFolder().
BOOL ShouldWorkAroundBCBug(LPCITEMIDLIST pidl) { BOOL fWillBCCauseBug = FALSE;
if (4 == GetUIVersion()) { LPITEMIDLIST pidlCopy = ILClone(pidl); LPITEMIDLIST pidlIterate = pidlCopy;
// Skip the first two ItemIDs. (#1 could be My Computer)
if (!ILIsEmpty(pidlIterate)) { IShellFolder * psf;
// (#2 could be CFSFolder::BindToObject())
pidlIterate = _ILNext(pidlIterate); if (!ILIsEmpty(pidlIterate)) { pidlIterate = _ILNext(pidlIterate); // Remove everything else so we bind directly to CFSFolder::BindToObject()
pidlIterate->mkid.cb = 0;
if (SUCCEEDED(IEBindToObject(pidlCopy, &psf))) { IPersist * pp;
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersist, &pp)))) { CLSID clsid;
if (SUCCEEDED(pp->GetClassID(&clsid)) && IsEqualCLSID(clsid, CLSID_ShellFSFolder)) { fWillBCCauseBug = TRUE; }
pp->Release(); } psf->Release(); } }
}
ILFree(pidlCopy); }
return fWillBCCauseBug; }
typedef enum { SHOULDBIND_DOCOBJ, SHOULDBIND_DESKTOP, SHOULDBIND_NONE, } SHOULDBIND;
//
// _ShouldDocObjBind()
// returns
// SHOULDBIND_DOCOBJ - Should just use DocObjectFolder directly
// SHOULDBIND_DESKTOP - bind through the desktop
// SHOULDBIND_NONE - FAIL the bind...
//
SHOULDBIND _ShouldDocObjBind(DWORD dwAttribs, BOOL fStrictBind) { if (fStrictBind) { if ((dwAttribs & (SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_FILESYSTEM)) == (SFGAO_BROWSABLE | SFGAO_FILESYSTEM)) return SHOULDBIND_DOCOBJ; else return SHOULDBIND_DESKTOP; } else { if (dwAttribs & (SFGAO_FOLDER | SFGAO_BROWSABLE)) return SHOULDBIND_DESKTOP;
// manually bind using our CDocObjectFolder for
// files which are not DocObject. Without this code, file:
// to non-Docobject files (such as multi-media files)
// won't do anything.
//
// is is needed for non integraded browser mode
//
if (dwAttribs & SFGAO_FILESYSTEM) return SHOULDBIND_DOCOBJ; else return SHOULDBIND_NONE; } }
STDAPI _IEBindToObjectInternal(BOOL fStrictBind, LPCITEMIDLIST pidl, IBindCtx * pbc, REFIID riid, void **ppvOut) { IShellFolder *psfTemp; HRESULT hr;
*ppvOut = NULL;
// Special case: If we have the pidl for the "Desktop" then just use the Desktop folder itself
if (ILIsEmpty(pidl)) { hr = SHGetDesktopFolder(&psfTemp); if (SUCCEEDED(hr)) { hr = psfTemp->QueryInterface(riid, ppvOut); psfTemp->Release(); } } else { BOOL fIsUrlChild = IsURLChild(pidl, TRUE);
if (fIsUrlChild || ILIsRooted(pidl)) { hr = _GetRoot(pidl, fIsUrlChild, &psfTemp); if (SUCCEEDED(hr)) { pidl = _ILNext(pidl); if (!ILIsEmpty(pidl)) hr = psfTemp->BindToObject(pidl, pbc, riid, ppvOut); else hr = psfTemp->QueryInterface(riid, ppvOut);
psfTemp->Release(); } } else { // non integrated browser mode will succeed on
// BindToObject(IID_IShellFolder) even for things that should
// fail (files). to avoid the down stream problems caused by this we
// filter out things that are not "browseable" up front,
//
// NOTE: this does not work on simple PIDLs
DWORD dwAttribs = SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_FILESYSTEM;
hr = _IEGetAttributesOf(pidl, &dwAttribs, fStrictBind); if (SUCCEEDED(hr)) { switch (_ShouldDocObjBind(dwAttribs, fStrictBind)) { case SHOULDBIND_DOCOBJ: { //
// shortcircuit and bind using our CDocObjectFolder for
// files which are BROWSABLE. Without this code, file:
// to non-Docobject files (such as multi-media files)
// won't do anything.
//
// is is needed for non integraded browser mode
//
CDocObjectFolder *pdof = new CDocObjectFolder();
TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using DocObjectFolder", pidl); if (pdof) { hr = pdof->Initialize(pidl); if (SUCCEEDED(hr)) hr = pdof->QueryInterface(riid, ppvOut); pdof->Release(); } else hr = E_OUTOFMEMORY; } break;
case SHOULDBIND_DESKTOP: { //
// This is the normal case. We just bind down through the desktop...
//
TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using Desktop", pidl);
hr = SHGetDesktopFolder(&psfTemp); if (SUCCEEDED(hr)) { // BRYANST: 7/22/97 - NT Bug #188099
// shell32.dll in IE4 SI and only in that version had a bug if pbc was passed
// to IShellFolder::BindToObject() (fstreex.c!FSBindToFSFolder), it would fail
// to bind to Shell Extensions that extended file system folders, such as:
// the history folder, the occache, etc. We work around this by passing a NULL pbc
// if the destination is an IE4 shell32.dll and it will go thru FSBindToFSFolder().
if (pbc && ShouldWorkAroundBCBug(pidl)) { pbc = NULL; }
hr = psfTemp->BindToObject(pidl, pbc, riid, ppvOut); psfTemp->Release(); } } break;
default: hr = E_FAIL; } } } }
if (SUCCEEDED(hr) && !*ppvOut) { // Some NSEs have bugs where they will fail to fill in the
// out pointer but return SUCCEEDED(hr). WS_FTP is one example
// in NT #413950.
TraceMsg(TF_URLNAMESPACE, "IEBTO() BUG!!! An NSE succeeded but returned a NULL interface pointer."); hr = E_FAIL; } TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) returning %x", pidl, hr);
return hr; }
STDAPI IEBindToObjectEx(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppvOut) { return _IEBindToObjectInternal(TRUE, pidl, pbc, riid, ppvOut); }
STDAPI IEBindToObject(LPCITEMIDLIST pidl, IShellFolder **ppsfOut) { return _IEBindToObjectInternal(TRUE, pidl, NULL, IID_PPV_ARG(IShellFolder, ppsfOut)); }
STDAPI IEBindToObjectWithBC(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsfOut) { return _IEBindToObjectInternal(TRUE, pidl, pbc, IID_PPV_ARG(IShellFolder, ppsfOut)); }
// CLASSIC BIND here
HRESULT IEBindToObjectForNavigate(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsfOut) { return _IEBindToObjectInternal(FALSE, pidl, pbc, IID_PPV_ARG(IShellFolder, ppsfOut)); }
//
// CDwnCodePage: Dummy supports IBindCtx interface object only for casting
// It holds codepage info to pass via LPBC parameter
//
class CDwnCodePage : public IBindCtx , public IDwnCodePage { public: // IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void);
// IBindCtx methods
STDMETHODIMP RegisterObjectBound(IUnknown *punk) { return (_pbc ? _pbc->RegisterObjectBound(punk) : E_NOTIMPL); }; STDMETHODIMP RevokeObjectBound(IUnknown *punk) { return (_pbc ? _pbc->RevokeObjectBound(punk) : E_NOTIMPL); }; STDMETHODIMP ReleaseBoundObjects(void) { return (_pbc ? _pbc->ReleaseBoundObjects() : E_NOTIMPL); }; STDMETHODIMP SetBindOptions(BIND_OPTS *pbindopts) { return (_pbc ? _pbc->SetBindOptions(pbindopts) : E_NOTIMPL); }; STDMETHODIMP GetBindOptions(BIND_OPTS *pbindopts) { return (_pbc ? _pbc->GetBindOptions(pbindopts) : E_NOTIMPL); }; STDMETHODIMP GetRunningObjectTable(IRunningObjectTable **pprot) { *pprot = NULL; return (_pbc ? _pbc->GetRunningObjectTable(pprot) : E_NOTIMPL); }; STDMETHODIMP RegisterObjectParam(LPOLESTR pszKey, IUnknown *punk) { return (_pbc ? _pbc->RegisterObjectParam(pszKey, punk) : E_NOTIMPL); }; STDMETHODIMP GetObjectParam(LPOLESTR pszKey, IUnknown **ppunk) { *ppunk = NULL; return (_pbc ? _pbc->GetObjectParam(pszKey, ppunk) : E_NOTIMPL); }; STDMETHODIMP EnumObjectParam(IEnumString **ppenum) { *ppenum = NULL; return (_pbc ? _pbc->EnumObjectParam(ppenum) : E_NOTIMPL); }; STDMETHODIMP RevokeObjectParam(LPOLESTR pszKey) { return (_pbc ? _pbc->RevokeObjectParam(pszKey) : E_NOTIMPL); };
STDMETHODIMP RemoteSetBindOptions(BIND_OPTS2 *pbindopts) { return E_NOTIMPL; }; STDMETHODIMP RemoteGetBindOptions(BIND_OPTS2 *pbindopts) { return E_NOTIMPL; };
// IDwnCodePage methods
STDMETHODIMP_(UINT) GetCodePage(void) { return _uiCodePage; }; STDMETHODIMP SetCodePage(UINT uiCodePage) { _uiCodePage = uiCodePage; return S_OK; };
// Constructor
CDwnCodePage(IBindCtx * pbc, UINT uiCodePage) : _cRef(1) { _uiCodePage = uiCodePage; _pbc = NULL; IUnknown_Set((IUnknown **)&_pbc, (IUnknown *)pbc); }; ~CDwnCodePage() { ATOMICRELEASE(_pbc); };
private: int _cRef; UINT _uiCodePage; IBindCtx * _pbc; };
STDAPI CDwnCodePage::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDwnCodePage, IBindCtx), QITABENT(CDwnCodePage, IDwnCodePage), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
STDAPI_(ULONG) CDwnCodePage::AddRef() { _cRef++; return _cRef; }
STDAPI_(ULONG) CDwnCodePage::Release() { _cRef--; if (0 < _cRef) return _cRef;
delete this; return 0; }
// IEParseDisplayName() will do all of the below functionality in IECreateFromPathCPWithBC()
// plus the following two things:
// 1. It will call ParseURLFromOutsideSource(), so this is more friendly to
// strings from outside sources.
// 2. If the URL has a fragment, this function will pass out a PIDL with the last
// ID being the location.
HRESULT IECreateFromPathCPWithBCW(UINT uiCP, LPCWSTR pszPath, IBindCtx * pbc, LPITEMIDLIST *ppidlOut) { TraceMsg(TF_URLNAMESPACE, "IECFP(%s) called", pszPath);
HRESULT hr = S_OK; WCHAR szPath[MAX_URL_STRING]; WCHAR szBuf[MAX_PATH]; DWORD cchBuf = ARRAYSIZE(szBuf); CDwnCodePage DwnCodePage(pbc, uiCP); DWORD len;
// Initialize for failure case
*ppidlOut = NULL;
// if we are passed a NULL path, then there is no way we can convert it to a pidl.
// in some cases the reason we are passed a NULL path is because the IShellFolder
// provider was unable to generate a parseable display name (MSN Classic 1.3 is
// a very good example, they return E_NOTIMPL).
if ( ((len = lstrlen( pszPath )) == 0) || len >= MAX_URL_STRING ) { return E_FAIL; }
// Is this a "file:" URL?
if (IsFileUrlW(pszPath) && SUCCEEDED(hr = PathCreateFromUrl(pszPath, szBuf, &cchBuf, 0))) pszPath = szBuf;
BOOL fIsFilePath = PathIsFilePath(pszPath);
#ifdef FEATURE_IE_USE_DESKTOP_PARSING
//
// in order to take advantage of whatever enhancements the desktop
// makes to parsing (eg, WebFolders and shell: URLs), then we allow
// the desktop first go at it. it will loop back into the internet
// shell folder if all the special cases fail.
// maybe use a reg setting to control???
//
//
if (fIsFilePath || GetUIVersion() >= 5) #else // !FEATURE_IE_USE_DESKTOP_PARSING
//
// right now we just use the desktop if its a file path or
// it is a shell: URL on NT5
//
if (fIsFilePath || (GetUIVersion() >= 5 && URL_SCHEME_SHELL == GetUrlSchemeW(pszPath))) #endif // FEATURE_IE_USE_DESKTOP_PARSING
{ ASSERT(SUCCEEDED(hr)); // Resolve any dot-dot path reference and remove trailing backslash
if (fIsFilePath) { PathCanonicalize(szPath, pszPath); pszPath = szPath;
// This call will cause a network hit: one connection attempt to \\server\IPC$
// and then a series of FindFirst's - one for each directory.
if (StrChr(pszPath, L'*') || StrChr(pszPath, L'?')) { hr = E_FAIL; } } if (SUCCEEDED(hr)) { hr = SHILCreateFromPath(pszPath, ppidlOut, NULL); TraceMsg(DM_CDOFPDN, "IECreateFromPath psDesktop->PDN(%s) returned %x", pszPath, hr); } } else { //
// Need to put in buffer since ParseDisplayName doesn't take a 'const' string.
StrCpyN(szPath, pszPath, ARRAYSIZE(szPath)); pszPath = szPath;
// Avoid the network and disk hits above for non-file urls.
// This code path is taken on http: folks so a nice optimization. We will then drop
// down below where we check the internet namespace.
IShellFolder *psfRoot; hr = _GetInternetRoot(&psfRoot); if (SUCCEEDED(hr)) { TraceMsg(TF_URLNAMESPACE, "IECFP(%s) calling g_psfInternet->PDN %x", pszPath, hr); LPITEMIDLIST pidlRel;
hr = psfRoot->ParseDisplayName(NULL, (IBindCtx*)&DwnCodePage, szPath, NULL, &pidlRel, NULL); TraceMsg(DM_CDOFPDN, "IECreateFromPath called psfInternet->PDN(%s) %x", pszPath, hr); if (SUCCEEDED(hr)) { *ppidlOut = ILCombine(c_pidlURLRoot, pidlRel); if (!*ppidlOut) hr = E_OUTOFMEMORY; ILFree(pidlRel); } psfRoot->Release(); }
}
// NOTE: NT5 beta 3 and before had a call to SHSimpleIDListFromPath().
// This is very bad because it will parse any garbage and prevent
// the caller from finding invalid strings. I(BryanSt) needed
// this fixed for IEParseDisplayNameWithBCW() would fail on invalid
// address bar strings ("Search Get Rich Quick").
TraceMsg(TF_URLNAMESPACE, "IECFP(%s) returning %x (hr=%x)", pszPath, *ppidlOut, hr);
return hr; }
HRESULT IECreateFromPathCPWithBCA(UINT uiCP, LPCSTR pszPath, IBindCtx * pbc, LPITEMIDLIST *ppidlOut) { WCHAR szPath[MAX_URL_STRING];
ASSERT(lstrlenA(pszPath) < ARRAYSIZE(szPath)); SHAnsiToUnicodeCP(uiCP, pszPath, szPath, ARRAYSIZE(szPath));
return IECreateFromPathCPWithBCW(uiCP, szPath, pbc, ppidlOut); }
HRESULT IEParseDisplayName(UINT uiCP, LPCTSTR pszPath, LPITEMIDLIST * ppidlOut) { return IEParseDisplayNameWithBCW(uiCP, pszPath, NULL, ppidlOut); }
// This function will do two things that IECreateFromPathCPWithBC() will not do:
// 1. It will add the "Query" section of the URL into the pidl.
// 2. If the URL has a fragment, this function will pass out a PIDL with the last
// ID being the location.
// NOTE: If the caller needs the string to be "cleaned up" because the user manually
// entered the URL, the caller needs to call ParseURLFromOutsideSource() before
// calling this function. That function should only be called on strings entered
// by the user because of the perf hit and it could incorrectly format valid
// parsible display names. For example, ParseURLFromOutsideSource() will
// convert the string "My Computer" into a search URL for yahoo.com
// (http://www.yahoo.com/search.asp?p=My+p=Computer) when some callers
// want that string parsed by an IShellFolder in the desktop.
HRESULT IEParseDisplayNameWithBCW(UINT uiCP, LPCWSTR pwszPath, IBindCtx * pbc, LPITEMIDLIST * ppidlOut) { TCHAR szPath[MAX_URL_STRING]; LPCWSTR pwszFileLocation = NULL; WCHAR szQuery[MAX_URL_STRING]; HRESULT hres;
szQuery[0] = TEXT('\0'); #ifdef DEBUG
if (IsFlagSet(g_dwDumpFlags, DF_URL)) { TraceMsg(DM_TRACE, "IEParseDisplayName got %s", szPath); } #endif
// We want to remove QUERY and FRAGMENT sections of
// FILE URLs because they need to be added in "Hidden" pidls.
// Also, URLs need to be escaped all the time except for paths
// to facility parsing and because we already removed all other
// parts of the URL (Query and Fragment).
if (IsFileUrlW(pwszPath)) { DWORD cchQuery = SIZECHARS(szQuery) - 1; pwszFileLocation = UrlGetLocationW(pwszPath);
if (SUCCEEDED(UrlGetPart(pwszPath, szQuery+1, &cchQuery, URL_PART_QUERY, 0)) && cchQuery) szQuery[0] = TEXT('?');
DWORD cchPath = ARRAYSIZE(szPath); if (FAILED(PathCreateFromUrl(pwszPath, szPath, &cchPath, 0))) { // Failed to parse it back. Use the original.
StrCpyN(szPath, pwszPath, ARRAYSIZE(szPath)); } } else { // If we failed, just try to use the original
StrCpyN(szPath, pwszPath, ARRAYSIZE(szPath)); }
#ifdef DEBUG
if (IsFlagSet(g_dwDumpFlags, DF_URL)) TraceMsg(DM_TRACE, "IEParseDisplayName calling IECreateFromPath %s", szPath); #endif
hres = IECreateFromPathCPWithBC(uiCP, szPath, pbc, ppidlOut); if (SUCCEEDED(hres) && pwszFileLocation) { ASSERT(*ppidlOut); *ppidlOut = IEILAppendFragment(*ppidlOut, pwszFileLocation); hres = *ppidlOut ? S_OK : E_OUTOFMEMORY; }
if (SUCCEEDED(hres) && szQuery[0] == TEXT('?')) { *ppidlOut = ILAppendHiddenString(*ppidlOut, IDLHID_URLQUERY, szQuery); hres = *ppidlOut ? S_OK : E_OUTOFMEMORY; }
return hres; }
|