windows-nt/Source/XPSP1/NT/shell/shell32/sdspatch/sdwindow.cpp
2020-09-26 16:20:57 +08:00

567 lines
16 KiB
C++

#include "precomp.h"
#pragma hdrstop
#include "stdenum.h"
#define DM_SDFOLDER 0
class CShellFolderView : public IShellFolderViewDual2,
public IShellService,
public IServiceProvider,
public CObjectSafety,
public CObjectWithSite,
protected CImpIConnectionPointContainer,
protected CImpIExpDispSupport,
protected CImpIDispatch
{
public:
CShellFolderView(void);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IDispatch
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo)
{ return CImpIDispatch::GetTypeInfoCount(pctinfo); }
STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
{ return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo); }
STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
{ return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); }
STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
// IServiceProvider
STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObj);
// IShellFolderViewDual2
STDMETHODIMP get_Application(IDispatch **ppid);
STDMETHODIMP get_Parent(IDispatch **ppid);
STDMETHODIMP get_Folder(Folder **ppid);
STDMETHODIMP SelectedItems(FolderItems **ppid);
STDMETHODIMP get_FocusedItem(FolderItem **ppid);
STDMETHODIMP SelectItem(VARIANT *pvfi, int dwFlags);
STDMETHODIMP PopupItemMenu(FolderItem * pfi, VARIANT vx, VARIANT vy, BSTR * pbs);
STDMETHODIMP get_Script(IDispatch **ppid);
STDMETHODIMP get_ViewOptions(long *plSetting);
STDMETHODIMP get_CurrentViewMode(UINT *pViewMode);
STDMETHODIMP put_CurrentViewMode(UINT ViewMode);
STDMETHODIMP SelectItemRelative(int iRelative);
// IShellService
STDMETHODIMP SetOwner(IUnknown* punkOwner);
// CImpIConnectionPoint
STDMETHODIMP EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum);
// CObjectWithSite overriding
STDMETHODIMP SetSite(IUnknown *punkSite);
private:
HRESULT _IsSafe()
{
return (0 == _dwSafetyOptions) ? S_OK : IsSafePage(_punkSite);
}
~CShellFolderView(void);
HRESULT _GetFolder();
// CImpIExpDispSupport
virtual CConnectionPoint* _FindCConnectionPointNoRef(BOOL fdisp, REFIID iid);
LONG _cRef;
CFolder *_psdf; // The shell folder we talk to ...
IUnknown *_punkOwner; // back link to defview (owner object of us...)
IShellFolderView *_psfvOwner; // The owners Shell folder view...
HWND _hwnd; // the hwnd for the window...
// Embed our Connection Point object - implmentation in cnctnpt.cpp
CConnectionPoint _cpEvents;
};
// implementation of CLSID_ShellFolderView, class factory is in shell32 (and was in mars, but that project is dead)
STDAPI CShellFolderView_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppvOut)
{
*ppvOut = NULL;
HRESULT hr = E_OUTOFMEMORY;
CShellFolderView* psfv = new CShellFolderView();
if (psfv)
{
hr = psfv->QueryInterface(riid, ppvOut);
psfv->Release();
}
return hr;
}
CShellFolderView::CShellFolderView(void) :
CImpIDispatch(SDSPATCH_TYPELIB, IID_IShellFolderViewDual2), _cRef(1), _psdf(NULL)
{
DllAddRef();
_cpEvents.SetOwner((IUnknown*)SAFECAST(this, IShellFolderViewDual2*), &DIID_DShellFolderViewEvents);
}
CShellFolderView::~CShellFolderView(void)
{
// if we ever grabbed a shell folder for this window release it also
if (_psdf)
{
_psdf->SetSite(NULL);
_psdf->SetOwner(NULL);
_psdf->Release();
}
ASSERT(_punkOwner == NULL);
ASSERT(_psfvOwner == NULL);
ASSERT(_punkSite == NULL);
DllRelease();
}
STDMETHODIMP CShellFolderView::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CShellFolderView, IShellFolderViewDual),
QITABENT(CShellFolderView, IShellFolderViewDual2),
QITABENTMULTI(CShellFolderView, IDispatch, IShellFolderViewDual),
QITABENTMULTI(CShellFolderView, IDispatch, IShellFolderViewDual2),
QITABENT(CShellFolderView, IShellService),
QITABENT(CShellFolderView, IConnectionPointContainer),
QITABENT(CShellFolderView, IExpDispSupport),
QITABENT(CShellFolderView, IObjectSafety),
QITABENT(CShellFolderView, IObjectWithSite),
QITABENT(CShellFolderView, IServiceProvider),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CShellFolderView::AddRef(void)
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CShellFolderView::Release(void)
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
//The ShellWindow implementation
// let folder we have handle this. Probably won't work for webviews as this object
// is not secure...
STDMETHODIMP CShellFolderView::get_Application(IDispatch **ppid)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
HRESULT hres = _GetFolder();
if (SUCCEEDED(hres))
hres = _psdf->get_Application(ppid);
return hres;
}
STDMETHODIMP CShellFolderView::get_Parent(IDispatch **ppid)
{
*ppid = NULL;
return E_FAIL;
}
HRESULT CShellFolderView::_GetFolder()
{
if (_psdf)
return S_OK;
HRESULT hr;
LPITEMIDLIST pidl = NULL;
IShellFolder *psf = NULL;
if (_psfvOwner)
{
IFolderView *pfv;
if (SUCCEEDED(_psfvOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
{
if (SUCCEEDED(pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf))))
{
if (SHGetIDListFromUnk(psf, &pidl) != S_OK)
{
psf->Release();
psf = NULL;
}
}
pfv->Release();
}
if (!pidl)
{
LPCITEMIDLIST pidlT;
// dsheldon - the -42 is a hack that says "give me the folder pidl"
hr = GetObjectSafely(_psfvOwner, &pidlT, (UINT)-42);
if (SUCCEEDED(hr))
{
pidl = ILClone(pidlT);
}
}
}
if (pidl)
{
hr = CFolder_Create2(_hwnd, pidl, psf, &_psdf);
if (SUCCEEDED(hr))
{
_psdf->SetSite(_punkSite);
_psdf->SetOwner(_punkOwner);
if (_dwSafetyOptions)
hr = MakeSafeForScripting((IUnknown**)&_psdf);
}
if (psf)
psf->Release();
ILFree(pidl);
}
else
hr = E_FAIL;
return hr;
}
STDMETHODIMP CShellFolderView::SetSite(IUnknown *punkSite)
{
if (_psdf)
_psdf->SetSite(punkSite);
return CObjectWithSite::SetSite(punkSite);
}
STDMETHODIMP CShellFolderView::get_Folder(Folder **ppid)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
*ppid = NULL;
HRESULT hr = _GetFolder();
if (SUCCEEDED(hr))
hr = _psdf->QueryInterface(IID_PPV_ARG(Folder, ppid));
return hr;
}
STDMETHODIMP CShellFolderView::SelectedItems(FolderItems **ppid)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
HRESULT hr = _GetFolder();
if (SUCCEEDED(hr))
{
hr = CFolderItems_Create(_psdf, TRUE, ppid);
if (SUCCEEDED(hr))
{
IUnknown_SetSite(*ppid, _punkSite);
if (_dwSafetyOptions)
{
hr = MakeSafeForScripting((IUnknown**)ppid);
}
}
}
return hr;
}
// NOTE: this returns an alias pointer, it is not allocated
HRESULT GetObjectSafely(IShellFolderView *psfv, LPCITEMIDLIST *ppidl, UINT iType)
{
// cast needed because GetObject() returns an alias!
HRESULT hr = psfv->GetObject((LPITEMIDLIST *)ppidl, iType);
if (SUCCEEDED(hr))
{
// On the off chance this is coppied across process boundries...
__try
{
// force a full deref this PIDL to generate a fault if cross process
if (ILGetSize(*ppidl) > 0)
hr = S_OK;
// Don't free it as it was not cloned...
}
__except(SetErrorMode(SEM_NOGPFAULTERRORBOX), UnhandledExceptionFilter(GetExceptionInformation()))
{
*ppidl = NULL;
hr = E_FAIL;
}
}
return hr;
}
STDMETHODIMP CShellFolderView::get_FocusedItem(FolderItem **ppid)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
HRESULT hr = _GetFolder();
if (SUCCEEDED(hr))
{
*ppid = NULL;
hr = S_FALSE;
if (_psfvOwner)
{
// Warning:
// It is common for the following function to fail (which means no item has the focus).
// So, do not save the return code from GetObjectSafely() into "hr" that will ruin the
// S_FALSE value already stored there and result in script errors. (Bug #301306)
//
LPCITEMIDLIST pidl;
// dsheldon - the -2 is a hack that says "give me the selected pidl"
if (SUCCEEDED(GetObjectSafely(_psfvOwner, &pidl, (UINT)-2)))
{
hr = CFolderItem_Create(_psdf, pidl, ppid);
if (_dwSafetyOptions && SUCCEEDED(hr))
hr = MakeSafeForScripting((IUnknown**)ppid);
}
}
else
hr = E_FAIL;
}
return hr;
}
STDMETHODIMP CShellFolderView::get_CurrentViewMode(UINT *pViewMode)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
HRESULT hr = S_FALSE;
IFolderView *pfv;
if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
{
hr = pfv->GetCurrentViewMode(pViewMode);
pfv->Release();
}
return hr;
}
STDMETHODIMP CShellFolderView::put_CurrentViewMode(UINT ViewMode)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
HRESULT hr = S_FALSE;
IFolderView *pfv;
if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
{
hr = pfv->SetCurrentViewMode(ViewMode);
pfv->Release();
}
return hr;
}
STDMETHODIMP CShellFolderView::SelectItemRelative(int iRelative)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
HRESULT hr = S_FALSE;
IFolderView *pfv;
if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
{
int iCurrent, cItems;
if (SUCCEEDED(pfv->ItemCount(SVGIO_ALLVIEW, &cItems)) && (cItems > 1) &&
SUCCEEDED(pfv->GetSelectionMarkedItem(&iCurrent)))
{
int iToSelect = iCurrent + iRelative;
if (iToSelect < 0)
iToSelect = 0;
else if (iToSelect > cItems - 1)
iToSelect = cItems - 1;
hr = pfv->SelectItem(iToSelect, SVSI_SELECTIONMARK | SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS);
}
pfv->Release();
}
return hr;
}
// pvfi should be a "FolderItem" IDispatch
STDMETHODIMP CShellFolderView::SelectItem(VARIANT *pvfi, int dwFlags)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
HRESULT hr = E_FAIL;
LPITEMIDLIST pidl = VariantToIDList(pvfi);
// if pvfi is NULL that means all items in the folder...
if (pidl || (pvfi && pvfi->vt == VT_NULL))
{
IShellView *psv; // use this to select the item...
if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
{
// ILFindLastID checks for NULL...
hr = psv->SelectItem(ILFindLastID(pidl), dwFlags);
psv->Release();
}
ILFree(pidl);
}
return hr;
}
STDMETHODIMP CShellFolderView::PopupItemMenu(FolderItem *pfi, VARIANT vx, VARIANT vy, BSTR * pbs)
{
return E_NOTIMPL;
}
STDMETHODIMP CShellFolderView::get_Script(IDispatch **ppid)
{
*ppid = NULL; // Say that we got nothing...
if (!_punkOwner)
return S_FALSE;
IShellView *psv;
HRESULT hres = _punkOwner->QueryInterface(IID_PPV_ARG(IShellView, &psv));
if (SUCCEEDED(hres))
{
// lets see if there is a IHTMLDocument that is below us now...
IHTMLDocument *phtmld;
hres = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IHTMLDocument, &phtmld));
if (SUCCEEDED(hres))
{
if (_dwSafetyOptions)
hres = MakeSafeForScripting((IUnknown **)&phtmld);
if (SUCCEEDED(hres))
{
hres = phtmld->get_Script(ppid);
}
phtmld->Release();
}
psv->Release();
}
return hres;
}
// Helper function that can be used to get the current view options...
// (ripped out of dvoc.cpp)
DWORD GetViewOptionsForDispatch()
{
SHELLSTATE ss = {0};
DWORD dwSetting = 0;
// Get the view options to return...
SHGetSetSettings(&ss,
SSF_SHOWALLOBJECTS|SSF_SHOWEXTENSIONS|SSF_SHOWCOMPCOLOR|
SSF_SHOWSYSFILES|SSF_DOUBLECLICKINWEBVIEW|SSF_DESKTOPHTML|SSF_WIN95CLASSIC,
FALSE);
// Aarg: mnuch the Bool:1 fields into a dword...
if (ss.fShowAllObjects) dwSetting |= SFVVO_SHOWALLOBJECTS;
if (ss.fShowExtensions) dwSetting |= SFVVO_SHOWEXTENSIONS;
if (ss.fShowCompColor) dwSetting |= SFVVO_SHOWCOMPCOLOR;
if (ss.fShowSysFiles) dwSetting |= SFVVO_SHOWSYSFILES;
if (ss.fDoubleClickInWebView) dwSetting |= SFVVO_DOUBLECLICKINWEBVIEW;
if (ss.fDesktopHTML) dwSetting |= SFVVO_DESKTOPHTML;
if (ss.fWin95Classic) dwSetting |= SFVVO_WIN95CLASSIC;
return dwSetting;
}
STDMETHODIMP CShellFolderView::get_ViewOptions(long *plSetting)
{
if (S_OK != _IsSafe())
return E_ACCESSDENIED;
*plSetting = (LONG)GetViewOptionsForDispatch();
return S_OK;
}
STDMETHODIMP CShellFolderView::SetOwner(IUnknown* punkOwner)
{
if (_psdf)
_psdf->SetOwner(punkOwner);
IUnknown_Set((IUnknown **)&_psfvOwner, NULL); // free cached interface
IUnknown_Set(&_punkOwner, punkOwner); // hold this guy
if (_punkOwner)
{
_punkOwner->QueryInterface(IID_PPV_ARG(IShellFolderView, &_psfvOwner));
if (!_hwnd)
{
IShellView *psv;
// this is gross, until we can merge the two models, create one of our
// Window objects.
if (SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
{
HWND hwndFldr;
psv->GetWindow(&hwndFldr);
// Really gross, but assume parent HWND is the HWND we are after...
_hwnd = GetParent(hwndFldr);
psv->Release();
}
}
}
return S_OK;
}
STDMETHODIMP CShellFolderView::EnumConnectionPoints(IEnumConnectionPoints **ppEnum)
{
return CreateInstance_IEnumConnectionPoints(ppEnum, 1, _cpEvents.CastToIConnectionPoint());
}
CConnectionPoint* CShellFolderView::_FindCConnectionPointNoRef(BOOL fdisp, REFIID iid)
{
if (IsEqualIID(iid, DIID_DShellFolderViewEvents) || (fdisp && IsEqualIID(iid, IID_IDispatch)))
return &_cpEvents;
return NULL;
}
HRESULT CShellFolderView::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
{
HRESULT hr;
if (dispidMember == DISPID_READYSTATE)
return DISP_E_MEMBERNOTFOUND; // perf: what typeinfo would return.
if (dispidMember == DISPID_WINDOWOBJECT)
{
IDispatch *pdisp;
if (SUCCEEDED(get_Script(&pdisp)))
{
hr = pdisp->Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
pdisp->Release();
}
else
{
hr = DISP_E_MEMBERNOTFOUND;
}
}
else
hr = CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
return hr;
}
HRESULT CShellFolderView::QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
HRESULT hr = IUnknown_QueryService(_punkOwner, guidService, riid, ppv);
if (FAILED(hr))
hr = IUnknown_QueryService(_punkSite, guidService, riid, ppv);
return hr;
}