mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
609 lines
18 KiB
609 lines
18 KiB
#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;
|
|
DSQUERYINITPARAMS dqip;
|
|
LPFINDSTATE pFindState = (LPFINDSTATE)pThreadData;
|
|
|
|
TraceEnter(TRACE_UI, "_FindInDsThread");
|
|
|
|
hres = hresCoInit = CoInitialize(NULL);
|
|
FailGracefully(hres, "Failed in call to CoInitialize");
|
|
|
|
hres = CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER, IID_ICommonQuery, (LPVOID*)&pCommonQuery);
|
|
FailGracefully(hres, "Failed in CoCreateInstance of CLSID_CommonQuery");
|
|
|
|
dqip.cbStruct = SIZEOF(dqip);
|
|
dqip.dwFlags = 0;
|
|
dqip.pDefaultScope = NULL;
|
|
|
|
if (pFindState->pScope)
|
|
dqip.pDefaultScope = pFindState->pScope;
|
|
|
|
oqw.cbStruct = SIZEOF(oqw);
|
|
oqw.dwFlags = 0;
|
|
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;
|
|
USES_CONVERSION;
|
|
|
|
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"), W2T(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()
|
|
{
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
LPWSTR pDsObjectName;
|
|
USES_CONVERSION;
|
|
|
|
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)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 ? W2T(_pDsObjectName):TEXT("<not defined>"));
|
|
#endif
|
|
|
|
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);
|
|
|
|
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)
|
|
{
|
|
LPTSTR pszPolicy;
|
|
|
|
// 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);
|
|
USES_CONVERSION;
|
|
|
|
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 ? W2T(_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;
|
|
}
|