Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1147 lines
29 KiB

#include "precomp.h"
#pragma hdrstop
// BUGBUG: from shell32\prop.cpp
STDAPI_(BOOL) ParseSCIDString(LPCTSTR pszString, SHCOLUMNID *pscid, UINT *pidRes);
#define TF_SHELLAUTO TF_CUSTOM1
#define DEFINE_FLOAT_STUFF // Do this because DATE is being used below
class CFolderItemVerbs;
class CEnumFolderItemVerbs;
class CFolderItemVerb;
class CFolderItemVerbs : public FolderItemVerbs,
public CObjectSafety,
protected CImpIDispatch,
protected CObjectWithSite
{
friend class CEnumFolderItemVerbs;
friend class CFolderItemVerb;
public:
CFolderItemVerbs(IContextMenu *pcm);
BOOL Init(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)
{ return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); }
// FolderItemVerbs
STDMETHODIMP get_Application(IDispatch **ppid);
STDMETHODIMP get_Parent(IDispatch **ppid);
STDMETHODIMP get_Count(long *plCount);
STDMETHODIMP Item(VARIANT, FolderItemVerb**);
STDMETHODIMP _NewEnum(IUnknown **);
private:
~CFolderItemVerbs(void);
HRESULT _SecurityCheck();
LONG _cRef;
HMENU _hmenu;
IContextMenu *_pcm;
};
class CEnumFolderItemVerbs : public IEnumVARIANT,
public CObjectSafety
{
public:
CEnumFolderItemVerbs(CFolderItemVerbs *psdfiv);
BOOL Init();
// IUnknown
STDMETHODIMP QueryInterface(REFIID, void **);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IEnumFORMATETC
STDMETHODIMP Next(ULONG, VARIANT *, ULONG *);
STDMETHODIMP Skip(ULONG);
STDMETHODIMP Reset(void);
STDMETHODIMP Clone(IEnumVARIANT **);
private:
~CEnumFolderItemVerbs();
LONG _cRef;
int _iCur;
CFolderItemVerbs *_psdfiv;
};
HRESULT CFolderItemVerb_Create(CFolderItemVerbs *psdfivs, UINT id, FolderItemVerb **pfiv);
class CFolderItemVerb :
public FolderItemVerb,
public CObjectSafety,
protected CImpIDispatch
{
friend class CFolderItemVerbs;
public:
CFolderItemVerb(CFolderItemVerbs *psdfivs, UINT id);
// 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)
{ return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); }
// FolderItemVerbs
STDMETHODIMP get_Application(IDispatch **ppid);
STDMETHODIMP get_Parent(IDispatch **ppid);
STDMETHODIMP get_Name(BSTR *pbs);
STDMETHODIMP DoIt();
private:
~CFolderItemVerb(void);
LONG _cRef;
CFolderItemVerbs *_psdfivs;
UINT _id;
};
// in:
// psdf folder that contains pidl
// pidl pidl retlative to psdf (the item in this folder)
HRESULT CFolderItem_Create(CFolder *psdf, LPCITEMIDLIST pidl, FolderItem **ppid)
{
*ppid = NULL;
HRESULT hr = E_OUTOFMEMORY;
CFolderItem* psdfi = new CFolderItem();
if (psdfi)
{
hr = psdfi->Init(psdf, pidl);
if (SUCCEEDED(hr))
hr = psdfi->QueryInterface(IID_PPV_ARG(FolderItem, ppid));
psdfi->Release();
}
return hr;
}
STDAPI CFolderItem_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
{
HRESULT hr = E_OUTOFMEMORY;
*ppv = NULL;
CFolderItem *pfi = new CFolderItem();
if (pfi)
{
hr = pfi->QueryInterface(riid, ppv);
pfi->Release();
}
return hr;
}
// in:
// pidl fully qualified pidl
// hwnd hacks
HRESULT CFolderItem_CreateFromIDList(HWND hwnd, LPCITEMIDLIST pidl, FolderItem **ppfi)
{
LPITEMIDLIST pidlParent;
HRESULT hr = SHILClone(pidl, &pidlParent);
if (SUCCEEDED(hr))
{
ILRemoveLastID(pidlParent);
CFolder *psdf;
hr = CFolder_Create2(hwnd, pidlParent, NULL, &psdf);
if (SUCCEEDED(hr))
{
hr = CFolderItem_Create(psdf, ILFindLastID(pidl), ppfi);
psdf->Release();
}
ILFree(pidlParent);
}
return hr;
}
CFolderItem::CFolderItem() :
_cRef(1), _psdf(NULL), _pidl(NULL),
CImpIDispatch(SDSPATCH_TYPELIB, IID_FolderItem2)
{
DllAddRef();
}
CFolderItem::~CFolderItem(void)
{
if (_pidl)
ILFree(_pidl);
if (_psdf)
_psdf->Release();
DllRelease();
}
HRESULT GetItemFolder(CFolder *psdfRoot, LPCITEMIDLIST pidl, CFolder **ppsdf)
{
HRESULT hr = S_OK;
ASSERT(psdfRoot);
if (ILFindLastID(pidl) != pidl)
{
LPITEMIDLIST pidlIn = ILClone(pidl);
hr = E_OUTOFMEMORY;
if (pidlIn && ILRemoveLastID(pidlIn))
{
LPITEMIDLIST pidlParent;
LPITEMIDLIST pidlFolder = NULL;
hr = psdfRoot->GetCurFolder(&pidlFolder);
if (SUCCEEDED(hr))
{
pidlParent = ILCombine(pidlFolder, pidlIn);
if (pidlParent)
{
hr = CFolder_Create2(NULL, pidlParent, NULL, ppsdf);
ILFree(pidlParent);
}
ILFree(pidlFolder);
}
}
ILFree(pidlIn);// ilfree does null check
}
else
{
*ppsdf = psdfRoot;
psdfRoot->AddRef();
}
return hr;
}
HRESULT CFolderItem::Init(CFolder *psdf, LPCITEMIDLIST pidl)
{
HRESULT hr = GetItemFolder(psdf, pidl, &_psdf);
if (SUCCEEDED(hr))
{
hr = SHILClone(ILFindLastID(pidl), &_pidl);
}
return hr;
}
STDMETHODIMP CFolderItem::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CFolderItem, FolderItem2),
QITABENTMULTI(CFolderItem, FolderItem, FolderItem2),
QITABENTMULTI(CFolderItem, IDispatch, FolderItem2),
QITABENTMULTI(CFolderItem, IPersist, IPersistFolder2),
QITABENTMULTI(CFolderItem, IPersistFolder, IPersistFolder2),
QITABENT(CFolderItem, IPersistFolder2),
QITABENT(CFolderItem, IObjectSafety),
QITABENT(CFolderItem, IParentAndItem),
{ 0 },
};
HRESULT hr = QISearch(this, qit, riid, ppv);
if (FAILED(hr) && IsEqualGUID(CLSID_ShellFolderItem, riid))
{
*ppv = (CFolderItem *)this; // unrefed
hr = S_OK;
}
return hr;
}
STDMETHODIMP_(ULONG) CFolderItem::AddRef(void)
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CFolderItem::Release(void)
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
//The FolderItem implementation
STDMETHODIMP CFolderItem::get_Application(IDispatch **ppid)
{
// Let the folder object handle security and reference counting of site, etc
return _psdf->get_Application(ppid);
}
STDMETHODIMP CFolderItem::get_Parent(IDispatch **ppid)
{
// Assume that the Folder object is the parent of this object...
HRESULT hr = _psdf->QueryInterface(IID_PPV_ARG(IDispatch, ppid));
if (SUCCEEDED(hr) && _dwSafetyOptions)
hr = MakeSafeForScripting((IUnknown**)ppid);
return hr;
}
HRESULT CFolderItem::_ItemName(UINT dwFlags, BSTR *pbs)
{
HRESULT hr;
STRRET strret;
if (SUCCEEDED(_psdf->_psf->GetDisplayNameOf(_pidl, dwFlags, &strret)))
{
hr = StrRetToBSTR(&strret, _pidl, pbs);
}
else
{
*pbs = SysAllocString(L"");
hr = (*pbs) ? S_FALSE : E_OUTOFMEMORY;
}
return hr;
}
STDMETHODIMP CFolderItem::get_Name(BSTR *pbs)
{
*pbs = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = _ItemName(SHGDN_INFOLDER, pbs);
}
return hr;
}
STDMETHODIMP CFolderItem::put_Name(BSTR bs)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidlOut;
hr = _psdf->_psf->SetNameOf(_psdf->_hwnd, _pidl, bs, SHGDN_INFOLDER, &pidlOut);
if (SUCCEEDED(hr))
{
ILFree(_pidl);
_pidl = pidlOut;
}
}
return hr;
}
// BUGBUG: this should validate that the path is a file system
// object (SFGAO_FILESYSTEM)
STDMETHODIMP CFolderItem::get_Path(BSTR *pbs)
{
*pbs = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = _ItemName(SHGDN_FORPARSING, pbs);
}
return hr;
}
STDMETHODIMP CFolderItem::get_GetLink(IDispatch **ppid)
{
*ppid = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = CShortcut_CreateIDispatch(_psdf->_hwnd, _psdf->_psf, _pidl, ppid);
if (SUCCEEDED(hr) && _dwSafetyOptions)
{
hr = MakeSafeForScripting((IUnknown**)ppid);
}
}
return hr;
}
// BUGBUG:: this should return Folder **...
STDMETHODIMP CFolderItem::get_GetFolder(IDispatch **ppid /* Folder **ppf */)
{
*ppid = NULL;
// If in Safe mode we fail this one...
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidl;
hr = SHILCombine(_psdf->_pidl, _pidl, &pidl);
if (SUCCEEDED(hr))
{
hr = CFolder_Create(NULL, pidl, NULL, IID_PPV_ARG(IDispatch, ppid));
if (SUCCEEDED(hr))
{
if (_dwSafetyOptions)
{
// note, this is created without a site so script safety checks will fail
hr = MakeSafeForScripting((IUnknown**)ppid);
}
}
ILFree(pidl);
}
}
return hr;
}
HRESULT CFolderItem::_CheckAttribute(ULONG ulAttrIn, VARIANT_BOOL * pb)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
ULONG ulAttr = ulAttrIn;
hr = _psdf->_psf->GetAttributesOf(1, (LPCITEMIDLIST*)&_pidl, &ulAttr);
*pb = (SUCCEEDED(hr) && (ulAttr & ulAttrIn)) ? VARIANT_TRUE : VARIANT_FALSE;
}
return hr;
}
HRESULT CFolderItem::_GetUIObjectOf(REFIID riid, void **ppv)
{
return _psdf->_psf->GetUIObjectOf(_psdf->_hwnd, 1, (LPCITEMIDLIST*)&_pidl, riid, NULL, ppv);
}
HRESULT CFolderItem::_SecurityCheck()
{
if (!_dwSafetyOptions)
return S_OK;
return _psdf->_SecurityCheck();
}
// allow friend classes to get the IDList for a CFolderItem automation object
LPCITEMIDLIST CFolderItem::_GetIDListFromVariant(const VARIANT *pv)
{
LPCITEMIDLIST pidl = NULL;
if (pv)
{
VARIANT v;
if (pv->vt == (VT_BYREF | VT_VARIANT) && pv->pvarVal)
v = *pv->pvarVal;
else
v = *pv;
switch (v.vt)
{
case VT_DISPATCH | VT_BYREF:
if (v.ppdispVal == NULL)
break;
v.pdispVal = *v.ppdispVal;
// fall through...
case VT_DISPATCH:
CFolderItem *pfi;
if (v.pdispVal && SUCCEEDED(v.pdispVal->QueryInterface(CLSID_ShellFolderItem, (void **)&pfi)))
{
pidl = pfi->_pidl; // alias
}
break;
}
}
return pidl;
}
STDMETHODIMP CFolderItem::get_IsLink(VARIANT_BOOL * pb)
{
return _CheckAttribute(SFGAO_LINK, pb);
}
STDMETHODIMP CFolderItem::get_IsFolder(VARIANT_BOOL * pb)
{
return _CheckAttribute(SFGAO_FOLDER, pb);
}
STDMETHODIMP CFolderItem::get_IsFileSystem(VARIANT_BOOL * pb)
{
return _CheckAttribute(SFGAO_FILESYSTEM, pb);
}
STDMETHODIMP CFolderItem::get_IsBrowsable(VARIANT_BOOL * pb)
{
return _CheckAttribute(SFGAO_BROWSABLE, pb);
}
STDMETHODIMP CFolderItem::get_ModifyDate(DATE *pdt)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
WIN32_FIND_DATA finddata;
if (SUCCEEDED(SHGetDataFromIDList(_psdf->_psf, _pidl, SHGDFIL_FINDDATA, &finddata, sizeof(finddata))))
{
WORD wDosDate, wDosTime;
FILETIME filetime;
FileTimeToLocalFileTime(&finddata.ftLastWriteTime, &filetime);
FileTimeToDosDateTime(&filetime, &wDosDate, &wDosTime);
DosDateTimeToVariantTime(wDosDate, wDosTime, pdt);
}
hr = S_OK;
}
return hr;
}
STDMETHODIMP CFolderItem::put_ModifyDate(DATE dt)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = S_FALSE;
SYSTEMTIME st;
FILETIME ftLocal, ft;
BSTR bstrPath;
if (SUCCEEDED(VariantTimeToSystemTime(dt, &st)) && SystemTimeToFileTime(&st, &ftLocal)
&& LocalFileTimeToFileTime(&ftLocal, &ft) && SUCCEEDED(get_Path(&bstrPath)))
{
TCHAR szPath[MAX_PATH];
SHUnicodeToTChar(bstrPath, szPath, ARRAYSIZE(szPath));
SysFreeString(bstrPath);
HANDLE hFile = CreateFile(szPath, GENERIC_READ | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OPEN_NO_RECALL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
if (SetFileTime(hFile, NULL, NULL, &ft))
{
hr = S_OK;
}
CloseHandle(hFile);
}
}
}
return hr;
}
// BUGBUG:: Should see about getting larger numbers through
STDMETHODIMP CFolderItem::get_Size(LONG *pul)
{
HRESULT hr =_SecurityCheck();
if (SUCCEEDED(hr))
{
WIN32_FIND_DATA finddata;
if (SUCCEEDED(SHGetDataFromIDList(_psdf->_psf, _pidl, SHGDFIL_FINDDATA, &finddata, sizeof(finddata))))
{
*pul = (LONG)finddata.nFileSizeLow;
}
else
{
*pul = 0L;
}
hr = S_OK; // Scripts don't like error return values
}
return hr;
}
STDMETHODIMP CFolderItem::get_Type(BSTR *pbs)
{
*pbs = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_EMPTY;
if (SUCCEEDED(ExtendedProperty(L"Type", &var)) && (var.vt == VT_BSTR))
{
*pbs = SysAllocString(var.bstrVal);
}
else
{
*pbs = SysAllocString(L"");
}
VariantClear(&var);
hr = *pbs ? S_OK : E_OUTOFMEMORY;
}
return hr;
}
STDMETHODIMP CFolderItem::ExtendedProperty(BSTR bstrPropName, VARIANT *pvRet)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = E_FAIL;
VariantInit(pvRet);
// currently, MCNL is 80, guidstr is 39, and 6 is the width of an int
if (StrCmpIW(bstrPropName, L"infotip") == 0)
{
// They want the info tip for the item.
if (_pidl && _psdf)
{
TCHAR szInfo[INFOTIPSIZE];
GetInfoTipHelp(_psdf->_psf, _pidl, szInfo, ARRAYSIZE(szInfo));
hr = InitVariantFromStr(pvRet, szInfo);
}
}
else if (_psdf->_psf2)
{
SHCOLUMNID scid;
TCHAR szTemp[128];
SHUnicodeToTChar(bstrPropName, szTemp, ARRAYSIZE(szTemp));
if (ParseSCIDString(szTemp, &scid, NULL))
{
// Note that GetDetailsEx expects an absolute pidl
hr = _psdf->_psf2->GetDetailsEx(_pidl, &scid, pvRet);
}
}
hr = FAILED(hr) ? S_FALSE : hr; // Java scripts barf on error values
}
return hr;
}
STDMETHODIMP CFolderItem::Verbs(FolderItemVerbs **ppfic)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = S_FALSE;
IContextMenu *pcm;
if (SUCCEEDED(_GetUIObjectOf(IID_PPV_ARG(IContextMenu, &pcm))))
{
hr = CFolderItemVerbs_Create(pcm, ppfic);
if (SUCCEEDED(hr) && _dwSafetyOptions)
{
hr = MakeSafeForScripting((IUnknown**)ppfic);
if (SUCCEEDED(hr))
{
// Set the folder's site to FolderItemVerbs
IUnknown_SetSite(*ppfic, _psdf->_punkSite);
}
}
pcm->Release();
}
}
return hr;
}
STDMETHODIMP CFolderItem::InvokeVerbEx(VARIANT vVerb, VARIANT vArgs)
{
// security check handled by _psdf
return _psdf->InvokeVerbHelper(vVerb, vArgs, (LPCITEMIDLIST *)&_pidl, 1, _dwSafetyOptions);
}
STDMETHODIMP CFolderItem::InvokeVerb(VARIANT vVerb)
{
VARIANT vtEmpty = {VT_EMPTY};
return InvokeVerbEx(vVerb, vtEmpty);
}
STDMETHODIMP CFolderItem::GetClassID(CLSID *pClassID)
{
*pClassID = CLSID_ShellFolderItem;
return E_NOTIMPL;
}
// IPersistFolder
// note, this duplicates some of CFolderItem::Init() functionality
STDMETHODIMP CFolderItem::Initialize(LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlParent;
HRESULT hr = SHILClone(pidl, &pidlParent);
if (SUCCEEDED(hr))
{
ASSERT(_pidl == NULL);
hr = SHILClone(ILFindLastID(pidlParent), &_pidl);
if (SUCCEEDED(hr))
{
// Chop off the filename and get the folder that contains
// this FolderItem.
ILRemoveLastID(pidlParent);
// BUGBUG: we should find some way to get a valid hwnd to
// pass to CFolder_Create2 instead of NULL.
ASSERT(_psdf == NULL);
hr = CFolder_Create2(NULL, pidlParent, NULL, &_psdf);
}
ILFree(pidlParent);
}
return hr;
}
STDMETHODIMP CFolderItem::GetCurFolder(LPITEMIDLIST *ppidl)
{
LPITEMIDLIST pidlFolder;
HRESULT hr = SHGetIDListFromUnk(_psdf->_psf, &pidlFolder);
if (S_OK == hr)
{
hr = SHILCombine(pidlFolder, _pidl, ppidl);
ILFree(pidlFolder);
}
else
hr = E_FAIL;
return hr;
}
// IParentAndItem
STDMETHODIMP CFolderItem::SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidl)
{
return E_NOTIMPL;
}
STDMETHODIMP CFolderItem::GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidl)
{
if (ppidlParent)
{
*ppidlParent = _psdf->_pidl ? ILClone(_psdf->_pidl) : NULL;
}
if (ppsf)
{
*ppsf = NULL;
if (_psdf && _psdf->_psf)
_psdf->_psf->QueryInterface(IID_PPV_ARG(IShellFolder, ppsf));
}
if (ppidl)
*ppidl = ILClone(_pidl);
if ((ppidlParent && !*ppidlParent)
|| (ppsf && !*ppsf)
|| (ppidl && !*ppidl))
{
// this is failure
// but we dont know what failed
if (ppsf && *ppsf)
(*ppsf)->Release();
if (ppidlParent)
ILFree(*ppidlParent);
if (ppidl)
ILFree(*ppidl);
return E_OUTOFMEMORY;
}
return S_OK;
}
HRESULT CFolderItemVerbs_Create(IContextMenu *pcm, FolderItemVerbs ** ppid)
{
*ppid = NULL;
HRESULT hr = E_OUTOFMEMORY;
CFolderItemVerbs* pfiv = new CFolderItemVerbs(pcm);
if (pfiv)
{
if (pfiv->Init())
hr = pfiv->QueryInterface(IID_PPV_ARG(FolderItemVerbs, ppid));
pfiv->Release();
}
return hr;
}
CFolderItemVerbs::CFolderItemVerbs(IContextMenu *pcm) :
_cRef(1), _hmenu(NULL),
_pcm(pcm), CImpIDispatch(SDSPATCH_TYPELIB, IID_FolderItemVerbs)
{
DllAddRef();
_pcm->AddRef();
}
CFolderItemVerbs::~CFolderItemVerbs(void)
{
DllRelease();
if (_pcm)
_pcm->Release();
if (_hmenu)
DestroyMenu(_hmenu);
}
BOOL CFolderItemVerbs::Init()
{
TraceMsg(TF_SHELLAUTO, "CFolderItemVerbs::Init called");
// Start of only doing default verb...
if (_pcm)
{
_hmenu = CreatePopupMenu();
if (FAILED(_pcm->QueryContextMenu(_hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, CMF_CANRENAME|CMF_NORMAL)))
return FALSE;
}
else
return FALSE;
// Just for the heck of it, remove junk like sepearators from the menu...
for (int i = GetMenuItemCount(_hmenu) - 1; i >= 0; i--)
{
MENUITEMINFO mii;
TCHAR szText[80]; // should be big enough for this
mii.cbSize = sizeof(MENUITEMINFO);
mii.dwTypeData = szText;
mii.fMask = MIIM_TYPE | MIIM_ID;
mii.cch = ARRAYSIZE(szText);
mii.fType = MFT_SEPARATOR; // to avoid ramdom result.
mii.dwItemData = 0;
GetMenuItemInfo(_hmenu, i, TRUE, &mii);
if (mii.fType & MFT_SEPARATOR)
DeleteMenu(_hmenu, i, MF_BYPOSITION);
}
return TRUE;
}
STDMETHODIMP CFolderItemVerbs::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CFolderItemVerbs, FolderItemVerbs),
QITABENT(CFolderItemVerbs, IObjectSafety),
QITABENTMULTI(CFolderItemVerbs, IDispatch, FolderItemVerbs),
QITABENT(CFolderItemVerbs, IObjectWithSite),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CFolderItemVerbs::AddRef(void)
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CFolderItemVerbs::Release(void)
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CFolderItemVerbs::get_Application(IDispatch **ppid)
{
*ppid = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CFolderItemVerbs::get_Parent(IDispatch **ppid)
{
*ppid = NULL;
return E_NOTIMPL;
}
HRESULT CFolderItemVerbs::_SecurityCheck()
{
return (!_dwSafetyOptions || (IsSafePage(_punkSite) == S_OK)) ? S_OK : E_ACCESSDENIED;
}
STDMETHODIMP CFolderItemVerbs::get_Count(long *plCount)
{
*plCount = 0;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
*plCount = GetMenuItemCount(_hmenu);
}
return hr;
}
STDMETHODIMP CFolderItemVerbs::Item(VARIANT index, FolderItemVerb **ppid)
{
*ppid = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
// This is sortof gross, but if we are passed a pointer to another variant, simply
// update our copy here...
if (index.vt == (VT_BYREF | VT_VARIANT) && index.pvarVal)
index = *index.pvarVal;
switch (index.vt)
{
case VT_ERROR:
QueryInterface(IID_PPV_ARG(FolderItemVerb, ppid));
break;
case VT_I2:
index.lVal = (long)index.iVal;
// And fall through...
case VT_I4:
if ((index.lVal >= 0) && (index.lVal <= GetMenuItemCount(_hmenu)))
{
CFolderItemVerb_Create(this, GetMenuItemID(_hmenu, index.lVal), ppid);
}
break;
default:
hr = E_NOTIMPL;
}
if (*ppid && _dwSafetyOptions)
{
hr = MakeSafeForScripting((IUnknown**)ppid);
}
else if (hr != E_NOTIMPL)
{
hr = S_OK;
}
}
return hr;
}
STDMETHODIMP CFolderItemVerbs::_NewEnum(IUnknown **ppunk)
{
*ppunk = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = E_OUTOFMEMORY;
CEnumFolderItemVerbs *pNew = new CEnumFolderItemVerbs(SAFECAST(this, CFolderItemVerbs*));
if (pNew)
{
if (pNew->Init())
hr = pNew->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
pNew->Release();
}
if (SUCCEEDED(hr) && _dwSafetyOptions)
hr = MakeSafeForScripting(ppunk);
}
return hr;
}
CEnumFolderItemVerbs::CEnumFolderItemVerbs(CFolderItemVerbs *pfiv) :
_cRef(1), _iCur(0), _psdfiv(pfiv)
{
_psdfiv->AddRef();
DllAddRef();
}
CEnumFolderItemVerbs::~CEnumFolderItemVerbs(void)
{
DllRelease();
_psdfiv->Release();
}
BOOL CEnumFolderItemVerbs::Init()
{
return TRUE; // Currently no initialization needed
}
STDMETHODIMP CEnumFolderItemVerbs::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CEnumFolderItemVerbs, IEnumVARIANT),
QITABENT(CEnumFolderItemVerbs, IObjectSafety),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CEnumFolderItemVerbs::AddRef(void)
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CEnumFolderItemVerbs::Release(void)
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CEnumFolderItemVerbs::Next(ULONG cVar, VARIANT *pVar, ULONG *pulVar)
{
ULONG cReturn = 0;
HRESULT hr;
if (!pulVar)
{
if (cVar != 1)
return E_POINTER;
}
else
*pulVar = 0;
if (!pVar || _iCur >= GetMenuItemCount(_psdfiv->_hmenu))
return S_FALSE;
while (_iCur < GetMenuItemCount(_psdfiv->_hmenu) && cVar > 0)
{
FolderItemVerb *pidv;
hr = CFolderItemVerb_Create(_psdfiv, GetMenuItemID(_psdfiv->_hmenu, _iCur), &pidv);
if (SUCCEEDED(hr) && _dwSafetyOptions)
hr = MakeSafeForScripting((IUnknown**)&pidv);
_iCur++;
if (SUCCEEDED(hr))
{
pVar->pdispVal = pidv;
pVar->vt = VT_DISPATCH;
pVar++;
cReturn++;
cVar--;
}
}
if (pulVar)
*pulVar = cReturn;
return S_OK;
}
STDMETHODIMP CEnumFolderItemVerbs::Skip(ULONG cSkip)
{
if ((int)(_iCur + cSkip) >= GetMenuItemCount(_psdfiv->_hmenu))
return S_FALSE;
_iCur += cSkip;
return S_OK;
}
STDMETHODIMP CEnumFolderItemVerbs::Reset(void)
{
_iCur = 0;
return S_OK;
}
STDMETHODIMP CEnumFolderItemVerbs::Clone(IEnumVARIANT **ppEnum)
{
*ppEnum = NULL;
HRESULT hr = E_OUTOFMEMORY;
CEnumFolderItemVerbs *pNew = new CEnumFolderItemVerbs(_psdfiv);
if (pNew)
{
if (pNew->Init())
hr = pNew->QueryInterface(IID_PPV_ARG(IEnumVARIANT, ppEnum));
pNew->Release();
}
if (SUCCEEDED(hr) && _dwSafetyOptions)
hr = MakeSafeForScripting((IUnknown**)ppEnum);
return hr;
}
HRESULT CFolderItemVerb_Create(CFolderItemVerbs *psdfivs, UINT id, FolderItemVerb **ppid)
{
*ppid = NULL;
HRESULT hr = E_OUTOFMEMORY;
CFolderItemVerb* psdfiv = new CFolderItemVerb(psdfivs, id);
if (psdfiv)
{
hr = psdfiv->QueryInterface(IID_PPV_ARG(FolderItemVerb, ppid));
psdfiv->Release();
}
return hr;
}
CFolderItemVerb::CFolderItemVerb(CFolderItemVerbs *psdfivs, UINT id) :
_cRef(1), _psdfivs(psdfivs), _id(id),
CImpIDispatch(SDSPATCH_TYPELIB, IID_FolderItemVerb)
{
_psdfivs->AddRef();
DllAddRef();
}
CFolderItemVerb::~CFolderItemVerb(void)
{
DllRelease();
_psdfivs->Release();
}
STDMETHODIMP CFolderItemVerb::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CFolderItemVerb, FolderItemVerb),
QITABENT(CFolderItemVerb, IObjectSafety),
QITABENTMULTI(CFolderItemVerb, IDispatch, FolderItemVerb),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CFolderItemVerb::AddRef(void)
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CFolderItemVerb::Release(void)
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CFolderItemVerb::get_Application(IDispatch **ppid)
{
// _psdfivs returns E_NOTIMPL
return _psdfivs->get_Application(ppid);
}
STDMETHODIMP CFolderItemVerb::get_Parent(IDispatch **ppid)
{
*ppid = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CFolderItemVerb::get_Name(BSTR *pbs)
{
TCHAR szMenuText[MAX_PATH];
// Warning: did not check security here as could not get here if unsafe...
GetMenuString(_psdfivs->_hmenu, _id, szMenuText, ARRAYSIZE(szMenuText), MF_BYCOMMAND);
*pbs = SysAllocStringT(szMenuText);
return *pbs ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP CFolderItemVerb::DoIt()
{
CMINVOKECOMMANDINFO ici = {
sizeof(CMINVOKECOMMANDINFO),
0L,
NULL,
NULL,
NULL, NULL,
SW_SHOWNORMAL,
};
// Warning: did not check security here as could not get here if unsafe...
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(_id - CONTEXTMENU_IDCMD_FIRST);
return _psdfivs->_pcm->InvokeCommand(&ici);
}