reactos/dll/win32/shell32/defcontextmenu.cpp
Hermès Bélusca-Maïto f66525ee02 Synchronize with trunk revision 59636 (just before Alex's CreateProcess revamp).
svn path=/branches/ntvdm/; revision=59665
2013-08-07 14:57:30 +00:00

1786 lines
51 KiB
C++

/*
* PROJECT: shell32
* LICENSE: GPL - See COPYING in the top level directory
* FILE: dll/win32/shell32/shv_item_new.c
* PURPOSE: provides default context menu implementation
* PROGRAMMERS: Johannes Anderwald (janderwald@reactos.org)
*/
/*
TODO:
The code in NotifyShellViewWindow to deliver commands to the view is broken. It is an excellent
example of the wrong way to do it.
*/
#include "precomp.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
typedef struct _DynamicShellEntry_
{
UINT iIdCmdFirst;
UINT NumIds;
CLSID ClassID;
IContextMenu *pCM;
struct _DynamicShellEntry_ *pNext;
} DynamicShellEntry, *PDynamicShellEntry;
typedef struct _StaticShellEntry_
{
LPWSTR szVerb;
LPWSTR szClass;
struct _StaticShellEntry_ *pNext;
} StaticShellEntry, *PStaticShellEntry;
class CDefaultContextMenu :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IContextMenu2
{
private:
DEFCONTEXTMENU m_Dcm;
IDataObject *m_pDataObj;
LPCITEMIDLIST m_pidlFolder;
DWORD m_bGroupPolicyActive;
PDynamicShellEntry m_pDynamicEntries; /* first dynamic shell extension entry */
UINT m_iIdSHEFirst; /* first used id */
UINT m_iIdSHELast; /* last used id */
PStaticShellEntry m_pStaticEntries; /* first static shell extension entry */
UINT m_iIdSCMFirst; /* first static used id */
UINT m_iIdSCMLast; /* last static used id */
void AddStaticEntry(LPCWSTR pwszVerb, LPCWSTR pwszClass);
void AddStaticEntryForKey(HKEY hKey, LPCWSTR pwszClass);
void AddStaticEntryForFileClass(LPCWSTR pwszExt);
BOOL IsShellExtensionAlreadyLoaded(const CLSID *pclsid);
HRESULT LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid);
BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey);
UINT InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast);
UINT BuildBackgroundContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT IndexMenu);
UINT BuildShellItemContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
HRESULT DoPaste(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoDelete(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy);
HRESULT DoRename(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoProperties(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoFormat(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoDynamicShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
HRESULT DoStaticShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry);
HRESULT TryToBrowse(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags);
HRESULT InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry);
public:
CDefaultContextMenu();
~CDefaultContextMenu();
HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm);
// IContextMenu
virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
// IContextMenu2
virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
BEGIN_COM_MAP(CDefaultContextMenu)
COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
END_COM_MAP()
};
CDefaultContextMenu::CDefaultContextMenu()
{
memset(&m_Dcm, 0, sizeof(m_Dcm));
m_pDataObj = NULL;
m_pidlFolder = NULL;
m_bGroupPolicyActive = 0;
m_pDynamicEntries = NULL;
m_iIdSHEFirst = 0;
m_iIdSHELast = 0;
m_pStaticEntries = NULL;
m_iIdSCMFirst = 0;
m_iIdSCMLast = 0;
}
CDefaultContextMenu::~CDefaultContextMenu()
{
/* Free dynamic shell extension entries */
PDynamicShellEntry pDynamicEntry = m_pDynamicEntries, pNextDynamic;
while (pDynamicEntry)
{
pNextDynamic = pDynamicEntry->pNext;
pDynamicEntry->pCM->Release();
HeapFree(GetProcessHeap(), 0, pDynamicEntry);
pDynamicEntry = pNextDynamic;
}
/* Free static shell extension entries */
PStaticShellEntry pStaticEntry = m_pStaticEntries, pNextStatic;
while (pStaticEntry)
{
pNextStatic = pStaticEntry->pNext;
HeapFree(GetProcessHeap(), 0, pStaticEntry->szClass);
HeapFree(GetProcessHeap(), 0, pStaticEntry->szVerb);
HeapFree(GetProcessHeap(), 0, pStaticEntry);
pStaticEntry = pNextStatic;
}
if (m_pidlFolder)
ILFree((_ITEMIDLIST*)m_pidlFolder);
if (m_pDataObj)
m_pDataObj->Release();
}
HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm)
{
IDataObject *pDataObj;
TRACE("cidl %u\n", pdcm->cidl);
if (SUCCEEDED(SHCreateDataObject(pdcm->pidlFolder, pdcm->cidl, pdcm->apidl, NULL, IID_IDataObject, (void**)&pDataObj)))
m_pDataObj = pDataObj;
if (!pdcm->cidl)
{
/* Init pidlFolder only if it is background context menu. See IShellExtInit::Initialize */
if (pdcm->pidlFolder)
m_pidlFolder = ILClone(pdcm->pidlFolder);
else
{
IPersistFolder2 *pf = NULL;
if (SUCCEEDED(pdcm->psf->QueryInterface(IID_IPersistFolder2, (PVOID*)&pf)))
{
if (FAILED(pf->GetCurFolder((_ITEMIDLIST**)&m_pidlFolder)))
ERR("GetCurFolder failed\n");
pf->Release();
}
}
TRACE("pidlFolder %p\n", m_pidlFolder);
}
CopyMemory(&m_Dcm, pdcm, sizeof(DEFCONTEXTMENU));
return S_OK;
}
void
CDefaultContextMenu::AddStaticEntry(const WCHAR *szVerb, const WCHAR *szClass)
{
PStaticShellEntry pEntry = m_pStaticEntries, pLastEntry = NULL;
while(pEntry)
{
if (!wcsicmp(pEntry->szVerb, szVerb))
{
/* entry already exists */
return;
}
pLastEntry = pEntry;
pEntry = pEntry->pNext;
}
TRACE("adding verb %s szClass %s\n", debugstr_w(szVerb), debugstr_w(szClass));
pEntry = (StaticShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry));
if (pEntry)
{
pEntry->pNext = NULL;
pEntry->szVerb = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb) + 1) * sizeof(WCHAR));
if (pEntry->szVerb)
wcscpy(pEntry->szVerb, szVerb);
pEntry->szClass = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szClass) + 1) * sizeof(WCHAR));
if (pEntry->szClass)
wcscpy(pEntry->szClass, szClass);
}
if (!wcsicmp(szVerb, L"open"))
{
/* open verb is always inserted in front */
pEntry->pNext = m_pStaticEntries;
m_pStaticEntries = pEntry;
}
else if (pLastEntry)
pLastEntry->pNext = pEntry;
else
m_pStaticEntries = pEntry;
}
void
CDefaultContextMenu::AddStaticEntryForKey(HKEY hKey, const WCHAR *pwszClass)
{
WCHAR wszName[40];
DWORD cchName, dwIndex = 0;
TRACE("AddStaticEntryForKey %x %ls\n", hKey, pwszClass);
while(TRUE)
{
cchName = _countof(wszName);
if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
break;
AddStaticEntry(wszName, pwszClass);
}
}
void
CDefaultContextMenu::AddStaticEntryForFileClass(const WCHAR * szExt)
{
WCHAR szBuffer[100];
HKEY hKey;
LONG result;
DWORD dwBuffer;
UINT Length;
static WCHAR szShell[] = L"\\shell";
static WCHAR szShellAssoc[] = L"SystemFileAssociations\\";
TRACE("AddStaticEntryForFileClass entered with %s\n", debugstr_w(szExt));
Length = wcslen(szExt);
if (Length + (sizeof(szShell) / sizeof(WCHAR)) + 1 < sizeof(szBuffer) / sizeof(WCHAR))
{
wcscpy(szBuffer, szExt);
wcscpy(&szBuffer[Length], szShell);
result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
szBuffer[Length] = 0;
AddStaticEntryForKey(hKey, szExt);
RegCloseKey(hKey);
}
}
dwBuffer = sizeof(szBuffer);
result = RegGetValueW(HKEY_CLASSES_ROOT, szExt, NULL, RRF_RT_REG_SZ, NULL, (LPBYTE)szBuffer, &dwBuffer);
if (result == ERROR_SUCCESS)
{
Length = wcslen(szBuffer);
if (Length + (sizeof(szShell) / sizeof(WCHAR)) + 1 < sizeof(szBuffer) / sizeof(WCHAR))
{
wcscpy(&szBuffer[Length], szShell);
TRACE("szBuffer %s\n", debugstr_w(szBuffer));
result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
szBuffer[Length] = 0;
AddStaticEntryForKey(hKey, szBuffer);
RegCloseKey(hKey);
}
}
}
wcscpy(szBuffer, szShellAssoc);
dwBuffer = sizeof(szBuffer) - sizeof(szShellAssoc) - sizeof(WCHAR);
result = RegGetValueW(HKEY_CLASSES_ROOT, szExt, L"PerceivedType", RRF_RT_REG_SZ, NULL, (LPBYTE)&szBuffer[_countof(szShellAssoc) - 1], &dwBuffer);
if (result == ERROR_SUCCESS)
{
Length = wcslen(&szBuffer[_countof(szShellAssoc)]) + _countof(szShellAssoc);
wcscat(szBuffer, L"\\shell");
TRACE("szBuffer %s\n", debugstr_w(szBuffer));
result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
szBuffer[Length] = 0;
AddStaticEntryForKey(hKey, szBuffer);
RegCloseKey(hKey);
}
}
}
static
BOOL
HasClipboardData()
{
BOOL bRet = FALSE;
IDataObject *pDataObj;
if(SUCCEEDED(OleGetClipboard(&pDataObj)))
{
STGMEDIUM medium;
FORMATETC formatetc;
TRACE("pDataObj=%p\n", pDataObj);
/* Set the FORMATETC structure*/
InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
if(SUCCEEDED(pDataObj->GetData(&formatetc, &medium)))
{
bRet = TRUE;
ReleaseStgMedium(&medium);
}
pDataObj->Release();
}
return bRet;
}
static
VOID
DisablePasteOptions(HMENU hMenu)
{
MENUITEMINFOW mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
mii.fState = MFS_DISABLED;
TRACE("result %d\n", SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERT, FALSE, &mii));
TRACE("result %d\n", SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERTLINK, FALSE, &mii));
}
BOOL
CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID *pclsid)
{
PDynamicShellEntry pEntry = m_pDynamicEntries;
while (pEntry)
{
if (!memcmp(&pEntry->ClassID, pclsid, sizeof(CLSID)))
return TRUE;
pEntry = pEntry->pNext;
}
return FALSE;
}
HRESULT
CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid)
{
HRESULT hr;
TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(pclsid));
if (IsShellExtensionAlreadyLoaded(pclsid))
return S_OK;
IContextMenu *pcm;
hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_IContextMenu, (void**)&pcm);
if (hr != S_OK)
{
ERR("SHCoCreateInstance failed %x\n", GetLastError());
return hr;
}
IShellExtInit *pExtInit;
hr = pcm->QueryInterface(IID_IShellExtInit, (void**)&pExtInit);
if (hr != S_OK)
{
ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
pcm->Release();
return hr;
}
hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey);
pExtInit->Release();
if (hr != S_OK)
{
TRACE("Failed to initialize shell extension error %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
pcm->Release();
return hr;
}
PDynamicShellEntry pEntry = (DynamicShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry));
if (!pEntry)
{
pcm->Release();
return E_OUTOFMEMORY;
}
pEntry->iIdCmdFirst = 0;
pEntry->pNext = NULL;
pEntry->NumIds = 0;
pEntry->pCM = pcm;
memcpy(&pEntry->ClassID, pclsid, sizeof(CLSID));
if (m_pDynamicEntries)
{
PDynamicShellEntry pLastEntry = m_pDynamicEntries;
while (pLastEntry->pNext)
pLastEntry = pLastEntry->pNext;
pLastEntry->pNext = pEntry;
}
else
m_pDynamicEntries = pEntry;
return S_OK;
}
BOOL
CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
{
WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid;
DWORD cchName;
HRESULT hr;
HKEY hKey;
if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
{
TRACE("RegOpenKeyExW failed\n");
return FALSE;
}
DWORD dwIndex = 0;
while (TRUE)
{
cchName = _countof(wszName);
if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
break;
/* Key name or key value is CLSID */
CLSID clsid;
hr = CLSIDFromString(wszName, &clsid);
if (hr == S_OK)
pwszClsid = wszName;
else
{
DWORD cchBuf = _countof(wszBuf);
if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS)
hr = CLSIDFromString(wszBuf, &clsid);
pwszClsid = wszBuf;
}
if (SUCCEEDED(hr))
{
if (m_bGroupPolicyActive)
{
if (RegGetValueW(HKEY_LOCAL_MACHINE,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
pwszClsid,
RRF_RT_REG_SZ,
NULL,
NULL,
NULL) == ERROR_SUCCESS)
{
LoadDynamicContextMenuHandler(hKey, &clsid);
}
}
else
LoadDynamicContextMenuHandler(hKey, &clsid);
}
}
RegCloseKey(hKey);
return TRUE;
}
UINT
CDefaultContextMenu::InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast)
{
if (!m_pDynamicEntries)
{
m_iIdSHEFirst = 0;
m_iIdSHELast = 0;
return IndexMenu;
}
PDynamicShellEntry pEntry = m_pDynamicEntries;
idCmdFirst = 0x5000;
idCmdLast = 0x6000;
m_iIdSHEFirst = idCmdFirst;
do
{
HRESULT hr = pEntry->pCM->QueryContextMenu(hMenu, IndexMenu++, idCmdFirst, idCmdLast, CMF_NORMAL);
if (SUCCEEDED(hr))
{
pEntry->iIdCmdFirst = idCmdFirst;
pEntry->NumIds = LOWORD(hr);
IndexMenu += pEntry->NumIds;
idCmdFirst += pEntry->NumIds + 0x10;
}
TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry, hr, pEntry->pCM, pEntry->iIdCmdFirst, pEntry->NumIds);
pEntry = pEntry->pNext;
} while (pEntry);
m_iIdSHELast = idCmdFirst;
TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast);
return IndexMenu;
}
UINT
CDefaultContextMenu::BuildBackgroundContextMenu(
HMENU hMenu,
UINT iIdCmdFirst,
UINT iIdCmdLast,
UINT uFlags)
{
UINT IndexMenu = 0;
HMENU hSubMenu;
TRACE("BuildBackgroundContextMenu entered\n");
if (!_ILIsDesktop(m_pidlFolder))
{
WCHAR wszBuf[MAX_PATH];
/* view option is only available in browsing mode */
hSubMenu = LoadMenuW(shell32_hInstance, L"MENU_001");
if (hSubMenu && LoadStringW(shell32_hInstance, FCIDM_SHVIEW_VIEW, wszBuf, _countof(wszBuf)))
{
TRACE("wszBuf %s\n", debugstr_w(wszBuf));
MENUITEMINFOW mii;
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_SUBMENU | MIIM_ID;
mii.fType = MFT_STRING;
mii.wID = iIdCmdFirst++;
mii.dwTypeData = wszBuf;
mii.cch = wcslen(mii.dwTypeData);
mii.fState = MFS_ENABLED;
mii.hSubMenu = hSubMenu;
InsertMenuItemW(hMenu, IndexMenu++, TRUE, &mii);
DestroyMenu(hSubMenu);
}
}
hSubMenu = LoadMenuW(shell32_hInstance, L"MENU_002");
if (hSubMenu)
{
/* merge general background context menu in */
iIdCmdFirst = Shell_MergeMenus(hMenu, GetSubMenu(hSubMenu, 0), IndexMenu, 0, 0xFFFF, MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS) + 1;
DestroyMenu(hSubMenu);
}
if (!HasClipboardData())
{
TRACE("disabling paste options\n");
DisablePasteOptions(hMenu);
}
/* Directory is progid of filesystem folders only */
LPITEMIDLIST pidlFolderLast = ILFindLastID(m_pidlFolder);
if (_ILIsDesktop(pidlFolderLast) || _ILIsDrive(pidlFolderLast) || _ILIsFolder(pidlFolderLast))
{
/* Load context menu handlers */
TRACE("Add background handlers: %p\n", m_pidlFolder);
HKEY hKey;
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Directory\\Background", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
if (InsertMenuItemsOfDynamicContextMenuExtension(hMenu, GetMenuItemCount(hMenu) - 1, iIdCmdFirst, iIdCmdLast))
{
/* seperate dynamic context menu items */
_InsertMenuItemW(hMenu, GetMenuItemCount(hMenu) - 1, TRUE, -1, MFT_SEPARATOR, NULL, MFS_ENABLED);
}
}
return iIdCmdLast;
}
UINT
CDefaultContextMenu::AddStaticContextMenusToMenu(
HMENU hMenu,
UINT IndexMenu)
{
MENUITEMINFOW mii;
UINT idResource;
WCHAR wszVerb[40];
UINT fState;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
mii.fType = MFT_STRING;
mii.wID = 0x4000;
mii.dwTypeData = NULL;
m_iIdSCMFirst = mii.wID;
PStaticShellEntry pEntry = m_pStaticEntries;
while (pEntry)
{
fState = MFS_ENABLED;
mii.dwTypeData = NULL;
if (!wcsicmp(pEntry->szVerb, L"open"))
{
fState |= MFS_DEFAULT;
idResource = IDS_OPEN_VERB;
}
else if (!wcsicmp(pEntry->szVerb, L"explore"))
idResource = IDS_EXPLORE_VERB;
else if (!wcsicmp(pEntry->szVerb, L"runas"))
idResource = IDS_RUNAS_VERB;
else if (!wcsicmp(pEntry->szVerb, L"edit"))
idResource = IDS_EDIT_VERB;
else if (!wcsicmp(pEntry->szVerb, L"find"))
idResource = IDS_FIND_VERB;
else if (!wcsicmp(pEntry->szVerb, L"print"))
idResource = IDS_PRINT_VERB;
else if (!wcsicmp(pEntry->szVerb, L"printto"))
{
pEntry = pEntry->pNext;
continue;
}
else
idResource = 0;
/* By default use verb for menu item name */
mii.dwTypeData = pEntry->szVerb;
if (idResource > 0)
{
if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb)))
mii.dwTypeData = wszVerb; /* use translated verb */
else
ERR("Failed to load string\n");
}
else
{
WCHAR wszKey[256];
HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"%s\\shell\\%s", pEntry->szClass, pEntry->szVerb);
if (SUCCEEDED(hr))
{
DWORD cbVerb = sizeof(wszVerb);
if (RegGetValueW(HKEY_CLASSES_ROOT, wszKey, NULL, RRF_RT_REG_SZ, NULL, wszVerb, &cbVerb) == ERROR_SUCCESS)
mii.dwTypeData = wszVerb; /* use description for the menu entry */
}
}
mii.cch = wcslen(mii.dwTypeData);
mii.fState = fState;
InsertMenuItemW(hMenu, IndexMenu++, TRUE, &mii);
mii.wID++;
pEntry = pEntry->pNext;
}
m_iIdSCMLast = mii.wID - 1;
return IndexMenu;
}
void WINAPI _InsertMenuItemW(
HMENU hMenu,
UINT indexMenu,
BOOL fByPosition,
UINT wID,
UINT fType,
LPCWSTR dwTypeData,
UINT fState)
{
MENUITEMINFOW mii;
WCHAR wszText[100];
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
if (fType == MFT_SEPARATOR)
mii.fMask = MIIM_ID | MIIM_TYPE;
else if (fType == MFT_STRING)
{
mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
if ((ULONG_PTR)HIWORD((ULONG_PTR)dwTypeData) == 0)
{
if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText)))
mii.dwTypeData = wszText;
else
{
ERR("failed to load string %p\n", dwTypeData);
return;
}
}
else
mii.dwTypeData = (LPWSTR)dwTypeData;
mii.fState = fState;
}
mii.wID = wID;
mii.fType = fType;
InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
}
UINT
CDefaultContextMenu::BuildShellItemContextMenu(
HMENU hMenu,
UINT iIdCmdFirst,
UINT iIdCmdLast,
UINT uFlags)
{
HKEY hKey;
HRESULT hr;
TRACE("BuildShellItemContextMenu entered\n");
ASSERT(m_Dcm.cidl >= 1);
STRRET strFile;
hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strFile);
if (hr == S_OK)
{
WCHAR wszPath[MAX_PATH];
hr = StrRetToBufW(&strFile, m_Dcm.apidl[0], wszPath, _countof(wszPath));
if (hr == S_OK)
{
LPCWSTR pwszExt = PathFindExtensionW(wszPath);
if (pwszExt[0])
{
/* enumerate dynamic/static for a given file class */
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
/* add static verbs */
AddStaticEntryForFileClass(pwszExt);
/* load dynamic extensions from file extension key */
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
WCHAR wszTemp[40];
DWORD dwSize = sizeof(wszTemp);
if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
{
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
/* add static verbs from progid key */
AddStaticEntryForFileClass(wszTemp);
/* load dynamic extensions from progid key */
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
}
}
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"*", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
/* load default extensions */
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
}
}
else
ERR("GetDisplayNameOf failed: %x\n", hr);
GUID *pGuid = _ILGetGUIDPointer(m_Dcm.apidl[0]);
if (pGuid)
{
LPOLESTR pwszCLSID;
WCHAR buffer[60];
wcscpy(buffer, L"CLSID\\");
hr = StringFromCLSID(*pGuid, &pwszCLSID);
if (hr == S_OK)
{
wcscpy(&buffer[6], pwszCLSID);
TRACE("buffer %s\n", debugstr_w(buffer));
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, buffer, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
EnumerateDynamicContextHandlerForKey(hKey);
AddStaticEntryForFileClass(buffer);
RegCloseKey(hKey);
}
CoTaskMemFree(pwszCLSID);
}
}
if (_ILIsDrive(m_Dcm.apidl[0]))
{
AddStaticEntryForFileClass(L"Drive");
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Drive", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
}
/* add static actions */
SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
hr = m_Dcm.psf->GetAttributesOf(m_Dcm.cidl, m_Dcm.apidl, &rfg);
if (FAILED(hr))
{
ERR("GetAttributesOf failed: %x\n", hr);
rfg = 0;
}
if (rfg & SFGAO_FOLDER)
{
/* add the default verbs open / explore */
AddStaticEntryForFileClass(L"Folder");
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Folder", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
/* Directory is only loaded for real filesystem directories */
if (_ILIsFolder(m_Dcm.apidl[0]))
{
AddStaticEntryForFileClass(L"Directory");
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Directory", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
}
}
/* AllFilesystemObjects class is loaded only for files and directories */
if (_ILIsFolder(m_Dcm.apidl[0]) || _ILIsValue(m_Dcm.apidl[0]))
{
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"AllFilesystemObjects", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
/* sendto service is registered here */
EnumerateDynamicContextHandlerForKey(hKey);
RegCloseKey(hKey);
}
}
/* add static context menu handlers */
UINT IndexMenu = AddStaticContextMenusToMenu(hMenu, 0);
/* now process dynamic context menu handlers */
BOOL bAddSep = FALSE;
IndexMenu = InsertMenuItemsOfDynamicContextMenuExtension(hMenu, IndexMenu, iIdCmdFirst, iIdCmdLast);
TRACE("IndexMenu %d\n", IndexMenu);
if (_ILIsDrive(m_Dcm.apidl[0]))
{
/* The 'Format' option must be always available,
* thus it is not registered as a static shell extension */
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0x7ABC, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
bAddSep = TRUE;
}
BOOL bClipboardData = (HasClipboardData() && (rfg & SFGAO_FILESYSTEM));
if (rfg & (SFGAO_CANCOPY | SFGAO_CANMOVE) || bClipboardData)
{
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
if (rfg & SFGAO_CANMOVE)
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, MAKEINTRESOURCEW(IDS_CUT), MFS_ENABLED);
if (rfg & SFGAO_CANCOPY)
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, MAKEINTRESOURCEW(IDS_COPY), MFS_ENABLED);
if (bClipboardData)
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_INSERT, MFT_STRING, MAKEINTRESOURCEW(IDS_INSERT), MFS_ENABLED);
bAddSep = TRUE;
}
if (rfg & SFGAO_CANLINK)
{
bAddSep = FALSE;
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CREATELINK, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
}
if (rfg & SFGAO_CANDELETE)
{
if (bAddSep)
{
bAddSep = FALSE;
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
}
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, MAKEINTRESOURCEW(IDS_DELETE), MFS_ENABLED);
}
if (rfg & SFGAO_CANRENAME)
{
if (bAddSep)
{
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
}
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, MAKEINTRESOURCEW(IDS_RENAME), MFS_ENABLED);
bAddSep = TRUE;
}
if (rfg & SFGAO_HASPROPSHEET)
{
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
_InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
}
return iIdCmdLast;
}
HRESULT
WINAPI
CDefaultContextMenu::QueryContextMenu(
HMENU hMenu,
UINT IndexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
if (m_Dcm.cidl)
idCmdFirst = BuildShellItemContextMenu(hMenu, idCmdFirst, idCmdLast, uFlags);
else
idCmdFirst = BuildBackgroundContextMenu(hMenu, idCmdFirst, idCmdLast, uFlags);
return S_OK;
}
static
HRESULT
NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi, BOOL bRefresh)
{
/* Note: CWM_GETISHELLBROWSER returns not referenced object */
LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
if (!lpSB)
return E_FAIL;
LPSHELLVIEW lpSV = NULL;
if (FAILED(lpSB->QueryActiveShellView(&lpSV)))
return E_FAIL;
if (LOWORD(lpcmi->lpVerb) == FCIDM_SHVIEW_REFRESH || bRefresh)
lpSV->Refresh();
else
{
HWND hwndSV = NULL;
if (SUCCEEDED(lpSV->GetWindow(&hwndSV)))
SendMessageW(hwndSV, WM_COMMAND, MAKEWPARAM(LOWORD(lpcmi->lpVerb), 0), 0);
}
lpSV->Release();
return S_OK;
}
HRESULT
CDefaultContextMenu::DoPaste(
LPCMINVOKECOMMANDINFO lpcmi)
{
HRESULT hr;
IDataObject *pda;
if (OleGetClipboard(&pda) != S_OK)
return E_FAIL;
STGMEDIUM medium;
FORMATETC formatetc;
InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
hr = pda->GetData(&formatetc, &medium);
if (FAILED(hr))
{
pda->Release();
return E_FAIL;
}
/* lock the handle */
LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
if (!lpcida)
{
ReleaseStgMedium(&medium);
pda->Release();
return E_FAIL;
}
/* convert the data into pidl */
LPITEMIDLIST pidl;
LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
if (!apidl)
return E_FAIL;
IShellFolder *psfDesktop;
if (FAILED(SHGetDesktopFolder(&psfDesktop)))
{
SHFree(pidl);
_ILFreeaPidl(apidl, lpcida->cidl);
ReleaseStgMedium(&medium);
pda->Release();
return E_FAIL;
}
/* Find source folder */
IShellFolder *psfFrom = NULL;
if (_ILIsDesktop(pidl))
{
/* use desktop shellfolder */
psfFrom = psfDesktop;
}
else if (FAILED(psfDesktop->BindToObject(pidl, NULL, IID_IShellFolder, (LPVOID*)&psfFrom)))
{
ERR("no IShellFolder\n");
psfDesktop->Release();
SHFree(pidl);
_ILFreeaPidl(apidl, lpcida->cidl);
ReleaseStgMedium(&medium);
pda->Release();
return E_FAIL;
}
/* Find target folder */
IShellFolder *psfTarget = NULL;
if (m_Dcm.cidl)
{
psfDesktop->Release();
hr = m_Dcm.psf->BindToObject(m_Dcm.apidl[0], NULL, IID_IShellFolder, (LPVOID*)&psfTarget);
}
else
{
IPersistFolder2 *ppf2 = NULL;
LPITEMIDLIST pidl;
/* cidl is zero due to explorer view */
hr = m_Dcm.psf->QueryInterface(IID_IPersistFolder2, (LPVOID *) &ppf2);
if (SUCCEEDED(hr))
{
hr = ppf2->GetCurFolder(&pidl);
ppf2->Release();
if (SUCCEEDED(hr))
{
if (_ILIsDesktop(pidl))
{
/* use desktop shellfolder */
psfTarget = psfDesktop;
}
else
{
/* retrieve target desktop folder */
hr = psfDesktop->BindToObject(pidl, NULL, IID_IShellFolder, (LPVOID*)&psfTarget);
}
TRACE("psfTarget %x %p, Desktop %u\n", hr, psfTarget, _ILIsDesktop(pidl));
ILFree(pidl);
}
}
}
if (FAILED(hr))
{
ERR("no IShellFolder\n");
psfFrom->Release();
SHFree(pidl);
_ILFreeaPidl(apidl, lpcida->cidl);
ReleaseStgMedium(&medium);
pda->Release();
return E_FAIL;
}
/* get source and destination shellfolder */
ISFHelper *psfhlpdst;
if (FAILED(psfTarget->QueryInterface(IID_ISFHelper, (LPVOID*)&psfhlpdst)))
{
ERR("no IID_ISFHelper for destination\n");
psfFrom->Release();
psfTarget->Release();
SHFree(pidl);
_ILFreeaPidl(apidl, lpcida->cidl);
ReleaseStgMedium(&medium);
pda->Release();
return E_FAIL;
}
ISFHelper *psfhlpsrc;
if (FAILED(psfFrom->QueryInterface(IID_ISFHelper, (LPVOID*)&psfhlpsrc)))
{
ERR("no IID_ISFHelper for source\n");
psfhlpdst->Release();
psfFrom->Release();
psfTarget->Release();
SHFree(pidl);
_ILFreeaPidl(apidl, lpcida->cidl);
ReleaseStgMedium(&medium);
pda->Release();
return E_FAIL;
}
/* FIXXME
* do we want to perform a copy or move ???
*/
hr = psfhlpdst->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl);
psfhlpdst->Release();
psfhlpsrc->Release();
psfFrom->Release();
psfTarget->Release();
SHFree(pidl);
_ILFreeaPidl(apidl, lpcida->cidl);
ReleaseStgMedium(&medium);
pda->Release();
TRACE("CP result %x\n", hr);
return S_OK;
}
HRESULT
CDefaultContextMenu::DoOpenOrExplore(
LPCMINVOKECOMMANDINFO lpcmi)
{
UNIMPLEMENTED;
return E_FAIL;
}
BOOL
GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut)
{
WCHAR wszLink[40];
if (!bShortcut)
{
if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink)))
wszLink[0] = L'\0';
}
if (!bShortcut)
swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt);
else
swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt);
for (UINT i = 2; PathFileExistsW(pwszTarget); ++i)
{
if (!bShortcut)
swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt);
else
swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt);
}
return TRUE;
}
HRESULT
CDefaultContextMenu::DoCreateLink(
LPCMINVOKECOMMANDINFO lpcmi)
{
WCHAR wszTarget[MAX_PATH];
IPersistFile *ppf;
HRESULT hr;
STRRET strFile;
if (m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strFile) != S_OK)
{
ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
return E_FAIL;
}
WCHAR wszPath[MAX_PATH];
if (StrRetToBufW(&strFile, m_Dcm.apidl[0], wszPath, _countof(wszPath)) != S_OK)
return E_FAIL;
LPWSTR pwszExt = PathFindExtensionW(wszPath);
if (!wcsicmp(pwszExt, L".lnk"))
{
if (!GetUniqueFileName(wszPath, pwszExt, wszTarget, TRUE))
return E_FAIL;
hr = IShellLink_ConstructFromFile(NULL, IID_IPersistFile, m_Dcm.apidl[0], (LPVOID*)&ppf);
if (hr != S_OK)
return hr;
hr = ppf->Save(wszTarget, FALSE);
ppf->Release();
NotifyShellViewWindow(lpcmi, TRUE);
return hr;
}
else
{
if (!GetUniqueFileName(wszPath, L".lnk", wszTarget, TRUE))
return E_FAIL;
IShellLinkW *pLink;
hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_IShellLinkW, (void**)&pLink);
if (hr != S_OK)
return hr;
WCHAR szDirPath[MAX_PATH], *pwszFile;
GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
if (pwszFile) pwszFile[0] = 0;
if (SUCCEEDED(pLink->SetPath(wszPath)) &&
SUCCEEDED(pLink->SetWorkingDirectory(szDirPath)))
{
if (SUCCEEDED(pLink->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)))
{
hr = ppf->Save(wszTarget, TRUE);
ppf->Release();
}
}
pLink->Release();
NotifyShellViewWindow(lpcmi, TRUE);
return hr;
}
}
HRESULT
CDefaultContextMenu::DoDelete(
LPCMINVOKECOMMANDINFO lpcmi)
{
STRRET strTemp;
HRESULT hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strTemp);
if(hr != S_OK)
{
ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr);
return hr;
}
WCHAR wszPath[MAX_PATH];
hr = StrRetToBufW(&strTemp, m_Dcm.apidl[0], wszPath, _countof(wszPath));
if (hr != S_OK)
{
ERR("StrRetToBufW failed with %x\n", hr);
return hr;
}
/* Only keep the base path */
LPWSTR pwszFilename = PathFindFileNameW(wszPath);
*pwszFilename = L'\0';
/* Build paths list */
LPWSTR pwszPaths = BuildPathsList(wszPath, m_Dcm.cidl, m_Dcm.apidl);
if (!pwszPaths)
return E_FAIL;
/* Delete them */
SHFILEOPSTRUCTW FileOp;
ZeroMemory(&FileOp, sizeof(FileOp));
FileOp.hwnd = GetActiveWindow();
FileOp.wFunc = FO_DELETE;
FileOp.pFrom = pwszPaths;
FileOp.fFlags = FOF_ALLOWUNDO;
if (SHFileOperationW(&FileOp) != 0)
{
ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(), debugstr_w(pwszPaths));
return S_OK;
}
/* Get the active IShellView */
LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageW(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
if (lpSB)
{
/* Is the treeview focused */
HWND hwnd;
if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwnd)))
{
/* Remove selected items from treeview */
HTREEITEM hItem = TreeView_GetSelection(hwnd);
if (hItem)
(void)TreeView_DeleteItem(hwnd, hItem);
}
}
NotifyShellViewWindow(lpcmi, TRUE);
HeapFree(GetProcessHeap(), 0, pwszPaths);
return S_OK;
}
HRESULT
CDefaultContextMenu::DoCopyOrCut(
LPCMINVOKECOMMANDINFO lpcmi,
BOOL bCopy)
{
LPDATAOBJECT pDataObj;
HRESULT hr;
if (SUCCEEDED(SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_IDataObject, (void**)&pDataObj)))
{
hr = OleSetClipboard(pDataObj);
pDataObj->Release();
return hr;
}
/* Note: CWM_GETISHELLBROWSER returns not referenced object */
LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
if (!lpSB)
{
ERR("failed to get shellbrowser\n");
return E_FAIL;
}
LPSHELLVIEW lpSV;
hr = lpSB->QueryActiveShellView(&lpSV);
if (FAILED(hr))
{
ERR("failed to query the active shellview\n");
return hr;
}
hr = lpSV->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (LPVOID*)&pDataObj);
if (SUCCEEDED(hr))
{
hr = OleSetClipboard(pDataObj);
if (FAILED(hr))
ERR("OleSetClipboard failed");
pDataObj->Release();
} else
ERR("failed to get item object\n");
lpSV->Release();
return hr;
}
HRESULT
CDefaultContextMenu::DoRename(
LPCMINVOKECOMMANDINFO lpcmi)
{
/* get the active IShellView. Note: CWM_GETISHELLBROWSER returns not referenced object */
LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
if (!lpSB)
{
ERR("CWM_GETISHELLBROWSER failed\n");
return E_FAIL;
}
/* is the treeview focused */
HWND hwnd;
if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwnd)))
{
HTREEITEM hItem = TreeView_GetSelection(hwnd);
if (hItem)
(void)TreeView_EditLabel(hwnd, hItem);
}
LPSHELLVIEW lpSV;
HRESULT hr = lpSB->QueryActiveShellView(&lpSV);
if (FAILED(hr))
{
ERR("CWM_GETISHELLBROWSER failed\n");
return hr;
}
lpSV->SelectItem(m_Dcm.apidl[0],
SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT);
lpSV->Release();
return S_OK;
}
HRESULT
CDefaultContextMenu::DoProperties(
LPCMINVOKECOMMANDINFO lpcmi)
{
HRESULT hr = S_OK;
const ITEMIDLIST *pidlParent = m_Dcm.pidlFolder, *pidlChild;
if (!pidlParent)
{
IPersistFolder2 *pf;
/* pidlFolder is optional */
if (SUCCEEDED(m_Dcm.psf->QueryInterface(IID_IPersistFolder2, (PVOID*)&pf)))
{
pf->GetCurFolder((_ITEMIDLIST**)&pidlParent);
pf->Release();
}
}
if (m_Dcm.cidl > 0)
pidlChild = m_Dcm.apidl[0];
else
{
/* Set pidlChild to last pidl of current folder */
if (pidlParent == m_Dcm.pidlFolder)
pidlParent = (ITEMIDLIST*)ILClone(pidlParent);
pidlChild = (ITEMIDLIST*)ILClone(ILFindLastID(pidlParent));
ILRemoveLastID((ITEMIDLIST*)pidlParent);
}
if (_ILIsMyComputer(pidlChild))
{
if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL, NULL, SW_SHOWNORMAL))
hr = E_FAIL;
}
else if (_ILIsDesktop(pidlChild))
{
if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL, NULL, SW_SHOWNORMAL))
hr = E_FAIL;
}
else if (_ILIsDrive(pidlChild))
{
WCHAR wszBuf[MAX_PATH];
ILGetDisplayName(pidlChild, wszBuf);
if (!SH_ShowDriveProperties(wszBuf, pidlParent, &pidlChild))
hr = E_FAIL;
}
else if (_ILIsNetHood(pidlChild))
{
// FIXME path!
if (32 >= (UINT)ShellExecuteW(NULL, L"open", L"explorer.exe",
L"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
NULL, SW_SHOWDEFAULT))
hr = E_FAIL;
}
else if (_ILIsBitBucket(pidlChild))
{
/* FIXME: detect the drive path of bitbucket if appropiate */
if(!SH_ShowRecycleBinProperties(L'C'))
hr = E_FAIL;
}
else
{
if (m_Dcm.cidl > 1)
WARN("SHMultiFileProperties is not yet implemented\n");
STRRET strFile;
hr = m_Dcm.psf->GetDisplayNameOf(pidlChild, SHGDN_FORPARSING, &strFile);
if (SUCCEEDED(hr))
{
WCHAR wszBuf[MAX_PATH];
hr = StrRetToBufW(&strFile, pidlChild, wszBuf, _countof(wszBuf));
if (SUCCEEDED(hr))
hr = SH_ShowPropertiesDialog(wszBuf, pidlParent, &pidlChild);
else
ERR("StrRetToBufW failed\n");
}
else
ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
}
/* Free allocated PIDLs */
if (pidlParent != m_Dcm.pidlFolder)
ILFree((ITEMIDLIST*)pidlParent);
if (m_Dcm.cidl < 1 || pidlChild != m_Dcm.apidl[0])
ILFree((ITEMIDLIST*)pidlChild);
return hr;
}
HRESULT
CDefaultContextMenu::DoFormat(
LPCMINVOKECOMMANDINFO lpcmi)
{
char szDrive[8] = {0};
if (!_ILGetDrive(m_Dcm.apidl[0], szDrive, sizeof(szDrive)))
{
ERR("pidl is not a drive\n");
return E_FAIL;
}
SHFormatDrive(lpcmi->hwnd, szDrive[0] - 'A', SHFMT_ID_DEFAULT, 0);
return S_OK;
}
HRESULT
CDefaultContextMenu::DoDynamicShellExtensions(
LPCMINVOKECOMMANDINFO lpcmi)
{
UINT idCmd = LOWORD(lpcmi->lpVerb);
PDynamicShellEntry pEntry = m_pDynamicEntries;
TRACE("verb %p first %x last %x", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
while(pEntry && idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
pEntry = pEntry->pNext;
if (!pEntry)
return E_FAIL;
if (idCmd >= pEntry->iIdCmdFirst && idCmd <= pEntry->iIdCmdFirst + pEntry->NumIds)
{
/* invoke the dynamic context menu */
lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
return pEntry->pCM->InvokeCommand(lpcmi);
}
return E_FAIL;
}
DWORD
CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry)
{
LPSHELLBROWSER lpSB;
HWND hwndTree;
LPWSTR FlagsName;
WCHAR wszKey[256];
HRESULT hr;
DWORD wFlags;
DWORD cbVerb;
/* Get a pointer to the shell browser */
lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
if (lpSB == NULL)
return 0;
/* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
FlagsName = L"ExplorerFlags";
else
FlagsName = L"BrowserFlags";
/* Try to get the flag from the verb */
hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"%s\\shell\\%s", pEntry->szClass, pEntry->szVerb);
if (!SUCCEEDED(hr))
return 0;
cbVerb = sizeof(wFlags);
if (RegGetValueW(HKEY_CLASSES_ROOT, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
{
return wFlags;
}
return 0;
}
HRESULT
CDefaultContextMenu::TryToBrowse(
LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
{
LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageW(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
HRESULT hr;
if (lpSB == NULL)
return E_FAIL;
hr = lpSB->BrowseObject(ILCombine(m_Dcm.pidlFolder, pidl), wFlags);
return hr;
}
HRESULT
CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
{
HRESULT hr;
STRRET strFile;
hr = m_Dcm.psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
if (hr != S_OK)
{
ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
return hr;
}
WCHAR wszPath[MAX_PATH];
hr = StrRetToBufW(&strFile, pidl, wszPath, MAX_PATH);
if (hr != S_OK)
return hr;
WCHAR wszDir[MAX_PATH];
wcscpy(wszDir, wszPath);
PathRemoveFileSpec(wszDir);
SHELLEXECUTEINFOW sei;
ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.hwnd = lpcmi->hwnd;
sei.nShow = SW_SHOWNORMAL;
sei.lpVerb = pEntry->szVerb;
sei.lpFile = wszPath;
sei.lpDirectory = wszDir;
ShellExecuteExW(&sei);
return S_OK;
}
HRESULT
CDefaultContextMenu::DoStaticShellExtensions(
LPCMINVOKECOMMANDINFO lpcmi)
{
PStaticShellEntry pEntry = m_pStaticEntries;
INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst;
HRESULT hr;
UINT i;
while (pEntry && (iCmd--) > 0)
pEntry = pEntry->pNext;
if (iCmd > 0)
return E_FAIL;
/* Get the browse flags to see if we need to browse */
DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
BOOL bBrowsed = FALSE;
for (i=0; i < m_Dcm.cidl; i++)
{
/* Check if we need to browse */
if (wFlags > 0)
{
/* In xp if we have browsed, we don't open any more folders .
* In win7 we browse to the first folder we find and
* open new windows fo for each of the rest of the folders */
if (bBrowsed)
continue;
hr = TryToBrowse(lpcmi, m_Dcm.apidl[i], wFlags);
if (SUCCEEDED(hr))
{
bBrowsed = TRUE;
continue;
}
}
InvokePidl(lpcmi, m_Dcm.apidl[i], pEntry);
}
return S_OK;
}
HRESULT
WINAPI
CDefaultContextMenu::InvokeCommand(
LPCMINVOKECOMMANDINFO lpcmi)
{
switch(LOWORD(lpcmi->lpVerb))
{
case FCIDM_SHVIEW_BIGICON:
case FCIDM_SHVIEW_SMALLICON:
case FCIDM_SHVIEW_LISTVIEW:
case FCIDM_SHVIEW_REPORTVIEW:
case 0x30: /* FIX IDS in resource files */
case 0x31:
case 0x32:
case 0x33:
case FCIDM_SHVIEW_AUTOARRANGE:
case FCIDM_SHVIEW_SNAPTOGRID:
case FCIDM_SHVIEW_REFRESH:
return NotifyShellViewWindow(lpcmi, FALSE);
case FCIDM_SHVIEW_INSERT:
case FCIDM_SHVIEW_INSERTLINK:
return DoPaste(lpcmi);
case FCIDM_SHVIEW_OPEN:
case FCIDM_SHVIEW_EXPLORE:
return DoOpenOrExplore(lpcmi);
case FCIDM_SHVIEW_COPY:
case FCIDM_SHVIEW_CUT:
return DoCopyOrCut(lpcmi, LOWORD(lpcmi->lpVerb) == FCIDM_SHVIEW_COPY);
case FCIDM_SHVIEW_CREATELINK:
return DoCreateLink(lpcmi);
case FCIDM_SHVIEW_DELETE:
return DoDelete(lpcmi);
case FCIDM_SHVIEW_RENAME:
return DoRename(lpcmi);
case FCIDM_SHVIEW_PROPERTIES:
return DoProperties(lpcmi);
case 0x7ABC:
return DoFormat(lpcmi);
}
if (m_iIdSHEFirst && m_iIdSHELast)
{
if (LOWORD(lpcmi->lpVerb) >= m_iIdSHEFirst && LOWORD(lpcmi->lpVerb) <= m_iIdSHELast)
return DoDynamicShellExtensions(lpcmi);
}
if (m_iIdSCMFirst && m_iIdSCMLast)
{
if (LOWORD(lpcmi->lpVerb) >= m_iIdSCMFirst && LOWORD(lpcmi->lpVerb) <= m_iIdSCMLast)
return DoStaticShellExtensions(lpcmi);
}
FIXME("Unhandled Verb %xl\n", LOWORD(lpcmi->lpVerb));
return E_UNEXPECTED;
}
HRESULT
WINAPI
CDefaultContextMenu::GetCommandString(
UINT_PTR idCommand,
UINT uFlags,
UINT* lpReserved,
LPSTR lpszName,
UINT uMaxNameLen)
{
return S_OK;
}
HRESULT
WINAPI
CDefaultContextMenu::HandleMenuMsg(
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
return S_OK;
}
static
HRESULT
IDefaultContextMenu_Constructor(
const DEFCONTEXTMENU *pdcm,
REFIID riid,
void **ppv)
{
if (ppv == NULL)
return E_POINTER;
*ppv = NULL;
CComObject<CDefaultContextMenu> *pCM;
HRESULT hr = CComObject<CDefaultContextMenu>::CreateInstance(&pCM);
if (FAILED(hr))
return hr;
pCM->AddRef(); // CreateInstance returns object with 0 ref count */
CComPtr<IUnknown> pResult;
hr = pCM->QueryInterface(riid, (void **)&pResult);
if (FAILED(hr))
{
pCM->Release();
return hr;
}
hr = pCM->Initialize(pdcm);
if (FAILED(hr))
{
pCM->Release();
return hr;
}
*ppv = pResult.Detach();
pCM->Release();
TRACE("This(%p) cidl %u\n", *ppv, pdcm->cidl);
return S_OK;
}
/*************************************************************************
* SHCreateDefaultContextMenu [SHELL32.325] Vista API
*
*/
HRESULT
WINAPI
SHCreateDefaultContextMenu(
const DEFCONTEXTMENU *pdcm,
REFIID riid,
void **ppv)
{
*ppv = NULL;
HRESULT hr = IDefaultContextMenu_Constructor(pdcm, riid, ppv);
if (FAILED(hr))
ERR("IDefaultContextMenu_Constructor failed: %x\n", hr);
TRACE("pcm %p hr %x\n", pdcm, hr);
return hr;
}
/*************************************************************************
* CDefFolderMenu_Create2 [SHELL32.701]
*
*/
HRESULT
WINAPI
CDefFolderMenu_Create2(
LPCITEMIDLIST pidlFolder,
HWND hwnd,
UINT cidl,
LPCITEMIDLIST *apidl,
IShellFolder *psf,
LPFNDFMCALLBACK lpfn,
UINT nKeys,
const HKEY *ahkeyClsKeys,
IContextMenu **ppcm)
{
DEFCONTEXTMENU pdcm;
pdcm.hwnd = hwnd;
pdcm.pcmcb = NULL;
pdcm.pidlFolder = pidlFolder;
pdcm.psf = psf;
pdcm.cidl = cidl;
pdcm.apidl = apidl;
pdcm.punkAssociationInfo = NULL;
pdcm.cKeys = nKeys;
pdcm.aKeys = ahkeyClsKeys;
HRESULT hr = SHCreateDefaultContextMenu(&pdcm, IID_IContextMenu, (void**)ppcm);
return hr;
}