2025-11-28 00:35:46 +09:00

312 lines
10 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "priv.h"
#include <strsafe.h>
class CNonDefaultDropMenuVerb : public IContextMenu,
public IShellExtInit
{
public:
CNonDefaultDropMenuVerb() : _cRef(1), _pdtobj(NULL)
{
_szTargetFolder[0] = 0;
DllAddRef();
}
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CNonDefaultDropMenuVerb, IContextMenu),
QITABENT(CNonDefaultDropMenuVerb, IShellExtInit),
{ 0 }
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (cRef == 0)
{
delete this;
}
return cRef;
}
// IContextMenu
IFACEMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
IFACEMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
IFACEMETHODIMP GetCommandString(UINT_PTR /*idCmd*/, UINT /*uType*/, UINT * /*pRes*/, LPSTR /*pszName*/, UINT /*cchMax*/) { return E_NOTIMPL; }
// IShellExtInit
IFACEMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
private:
~CNonDefaultDropMenuVerb()
{
SafeRelease(&_pdtobj);
DllRelease();
}
UINT _GetNetResource(HGLOBAL hnres, UINT iItem, NETRESOURCEW *pnresOut, UINT cbMax);
HRESULT _CheckForHNRES(HWND hwnd);
HRESULT _CheckForHDROP(HWND hwnd);
long _cRef;
WCHAR _szTargetFolder[MAX_PATH];
IDataObject *_pdtobj; // data object
};
// This function is called back from within IClassFactory::CreateInstance()
// of the default class factory object, which is created by SHCreateClassObject.
HRESULT CNonDefaultDropMenuVerb_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CNonDefaultDropMenuVerb *pnddmv = new (std::nothrow) CNonDefaultDropMenuVerb();
HRESULT hr = pnddmv ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pnddmv->QueryInterface(riid, ppv);
pnddmv->Release();
}
return hr;
}
// IShellExtInit
IFACEMETHODIMP CNonDefaultDropMenuVerb::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY /*hkeyProgID*/)
{
// Initialize can be called more than once.
SafeRelease(&_pdtobj);
_szTargetFolder[0] = 0;
HRESULT hr = pdtobj->QueryInterface(&_pdtobj);
// Get the path to the drop target folder
if (SUCCEEDED(hr) && pidlFolder)
{
hr = SHGetPathFromIDList(pidlFolder, _szTargetFolder);
}
return hr;
}
// IContextMenu
IFACEMETHODIMP CNonDefaultDropMenuVerb::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT /*idCmdLast*/, UINT /*uFlags*/)
{
WCHAR szMenuItem[80];
LoadString(g_hinst, IDS_CHECKDROP, szMenuItem, ARRAYSIZE(szMenuItem));
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION, idCmdFirst + IDM_CHECKDROP, szMenuItem);
LoadString(g_hinst, IDS_CHECKNETRESOURCES, szMenuItem, ARRAYSIZE(szMenuItem));
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION, idCmdFirst + IDM_CHECKNETRESOURCES, szMenuItem);
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(2)); // indicate that we added 2 verbs.
}
IFACEMETHODIMP CNonDefaultDropMenuVerb::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
{
HRESULT hr = E_INVALIDARG; // assume error
// No need to support string based command.
if (!HIWORD(lpici->lpVerb))
{
UINT const idCmd = LOWORD(lpici->lpVerb);
switch (idCmd)
{
case IDM_CHECKDROP:
hr = _CheckForHDROP(lpici->hwnd);
break;
case IDM_CHECKNETRESOURCES:
hr = _CheckForHNRES(lpici->hwnd);
break;
}
}
return hr;
}
HRESULT CNonDefaultDropMenuVerb::_CheckForHDROP(HWND hwnd)
{
WCHAR szMessageTitle[128];
LoadString(g_hinst, IDS_MESSAGEBOXTITLE, szMessageTitle, ARRAYSIZE(szMessageTitle));
FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM medium;
HRESULT hr = _pdtobj->GetData(&fmte, &medium);
if (SUCCEEDED(hr))
{
HDROP hdrop = (HDROP)medium.hGlobal;
WCHAR szFile[MAX_PATH];
DragQueryFile(hdrop, 0, szFile, ARRAYSIZE(szFile));
UINT cFiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0);
WCHAR szMessageBase[128];
LoadString(g_hinst, IDS_MESSAGETEMPLATEFS, szMessageBase, ARRAYSIZE(szMessageBase));
WCHAR szMessage[INTERNET_MAX_URL_LENGTH];
StringCchPrintf(szMessage, ARRAYSIZE(szMessage), szMessageBase, _szTargetFolder, cFiles, szFile);
MessageBox(hwnd, szMessage, szMessageTitle, MB_OK);
ReleaseStgMedium(&medium);
}
else
{
WCHAR szErrorMessage[128];
LoadString(g_hinst, IDS_ERRORMESSAGEFS, szErrorMessage, ARRAYSIZE(szErrorMessage));
MessageBox(hwnd, szErrorMessage, szMessageTitle, MB_OK);
}
return hr;
}
PCWSTR _Offset2Ptr(PCWSTR pszBase, size_t cbBase, size_t uOffset)
{
PWSTR pszRet = NULL;
if (uOffset && uOffset < cbBase)
{
pszRet = (PWSTR)((BYTE *)pszBase + uOffset);
}
return pszRet;
}
// fill in pmedium with a NRESARRAY structure. this is a flat clipboard format on an HGLOBAL
// the name of this format is CFSTR_NETRESOURCES
//
// typedef struct
// {
// UINT cItems;
// NETRESOURCE nr[1]; // variable # of these, with string pointers converted to offsets
// <strings stored here>
// } NRESARRAY;
// This is a helper routine which extracts a specified NETRESOURCE from hnres.
UINT CNonDefaultDropMenuVerb::_GetNetResource(HGLOBAL hnres, UINT iItem, NETRESOURCEW *pnresOut, UINT cbMax)
{
UINT iRet = 0; // assume error
NRESARRAY *panr = (NRESARRAY *)GlobalLock(hnres);
if (panr)
{
if (iItem == (UINT)-1)
{
iRet = panr->cItems;
}
else if (iItem < panr->cItems)
{
HRESULT hr = ERROR_INSUFFICIENT_BUFFER;
size_t const cbResArraySize = GlobalSize(hnres);
PCWSTR pszProvider = _Offset2Ptr(reinterpret_cast<PCWSTR>(panr), cbResArraySize, reinterpret_cast<size_t>(panr->nr[iItem].lpProvider));
PCWSTR pszRemoteName = _Offset2Ptr(reinterpret_cast<PCWSTR>(panr), cbResArraySize, reinterpret_cast<size_t>(panr->nr[iItem].lpRemoteName));
if (cbMax >= sizeof(*pnresOut))
{
*pnresOut = panr->nr[iItem];
hr = S_OK;
PWSTR psz = (PWSTR)(pnresOut + 1);
size_t cch = (cbMax - sizeof(*pnresOut)) / sizeof(*psz);
if (pnresOut->lpProvider)
{
pnresOut->lpProvider = psz;
if (pszProvider)
{
hr = StringCchCopyExW(psz, cch, pszProvider, &psz, &cch, 0);
if (SUCCEEDED(hr))
{
if (cch) // point after NULL for potential next string, if we have room
{
psz++;
cch--;
}
}
}
else
{
// If this string is Null there was an issue with the NRESARRAY's data
hr = E_FAIL;
}
}
if (pnresOut->lpRemoteName && SUCCEEDED(hr))
{
pnresOut->lpRemoteName = psz;
if (pszRemoteName)
{
hr = StringCchCopy(psz, cch, pszRemoteName);
}
else
{
// If this string is Null there was an issue with the NRESARRAY's data
hr = E_FAIL;
}
}
}
iRet = SUCCEEDED(hr) ? 1 : 0;
}
GlobalUnlock(hnres);
}
return iRet;
}
HRESULT CNonDefaultDropMenuVerb::_CheckForHNRES(HWND hwnd)
{
WCHAR szMessageTitle[128];
LoadString(g_hinst, IDS_MESSAGEBOXTITLE, szMessageTitle, ARRAYSIZE(szMessageTitle));
static CLIPFORMAT g_cfNetResource = 0; // Clipboard format
if (g_cfNetResource == 0)
{
g_cfNetResource = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_NETRESOURCES);
}
FORMATETC fmte = { g_cfNetResource, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM medium;
HRESULT hr = _pdtobj->GetData(&fmte, &medium);
if (SUCCEEDED(hr))
{
size_t const cbNetResource = 2048;
NETRESOURCEW *pnr = (NETRESOURCEW *)LocalAlloc(LPTR, cbNetResource);
hr = pnr ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
// Get the NETRESOURCE of the first item
_GetNetResource(medium.hGlobal, 0, pnr, cbNetResource);
UINT const cItems = _GetNetResource(medium.hGlobal, (UINT)-1, NULL, 0);
WCHAR szMessageBase[128];
LoadString(g_hinst, IDS_MESSAGETEMPLATENET, szMessageBase, ARRAYSIZE(szMessageBase));
WCHAR szNA[128];
LoadString(g_hinst, IDS_ERRORMESSAGENA, szNA, ARRAYSIZE(szNA));
WCHAR szMessage[MAX_PATH];
StringCchPrintf(szMessage, ARRAYSIZE(szMessage), szMessageBase, cItems,
pnr->lpProvider, pnr->lpRemoteName ? pnr->lpRemoteName : szNA,
pnr->dwDisplayType, pnr->dwType, pnr->dwUsage);
MessageBox(hwnd, szMessage, szMessageTitle, MB_OK);
LocalFree(pnr);
}
ReleaseStgMedium(&medium);
}
else
{
WCHAR szErrorMessage[128];
LoadString(g_hinst, IDS_ERRORMESSAGENET, szErrorMessage, ARRAYSIZE(szErrorMessage));
MessageBox(hwnd, szErrorMessage, szMessageTitle, MB_OK);
}
return hr;
}