#include "shellprv.h" #include "enumuicommand.h" #include "datautil.h" HRESULT CWVTASKITEM::_get_String(const WVTASKITEM* pTask, DWORD dwIndex, LPWSTR * ppsz, DWORD cchMin, BOOL bIsIcon) { HRESULT hr; DWORD cchIcon = (unsigned)(lstrlen(pTask->pszDllName) + 9); // 9 = comma + minus + 2*65535 + \0 DWORD cch = bIsIcon ? cchIcon // "DLL,-0" string format required for loading icons from DLL resource : max(cchIcon + 1, cchMin); // "@DLL,-0" string format required for loading strings from DLL resource LPWSTR psz = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR)); if (psz) { if (bIsIcon) { wnsprintf(psz, cch, L"%s,-%u", pTask->pszDllName, dwIndex); hr = S_OK; } else { wnsprintf(psz, cch, L"@%s,-%u", pTask->pszDllName, dwIndex); hr = SHLoadIndirectString(psz, psz, cch, NULL); } } else { hr = E_OUTOFMEMORY; } *ppsz = psz; return hr; } #define SS_UNKNOWN 0 #define SS_NOTSUPPORTED 1 #define SS_NONE 2 #define SS_FILE 3 #define SS_FOLDER 4 #define SS_MULTI 5 DWORD CWVTASKITEM::_GetSelectionState(const WVTASKITEM* pTask, IShellItemArray *psiItemArray) { DWORD dwSelectionState; DWORD cItems = 0; if (psiItemArray) { if (FAILED(psiItemArray->GetCount(&cItems))) { cItems = 0; } } switch (cItems) { case 0: dwSelectionState = SS_NONE; break; case 1: { DWORD dwAttribs = 0; if (psiItemArray) { if (FAILED(psiItemArray->GetAttributes(SIATTRIBFLAGS_AND, SFGAO_FOLDER|SFGAO_STREAM,&dwAttribs))) { dwAttribs = 0; } } switch (dwAttribs) { case SFGAO_FOLDER: dwSelectionState = SS_FOLDER; break; case SFGAO_FOLDER|SFGAO_STREAM: // zip and cab files are the only things that get here. // we'll call them files unless somebody has a better idea // (SS_MULTI has plurality that sounds funny). // fall through default: dwSelectionState = SS_FILE; break; } } break; default: dwSelectionState = SS_MULTI; break; } if ((SS_NONE == dwSelectionState && 0 == pTask->dwTitleIndexNoSelection) || (SS_FILE == dwSelectionState && 0 == pTask->dwTitleIndexFileSelected) || (SS_FOLDER == dwSelectionState && 0 == pTask->dwTitleIndexFolderSelected) || (SS_MULTI == dwSelectionState && 0 == pTask->dwTitleIndexMultiSelected)) { dwSelectionState = SS_NOTSUPPORTED; } return dwSelectionState; } HRESULT CWVTASKITEM::get_Name(const WVTASKITEM* pTask, IShellItemArray *psiItemArray, LPWSTR *ppszName) { DWORD dwSelState = _GetSelectionState(pTask, psiItemArray); switch (dwSelState) { case SS_NONE: return _get_String(pTask, pTask->dwTitleIndexNoSelection, ppszName, MAX_PATH, FALSE); case SS_FILE: return _get_String(pTask, pTask->dwTitleIndexFileSelected, ppszName, MAX_PATH, FALSE); case SS_FOLDER: return _get_String(pTask, pTask->dwTitleIndexFolderSelected, ppszName, MAX_PATH, FALSE); case SS_MULTI: return _get_String(pTask, pTask->dwTitleIndexMultiSelected, ppszName, MAX_PATH, FALSE); } *ppszName = NULL; return E_NOTIMPL; } HRESULT CWVTASKITEM::get_Icon(const WVTASKITEM* pTask, IShellItemArray *psiItemArray, LPWSTR *ppszIcon) { return _get_String(pTask, pTask->dwIconIndex, ppszIcon, 0, TRUE); } HRESULT CWVTASKITEM::get_Tooltip(const WVTASKITEM* pTask, IShellItemArray *psiItemArray, LPWSTR *ppszInfotip) { return _get_String(pTask, pTask->dwTooltipIndex, ppszInfotip, INFOTIPSIZE, FALSE); } HRESULT CWVTASKITEM::get_CanonicalName(const WVTASKITEM* pTask, GUID* pguidCommandName) { *pguidCommandName = *(pTask->pguidCanonicalName); return S_OK; } HRESULT CWVTASKITEM::get_State(const WVTASKITEM* pTask, IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { HRESULT hr = S_OK; *puisState = UIS_DISABLED; if (_GetSelectionState(pTask, psiItemArray) != SS_NOTSUPPORTED) { if (pTask->pfn_get_State) hr = pTask->pfn_get_State(pv, psiItemArray, fOkToBeSlow, puisState); else *puisState = UIS_ENABLED; } return hr; } HRESULT CWVTASKITEM::Invoke(const WVTASKITEM* pTask, IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { return pTask->pfn_Invoke(pv, psiItemArray, pbc); } class CUIElement : public CWVTASKITEM, public IUIElement { public: STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IUIElement STDMETHODIMP get_Name(IShellItemArray *psiItemArray, LPWSTR *ppszName) {return CWVTASKITEM::get_Name(_pTask, psiItemArray, ppszName);} STDMETHODIMP get_Icon(IShellItemArray *psiItemArray, LPWSTR *ppszIcon) {return CWVTASKITEM::get_Icon(_pTask, psiItemArray, ppszIcon);} STDMETHODIMP get_Tooltip(IShellItemArray *psiItemArray, LPWSTR *ppszInfotip) {return CWVTASKITEM::get_Tooltip(_pTask, psiItemArray, ppszInfotip);} friend HRESULT Create_IUIElement(const WVTASKITEM* pwvti, IUIElement**ppuie); protected: CUIElement(const WVTASKITEM* pTask) { _cRef = 1; _pTask=pTask; } ~CUIElement() {} LONG _cRef; const WVTASKITEM* _pTask; }; HRESULT Create_IUIElement(const WVTASKITEM* pwvti, IUIElement**ppuie) { HRESULT hr; if (NULL!=pwvti) { CUIElement* p = new CUIElement(pwvti); if (p) { hr = p->QueryInterface(IID_PPV_ARG(IUIElement, ppuie)); p->Release(); } else { hr = E_OUTOFMEMORY; *ppuie = NULL; } } else { TraceMsg(TF_WARNING, "Create_IUIElement: caller passed in bad pwvti."); hr = E_INVALIDARG; *ppuie = NULL; } return hr; } HRESULT CUIElement::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CUIElement, IUIElement), { 0 }, }; return QISearch(this, qit, riid, ppv); } ULONG CUIElement::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CUIElement::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } class CUICommand : public CUIElement, public IUICommand { public: STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef() { return CUIElement::AddRef(); } STDMETHODIMP_(ULONG) Release() { return CUIElement::Release(); } // IUICommand STDMETHODIMP get_Name(IShellItemArray *psiItemArray, LPWSTR *ppszName) { return CUIElement::get_Name(psiItemArray, ppszName); } STDMETHODIMP get_Icon(IShellItemArray *psiItemArray, LPWSTR *ppszIcon) { return CUIElement::get_Icon(psiItemArray, ppszIcon); } STDMETHODIMP get_Tooltip(IShellItemArray *psiItemArray, LPWSTR *ppszInfotip) { return CUIElement::get_Tooltip(psiItemArray, ppszInfotip); } STDMETHODIMP get_CanonicalName(GUID* pguidCommandName) { return CWVTASKITEM::get_CanonicalName(_pTask, pguidCommandName); } STDMETHODIMP get_State(IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { return CWVTASKITEM::get_State(_pTask, _pv, psiItemArray, fOkToBeSlow, puisState); } STDMETHODIMP Invoke(IShellItemArray *psiItemArray, IBindCtx *pbc) { return CWVTASKITEM::Invoke(_pTask, _pv, psiItemArray, pbc); } friend HRESULT Create_IUICommand(IUnknown* pv, const WVTASKITEM* pwvti, IUICommand**ppuic); private: CUICommand(IUnknown* pv, const WVTASKITEM* pTask); ~CUICommand(); IUnknown* _pv; }; HRESULT Create_IUICommand(IUnknown* pv, const WVTASKITEM* pwvti, IUICommand**ppuic) { HRESULT hr; if (NULL!=pwvti) { CUICommand* p = new CUICommand(pv, pwvti); if (p) { hr = p->QueryInterface(IID_PPV_ARG(IUICommand, ppuic)); p->Release(); } else { hr = E_OUTOFMEMORY; *ppuic = NULL; } } else { TraceMsg(TF_WARNING, "Create_IUICommand: caller passed in bad pwvti."); hr = E_INVALIDARG; *ppuic = NULL; } return hr; } CUICommand::CUICommand(IUnknown* pv, const WVTASKITEM* pTask) : CUIElement(pTask) { _pv = pv; if (_pv) _pv->AddRef(); } CUICommand::~CUICommand() { if (_pv) _pv->Release(); } HRESULT CUICommand::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CUICommand, IUICommand), QITABENTMULTI(CUICommand, IUIElement, IUICommand), { 0 }, }; return QISearch(this, qit, riid, ppv); } #if 0 // { CUICommandOnPidl is currently not used, may come back for RC1 // a IUICommand wrapper around an IShellItem interface // class CUICommandOnPidl : public IUICommand { public: STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IUICommand STDMETHODIMP get_Name(IShellItemArray *psiItemArray, LPWSTR *ppszName); STDMETHODIMP get_Icon(IShellItemArray *psiItemArray, LPWSTR *ppszIcon); STDMETHODIMP get_Tooltip(IShellItemArray *psiItemArray, LPWSTR *ppszInfotip); STDMETHODIMP get_CanonicalName(GUID* pguidCommandName); STDMETHODIMP get_State(IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState); STDMETHODIMP Invoke(IShellItemArray *psiItemArray, IBindCtx *pbc); friend HRESULT Create_UICommandFromParseName(LPCWSTR pszParseName, REFGUID guidCanonicalName, HINSTANCE hinst, int idsName, int idsTip, IUICommand** ppuiCommand); private: CUICommandOnPidl() { _cRef = 1; } ~CUICommandOnPidl(); HRESULT Initialize(LPCWSTR pszParseName, REFGUID guidCanonicalName, HINSTANCE hinst, int idsName, int idsTip); LONG _cRef; const GUID* _pguidCanonicalName; IShellFolder* _psf; LPCITEMIDLIST _pidl; LPITEMIDLIST _pidlAbsolute; // optional hinst,idsName,idsTip to override display text for the item HINSTANCE _hinst; int _idsName; int _idsTip; }; HRESULT Create_UICommandFromParseName(LPCWSTR pszParseName, REFGUID guidCanonicalName, HINSTANCE hinst, int idsName, int idsTip, IUICommand** ppuiCommand) { HRESULT hr = E_OUTOFMEMORY; *ppuiCommand = NULL; CUICommandOnPidl* p = new CUICommandOnPidl(); if (p) { if (SUCCEEDED(p->Initialize(pszParseName, guidCanonicalName, hinst, idsName, idsTip))) { hr = p->QueryInterface(IID_PPV_ARG(IUICommand, ppuiCommand)); } p->Release(); } return hr; } HRESULT CUICommandOnPidl::Initialize(LPCWSTR pszParseName, REFGUID guidCanonicalName, HINSTANCE hinst, int idsName, int idsTip) { _pguidCanonicalName = &guidCanonicalName; IShellFolder* psfDesktop; HRESULT hr = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hr)) { hr = psfDesktop->ParseDisplayName(NULL, NULL, (LPOLESTR)pszParseName, NULL, &_pidlAbsolute, NULL); if (SUCCEEDED(hr)) { hr = SHBindToIDListParent(_pidlAbsolute, IID_PPV_ARG(IShellFolder, &_psf), &_pidl); _hinst = hinst; _idsName = idsName; _idsTip = idsTip; } psfDesktop->Release(); } return hr; } CUICommandOnPidl::~CUICommandOnPidl() { if (_psf) _psf->Release(); ILFree(_pidlAbsolute); } HRESULT CUICommandOnPidl::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CUICommandOnPidl, IUICommand), QITABENTMULTI(CUICommandOnPidl, IUIElement, IUICommand), { 0 }, }; return QISearch(this, qit, riid, ppv); } ULONG CUICommandOnPidl::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CUICommandOnPidl::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CUICommandOnPidl::get_Name(IShellItemArray *psiItemArray, LPWSTR *ppszName) { if (_hinst && _idsName) { // TODO: load the string... but we have to fix dui to handle direct strings! return DisplayNameOfAsOLESTR(_psf, _pidl, SHGDN_INFOLDER, ppszName); } else return DisplayNameOfAsOLESTR(_psf, _pidl, SHGDN_INFOLDER, ppszName); } HRESULT CUICommandOnPidl::get_Icon(IShellItemArray *psiItemArray, LPWSTR *ppszIcon) { LPWSTR pszIconPath = NULL; // TODO: use SHGetIconFromPIDL so we get system imagelist support IExtractIcon* pxi; HRESULT hr = _psf->GetUIObjectOf(NULL, 1, &_pidl, IID_PPV_ARG_NULL(IExtractIcon, &pxi)); if (SUCCEEDED(hr)) { WCHAR szPath[MAX_PATH]; int iIndex; UINT wFlags=0; // BUGBUG: assume the location is a proper dll,-id value... hr = pxi->GetIconLocation(GIL_FORSHELL, szPath, ARRAYSIZE(szPath), &iIndex, &wFlags); if (SUCCEEDED(hr)) { pszIconPath = (LPWSTR)SHAlloc(sizeof(WCHAR)*(lstrlen(szPath)+1+8)); if (pszIconPath) { wsprintf(pszIconPath,L"%s,%d", szPath, iIndex); } else { hr = E_OUTOFMEMORY; } } pxi->Release(); } *ppszIcon = pszIconPath; return hr; } HRESULT CUICommandOnPidl::get_Tooltip(IShellItemArray *psiItemArray, LPWSTR *ppszInfotip) { *ppszInfotip = NULL; if (_hinst && _idsName) { // TODO: load the string... but we have to fix dui to handle direct strings! return E_NOTIMPL; } else return E_NOTIMPL; } HRESULT CUICommandOnPidl::get_CanonicalName(GUID* pguidCommandName) { *pguidCommandName = *_pguidCanonicalName; return S_OK; } HRESULT CUICommandOnPidl::get_State(IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState) { *puisState = UIS_ENABLED; return S_OK; } HRESULT CUICommandOnPidl::Invoke(IIShellItemArray *psiItemArray, IBindCtx *pbc) { SHELLEXECUTEINFO sei = { 0 }; sei.cbSize = sizeof(sei); sei.lpIDList = _pidlAbsolute; sei.fMask = SEE_MASK_IDLIST; sei.nShow = SW_SHOWNORMAL; return ShellExecuteEx(&sei) ? S_OK : E_FAIL; } #endif // } CUICommandOnPidl may come back for RC1 class CEnumUICommand : public IEnumUICommand { public: STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IEnumUICommand STDMETHODIMP Next(ULONG celt, IUICommand** pUICommand, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt); STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumUICommand **ppenum); friend HRESULT Create_IEnumUICommandWithArray(IUnknown *pv, const WVTASKITEM* rgwvti, UINT cwvti, IUICommand** rguiCommand, UINT cuiCommand, IEnumUICommand**ppenum); private: CEnumUICommand(IUnknown *pv, const WVTASKITEM* rgwvti, ULONG cwvti, IUICommand** rguiCommand, UINT cuiCommand); ~CEnumUICommand(); LONG _cRef; IUnknown* _pv; const WVTASKITEM* _rgwvti; ULONG _cItems; IUICommand** _prguiCommand; ULONG _cuiCommand; ULONG _ulIndex; }; HRESULT Create_IEnumUICommandWithArray(IUnknown *pv, const WVTASKITEM* rgwvti, UINT cwvti, IUICommand** rguiCommand, UINT cuiCommand, IEnumUICommand**ppenum) { HRESULT hr; if (NULL!=rgwvti) { CEnumUICommand* p = new CEnumUICommand(pv, rgwvti, cwvti, rguiCommand, cuiCommand); if (p) { hr = p->QueryInterface(IID_PPV_ARG(IEnumUICommand, ppenum)); p->Release(); } else { hr = E_OUTOFMEMORY; *ppenum = NULL; } } else { TraceMsg(TF_WARNING, "Create_IEnumUICommand: caller passed in bad pwvti."); hr = E_INVALIDARG; *ppenum = NULL; } return hr; } HRESULT Create_IEnumUICommand(IUnknown *pv, const WVTASKITEM* rgwvti, UINT cwvti, IEnumUICommand**ppenum) { return Create_IEnumUICommandWithArray(pv, rgwvti, cwvti, NULL, 0, ppenum); } CEnumUICommand::CEnumUICommand(IUnknown *pv, const WVTASKITEM* rgwvti, ULONG cwvti, IUICommand** rguiCommand, UINT cuiCommand) { if (pv) { _pv = pv; _pv->AddRef(); } _rgwvti = rgwvti; _cItems = cwvti; if (cuiCommand) { _prguiCommand = (IUICommand**)LocalAlloc(LPTR, cuiCommand*sizeof(IUICommand*)); if (_prguiCommand) { for (UINT i = 0 ; i < cuiCommand && rguiCommand[i]; i++) { _prguiCommand[i] = rguiCommand[i]; _prguiCommand[i]->AddRef(); } _cuiCommand = i; } } _cRef = 1; } CEnumUICommand::~CEnumUICommand() { if (_pv) _pv->Release(); if (_prguiCommand) { for (UINT i = 0 ; i < _cuiCommand ; i++) _prguiCommand[i]->Release(); LocalFree(_prguiCommand); } } HRESULT CEnumUICommand::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CEnumUICommand, IEnumUICommand), { 0 }, }; return QISearch(this, qit, riid, ppv); } ULONG CEnumUICommand::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CEnumUICommand::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CEnumUICommand::Next(ULONG celt, IUICommand** ppUICommand, ULONG *pceltFetched) { HRESULT hr; if (_ulIndex < _cItems) { hr = Create_IUICommand(_pv, &_rgwvti[_ulIndex++], ppUICommand); } else if (_ulIndex < _cItems + _cuiCommand) { *ppUICommand = _prguiCommand[_ulIndex++ - _cItems]; (*ppUICommand)->AddRef(); hr = S_OK; } else { *ppUICommand = NULL; hr = S_FALSE; } if (pceltFetched) *pceltFetched = (hr == S_OK) ? 1 : 0; return hr; } HRESULT CEnumUICommand::Skip(ULONG celt) { _ulIndex = min(_cItems, _ulIndex+celt); return S_OK; } HRESULT CEnumUICommand::Reset() { _ulIndex = 0; return S_OK; } HRESULT CEnumUICommand::Clone(IEnumUICommand **ppenum) { *ppenum = NULL; return E_NOTIMPL; }