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

366 lines
12 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
#pragma once
// CApplicationVerb is a template class that is used to implement shell verbs
// that integrate with an application using the DelegateExecute verb invoke method.
// the DelegateExecute method has a number of advantages over CommandLine and
// ContextMenuHandler methods discussed in the shell verb implementers guide.
// this could be adapted to use the DropTarget method to enable operation on Windows XP
//
// this design assumes that the application is expressed as a C++ class with
// methods that the verb implementation can call to do its work
//
// to use this class
// 1) run uuidgen.exe to generate a new CLSID for each verb you will implement
//
// 2) for each verb define a class using CApplicationVerb<>, provide the CLSID
// of the verb using __declspec(uuid()), declare the DoVerb() method
// this can be a nested class in the application class.
//
// class __declspec(uuid("4a4f70f8-0f4d-46dc-a4ee-3611308d885f"))
// CPlayVerb : public CApplicationVerb<CPlayerApplication, CPlayVerb>
// {
// CPlayVerb() : CApplicationVerb(AVF_DEFAULT) {}
// void StartVerb() const;
// void OnItem(IShellItem *psi) const;
// };
//
// 3) provide the implementation of the verb by implementing DoVerb(), this can
// be inline in the class declaration
//
// class __declspec(uuid("4a4f70f8-0f4d-46dc-a4ee-3611308d885f"))
// CPlayVerb : public CApplicationVerb<CPlayerApplication, CPlayVerb>
// {
// CPlayVerb() : CApplicationVerb(AVF_DEFAULT) {}
// void StartVerb() const
// {
// CPlayerApplication *pApp = GetApp();
// if (pApp)
// {
// // per verb invocation setup step
// }
// }
// void OnItem(IShellItem *psi) const
// {
// CPlayerApplication *pApp = GetApp();
// if (pApp)
// {
// // process item
// }
// }
// };
//
// 4) delcare an instance of each verb in your application class
//
// class CPlayerApplication
// {
// private:
// CPlayVerb _verbPlay;
// };
//
// 5) set the host application on the CPlayVerb members in the contructor of the class that hosts it
//
// _verbPlay.SetApplication(this);
//
// 6) at application startup time call CApplicationVerb<>.Register()
//
// void CPlayerApplication::_OnInitDlg()
// {
// _verbPlay.Register();
// }
//
// 7) at app shutdown time call CApplicationVerb<>.UnRegister() to unregister the verb
//
// void CPlayerApplication::_OnDestroyDlg()
// {
// _verbPlay.UnRegister();
// }
//
// 7) register the verbs in the registry as COM local servers that launches your
// application. note your app will get passed the "-Embedding" when COM
// launches you. you can use CRegisterExtension.RegisterAppAsLocalServer() to do this
#include <objbase.h>
// template parameters:
//
// TApplication - the application class that provides methods for the verb implementation
// to call to do its work
//
// TVerb - the verb class itself, used to determine the CLSID value of the COM object
// that implements the verb
//
// this class combines the drop target and the class factory into a single object
// and it expects to be declared as a member variable of the application calls
// giving it a lifetime that matches the application
typedef enum
{
AVF_DEFAULT = 0x0000,
AVF_ASYNC = 0x0001, // invoke the verb using the async methods provided by the app
AVF_ONE_IMPLIES_ALL = 0x0002, // implement one implies all behavior
} APPLICATION_VERB_FLAGS;
DEFINE_ENUM_FLAG_OPERATORS(APPLICATION_VERB_FLAGS);
template <class TApplication, class TVerb> class CApplicationVerb :
public IExecuteCommand,
public IObjectWithSelection,
public IObjectWithSite,
public IClassFactory,
public INamespaceWalkCB2
{
public:
CApplicationVerb(APPLICATION_VERB_FLAGS flags = AVF_DEFAULT) : _flags(flags),
_pApp(NULL), _dwRegisterClass(0), _psia(NULL), _punkSite(NULL), _grfKeyState(0)
{ }
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CApplicationVerb, IExecuteCommand), // required
QITABENT(CApplicationVerb, IObjectWithSelection), // required
QITABENT(CApplicationVerb, IObjectWithSite), // used to get to the view
QITABENT(CApplicationVerb, IClassFactory), // object is its own class factory
QITABENT(CApplicationVerb, INamespaceWalkCB),
QITABENT(CApplicationVerb, INamespaceWalkCB2),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return _pApp->AddRef();
}
IFACEMETHODIMP_(ULONG) Release()
{
return _pApp->Release();
}
// IExecuteCommand
IFACEMETHODIMP SetKeyState(DWORD grfKeyState)
{
_grfKeyState = grfKeyState;
return S_OK;
}
IFACEMETHODIMP SetParameters(PCWSTR /* pszParameters */)
{ return S_OK; }
IFACEMETHODIMP SetPosition(POINT /* pt */)
{
// to respect the monitor launched from capture this value
return S_OK;
}
IFACEMETHODIMP SetShowWindow(int /* nShow */)
{ return S_OK; }
IFACEMETHODIMP SetNoShowUI(BOOL /* fNoShowUI */)
{ return S_OK; }
IFACEMETHODIMP SetDirectory(PCWSTR /* pszDirectory */)
{ return S_OK; }
IFACEMETHODIMP Execute()
{
HRESULT hr;
if (_flags & AVF_ASYNC)
{
INamespaceWalk *pnsw;
hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&pnsw));
if (SUCCEEDED(hr))
{
StartVerb();
// try to get the items from the view if they are available
IShellView *psv;
hr = IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARGS(&psv));
DWORD const walkFlags = (_flags & AVF_ONE_IMPLIES_ALL) ?
NSWF_ONE_IMPLIES_ALL | NSWF_DONT_ACCUMULATE_RESULT | NSWF_ASYNC | NSWF_FLAG_VIEWORDER :
NSWF_DONT_ACCUMULATE_RESULT | NSWF_ASYNC | NSWF_FLAG_VIEWORDER;
UINT const walkDepth = /* psv ? 0 : */ 8;
hr = pnsw->Walk(psv ? static_cast<IUnknown*>(psv) : _psia,
walkFlags, walkDepth, this);
SafeRelease(&psv); // optional
pnsw->Release();
}
}
else if (_psia)
{
DoVerb(_psia); // provided by the class using this template
}
SafeRelease(&_psia); // don't need this any more
return S_OK;
}
// IObjectWithSelection
IFACEMETHODIMP SetSelection(IShellItemArray *psia)
{
SetInterface(&_psia, psia);
return S_OK;
}
IFACEMETHODIMP GetSelection(REFIID riid, void **ppv)
{
*ppv = NULL;
return _psia ? _psia->QueryInterface(riid, ppv) : E_FAIL;
}
// IClassFactory
IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
if (punkOuter)
{
*ppv = NULL;
return CLASS_E_NOAGGREGATION;
}
return QueryInterface(riid, ppv); // return this object
}
IFACEMETHODIMP LockServer(BOOL)
{
return S_OK;
}
// IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown *punkSite)
{
SetInterface(&_punkSite, punkSite);
__if_exists(TAppliation::SetSite)
{
_pApp->SetSite(punkSite);
}
return S_OK;
}
IFACEMETHODIMP GetSite(REFIID riid, void **ppv)
{
*ppv = NULL;
return _punkSite ? _punkSite->QueryInterface(riid, ppv) : E_FAIL;
}
// INamespaceWalkCB
IFACEMETHODIMP FoundItem(IShellFolder *psf, PCUITEMID_CHILD pidl)
{
IShellItem2 *psi;
HRESULT hr = SHCreateItemWithParent(NULL, psf, pidl, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
OnItem(psi);
psi->Release();
}
return ShouldContinue() ? hr : HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
IFACEMETHODIMP EnterFolder(IShellFolder * /* psf */, PCUITEMID_CHILD /* pidl */)
{
return ShouldContinue() ? S_OK : HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
IFACEMETHODIMP LeaveFolder(IShellFolder * /* psf */, PCUITEMID_CHILD /* pidl */)
{
return ShouldContinue() ? S_OK : HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
IFACEMETHODIMP InitializeProgressDialog(PWSTR *ppszTitle, PWSTR *ppszCancel)
{
*ppszTitle = *ppszCancel = NULL;
return E_NOTIMPL;
}
// INamespaceWalkCB
IFACEMETHODIMP WalkComplete(HRESULT /* hr */)
{
EndVerb();
return S_OK;
}
// application calls this when it is being created to enable the verbs
void Register()
{
CoRegisterClassObject(__uuidof(TVerb), static_cast<IClassFactory *>(this),
CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &_dwRegisterClass);
}
// calls this on shutdown to revoke the verb registration
void UnRegister()
{
if (_dwRegisterClass)
{
CoRevokeClassObject(_dwRegisterClass);
_dwRegisterClass = 0;
}
}
// used in the verb implementations to get a ref to the app
// the verb calls methods on the app to do its work
TApplication *GetApp() const { return _pApp; }
// call this in the constructor of the application to enable the verb to
// access the application
void SetApplication(TApplication *ptApp)
{
_pApp = ptApp; // weak reference to application
}
DWORD GetKeyState() const { return _grfKeyState; }
virtual ~CApplicationVerb()
{
SafeRelease(&_psia);
SafeRelease(&_punkSite);
}
private:
// This method is disabled.
// User-defined assignment operator is necessary for /W4 /WX since a default
// one cannot be created by the compiler because this class contains const members.
CApplicationVerb & operator=(const CApplicationVerb &);
// verb implementation provides these methods to get the verb behavior
// some are required and some are optional
// this is called when a verb is being invoked synchronously with the items
// implement this for sync verbs
virtual void DoVerb(IShellItemArray * /* psia */) const { };
// indicates that the verb has been invoked using the async method
// and items will be returned to the verb via OnItem()
virtual void StartVerb() { };
// the discovery if items in the async verb has is complete
virtual void EndVerb() { };
// as the items are being discovered this method is called to see
// if the discovery should continue
virtual bool ShouldContinue() const { return _dwRegisterClass != 0; }
// this method is called with each item when async discovery of the
// items is enabled
virtual void OnItem(IShellItem * /* psi */) { };
TApplication *_pApp;
DWORD _dwRegisterClass;
APPLICATION_VERB_FLAGS const _flags; // controls behavior of verb
IShellItemArray *_psia;
IUnknown *_punkSite;
DWORD _grfKeyState;
};