2771 lines
84 KiB
C++
2771 lines
84 KiB
C++
#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;
|
|
}
|