reactos/dll/win32/shell32/CNewMenu.cpp

781 lines
21 KiB
C++
Raw Normal View History

/*
* provides new shell item service
*
* Copyright 2007 Johannes Anderwald (johannes.anderwald@reactos.org)
* Copyright 2009 Andrew Hill
* Copyright 2012 Rafal Harabien
* Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "precomp.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
CNewMenu::CNewMenu() :
m_pidlFolder(NULL),
m_pItems(NULL),
m_pLinkItem(NULL),
m_pSite(NULL),
m_idCmdFirst(0),
m_idCmdFolder(-1),
m_idCmdLink(-1),
m_hIconFolder(NULL),
m_hIconLink(NULL)
{
}
CNewMenu::~CNewMenu()
{
UnloadAllItems();
if (m_pidlFolder)
ILFree(m_pidlFolder);
}
void CNewMenu::UnloadItem(SHELLNEW_ITEM *pItem)
{
/* Note: free allows NULL as argument */
free(pItem->pData);
free(pItem->pwszDesc);
free(pItem->pwszExt);
if (pItem->hIcon)
DestroyIcon(pItem->hIcon);
HeapFree(GetProcessHeap(), 0, pItem);
}
void CNewMenu::UnloadAllItems()
{
SHELLNEW_ITEM *pCurItem;
/* Unload the handler items, including the link item */
while (m_pItems)
{
pCurItem = m_pItems;
m_pItems = m_pItems->pNext;
UnloadItem(pCurItem);
}
m_pItems = NULL;
m_pLinkItem = NULL;
}
CNewMenu::SHELLNEW_ITEM *CNewMenu::LoadItem(LPCWSTR pwszExt)
{
HKEY hKey;
WCHAR wszBuf[MAX_PATH];
PBYTE pData = NULL;
DWORD cbData;
StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s\\ShellNew", pwszExt);
TRACE("LoadItem Keyname %s Name %s\n", debugstr_w(pwszExt), debugstr_w(wszBuf));
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
{
TRACE("Failed to open key\n");
return NULL;
}
/* Find first valid value */
struct
{
LPCWSTR pszName;
SHELLNEW_TYPE Type;
BOOL bNeedData;
BOOL bStr;
} Types[] = {
{L"FileName", SHELLNEW_TYPE_FILENAME, TRUE, TRUE},
{L"Command", SHELLNEW_TYPE_COMMAND, TRUE, TRUE},
{L"Data", SHELLNEW_TYPE_DATA, TRUE, FALSE},
{L"NullFile", SHELLNEW_TYPE_NULLFILE, FALSE},
{NULL}
};
UINT i;
for (i = 0; Types[i].pszName; ++i)
{
/* Note: We are using ANSI function because strings can be treated as data */
cbData = 0;
DWORD dwFlags = Types[i].bStr ? RRF_RT_REG_SZ : RRF_RT_ANY;
DWORD dwType;
if (RegGetValueW(hKey, NULL, Types[i].pszName, dwFlags, NULL, NULL, &cbData) == ERROR_SUCCESS)
{
if (Types[i].bNeedData && cbData > 0)
{
pData = (PBYTE)malloc(cbData);
RegGetValueW(hKey, NULL, Types[i].pszName, dwFlags, &dwType, pData, &cbData);
if (!Types[i].bStr && (dwType == REG_SZ || dwType == REG_EXPAND_SZ))
{
PBYTE pData2 = (PBYTE)malloc(cbData);
cbData = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)pData, -1, (LPSTR)pData2, cbData, NULL, NULL);
free(pData);
pData = pData2;
}
}
break;
}
}
RegCloseKey(hKey);
/* Was any key found? */
if (!Types[i].pszName)
{
free(pData);
return NULL;
}
SHFILEINFOW fi;
if (!SHGetFileInfoW(pwszExt, FILE_ATTRIBUTE_NORMAL, &fi, sizeof(fi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME | SHGFI_ICON | SHGFI_SMALLICON))
{
free(pData);
return NULL;
}
/* Create new item */
SHELLNEW_ITEM *pNewItem = static_cast<SHELLNEW_ITEM *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SHELLNEW_ITEM)));
if (!pNewItem)
{
free(pData);
return NULL;
}
TRACE("new item %ls\n", fi.szTypeName);
pNewItem->Type = Types[i].Type;
pNewItem->pData = pData;
pNewItem->cbData = pData ? cbData : 0;
pNewItem->pwszExt = _wcsdup(pwszExt);
pNewItem->pwszDesc = _wcsdup(fi.szTypeName);
if (fi.hIcon)
pNewItem->hIcon = fi.hIcon;
return pNewItem;
}
BOOL
CNewMenu::CacheItems()
{
HKEY hKey;
DWORD dwSize = 0;
DWORD dwIndex = 0;
LPWSTR lpValue;
LPWSTR lpValues;
WCHAR wszName[MAX_PATH];
SHELLNEW_ITEM *pNewItem;
SHELLNEW_ITEM *pCurItem = NULL;
/* Enumerate all extensions */
while (RegEnumKeyW(HKEY_CLASSES_ROOT, dwIndex++, wszName, _countof(wszName)) == ERROR_SUCCESS)
{
if (wszName[0] != L'.')
continue;
pNewItem = LoadItem(wszName);
if (pNewItem)
{
dwSize += wcslen(wszName) + 1;
if (!m_pLinkItem && wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
{
/* The unique link handler */
m_pLinkItem = pNewItem;
}
/* Add at the end of the list */
if (pCurItem)
{
pCurItem->pNext = pNewItem;
pCurItem = pNewItem;
}
else
{
pCurItem = m_pItems = pNewItem;
}
}
}
dwSize++;
lpValues = (LPWSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize * sizeof(WCHAR));
if (!lpValues)
return FALSE;
for (pCurItem = m_pItems, lpValue = lpValues; pCurItem; pCurItem = pCurItem->pNext)
{
memcpy(lpValue, pCurItem->pwszExt, (wcslen(pCurItem->pwszExt) + 1) * sizeof(WCHAR));
lpValue += wcslen(pCurItem->pwszExt) + 1;
}
if (RegCreateKeyEx(HKEY_CURRENT_USER, ShellNewKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
{
HeapFree(GetProcessHeap(), 0, lpValues);
return FALSE;
}
if (RegSetValueExW(hKey, L"Classes", NULL, REG_MULTI_SZ, (LPBYTE)lpValues, dwSize * sizeof(WCHAR)) != ERROR_SUCCESS)
{
HeapFree(GetProcessHeap(), 0, lpValues);
RegCloseKey(hKey);
return FALSE;
}
HeapFree(GetProcessHeap(), 0, lpValues);
RegCloseKey(hKey);
return TRUE;
}
BOOL
CNewMenu::LoadCachedItems()
{
LPWSTR wszName;
LPWSTR lpValues;
DWORD dwSize;
HKEY hKey;
SHELLNEW_ITEM *pNewItem;
SHELLNEW_ITEM *pCurItem = NULL;
if (RegOpenKeyExW(HKEY_CURRENT_USER, ShellNewKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
return FALSE;
if (RegQueryValueExW(hKey, L"Classes", NULL, NULL, NULL, &dwSize) != ERROR_SUCCESS)
return FALSE;
lpValues = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dwSize);
if (!lpValues)
return FALSE;
if (RegQueryValueExW(hKey, L"Classes", NULL, NULL, (LPBYTE)lpValues, &dwSize) != ERROR_SUCCESS)
{
HeapFree(GetProcessHeap(), 0, lpValues);
return FALSE;
}
wszName = lpValues;
for (; *wszName != '\0'; wszName += wcslen(wszName) + 1)
{
pNewItem = LoadItem(wszName);
if (pNewItem)
{
if (!m_pLinkItem && wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
{
/* The unique link handler */
m_pLinkItem = pNewItem;
}
/* Add at the end of the list */
if (pCurItem)
{
pCurItem->pNext = pNewItem;
pCurItem = pNewItem;
}
else
{
pCurItem = m_pItems = pNewItem;
}
}
}
HeapFree(GetProcessHeap(), 0, lpValues);
RegCloseKey(hKey);
return TRUE;
}
BOOL
CNewMenu::LoadAllItems()
{
// TODO: We need to find a way to refresh the cache from time to time, when
// e.g. new extensions with ShellNew handlers have been added or removed.
/* If there are any unload them */
UnloadAllItems();
if (!LoadCachedItems())
{
CacheItems();
}
return (m_pItems != NULL);
}
UINT
CNewMenu::InsertShellNewItems(HMENU hMenu, UINT idCmdFirst, UINT Pos)
{
MENUITEMINFOW mii;
UINT idCmd = idCmdFirst;
WCHAR wszBuf[256];
if (m_pItems == NULL)
{
if (!LoadAllItems())
return 0;
}
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
m_idCmdFirst = idCmd;
/* Insert the new folder action */
if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWFOLDER, wszBuf, _countof(wszBuf)))
wszBuf[0] = 0;
mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_STRING;
mii.dwTypeData = wszBuf;
mii.cch = wcslen(mii.dwTypeData);
mii.wID = idCmd;
mii.hbmpItem = HBMMENU_CALLBACK;
if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
m_idCmdFolder = idCmd++;
/* Insert the new shortcut action */
if (m_pLinkItem)
{
if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWLINK, wszBuf, _countof(wszBuf)))
wszBuf[0] = 0;
mii.dwTypeData = wszBuf;
mii.cch = wcslen(mii.dwTypeData);
mii.wID = idCmd;
if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
m_idCmdLink = idCmd++;
}
/* Insert a seperator for the custom new action */
mii.fMask = MIIM_TYPE | MIIM_ID;
mii.fType = MFT_SEPARATOR;
mii.wID = -1;
InsertMenuItemW(hMenu, Pos++, TRUE, &mii);
/* Insert the rest of the items */
mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_STRING | MIIM_DATA;
mii.fType = 0;
for (SHELLNEW_ITEM *pCurItem = m_pItems; pCurItem; pCurItem = pCurItem->pNext)
{
/* Skip shortcut item */
if (pCurItem == m_pLinkItem)
continue;
TRACE("szDesc %s\n", debugstr_w(pCurItem->pwszDesc));
mii.dwItemData = (ULONG_PTR)pCurItem;
mii.dwTypeData = pCurItem->pwszDesc;
mii.cch = wcslen(mii.dwTypeData);
mii.wID = idCmd;
if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
++idCmd;
}
return idCmd - idCmdFirst;
}
CNewMenu::SHELLNEW_ITEM *CNewMenu::FindItemFromIdOffset(UINT IdOffset)
{
/* Folder */
if (m_idCmdFirst + IdOffset == m_idCmdFolder)
return NULL;
/* Shortcut */
if (m_idCmdFirst + IdOffset == m_idCmdLink)
return m_pLinkItem;
/* Find shell new item - Retrieve menu item info */
MENUITEMINFOW mii;
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_DATA;
if (GetMenuItemInfoW(m_hSubMenu, m_idCmdFirst + IdOffset, FALSE, &mii) && mii.dwItemData)
return (SHELLNEW_ITEM *)mii.dwItemData;
else
return NULL;
}
HRESULT CNewMenu::SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName, BOOL bRename)
{
CComPtr<IShellBrowser> lpSB;
CComPtr<IShellView> lpSV;
HRESULT hr = E_FAIL;
LPITEMIDLIST pidl;
PITEMID_CHILD pidlNewItem;
DWORD dwSelectFlags;
dwSelectFlags = SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
if (bRename)
dwSelectFlags |= SVSI_EDIT;
/* Notify the view object about the new item */
SHChangeNotify(wEventId, uFlags | SHCNF_FLUSH, (LPCVOID)pszName, NULL);
if (!m_pSite)
return S_OK;
/* Get a pointer to the shell view */
hr = IUnknown_QueryService(m_pSite, SID_IFolderView, IID_PPV_ARG(IShellView, &lpSV));
if (FAILED_UNEXPECTEDLY(hr))
return S_OK;
/* Attempt to get the pidl of the new item */
hr = SHILCreateFromPathW(pszName, &pidl, NULL);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
pidlNewItem = ILFindLastID(pidl);
hr = lpSV->SelectItem(pidlNewItem, dwSelectFlags);
SHFree(pidl);
return hr;
}
// Code is duplicated in CDefaultContextMenu
HRESULT CNewMenu::CreateNewFolder(LPCMINVOKECOMMANDINFO lpici)
{
WCHAR wszPath[MAX_PATH];
WCHAR wszName[MAX_PATH];
WCHAR wszNewFolder[25];
HRESULT hr;
/* Get folder path */
hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
return E_FAIL;
/* Create the name of the new directory */
if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
return E_FAIL;
/* Create the new directory and show the appropriate dialog in case of error */
if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
return E_FAIL;
/* Show and select the new item in the def view */
SelectNewItem(SHCNE_MKDIR, SHCNF_PATHW, wszName, TRUE);
return S_OK;
}
HRESULT CNewMenu::NewItemByCommand(SHELLNEW_ITEM *pItem, LPCWSTR wszPath)
{
WCHAR wszBuf[MAX_PATH];
LPWSTR Ptr, pwszCmd;
WCHAR wszTemp[MAX_PATH];
STARTUPINFOW si;
PROCESS_INFORMATION pi;
if (!ExpandEnvironmentStringsW((LPWSTR)pItem->pData, wszBuf, _countof(wszBuf)))
{
TRACE("ExpandEnvironmentStrings failed\n");
return E_FAIL;
}
/* Expand command parameter, FIXME: there can be more modifiers */
Ptr = wcsstr(wszBuf, L"%1");
if (Ptr)
{
Ptr[1] = L's';
StringCbPrintfW(wszTemp, sizeof(wszTemp), wszBuf, wszPath);
pwszCmd = wszTemp;
}
else
{
pwszCmd = wszBuf;
}
/* Create process */
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
if (CreateProcessW(NULL, pwszCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return S_OK;
}
else
{
ERR("Failed to create process\n");
return E_FAIL;
}
}
HRESULT CNewMenu::NewItemByNonCommand(SHELLNEW_ITEM *pItem, LPWSTR wszName,
DWORD cchNameMax, LPCWSTR wszPath)
{
WCHAR wszBuf[MAX_PATH];
WCHAR wszNewFile[MAX_PATH];
BOOL bSuccess = TRUE;
if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEW, wszBuf, _countof(wszBuf)))
return E_FAIL;
StringCchPrintfW(wszNewFile, _countof(wszNewFile), L"%s %s%s", wszBuf, pItem->pwszDesc, pItem->pwszExt);
/* Create the name of the new file */
if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFile))
return E_FAIL;
/* Create new file */
HANDLE hFile = CreateFileW(wszName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
if (pItem->Type == SHELLNEW_TYPE_DATA)
{
/* Write a content */
DWORD cbWritten;
WriteFile(hFile, pItem->pData, pItem->cbData, &cbWritten, NULL);
}
/* Close file now */
CloseHandle(hFile);
}
else
{
bSuccess = FALSE;
}
if (pItem->Type == SHELLNEW_TYPE_FILENAME)
{
/* Copy file */
if (!CopyFileW((LPWSTR)pItem->pData, wszName, FALSE))
ERR("Copy file failed: %ls\n", (LPWSTR)pItem->pData);
}
/* Show message if we failed */
if (bSuccess)
{
TRACE("Notifying fs %s\n", debugstr_w(wszName));
SelectNewItem(SHCNE_CREATE, SHCNF_PATHW, wszName, pItem != m_pLinkItem);
}
else
{
CStringW Caption(MAKEINTRESOURCEW(IDS_CREATEFILE_CAPTION));
CStringW Message(MAKEINTRESOURCEW(IDS_CREATEFILE_DENIED));
Message.FormatMessage(Message.GetString(), wszName);
MessageBoxW(0, Message, Caption, MB_ICONEXCLAMATION | MB_OK);
}
return S_OK;
}
HRESULT CNewMenu::CreateNewItem(SHELLNEW_ITEM *pItem, LPCMINVOKECOMMANDINFO lpcmi)
{
HRESULT hr;
WCHAR wszPath[MAX_PATH], wszName[MAX_PATH];
/* Get folder path */
hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (pItem == m_pLinkItem)
{
NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
NewItemByCommand(pItem, wszName);
return S_OK;
}
switch (pItem->Type)
{
case SHELLNEW_TYPE_COMMAND:
NewItemByCommand(pItem, wszPath);
break;
case SHELLNEW_TYPE_DATA:
case SHELLNEW_TYPE_FILENAME:
case SHELLNEW_TYPE_NULLFILE:
NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
break;
case SHELLNEW_TYPE_INVALID:
ERR("Invalid type\n");
break;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CNewMenu::SetSite(IUnknown *pUnkSite)
{
m_pSite = pUnkSite;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CNewMenu::GetSite(REFIID riid, void **ppvSite)
{
return m_pSite->QueryInterface(riid, ppvSite);
}
HRESULT
WINAPI
CNewMenu::QueryContextMenu(HMENU hMenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
MENUITEMINFOW mii;
UINT cItems = 0;
WCHAR wszNew[200];
TRACE("%p %p %u %u %u %u\n", this,
hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEW, wszNew, _countof(wszNew)))
return E_FAIL;
m_hSubMenu = CreateMenu();
if (!m_hSubMenu)
return E_FAIL;
cItems = InsertShellNewItems(m_hSubMenu, idCmdFirst, 0);
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE | MIIM_SUBMENU;
mii.fType = MFT_STRING;
mii.wID = -1;
mii.dwTypeData = wszNew;
mii.cch = wcslen(mii.dwTypeData);
mii.fState = MFS_ENABLED;
mii.hSubMenu = m_hSubMenu;
if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
return E_FAIL;
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cItems);
}
HRESULT
WINAPI
CNewMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
{
HRESULT hr = E_FAIL;
if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdFolder)
{
hr = CreateNewFolder(lpici);
}
else
{
SHELLNEW_ITEM *pItem = FindItemFromIdOffset(LOWORD(lpici->lpVerb));
if (pItem)
hr = CreateNewItem(pItem, lpici);
}
TRACE("CNewMenu::InvokeCommand %x\n", hr);
return hr;
}
HRESULT
WINAPI
CNewMenu::GetCommandString(UINT_PTR idCmd,
UINT uType,
UINT *pwReserved,
LPSTR pszName,
UINT cchMax)
{
FIXME("%p %lu %u %p %p %u\n", this,
idCmd, uType, pwReserved, pszName, cchMax );
return E_NOTIMPL;
}
HRESULT
WINAPI
CNewMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return S_OK;
}
HRESULT
WINAPI
CNewMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
{
switch (uMsg)
{
case WM_MEASUREITEM:
{
MEASUREITEMSTRUCT* lpmis = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
if (!lpmis || lpmis->CtlType != ODT_MENU)
break;
if (lpmis->itemWidth < (UINT)GetSystemMetrics(SM_CXMENUCHECK))
lpmis->itemWidth = GetSystemMetrics(SM_CXMENUCHECK);
if (lpmis->itemHeight < 16)
lpmis->itemHeight = 16;
if (plResult)
*plResult = TRUE;
break;
}
case WM_DRAWITEM:
{
DRAWITEMSTRUCT* lpdis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
if (!lpdis || lpdis->CtlType != ODT_MENU)
break;
[SHELL32] - CDefaultContextMenu: Make it respect the IContextMenu interface and expect menu id offsets instead of real menu ids and actually use the idCmdFirst and idCmdLast parameters in QueryContextMenu. Make the default part use and existed menu from the resources but changed accordingly so the ids of its elements can be adjusted to the next free id of the constructed menu. Rename InsertMenuItemsOfDynamicContextMenuExtension to AddShellExtensionsToMenu, DoDynamicShellExtensions to InvokeShellExt, and DoStaticShellExtensions to InvokeRegVerb. Make AddShellExtensionsToMenu and AddStaticContextMenusToMenu behave like QueryContextMenu to return the number of ids they occupy. Add two helpers SHGetMenuIdFromMenuMsg and SHSetMenuIdInMenuMsg to change the id that is contained in the lparam of WM_DRAWITEM and WM_MEASUREITEM before forwarding them. - CDefView: When calling QueryContextMenu before using TrackPopupMenu, use 1 as the first id that will filled by the IContextMenu because we want 0 to be used as an indicator that the menu was canceled. Use SHGetMenuIdFromMenuMsg and SHSetMenuIdInMenuMsg to change the lparam of the messages forwarded to the IContextMenu accordingly. - CDefViewBckgrndMenu: Add a hack so as to keep the code as simple as possible while respecting idCmdFirst and idCmdLast. - CNewMenu: Remove a hack that was needed because WM_DRAWITEM didn't come with the menu id offset but with the real menu id. - CDesktopFolder, CFSFolder: Make the callbacks avoid adding an extra separators. After that shell extensions in menus should work better and callbacks to shell folders should really be able to add several menu items. svn path=/trunk/; revision=75533
2017-08-14 15:25:58 +00:00
DWORD id = LOWORD(lpdis->itemID);
HICON hIcon = NULL;
if (m_idCmdFirst + id == m_idCmdFolder)
{
hIcon = m_hIconFolder;
}
else if (m_idCmdFirst + id == m_idCmdLink)
{
hIcon = m_hIconLink;
}
else
{
SHELLNEW_ITEM *pItem = FindItemFromIdOffset(id);
if (pItem)
hIcon = pItem->hIcon;
}
if (!hIcon)
break;
DrawIconEx(lpdis->hDC,
2,
lpdis->rcItem.top + (lpdis->rcItem.bottom - lpdis->rcItem.top - 16) / 2,
hIcon,
16,
16,
0, NULL, DI_NORMAL);
if(plResult)
*plResult = TRUE;
}
}
return S_OK;
}
HRESULT WINAPI
CNewMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder,
IDataObject *pdtobj, HKEY hkeyProgID)
{
m_pidlFolder = ILClone(pidlFolder);
/* Load folder and shortcut icons */
m_hIconFolder = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_FOLDER), IMAGE_ICON, 16, 16, LR_SHARED);
m_hIconLink = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_SHORTCUT), IMAGE_ICON, 16, 16, LR_SHARED);
return S_OK;
}