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

262 lines
7.7 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
// ExplorerCommand handlers are an inproc verb implementation method that can provide
// dynamic behavior including computing the name of the command, its icon and its visibility state.
// only use this verb implemetnation method if you are implementing a command handler on
// the commands module and need the same functionality on a context menu.
//
// each ExplorerCommand handler needs to have a unique COM object, run uuidgen to
// create new CLSID values for your handler. a handler can implement multiple
// different verbs using the information provided via IInitializeCommand (the verb name).
// your code can switch off those different verb names or the properties provided
// in the property bag
#include "Dll.h"
static WCHAR const c_szVerbDisplayName[] = L"ExplorerCommand Verb Sample";
static WCHAR const c_szVerbName[] = L"Sample.ExplorerCommandVerb";
class CExplorerCommandVerb : public IExplorerCommand,
public IInitializeCommand,
public IObjectWithSite
{
public:
CExplorerCommandVerb() : _cRef(1), _punkSite(NULL), _hwnd(NULL), _pstmShellItemArray(NULL)
{
DllAddRef();
}
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CExplorerCommandVerb, IExplorerCommand), // required
QITABENT(CExplorerCommandVerb, IInitializeCommand), // optional
QITABENT(CExplorerCommandVerb, IObjectWithSite), // optional
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
{
delete this;
}
return cRef;
}
// IExplorerCommand
IFACEMETHODIMP GetTitle(IShellItemArray * /* psiItemArray */, LPWSTR *ppszName)
{
// the verb name can be computed here, in this example it is static
return SHStrDup(c_szVerbDisplayName, ppszName);
}
IFACEMETHODIMP GetIcon(IShellItemArray * /* psiItemArray */, LPWSTR *ppszIcon)
{
// the icon ref ("dll,-<resid>") is provied here, in this case none is provieded
*ppszIcon = NULL;
return E_NOTIMPL;
}
IFACEMETHODIMP GetToolTip(IShellItemArray * /* psiItemArray */, LPWSTR *ppszInfotip)
{
// tooltip provided here, in this case none is provieded
*ppszInfotip = NULL;
return E_NOTIMPL;
}
IFACEMETHODIMP GetCanonicalName(GUID* pguidCommandName)
{
*pguidCommandName = __uuidof(this);
return S_OK;
}
// compute the visibility of the verb here, respect "fOkToBeSlow" if this is slow (does IO for example)
// when called with fOkToBeSlow == FALSE return E_PENDING and this object will be called
// back on a background thread with fOkToBeSlow == TRUE
IFACEMETHODIMP GetState(IShellItemArray * /* psiItemArray */, BOOL fOkToBeSlow, EXPCMDSTATE *pCmdState)
{
HRESULT hr;
if (fOkToBeSlow)
{
Sleep(4 * 1000); // simulate expensive work
*pCmdState = ECS_ENABLED;
hr = S_OK;
}
else
{
*pCmdState = ECS_DISABLED;
// returning E_PENDING requests that a new instance of this object be called back
// on a background thread so that it can do work that might be slow
hr = E_PENDING;
}
return hr;
}
IFACEMETHODIMP Invoke(IShellItemArray *psiItemArray, IBindCtx *pbc);
IFACEMETHODIMP GetFlags(EXPCMDFLAGS *pFlags)
{
*pFlags = ECF_DEFAULT;
return S_OK;
}
IFACEMETHODIMP EnumSubCommands(IEnumExplorerCommand **ppEnum)
{
*ppEnum = NULL;
return E_NOTIMPL;
}
// IInitializeCommand
IFACEMETHODIMP Initialize(PCWSTR /* pszCommandName */, IPropertyBag * /* ppb */)
{
// the verb name is in pszCommandName, this handler can vary its behavior
// based on the command name (implementing different verbs) or the
// data stored under that verb in the registry can be read via ppb
return S_OK;
}
// IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown *punkSite)
{
SetInterface(&_punkSite, punkSite);
return S_OK;
}
IFACEMETHODIMP GetSite(REFIID riid, void **ppv)
{
*ppv = NULL;
return _punkSite ? _punkSite->QueryInterface(riid, ppv) : E_FAIL;
}
private:
~CExplorerCommandVerb()
{
SafeRelease(&_punkSite);
SafeRelease(&_pstmShellItemArray);
DllRelease();
}
DWORD _ThreadProc();
static DWORD __stdcall s_ThreadProc(void *pv)
{
CExplorerCommandVerb *pecv = (CExplorerCommandVerb *)pv;
const DWORD ret = pecv->_ThreadProc();
pecv->Release();
return ret;
}
long _cRef;
IUnknown *_punkSite;
HWND _hwnd;
IStream *_pstmShellItemArray;
};
DWORD CExplorerCommandVerb::_ThreadProc()
{
IShellItemArray *psia;
HRESULT hr = CoGetInterfaceAndReleaseStream(_pstmShellItemArray, IID_PPV_ARGS(&psia));
_pstmShellItemArray = NULL;
if (SUCCEEDED(hr))
{
DWORD count;
psia->GetCount(&count);
IShellItem2 *psi;
HRESULT hr = GetItemAt(psia, 0, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = psi->GetDisplayName(SIGDN_PARENTRELATIVEPARSING, &pszName);
if (SUCCEEDED(hr))
{
WCHAR szMsg[128];
StringCchPrintf(szMsg, ARRAYSIZE(szMsg), L"%d item(s), first item is named %s", count, pszName);
MessageBox(_hwnd, szMsg, L"ExplorerCommand Sample Verb", MB_OK);
CoTaskMemFree(pszName);
}
psi->Release();
}
psia->Release();
}
return 0;
}
IFACEMETHODIMP CExplorerCommandVerb::Invoke(IShellItemArray *psia, IBindCtx * /* pbc */)
{
IUnknown_GetWindow(_punkSite, &_hwnd);
HRESULT hr = CoMarshalInterThreadInterfaceInStream(__uuidof(psia), psia, &_pstmShellItemArray);
if (SUCCEEDED(hr))
{
AddRef();
if (!SHCreateThread(s_ThreadProc, this, CTF_COINIT_STA | CTF_PROCESS_REF, NULL))
{
Release();
}
}
return S_OK;
}
static WCHAR const c_szProgID[] = L"txtfile";
HRESULT CExplorerCommandVerb_RegisterUnRegister(bool fRegister)
{
CRegisterExtension re(__uuidof(CExplorerCommandVerb));
HRESULT hr;
if (fRegister)
{
hr = re.RegisterInProcServer(c_szVerbDisplayName, L"Apartment");
if (SUCCEEDED(hr))
{
// register this verb on .txt files ProgID
hr = re.RegisterExplorerCommandVerb(c_szProgID, c_szVerbName, c_szVerbDisplayName);
if (SUCCEEDED(hr))
{
hr = re.RegisterVerbAttribute(c_szProgID, c_szVerbName, L"NeverDefault");
}
}
}
else
{
// best effort
hr = re.UnRegisterVerb(c_szProgID, c_szVerbName);
hr = re.UnRegisterObject();
}
return hr;
}
HRESULT CExplorerCommandVerb_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CExplorerCommandVerb *pVerb = new (std::nothrow) CExplorerCommandVerb();
HRESULT hr = pVerb ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
pVerb->QueryInterface(riid, ppv);
pVerb->Release();
}
return hr;
}