|
|
#include "pch.h"
#pragma hdrstop
#define IDC_DSFIND 0x0000
typedef struct { CLSID clsidForm; LPTSTR pCaption; LPTSTR pIconPath; INT idIcon; } FORMLISTITEM, * LPFORMLISTITEM;
/*-----------------------------------------------------------------------------
/ Helper functions /----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ _FindInDs / --------- / Launch the Directory Search UI given a CLSID (for the form) or a / scope to invoke off. / / In: / pScope -> scope to root the search at / == NULL / pCLSID -> clsid for the form / == NULL / / Out: / HRESULT /----------------------------------------------------------------------------*/
typedef struct { LPWSTR pScope; CLSID clsidForm; } FINDSTATE, * LPFINDSTATE;
//
// bg thread used to display the query UI in a non-clocking way
//
DWORD WINAPI _FindInDsThread(LPVOID pThreadData) { HRESULT hres, hresCoInit; ICommonQuery* pCommonQuery = NULL; OPENQUERYWINDOW oqw = {0}; DSQUERYINITPARAMS dqip = {0}; LPFINDSTATE pFindState = (LPFINDSTATE)pThreadData; TraceEnter(TRACE_UI, "_FindInDsThread");
hresCoInit = CoInitialize(NULL); // can fail, b/c CoInit already preformed
hres = CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ICommonQuery, &pCommonQuery)); FailGracefully(hres, "Failed in CoCreateInstance of CLSID_CommonQuery");
dqip.cbStruct = SIZEOF(dqip); dqip.pDefaultScope = NULL; if (pFindState->pScope) dqip.pDefaultScope = pFindState->pScope;
oqw.cbStruct = SIZEOF(oqw); oqw.clsidHandler = CLSID_DsQuery; oqw.pHandlerParameters = &dqip;
if (!pFindState->pScope) { oqw.dwFlags |= OQWF_DEFAULTFORM|OQWF_REMOVEFORMS; oqw.clsidDefaultForm = pFindState->clsidForm; } hres = pCommonQuery->OpenQueryWindow(NULL, &oqw, NULL); FailGracefully(hres, "OpenQueryWindow failed");
exit_gracefully:
LocalFreeStringW(&pFindState->pScope); LocalFree(pFindState);
DoRelease(pCommonQuery);
if (SUCCEEDED(hresCoInit)) CoUninitialize();
TraceLeave();
DllRelease(); ExitThread(0); return 0; }
//
// API for invoking the query UI
//
HRESULT _FindInDs(LPWSTR pScope, LPCLSID pCLSID) { HRESULT hres; LPFINDSTATE pFindState; HANDLE hThread; DWORD dwThreadID;
TraceEnter(TRACE_UI, "_FindInDs");
if ((!pScope && !pCLSID) || (pScope && pCLSID)) ExitGracefully(hres, E_INVALIDARG, "Bad arguments for invoking the search");
pFindState = (LPFINDSTATE)LocalAlloc(LPTR, SIZEOF(FINDSTATE)); TraceAssert(pFindState);
if (!pFindState) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate state block");
// pFindState->pScope = NULL;
// pFindState->clsidForm = { 0 };
if (pScope) { Trace(TEXT("Defaulting to scope: %s"), pScope); hres = LocalAllocStringW(&pFindState->pScope, pScope); FailGracefully(hres, "Failed to copy scope"); }
if (pCLSID) { TraceGUID("Invoking with form: ", *pCLSID); pFindState->clsidForm = *pCLSID; }
DllAddRef();
hThread = CreateThread(NULL, 0, _FindInDsThread, (LPVOID)pFindState, 0, &dwThreadID); TraceAssert(hThread);
if (!hThread) { LocalFreeStringW(&pFindState->pScope); LocalFree((HLOCAL)pFindState); DllRelease(); ExitGracefully(hres, E_FAIL, "Failed to create thread and issue query on it"); } CloseHandle(hThread); hres = S_OK; // success
exit_gracefully:
TraceLeaveResult(hres); }
// object for invoking the find UI from the search menu in the shell (or off a context menu)
class CFindMenu : public IShellExtInit, IContextMenu { private: LONG _cRef; CLSID _clsidFindEntry; LPWSTR _pDsObjectName; HDSA _hdsaFormList;
public: CFindMenu(REFCLSID clsidFindEntry); ~CFindMenu();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// IShellExtInit
STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
// IContextMenu
STDMETHODIMP QueryContextMenu(HMENU hShellMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi); STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax); };
CFindMenu::CFindMenu(REFCLSID clsidFindEntry) : _clsidFindEntry(clsidFindEntry), _pDsObjectName(NULL), _hdsaFormList(NULL), _cRef(1) { DllAddRef(); }
INT _FreeFormListCB(LPVOID pItem, LPVOID pData) { LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)pItem; TraceAssert(pFormListItem);
LocalFreeString(&pFormListItem->pCaption); LocalFreeString(&pFormListItem->pIconPath);
return 1; }
CFindMenu::~CFindMenu() { LocalFreeStringW(&_pDsObjectName);
if (_hdsaFormList) DSA_DestroyCallback(_hdsaFormList, _FreeFormListCB, NULL);
DllRelease(); }
// QI handling
ULONG CFindMenu::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CFindMenu::Release() { TraceAssert( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
HRESULT CFindMenu::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFindMenu, IShellExtInit), // IID_IShellExtInit
QITABENT(CFindMenu, IContextMenu), // IID_IContextMenu
{0, 0 }, }; return QISearch(this, qit, riid, ppv); }
// IShellExtInit
STDMETHODIMP CFindMenu::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID) { HRESULT hres; FORMATETC fmte = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium = { TYMED_NULL }; LPDSOBJECTNAMES pDsObjects = NULL; LPWSTR pDsObjectName; TraceEnter(TRACE_UI, "CFindMenu::Initialize");
// when building the Start->Find menu we are invoked but we are not passed
// an IDataObject, therefore we know this is the case and we shall just
// build the "In the Directory" form list.
if (!ShowDirectoryUI()) ExitGracefully(hres, E_FAIL, "ShowDirectoryUI returns FALSE, so failing initialize");
if (IsEqualCLSID(_clsidFindEntry, CLSID_DsFind)) { if (pDataObj && SUCCEEDED(pDataObj->GetData(&fmte, &medium))) { pDsObjects = (LPDSOBJECTNAMES)GlobalLock(medium.hGlobal); pDsObjectName = (LPWSTR)ByteOffset(pDsObjects, pDsObjects->aObjects[0].offsetName); TraceAssert(pDsObjectName);
hres = LocalAllocStringW(&_pDsObjectName, pDsObjectName); FailGracefully(hres, "Failed to copy scope path"); }
if (!_pDsObjectName) ExitGracefully(hres, E_FAIL, "Failed to get root scope for this object"); }
hres = S_OK; // success
exit_gracefully:
#ifdef DSUI_DEBUG
if (SUCCEEDED(hres)) Trace(TEXT("Find rooted at -%s-"), _pDsObjectName ? _pDsObjectName:TEXT("<not defined>")); #endif
if (pDsObjects) GlobalUnlock(medium.hGlobal);
ReleaseStgMedium(&medium);
TraceLeaveResult(hres); }
// IContextMenu handling
//
// Helper to set the icon for the given menu item
//
VOID _SetMenuItemIcon(HMENU hMenu, UINT item, UINT uID, BOOL fPosition, LPTSTR pIconFile, INT idRes, LPTSTR pCaption, HMENU hSubMenu) { MENUITEMINFO mii;
TraceEnter(TRACE_UI, "_SetMenuItemIcon"); Trace(TEXT("hMenu %08x, item %d, pIconFile %s, idRes %d"), hMenu, item, pIconFile, idRes); Trace(TEXT("pCaption %s, hSubMenu %08x"), pCaption, hSubMenu);
mii.cbSize = SIZEOF(mii); mii.fMask = MIIM_DATA|MIIM_SUBMENU|MIIM_TYPE|MIIM_ID; mii.fType = MFT_STRING; mii.wID = uID; mii.hSubMenu = hSubMenu; mii.cch = lstrlen(pCaption); mii.dwTypeData = pCaption; mii.dwItemData = Shell_GetCachedImageIndex(pIconFile, idRes, 0); TraceAssert(mii.dwItemData != -1);
Trace(TEXT("Setting data to be %d"), mii.dwItemData); InsertMenuItem(hMenu, item, fPosition, &mii);
TraceLeave(); }
STDAPI _LocalQueryMUIString(LPTSTR* ppResult, HKEY hk, LPCTSTR lpSubKey) { HRESULT hr = LocalQueryString(ppResult, hk, lpSubKey); if (SUCCEEDED(hr)) { // If any of these steps fail, don't fail the call
TCHAR szExpanded[MAX_PATH]; if (SUCCEEDED(SHLoadIndirectString(*ppResult, szExpanded, ARRAYSIZE(szExpanded), NULL))) { LPTSTR pszExpanded; if (SUCCEEDED(LocalAllocString(&pszExpanded, szExpanded))) { LocalFreeString(ppResult); *ppResult = pszExpanded; } } } return hr; }
#define EXPLORER_POLICY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer")
STDMETHODIMP CFindMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { HRESULT hres; TCHAR szBuffer[MAX_PATH]; LPTSTR pBuffer = NULL; INT i, iItems = 0; FORMLISTITEM fli; HKEY hKey = NULL; HKEY hKeyForm = NULL; HKEY hkPolicy = NULL;
TraceEnter(TRACE_UI, "CFindMenu::QueryContextMenu");
// Just make sure we are allowed to surface this UI.
if (!ShowDirectoryUI()) ExitGracefully(hres, E_FAIL, "ShowDirectoryUI returns FALSE, so failing initialize");
// if we have no scope stored in our class then lets build the Start.Find menu entry
// which we get from data stored in the registry.
if (IsEqualCLSID(_clsidFindEntry, CLSID_DsStartFind)) { // enumerate the entries we are going to display in Start->Find from the registry
// this is then stored in a DSA so that we can invoke the Find UI on the
// correct query form.
_hdsaFormList = DSA_Create(SIZEOF(FORMLISTITEM), 4); TraceAssert(_hdsaFormList);
if (!_hdsaFormList) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate find entry DSA");
hres = GetKeyForCLSID(CLSID_DsQuery, TEXT("StartFindEntries"), &hKey); FailGracefully(hres, "Failed to get HKEY for the DsQuery CLSID");
//
// get the policy key so that we can check to see if we must disbale the entries
//
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, EXPLORER_POLICY, NULL, KEY_READ, &hkPolicy)) { TraceMsg("Explorer policy key not found"); hkPolicy = NULL; } for (i = 0 ; TRUE ; i++) { DWORD cchBuffer = ARRAYSIZE(szBuffer); if (ERROR_SUCCESS != RegEnumKeyEx(hKey, i, szBuffer, &cchBuffer, NULL, NULL, NULL, NULL)) { TraceMsg("RegEnumKeyEx failed, therefore stopping enumeration"); break; } else { // We have a caption for the query form we want to display for the
// menu item, now lets pick up the GUID that is stored with it
// so that we can invoke the form.
if (hKeyForm) { RegCloseKey(hKeyForm); hKeyForm = NULL; }
if (ERROR_SUCCESS == RegOpenKeyEx(hKey, szBuffer, NULL, KEY_READ, &hKeyForm)) { LPTSTR pszPolicy = NULL; BOOL fHideEntry = FALSE;
// fli.clsidForm = { 0 };
fli.pCaption = NULL; fli.pIconPath = NULL; fli.idIcon = 0;
//
// lets parse out the CLSID into a value that we can put into the structure.
//
Trace(TEXT("Form GUID: %s"), szBuffer); if (!GetGUIDFromString(szBuffer, &fli.clsidForm)) { TraceMsg("Failed to parse the CLSID of the form"); continue; }
//
// check to see if we have a policy key, if we do then we can disable the entry.
//
if (hkPolicy && SUCCEEDED(LocalQueryString(&pszPolicy, hKeyForm, TEXT("Policy")))) { Trace(TEXT("Policy value is: %s"), pszPolicy);
// we check for the presense of a DWORD under the policy key,
// if the value is non-NULL (doesn't matter what type)
// then we treat it as a "hide me policy"
DWORD dwType = REG_DWORD, cb = SIZEOF(fHideEntry); if (ERROR_SUCCESS != RegQueryValueEx(hkPolicy, pszPolicy, NULL, &dwType, (LPBYTE)&fHideEntry, &cb)) { TraceMsg("Failed to read the policy value"); }
LocalFreeString(&pszPolicy); }
//
// add the entry to the search menu list?
//
if (!fHideEntry) { // OK the GUID for the form parse OK and the policy says it is enabled
// therefore we must attempt to build a find menu entry for this object
if (SUCCEEDED(_LocalQueryMUIString(&fli.pCaption, hKeyForm, TEXT("LocalizedString"))) || SUCCEEDED(LocalQueryString(&fli.pCaption, hKeyForm, NULL))) { Trace(TEXT("Form title: %s"), fli.pCaption);
if (SUCCEEDED(LocalQueryString(&fli.pIconPath, hKeyForm, TEXT("Icon")))) { fli.idIcon = PathParseIconLocation(fli.pIconPath); Trace(TEXT("Icon is: %s, resource %d"), fli.pIconPath, fli.idIcon); }
if (-1 == DSA_AppendItem(_hdsaFormList, &fli)) { _FreeFormListCB(&fli, NULL); ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate FORMLISTITEM structure"); } } } } } }
// we now (hopefully) have a DS full of the items we want to display on
// the menu, so lets try and construct the menu around us.
for (i = 0 ; i < DSA_GetItemCount(_hdsaFormList) ; i++, iItems++) { LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)DSA_GetItemPtr(_hdsaFormList, i); TraceAssert(pFormListItem); _SetMenuItemIcon(hMenu, i, idCmdFirst+i, TRUE, pFormListItem->pIconPath, pFormListItem->idIcon, pFormListItem->pCaption, NULL); } } else { // when we are just a normal verb hanging off an objects context menu
// then lets just load the string we want to display and show it.
if (!LoadString(GLOBAL_HINSTANCE, IDS_FIND, szBuffer, ARRAYSIZE(szBuffer))) ExitGracefully(hres, E_FAIL, "Failed to load resource for menu item");
InsertMenu(hMenu, indexMenu, MF_BYPOSITION|MF_STRING, idCmdFirst+IDC_DSFIND, szBuffer); iItems++; }
hres = S_OK; exit_gracefully:
if (SUCCEEDED(hres)) hres = ResultFromShort(iItems);
if (hKey) RegCloseKey(hKey); if (hKeyForm) RegCloseKey(hKeyForm); if (hkPolicy) RegCloseKey(hkPolicy);
LocalFreeString(&pBuffer);
TraceLeaveValue(hres); }
STDMETHODIMP CFindMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { HRESULT hres = E_FAIL; INT id = LOWORD(lpcmi->lpVerb);
TraceEnter(TRACE_UI, "CFindMenu::InvokeCommand");
if (!HIWORD(lpcmi->lpVerb)) { // if we have a DSA and the verb is inside the DSA then lets invoke the
// query UI with the correct form displayed, otherwise we can default to
// using the scope we have (which can also be NULL)
if (IsEqualCLSID(_clsidFindEntry, CLSID_DsStartFind) && _hdsaFormList && (id < DSA_GetItemCount(_hdsaFormList))) { LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)DSA_GetItemPtr(_hdsaFormList, id); TraceAssert(pFormListItem);
TraceGUID("Invoking query form: ", pFormListItem->clsidForm); hres = _FindInDs(NULL, &pFormListItem->clsidForm); FailGracefully(hres, "FindInDs failed when invoking with a query form"); } else { Trace(TEXT("Scope is: %s"), _pDsObjectName ? _pDsObjectName:TEXT("<none>"));
hres = _FindInDs(_pDsObjectName, NULL); FailGracefully(hres, "FindInDs Failed when invoking with a scope"); } }
hres = S_OK; // success
exit_gracefully:
TraceLeaveResult(hres); }
STDMETHODIMP CFindMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax) { HRESULT hres = E_NOTIMPL; INT cc; TraceEnter(TRACE_UI, "CFindMenu::GetCommandString");
// "Find..."? on a DS object, if so then lets load the help text
// for it.
if (IsEqualCLSID(_clsidFindEntry, CLSID_DsFind)) { if ((idCmd == IDC_DSFIND) && (uFlags == GCS_HELPTEXT)) { if (!LoadString(g_hInstance, IDS_FINDHELP, (LPTSTR)pszName, ccMax)) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to load help caption for verb"); } }
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres); }
// handle construction
STDAPI CDsFind_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CFindMenu *pdf = new CFindMenu(*poi->pclsid); if (!pdf) return E_OUTOFMEMORY;
HRESULT hres = pdf->QueryInterface(IID_IUnknown, (void **)ppunk); pdf->Release(); return hres; }
|