2015-01-22 20:44:54 +03:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015 Daniel Molkentin <danimo@owncloud.com>. All rights reserved.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
#include "NCContextMenu.h"
|
|
|
|
#include "NCClientInterface.h"
|
2015-01-22 20:44:54 +03:00
|
|
|
|
|
|
|
#include <shobjidl.h>
|
|
|
|
#include <shlwapi.h>
|
|
|
|
#include <shellapi.h>
|
|
|
|
#include <StringUtil.h>
|
2020-08-17 21:35:39 +03:00
|
|
|
#include <strsafe.h>
|
2015-01-22 20:44:54 +03:00
|
|
|
|
|
|
|
extern long g_cDllRef;
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
NCContextMenu::NCContextMenu(void)
|
2017-01-05 19:28:28 +03:00
|
|
|
: m_cRef(1)
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2017-01-05 19:28:28 +03:00
|
|
|
InterlockedIncrement(&g_cDllRef);
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
NCContextMenu::~NCContextMenu(void)
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2017-01-05 19:28:28 +03:00
|
|
|
InterlockedDecrement(&g_cDllRef);
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma region IUnknown
|
|
|
|
|
|
|
|
// Query to the interface the component supported.
|
2020-08-18 20:11:08 +03:00
|
|
|
IFACEMETHODIMP NCContextMenu::QueryInterface(REFIID riid, void **ppv)
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2017-01-05 19:28:28 +03:00
|
|
|
static const QITAB qit[] =
|
|
|
|
{
|
2020-08-18 20:11:08 +03:00
|
|
|
QITABENT(NCContextMenu, IContextMenu),
|
|
|
|
QITABENT(NCContextMenu, IShellExtInit),
|
2023-02-22 00:14:22 +03:00
|
|
|
{ nullptr },
|
2017-01-05 19:28:28 +03:00
|
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppv);
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Increase the reference count for an interface on an object.
|
2020-08-18 20:11:08 +03:00
|
|
|
IFACEMETHODIMP_(ULONG) NCContextMenu::AddRef()
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2017-01-05 19:28:28 +03:00
|
|
|
return InterlockedIncrement(&m_cRef);
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Decrease the reference count for an interface on an object.
|
2020-08-18 20:11:08 +03:00
|
|
|
IFACEMETHODIMP_(ULONG) NCContextMenu::Release()
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2017-01-05 19:28:28 +03:00
|
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
|
|
if (0 == cRef) {
|
|
|
|
delete this;
|
|
|
|
}
|
2015-01-22 20:44:54 +03:00
|
|
|
|
2017-01-05 19:28:28 +03:00
|
|
|
return cRef;
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
|
|
|
|
#pragma region IShellExtInit
|
|
|
|
|
|
|
|
// Initialize the context menu handler.
|
2020-08-18 20:11:08 +03:00
|
|
|
IFACEMETHODIMP NCContextMenu::Initialize(
|
2021-01-05 14:08:04 +03:00
|
|
|
LPCITEMIDLIST, LPDATAOBJECT pDataObj, HKEY)
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2018-01-19 21:44:10 +03:00
|
|
|
m_selectedFiles.clear();
|
|
|
|
|
2017-01-05 19:28:28 +03:00
|
|
|
if (!pDataObj) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
2020-06-05 01:51:32 +03:00
|
|
|
FORMATETC fe = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
2017-01-05 19:28:28 +03:00
|
|
|
STGMEDIUM stm;
|
|
|
|
|
|
|
|
if (SUCCEEDED(pDataObj->GetData(&fe, &stm))) {
|
|
|
|
// Get an HDROP handle.
|
2023-02-22 00:14:22 +03:00
|
|
|
const auto hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
|
2017-01-05 19:28:28 +03:00
|
|
|
if (hDrop) {
|
2020-06-05 01:51:32 +03:00
|
|
|
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, nullptr, 0);
|
2019-09-07 22:42:10 +03:00
|
|
|
for (UINT i = 0; i < nFiles; ++i) {
|
2017-01-05 19:28:28 +03:00
|
|
|
// Get the path of the file.
|
2018-01-19 21:44:10 +03:00
|
|
|
wchar_t buffer[MAX_PATH];
|
|
|
|
|
|
|
|
if (!DragQueryFile(hDrop, i, buffer, ARRAYSIZE(buffer))) {
|
|
|
|
m_selectedFiles.clear();
|
|
|
|
break;
|
2017-01-05 19:28:28 +03:00
|
|
|
}
|
2018-01-19 21:44:10 +03:00
|
|
|
|
|
|
|
if (i)
|
|
|
|
m_selectedFiles += L'\x1e';
|
|
|
|
m_selectedFiles += buffer;
|
2017-01-05 19:28:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
GlobalUnlock(stm.hGlobal);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseStgMedium(&stm);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If any value other than S_OK is returned from the method, the context
|
|
|
|
// menu item is not displayed.
|
2018-01-19 21:44:10 +03:00
|
|
|
return m_selectedFiles.empty() ? E_FAIL : S_OK;
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
|
|
|
|
#pragma region IContextMenu
|
|
|
|
|
|
|
|
void InsertSeperator(HMENU hMenu, UINT indexMenu)
|
|
|
|
{
|
2017-01-05 19:28:28 +03:00
|
|
|
// Add a separator.
|
|
|
|
MENUITEMINFO sep = { sizeof(sep) };
|
|
|
|
sep.fMask = MIIM_TYPE;
|
|
|
|
sep.fType = MFT_SEPARATOR;
|
|
|
|
InsertMenuItem(hMenu, indexMenu, TRUE, &sep);
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
IFACEMETHODIMP NCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2017-01-05 19:28:28 +03:00
|
|
|
// If uFlags include CMF_DEFAULTONLY then we should not do anything.
|
|
|
|
if (CMF_DEFAULTONLY & uFlags)
|
|
|
|
{
|
|
|
|
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
|
|
|
}
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
m_info = NCClientInterface::FetchInfo(m_selectedFiles);
|
2018-01-19 21:44:10 +03:00
|
|
|
if (m_info.menuItems.empty()) {
|
2017-01-05 19:28:28 +03:00
|
|
|
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
|
|
|
}
|
|
|
|
|
2017-07-04 17:58:55 +03:00
|
|
|
InsertSeperator(hMenu, indexMenu++);
|
|
|
|
|
|
|
|
HMENU hSubmenu = CreateMenu();
|
2017-01-05 19:28:28 +03:00
|
|
|
{
|
2017-07-04 17:58:55 +03:00
|
|
|
MENUITEMINFO mii = { sizeof(mii) };
|
|
|
|
mii.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
|
|
|
|
mii.hSubMenu = hSubmenu;
|
|
|
|
mii.fType = MFT_STRING;
|
2018-01-19 21:44:10 +03:00
|
|
|
mii.dwTypeData = &m_info.contextMenuTitle[0];
|
2017-07-04 17:58:55 +03:00
|
|
|
|
|
|
|
if (!InsertMenuItem(hMenu, indexMenu++, TRUE, &mii))
|
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
2017-01-05 19:28:28 +03:00
|
|
|
}
|
2017-07-04 17:58:55 +03:00
|
|
|
InsertSeperator(hMenu, indexMenu++);
|
2017-01-05 19:28:28 +03:00
|
|
|
|
2017-07-04 17:58:55 +03:00
|
|
|
UINT indexSubMenu = 0;
|
2018-01-19 21:44:10 +03:00
|
|
|
for (auto &item : m_info.menuItems) {
|
|
|
|
bool disabled = item.flags.find(L'd') != std::string::npos;
|
2017-07-04 17:58:55 +03:00
|
|
|
|
|
|
|
MENUITEMINFO mii = { sizeof(mii) };
|
2018-01-19 21:44:10 +03:00
|
|
|
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
|
2018-05-29 15:45:29 +03:00
|
|
|
mii.wID = idCmdFirst + indexSubMenu;
|
2017-07-04 17:58:55 +03:00
|
|
|
mii.fType = MFT_STRING;
|
2018-01-19 21:44:10 +03:00
|
|
|
mii.dwTypeData = &item.title[0];
|
|
|
|
mii.fState = disabled ? MFS_DISABLED : MFS_ENABLED;
|
2017-07-04 17:58:55 +03:00
|
|
|
|
2018-01-19 21:44:10 +03:00
|
|
|
if (!InsertMenuItem(hSubmenu, indexSubMenu, true, &mii))
|
2017-07-04 17:58:55 +03:00
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
2018-01-19 21:44:10 +03:00
|
|
|
indexSubMenu++;
|
2017-07-04 17:58:55 +03:00
|
|
|
}
|
2017-01-05 19:28:28 +03:00
|
|
|
|
|
|
|
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
|
|
|
|
// Set the code value to the offset of the largest command identifier
|
|
|
|
// that was assigned, plus one (1).
|
2018-01-19 21:44:10 +03:00
|
|
|
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(indexSubMenu));
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
IFACEMETHODIMP NCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2018-01-19 21:44:10 +03:00
|
|
|
std::wstring command;
|
2015-01-22 20:44:54 +03:00
|
|
|
|
2019-02-27 14:36:54 +03:00
|
|
|
CMINVOKECOMMANDINFOEX *piciEx = nullptr;
|
|
|
|
if (pici->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
|
|
|
|
piciEx = (CMINVOKECOMMANDINFOEX*)pici;
|
|
|
|
|
2017-01-05 19:28:28 +03:00
|
|
|
// For the Unicode case, if the high-order word is not zero, the
|
|
|
|
// command's verb string is in lpcmi->lpVerbW.
|
2019-02-27 14:36:54 +03:00
|
|
|
if (piciEx
|
|
|
|
&& (piciEx->fMask & CMIC_MASK_UNICODE)
|
|
|
|
&& HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW)) {
|
|
|
|
|
|
|
|
command = piciEx->lpVerbW;
|
|
|
|
|
|
|
|
// Verify that we handle the verb
|
|
|
|
bool handled = false;
|
|
|
|
for (auto &item : m_info.menuItems) {
|
|
|
|
if (item.command == command) {
|
|
|
|
handled = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!handled)
|
|
|
|
return E_FAIL;
|
|
|
|
} else if (IS_INTRESOURCE(pici->lpVerb)) {
|
2018-01-19 21:44:10 +03:00
|
|
|
// If the command cannot be identified through the verb string, then
|
|
|
|
// check the identifier offset.
|
|
|
|
auto offset = LOWORD(pici->lpVerb);
|
2018-05-29 15:45:29 +03:00
|
|
|
if (offset >= m_info.menuItems.size())
|
2017-01-05 19:28:28 +03:00
|
|
|
return E_FAIL;
|
2018-01-19 21:44:10 +03:00
|
|
|
|
|
|
|
command = m_info.menuItems[offset].command;
|
2019-02-27 14:36:54 +03:00
|
|
|
} else {
|
|
|
|
return E_FAIL;
|
2017-01-05 19:28:28 +03:00
|
|
|
}
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
NCClientInterface::SendRequest(command.data(), m_selectedFiles);
|
2017-01-05 19:28:28 +03:00
|
|
|
return S_OK;
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
2020-08-18 20:11:08 +03:00
|
|
|
IFACEMETHODIMP NCContextMenu::GetCommandString(UINT_PTR idCommand,
|
2017-01-05 19:28:28 +03:00
|
|
|
UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
|
2015-01-22 20:44:54 +03:00
|
|
|
{
|
2018-01-19 21:44:10 +03:00
|
|
|
if (idCommand < m_info.menuItems.size() && uFlags == GCS_VERBW) {
|
|
|
|
return StringCchCopyW(reinterpret_cast<PWSTR>(pszName), cchMax,
|
|
|
|
m_info.menuItems[idCommand].command.data());
|
2017-01-05 19:28:28 +03:00
|
|
|
}
|
2018-01-19 21:44:10 +03:00
|
|
|
return E_INVALIDARG;
|
2015-01-22 20:44:54 +03:00
|
|
|
}
|
|
|
|
|
2018-01-19 21:44:10 +03:00
|
|
|
#pragma endregion
|