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

544 lines
16 KiB
C++

#include "shellprv.h"
#include "defcm.h"
#include "datautil.h"
/////////////////////////////////////////////////////////////////////////////
// CClientExtractIcon
class CClientExtractIconCB;
class ATL_NO_VTABLE CClientExtractIcon
: public IShellExtInit
, public IExtractIconW
, public IExtractIconA
, public IContextMenu
, public IPersistPropertyBag
, public IServiceProvider
, public CComObjectRootEx<CComSingleThreadModel>
, public CComCoClass<CClientExtractIcon, &CLSID_ClientExtractIcon>
{
public:
BEGIN_COM_MAP(CClientExtractIcon)
COM_INTERFACE_ENTRY(IShellExtInit)
// Need to use COM_INTERFACE_ENTRY_IID for the interfaces
// that don't have an idl
COM_INTERFACE_ENTRY_IID(IID_IExtractIconA, IExtractIconA)
COM_INTERFACE_ENTRY_IID(IID_IExtractIconW, IExtractIconW)
COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
COM_INTERFACE_ENTRY(IPersist)
COM_INTERFACE_ENTRY(IPersistPropertyBag)
COM_INTERFACE_ENTRY(IServiceProvider)
END_COM_MAP()
DECLARE_NO_REGISTRY()
DECLARE_NOT_AGGREGATABLE(CClientExtractIcon)
public:
// *** IShellExtInit ***
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder,
IDataObject *pdto,
HKEY hkProgID);
// *** IExtractIconA ***
STDMETHODIMP GetIconLocation(UINT uFlags,
LPSTR szIconFile,
UINT cchMax,
int *piIndex,
UINT *pwFlags);
STDMETHODIMP Extract(LPCSTR pszFile,
UINT nIconIndex,
HICON *phiconLarge,
HICON *phiconSmall,
UINT nIconSize);
// *** IExtractIconW ***
STDMETHODIMP GetIconLocation(UINT uFlags,
LPWSTR szIconFile,
UINT cchMax,
int *piIndex,
UINT *pwFlags);
STDMETHODIMP Extract(LPCWSTR pszFile,
UINT nIconIndex,
HICON *phiconLarge,
HICON *phiconSmall,
UINT nIconSize);
// *** IContextMenu methods ***
STDMETHOD(QueryContextMenu)(HMENU hmenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags);
STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO pici);
STDMETHOD(GetCommandString)(UINT_PTR idCmd,
UINT uType,
UINT * pwReserved,
LPSTR pszName,
UINT cchMax);
// *** IPersist methods ***
STDMETHOD(GetClassID)(CLSID *pclsid)
{ *pclsid = CLSID_ClientExtractIcon; return S_OK; }
// *** IPersistPropertyBag methods ***
STDMETHOD(InitNew)();
STDMETHOD(Load)(IPropertyBag *pbg,
IErrorLog *plog);
STDMETHOD(Save)(IPropertyBag *pbg,
BOOL fClearDirty,
BOOL FSaveAllProperties) { return E_NOTIMPL; }
// *** IServiceProvider methods ***
STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObj);
public:
CClientExtractIcon() : _idmProperties(-1) { }
~CClientExtractIcon();
// *** IContextMenuCB forwarded back to us ***
HRESULT CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
IContextMenu * _pcmInner;
IAssociationElement * _pae;
IPropertyBag * _pbag;
LPITEMIDLIST _pidlObject;
CComObject<CClientExtractIconCB> * _pcb;
HKEY _hkClass;
UINT _idmProperties;
UINT _idCmdFirst;
};
/////////////////////////////////////////////////////////////////////////////
// CClientExtractIconCB - DefCM callback
//
class ATL_NO_VTABLE CClientExtractIconCB
: public IContextMenuCB
, public CComObjectRootEx<CComSingleThreadModel>
, public CComCoClass<CClientExtractIconCB>
{
public:
BEGIN_COM_MAP(CClientExtractIconCB)
// Need to use COM_INTERFACE_ENTRY_IID for the interfaces
// that don't have an idl
COM_INTERFACE_ENTRY_IID(IID_IContextMenuCB, IContextMenuCB)
END_COM_MAP()
DECLARE_NO_REGISTRY()
DECLARE_NOT_AGGREGATABLE(CClientExtractIconCB)
void Attach(CClientExtractIcon *pcxi) { _pcxi = pcxi; }
public:
// *** IContextMenuCB ***
STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (_pcxi)
return _pcxi->CallBack(psf, hwnd, pdtobj, uMsg, wParam, lParam);
return E_FAIL;
}
public:
CClientExtractIcon *_pcxi;
};
/////////////////////////////////////////////////////////////////////////////
STDAPI CClientExtractIcon_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppunk)
{
return CClientExtractIcon::_CreatorClass::CreateInstance(punkOuter, riid, ppunk);
}
CClientExtractIcon::~CClientExtractIcon()
{
InitNew();
}
// *** IPersistPropertyBag::InitNew ***
HRESULT CClientExtractIcon::InitNew()
{
ATOMICRELEASE(_pcmInner);
ATOMICRELEASE(_pae);
ATOMICRELEASE(_pbag);
ILFree(_pidlObject);
if (_hkClass) RegCloseKey(_hkClass);
if (_pcb)
{
_pcb->Attach(NULL); // break circular reference
_pcb->Release();
_pcb = NULL;
}
return S_OK;
}
//
// Property bag items:
//
// Element = CLSID_Assoc* element to create
// InitString = string to IPersistString2::SetString with
// opentext = display name for "open" command (if not overridden)
//
// If the client did not customize a "properties" command, then we
// also use these values:
//
// properties = program to execute for "properties"
// propertiestext = name for the "properties" command
//
// *** IPersistPropertyBag::Load ***
HRESULT CClientExtractIcon::Load(IPropertyBag *pbag, IErrorLog *plog)
{
HRESULT hr;
// Erase any old state
InitNew();
// Save the property bag so we can mess with him later
_pbag = pbag;
if (_pbag)
{
_pbag->AddRef();
}
// Get the CLSID we are dispatching through and initialize it
// with the InitString.
CLSID clsid;
hr = SHPropertyBag_ReadGUID(pbag, L"Element", &clsid);
if (SUCCEEDED(hr))
{
BSTR bs;
hr = SHPropertyBag_ReadBSTR(pbag, L"InitString", &bs);
if (SUCCEEDED(hr))
{
hr = THR(AssocElemCreateForClass(&clsid, bs, &_pae));
::SysFreeString(bs);
// Ignore failure of AssocElemCreateForClass
// It can fail if the user's default client got uninstalled
hr = S_OK;
}
}
return hr;
}
// *** IServiceProvider::QueryService ***
//
// We cheat and use ISericeProvider::QueryService as a sort of
// "QueryInterface that's allowed to break COM identity rules".
//
HRESULT CClientExtractIcon::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
{
if (guidService == IID_IAssociationElement && _pae)
{
return _pae->QueryInterface(riid, ppvObj);
}
*ppvObj = NULL;
return E_FAIL;
}
// *** IShellExtInit::Initialize
//
// Only if the HKEY specifies a ClientType.
//
HRESULT CClientExtractIcon::Initialize(LPCITEMIDLIST, IDataObject *pdto, HKEY hkClass)
{
ILFree(_pidlObject);
HRESULT hr = PidlFromDataObject(pdto, &_pidlObject);
if (_hkClass)
{
RegCloseKey(_hkClass);
}
if (hkClass)
{
_hkClass = SHRegDuplicateHKey(hkClass);
}
return hr;
}
// *** IExtractIconA::GetIconLocation
HRESULT CClientExtractIcon::GetIconLocation(
UINT uFlags, LPSTR szIconFile, UINT cchMax,
int *piIndex, UINT *pwFlags)
{
WCHAR wszPath[MAX_PATH];
HRESULT hr = GetIconLocation(uFlags, wszPath, ARRAYSIZE(wszPath), piIndex, pwFlags);
if (SUCCEEDED(hr))
{
SHUnicodeToAnsi(wszPath, szIconFile, cchMax);
}
return hr;
}
// *** IExtractIconA::Extract
HRESULT CClientExtractIcon::Extract(
LPCSTR pszFile, UINT nIconIndex,
HICON *phiconLarge, HICON *phiconSmall,
UINT nIconSize)
{
return S_FALSE;
}
// *** IExtractIconW::GetIconLocation
HRESULT CClientExtractIcon::GetIconLocation(
UINT uFlags, LPWSTR szIconFile, UINT cchMax,
int *piIndex, UINT *pwFlags)
{
szIconFile[0] = L'\0';
if (_pae)
{
LPWSTR pszIcon;
HRESULT hr = _pae->QueryString(AQS_DEFAULTICON, NULL, &pszIcon);
if (SUCCEEDED(hr))
{
lstrcpynW(szIconFile, pszIcon, cchMax);
SHFree(pszIcon);
}
}
if (!szIconFile[0] && _hkClass)
{
LONG cb = cchMax * sizeof(TCHAR);
RegQueryValueW(_hkClass, L"DefaultIcon", szIconFile, &cb);
}
if (szIconFile[0])
{
*pwFlags = GIL_PERCLASS;
*piIndex = PathParseIconLocationW(szIconFile);
return S_OK;
}
else
{
return E_FAIL;
}
}
// *** IExtractIconW::Extract
HRESULT CClientExtractIcon::Extract(
LPCWSTR pszFile, UINT nIconIndex,
HICON *phiconLarge, HICON *phiconSmall,
UINT nIconSize)
{
return S_FALSE;
}
// *** IContextMenuCB forwarded back to us ***
HRESULT CClientExtractIcon::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WCHAR wszBuf[MAX_PATH];
switch (uMsg)
{
case DFM_MERGECONTEXTMENU:
{
QCMINFO *pqcm = (QCMINFO *)lParam;
// Add a "Properties" command in case we end up needing one.
// (And if not, we'll remove it afterwards.)
if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"propertiestext", wszBuf, ARRAYSIZE(wszBuf))))
{
SHLoadIndirectString(wszBuf, wszBuf, ARRAYSIZE(wszBuf), NULL);
InsertMenuW(pqcm->hmenu, pqcm->indexMenu, MF_BYPOSITION | MF_STRING, pqcm->idCmdFirst, wszBuf);
_idmProperties = pqcm->idCmdFirst++;
}
return S_OK; // Please merge the HKEY\shell stuff
}
case DFM_MERGECONTEXTMENU_TOP:
// The app has added its menu items; see what needs to be cleaned up
{
QCMINFO *pqcm = (QCMINFO *)lParam;
// See if they added a "Properties" item.
// If so, then delete ours.
if (GetMenuIndexForCanonicalVerb(pqcm->hmenu, _pcmInner, _idCmdFirst, L"properties") != 0xFFFFFFFF)
{
// Yes they do, so delete our bogus one
DeleteMenu(pqcm->hmenu, _idmProperties, MF_BYCOMMAND);
_idmProperties = -1;
}
// Now see if their "open" command uses the default name.
// If so, then we replace it with a friendlier name.
BOOL fCustomOpenString = FALSE;
IAssociationElement *paeOpen;
if (_pae && SUCCEEDED(_pae->QueryObject(AQVO_SHELLVERB_DELEGATE, L"open", IID_PPV_ARG(IAssociationElement, &paeOpen))))
{
if (SUCCEEDED(paeOpen->QueryExists(AQN_NAMED_VALUE, L"MUIVerb")) ||
SUCCEEDED(paeOpen->QueryExists(AQN_NAMED_VALUE, NULL)))
{
fCustomOpenString = TRUE;
}
paeOpen->Release();
}
if (!fCustomOpenString)
{
UINT idm = GetMenuIndexForCanonicalVerb(pqcm->hmenu, _pcmInner, _idCmdFirst, L"open");
if (idm != 0xFFFFFFFF)
{
if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"opentext", wszBuf, ARRAYSIZE(wszBuf))) &&
wszBuf[0])
{
SHLoadIndirectString(wszBuf, wszBuf, ARRAYSIZE(wszBuf), NULL);
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STRING;
mii.dwTypeData = wszBuf;
SetMenuItemInfo(pqcm->hmenu, idm, TRUE, &mii);
}
}
}
}
return S_OK;
case DFM_INVOKECOMMANDEX:
switch (wParam)
{
case 0: // Properties
if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"properties", wszBuf, ARRAYSIZE(wszBuf))))
{
DFMICS *pdfmics = (DFMICS *)lParam;
if (ShellExecCmdLine(pdfmics->pici->hwnd, wszBuf, NULL, SW_SHOWNORMAL, NULL, 0))
{
return S_OK;
}
else
{
return E_FAIL;
}
}
break;
default:
ASSERT(!"Unexpected DFM_INVOKECOMMAND");
break;
}
return E_FAIL;
default:
return E_NOTIMPL;
}
}
// *** IContextMenu::QueryContextMenu ***
HRESULT CClientExtractIcon::QueryContextMenu(
HMENU hmenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
HRESULT hr;
if (!_pcmInner)
{
HKEY hk = NULL;
if (_pae)
{
// If this fails, we continue with a NULL key
AssocKeyFromElement(_pae, &hk);
}
if (!_pcb)
{
hr = CComObject<CClientExtractIconCB>::CreateInstance(&_pcb);
if (SUCCEEDED(hr))
{
_pcb->Attach(this);
_pcb->AddRef();
}
}
else
{
hr = S_OK;
}
if (SUCCEEDED(hr))
{
IShellFolder *psf;
hr = SHGetDesktopFolder(&psf);
if (SUCCEEDED(hr))
{
DEFCONTEXTMENU dcm = {
NULL, // hwnd
_pcb, // pcmcb
NULL, // pidlFolder
psf, // IShellFolder
1, // cidl
(LPCITEMIDLIST*)&_pidlObject, // apidl
NULL, // paa
1, // cKeys
&hk, // aKeys
};
hr = CreateDefaultContextMenu(&dcm, &_pcmInner);
psf->Release();
}
}
if (hk)
{
RegCloseKey(hk);
}
if (!_pcmInner)
{
return E_FAIL;
}
}
if (_pcmInner)
{
_idCmdFirst = idCmdFirst;
uFlags |= CMF_VERBSONLY; // Don't do cut/copy/paste/link
hr = _pcmInner->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
}
else
{
hr = E_FAIL;
}
return hr;
}
// *** IContextMenu::InvokeCommand ***
HRESULT CClientExtractIcon::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
HRESULT hr = E_FAIL;
if (_pcmInner)
{
hr = _pcmInner->InvokeCommand(pici);
}
return hr;
}
// *** IContextMenu::GetCommandString ***
HRESULT CClientExtractIcon::GetCommandString(
UINT_PTR idCmd,
UINT uType,
UINT * pwReserved,
LPSTR pszName,
UINT cchMax)
{
if (!_pcmInner) return E_FAIL;
return _pcmInner->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);
}