Source code of Windows XP (NT5)
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

  1. #include "pch.h"
  2. #pragma hdrstop
  3. #define IDC_DSFIND 0x0000
  4. typedef struct
  5. {
  6. CLSID clsidForm;
  7. LPTSTR pCaption;
  8. LPTSTR pIconPath;
  9. INT idIcon;
  10. } FORMLISTITEM, * LPFORMLISTITEM;
  11. /*-----------------------------------------------------------------------------
  12. / Helper functions
  13. /----------------------------------------------------------------------------*/
  14. /*-----------------------------------------------------------------------------
  15. / _FindInDs
  16. / ---------
  17. / Launch the Directory Search UI given a CLSID (for the form) or a
  18. / scope to invoke off.
  19. /
  20. / In:
  21. / pScope -> scope to root the search at / == NULL
  22. / pCLSID -> clsid for the form / == NULL
  23. /
  24. / Out:
  25. / HRESULT
  26. /----------------------------------------------------------------------------*/
  27. typedef struct
  28. {
  29. LPWSTR pScope;
  30. CLSID clsidForm;
  31. } FINDSTATE, * LPFINDSTATE;
  32. //
  33. // bg thread used to display the query UI in a non-clocking way
  34. //
  35. DWORD WINAPI _FindInDsThread(LPVOID pThreadData)
  36. {
  37. HRESULT hres, hresCoInit;
  38. ICommonQuery* pCommonQuery = NULL;
  39. OPENQUERYWINDOW oqw;
  40. DSQUERYINITPARAMS dqip;
  41. LPFINDSTATE pFindState = (LPFINDSTATE)pThreadData;
  42. TraceEnter(TRACE_UI, "_FindInDsThread");
  43. hres = hresCoInit = CoInitialize(NULL);
  44. FailGracefully(hres, "Failed in call to CoInitialize");
  45. hres = CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER, IID_ICommonQuery, (LPVOID*)&pCommonQuery);
  46. FailGracefully(hres, "Failed in CoCreateInstance of CLSID_CommonQuery");
  47. dqip.cbStruct = SIZEOF(dqip);
  48. dqip.dwFlags = 0;
  49. dqip.pDefaultScope = NULL;
  50. if (pFindState->pScope)
  51. dqip.pDefaultScope = pFindState->pScope;
  52. oqw.cbStruct = SIZEOF(oqw);
  53. oqw.dwFlags = 0;
  54. oqw.clsidHandler = CLSID_DsQuery;
  55. oqw.pHandlerParameters = &dqip;
  56. if (!pFindState->pScope)
  57. {
  58. oqw.dwFlags |= OQWF_DEFAULTFORM|OQWF_REMOVEFORMS;
  59. oqw.clsidDefaultForm = pFindState->clsidForm;
  60. }
  61. hres = pCommonQuery->OpenQueryWindow(NULL, &oqw, NULL);
  62. FailGracefully(hres, "OpenQueryWindow failed");
  63. exit_gracefully:
  64. LocalFreeStringW(&pFindState->pScope);
  65. LocalFree(pFindState);
  66. DoRelease(pCommonQuery);
  67. if (SUCCEEDED(hresCoInit))
  68. CoUninitialize();
  69. TraceLeave();
  70. DllRelease();
  71. ExitThread(0);
  72. return 0;
  73. }
  74. //
  75. // API for invoking the query UI
  76. //
  77. HRESULT _FindInDs(LPWSTR pScope, LPCLSID pCLSID)
  78. {
  79. HRESULT hres;
  80. LPFINDSTATE pFindState;
  81. HANDLE hThread;
  82. DWORD dwThreadID;
  83. USES_CONVERSION;
  84. TraceEnter(TRACE_UI, "_FindInDs");
  85. if ((!pScope && !pCLSID) || (pScope && pCLSID))
  86. ExitGracefully(hres, E_INVALIDARG, "Bad arguments for invoking the search");
  87. pFindState = (LPFINDSTATE)LocalAlloc(LPTR, SIZEOF(FINDSTATE));
  88. TraceAssert(pFindState);
  89. if (!pFindState)
  90. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate state block");
  91. // pFindState->pScope = NULL;
  92. // pFindState->clsidForm = { 0 };
  93. if (pScope)
  94. {
  95. Trace(TEXT("Defaulting to scope: %s"), W2T(pScope));
  96. hres = LocalAllocStringW(&pFindState->pScope, pScope);
  97. FailGracefully(hres, "Failed to copy scope");
  98. }
  99. if (pCLSID)
  100. {
  101. TraceGUID("Invoking with form: ", *pCLSID);
  102. pFindState->clsidForm = *pCLSID;
  103. }
  104. DllAddRef();
  105. hThread = CreateThread(NULL, 0, _FindInDsThread, (LPVOID)pFindState, 0, &dwThreadID);
  106. TraceAssert(hThread);
  107. if (!hThread)
  108. {
  109. LocalFreeStringW(&pFindState->pScope);
  110. LocalFree((HLOCAL)pFindState);
  111. DllRelease();
  112. ExitGracefully(hres, E_FAIL, "Failed to create thread and issue query on it");
  113. }
  114. CloseHandle(hThread);
  115. hres = S_OK; // success
  116. exit_gracefully:
  117. TraceLeaveResult(hres);
  118. }
  119. // object for invoking the find UI from the search menu in the shell (or off a context menu)
  120. class CFindMenu : public IShellExtInit, IContextMenu
  121. {
  122. private:
  123. LONG _cRef;
  124. CLSID _clsidFindEntry;
  125. LPWSTR _pDsObjectName;
  126. HDSA _hdsaFormList;
  127. public:
  128. CFindMenu(REFCLSID clsidFindEntry);
  129. ~CFindMenu();
  130. // IUnknown
  131. STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
  132. STDMETHOD_(ULONG, AddRef)();
  133. STDMETHOD_(ULONG, Release)();
  134. // IShellExtInit
  135. STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
  136. // IContextMenu
  137. STDMETHODIMP QueryContextMenu(HMENU hShellMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  138. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
  139. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax);
  140. };
  141. CFindMenu::CFindMenu(REFCLSID clsidFindEntry) :
  142. _clsidFindEntry(clsidFindEntry),
  143. _pDsObjectName(NULL),
  144. _hdsaFormList(NULL),
  145. _cRef(1)
  146. {
  147. DllAddRef();
  148. }
  149. INT _FreeFormListCB(LPVOID pItem, LPVOID pData)
  150. {
  151. LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)pItem;
  152. TraceAssert(pFormListItem);
  153. LocalFreeString(&pFormListItem->pCaption);
  154. LocalFreeString(&pFormListItem->pIconPath);
  155. return 1;
  156. }
  157. CFindMenu::~CFindMenu()
  158. {
  159. LocalFreeStringW(&_pDsObjectName);
  160. if (_hdsaFormList)
  161. DSA_DestroyCallback(_hdsaFormList, _FreeFormListCB, NULL);
  162. DllRelease();
  163. }
  164. // QI handling
  165. ULONG CFindMenu::AddRef()
  166. {
  167. return InterlockedIncrement(&_cRef);
  168. }
  169. ULONG CFindMenu::Release()
  170. {
  171. if (InterlockedDecrement(&_cRef))
  172. return _cRef;
  173. delete this;
  174. return 0;
  175. }
  176. HRESULT CFindMenu::QueryInterface(REFIID riid, void **ppv)
  177. {
  178. static const QITAB qit[] =
  179. {
  180. QITABENT(CFindMenu, IShellExtInit), // IID_IShellExtInit
  181. QITABENT(CFindMenu, IContextMenu), // IID_IContextMenu
  182. {0, 0 },
  183. };
  184. return QISearch(this, qit, riid, ppv);
  185. }
  186. // IShellExtInit
  187. STDMETHODIMP CFindMenu::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID)
  188. {
  189. HRESULT hres;
  190. FORMATETC fmte = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  191. STGMEDIUM medium = { TYMED_NULL };
  192. LPDSOBJECTNAMES pDsObjects;
  193. LPWSTR pDsObjectName;
  194. USES_CONVERSION;
  195. TraceEnter(TRACE_UI, "CFindMenu::Initialize");
  196. // when building the Start->Find menu we are invoked but we are not passed
  197. // an IDataObject, therefore we know this is the case and we shall just
  198. // build the "In the Directory" form list.
  199. if (!ShowDirectoryUI())
  200. ExitGracefully(hres, E_FAIL, "ShowDirectoryUI returns FALSE, so failing initialize");
  201. if (IsEqualCLSID(_clsidFindEntry, CLSID_DsFind))
  202. {
  203. if (pDataObj && SUCCEEDED(pDataObj->GetData(&fmte, &medium)))
  204. {
  205. pDsObjects = (LPDSOBJECTNAMES)medium.hGlobal;
  206. pDsObjectName = (LPWSTR)ByteOffset(pDsObjects, pDsObjects->aObjects[0].offsetName);
  207. TraceAssert(pDsObjectName);
  208. hres = LocalAllocStringW(&_pDsObjectName, pDsObjectName);
  209. FailGracefully(hres, "Failed to copy scope path");
  210. }
  211. if (!_pDsObjectName)
  212. ExitGracefully(hres, E_FAIL, "Failed to get root scope for this object");
  213. }
  214. hres = S_OK; // success
  215. exit_gracefully:
  216. #ifdef DSUI_DEBUG
  217. if (SUCCEEDED(hres))
  218. Trace(TEXT("Find rooted at -%s-"), _pDsObjectName ? W2T(_pDsObjectName):TEXT("<not defined>"));
  219. #endif
  220. ReleaseStgMedium(&medium);
  221. TraceLeaveResult(hres);
  222. }
  223. // IContextMenu handling
  224. //
  225. // Helper to set the icon for the given menu item
  226. //
  227. VOID _SetMenuItemIcon(HMENU hMenu, UINT item, UINT uID, BOOL fPosition, LPTSTR pIconFile, INT idRes, LPTSTR pCaption, HMENU hSubMenu)
  228. {
  229. MENUITEMINFO mii;
  230. TraceEnter(TRACE_UI, "_SetMenuItemIcon");
  231. Trace(TEXT("hMenu %08x, item %d, pIconFile %s, idRes %d"), hMenu, item, pIconFile, idRes);
  232. Trace(TEXT("pCaption %s, hSubMenu %08x"), pCaption, hSubMenu);
  233. mii.cbSize = SIZEOF(mii);
  234. mii.fMask = MIIM_DATA|MIIM_SUBMENU|MIIM_TYPE|MIIM_ID;
  235. mii.fType = MFT_STRING;
  236. mii.wID = uID;
  237. mii.hSubMenu = hSubMenu;
  238. mii.cch = lstrlen(pCaption);
  239. mii.dwTypeData = pCaption;
  240. mii.dwItemData = Shell_GetCachedImageIndex(pIconFile, idRes, 0);
  241. TraceAssert(mii.dwItemData != -1);
  242. Trace(TEXT("Setting data to be %d"), mii.dwItemData);
  243. InsertMenuItem(hMenu, item, fPosition, &mii);
  244. TraceLeave();
  245. }
  246. STDAPI _LocalQueryMUIString(LPTSTR* ppResult, HKEY hk, LPCTSTR lpSubKey)
  247. {
  248. HRESULT hr = LocalQueryString(ppResult, hk, lpSubKey);
  249. if (SUCCEEDED(hr))
  250. {
  251. // If any of these steps fail, don't fail the call
  252. TCHAR szExpanded[MAX_PATH];
  253. if (SUCCEEDED(SHLoadIndirectString(*ppResult, szExpanded, ARRAYSIZE(szExpanded), NULL)))
  254. {
  255. LPTSTR pszExpanded;
  256. if (SUCCEEDED(LocalAllocString(&pszExpanded, szExpanded)))
  257. {
  258. LocalFreeString(ppResult);
  259. *ppResult = pszExpanded;
  260. }
  261. }
  262. }
  263. return hr;
  264. }
  265. #define EXPLORER_POLICY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer")
  266. STDMETHODIMP CFindMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  267. {
  268. HRESULT hres;
  269. TCHAR szBuffer[MAX_PATH];
  270. LPTSTR pBuffer = NULL;
  271. INT i, iItems = 0;
  272. FORMLISTITEM fli;
  273. HKEY hKey = NULL;
  274. HKEY hKeyForm = NULL;
  275. HKEY hkPolicy = NULL;
  276. TraceEnter(TRACE_UI, "CFindMenu::QueryContextMenu");
  277. // Just make sure we are allowed to surface this UI.
  278. if (!ShowDirectoryUI())
  279. ExitGracefully(hres, E_FAIL, "ShowDirectoryUI returns FALSE, so failing initialize");
  280. // if we have no scope stored in our class then lets build the Start.Find menu entry
  281. // which we get from data stored in the registry.
  282. if (IsEqualCLSID(_clsidFindEntry, CLSID_DsStartFind))
  283. {
  284. // enumerate the entries we are going to display in Start->Find from the registry
  285. // this is then stored in a DSA so that we can invoke the Find UI on the
  286. // correct query form.
  287. _hdsaFormList = DSA_Create(SIZEOF(FORMLISTITEM), 4);
  288. TraceAssert(_hdsaFormList);
  289. if (!_hdsaFormList)
  290. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate find entry DSA");
  291. hres = GetKeyForCLSID(CLSID_DsQuery, TEXT("StartFindEntries"), &hKey);
  292. FailGracefully(hres, "Failed to get HKEY for the DsQuery CLSID");
  293. //
  294. // get the policy key so that we can check to see if we must disbale the entries
  295. //
  296. if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, EXPLORER_POLICY, NULL, KEY_READ, &hkPolicy))
  297. {
  298. TraceMsg("Explorer policy key not found");
  299. hkPolicy = NULL;
  300. }
  301. for (i = 0 ; TRUE ; i++)
  302. {
  303. DWORD cchBuffer = ARRAYSIZE(szBuffer);
  304. if (ERROR_SUCCESS != RegEnumKeyEx(hKey, i, szBuffer, &cchBuffer, NULL, NULL, NULL, NULL))
  305. {
  306. TraceMsg("RegEnumKeyEx failed, therefore stopping enumeration");
  307. break;
  308. }
  309. else
  310. {
  311. // We have a caption for the query form we want to display for the
  312. // menu item, now lets pick up the GUID that is stored with it
  313. // so that we can invoke the form.
  314. if (hKeyForm)
  315. {
  316. RegCloseKey(hKeyForm);
  317. hKeyForm = NULL;
  318. }
  319. if (ERROR_SUCCESS == RegOpenKeyEx(hKey, szBuffer, NULL, KEY_READ, &hKeyForm))
  320. {
  321. LPTSTR pszPolicy = NULL;
  322. BOOL fHideEntry = FALSE;
  323. // fli.clsidForm = { 0 };
  324. fli.pCaption = NULL;
  325. fli.pIconPath = NULL;
  326. fli.idIcon = 0;
  327. //
  328. // lets parse out the CLSID into a value that we can put into the structure.
  329. //
  330. Trace(TEXT("Form GUID: %s"), szBuffer);
  331. if (!GetGUIDFromString(szBuffer, &fli.clsidForm))
  332. {
  333. TraceMsg("Failed to parse the CLSID of the form");
  334. continue;
  335. }
  336. //
  337. // check to see if we have a policy key, if we do then we can disable the entry.
  338. //
  339. if (hkPolicy && SUCCEEDED(LocalQueryString(&pszPolicy, hKeyForm, TEXT("Policy"))))
  340. {
  341. Trace(TEXT("Policy value is: %s"), pszPolicy);
  342. DWORD dwType = REG_DWORD, cb = SIZEOF(fHideEntry);
  343. if (ERROR_SUCCESS != RegQueryValueEx(hkPolicy, pszPolicy, NULL, &dwType, (LPBYTE)&fHideEntry, &cb))
  344. {
  345. TraceMsg("Failed to read the policy value");
  346. }
  347. LocalFreeString(&pszPolicy);
  348. }
  349. //
  350. // add the entry to the search menu list?
  351. //
  352. if (!fHideEntry)
  353. {
  354. LPTSTR pszPolicy;
  355. // OK the GUID for the form parse OK and the policy says it is enabled
  356. // therefore we must attempt to build a find menu entry for this object
  357. if (SUCCEEDED(_LocalQueryMUIString(&fli.pCaption, hKeyForm, TEXT("LocalizedString"))) ||
  358. SUCCEEDED(LocalQueryString(&fli.pCaption, hKeyForm, NULL)))
  359. {
  360. Trace(TEXT("Form title: %s"), fli.pCaption);
  361. if (SUCCEEDED(LocalQueryString(&fli.pIconPath, hKeyForm, TEXT("Icon"))))
  362. {
  363. fli.idIcon = PathParseIconLocation(fli.pIconPath);
  364. Trace(TEXT("Icon is: %s, resource %d"), fli.pIconPath, fli.idIcon);
  365. }
  366. if (-1 == DSA_AppendItem(_hdsaFormList, &fli))
  367. {
  368. _FreeFormListCB(&fli, NULL);
  369. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate FORMLISTITEM structure");
  370. }
  371. }
  372. }
  373. }
  374. }
  375. }
  376. // we now (hopefully) have a DS full of the items we want to display on
  377. // the menu, so lets try and construct the menu around us.
  378. for (i = 0 ; i < DSA_GetItemCount(_hdsaFormList) ; i++, iItems++)
  379. {
  380. LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)DSA_GetItemPtr(_hdsaFormList, i);
  381. TraceAssert(pFormListItem);
  382. _SetMenuItemIcon(hMenu, i, idCmdFirst+i, TRUE, pFormListItem->pIconPath, pFormListItem->idIcon, pFormListItem->pCaption, NULL);
  383. }
  384. }
  385. else
  386. {
  387. // when we are just a normal verb hanging off an objects context menu
  388. // then lets just load the string we want to display and show it.
  389. if (!LoadString(GLOBAL_HINSTANCE, IDS_FIND, szBuffer, ARRAYSIZE(szBuffer)))
  390. ExitGracefully(hres, E_FAIL, "Failed to load resource for menu item");
  391. InsertMenu(hMenu, indexMenu, MF_BYPOSITION|MF_STRING, idCmdFirst+IDC_DSFIND, szBuffer);
  392. iItems++;
  393. }
  394. hres = S_OK;
  395. exit_gracefully:
  396. if (SUCCEEDED(hres))
  397. hres = ResultFromShort(iItems);
  398. if (hKey)
  399. RegCloseKey(hKey);
  400. if (hKeyForm)
  401. RegCloseKey(hKeyForm);
  402. if (hkPolicy)
  403. RegCloseKey(hkPolicy);
  404. LocalFreeString(&pBuffer);
  405. TraceLeaveValue(hres);
  406. }
  407. STDMETHODIMP CFindMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
  408. {
  409. HRESULT hres = E_FAIL;
  410. INT id = LOWORD(lpcmi->lpVerb);
  411. USES_CONVERSION;
  412. TraceEnter(TRACE_UI, "CFindMenu::InvokeCommand");
  413. if (!HIWORD(lpcmi->lpVerb))
  414. {
  415. // if we have a DSA and the verb is inside the DSA then lets invoke the
  416. // query UI with the correct form displayed, otherwise we can default to
  417. // using the scope we have (which can also be NULL)
  418. if (IsEqualCLSID(_clsidFindEntry, CLSID_DsStartFind) &&
  419. _hdsaFormList && (id < DSA_GetItemCount(_hdsaFormList)))
  420. {
  421. LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)DSA_GetItemPtr(_hdsaFormList, id);
  422. TraceAssert(pFormListItem);
  423. TraceGUID("Invoking query form: ", pFormListItem->clsidForm);
  424. hres = _FindInDs(NULL, &pFormListItem->clsidForm);
  425. FailGracefully(hres, "FindInDs failed when invoking with a query form");
  426. }
  427. else
  428. {
  429. Trace(TEXT("Scope is: %s"), _pDsObjectName ? W2T(_pDsObjectName):TEXT("<none>"));
  430. hres = _FindInDs(_pDsObjectName, NULL);
  431. FailGracefully(hres, "FindInDs Failed when invoking with a scope");
  432. }
  433. }
  434. hres = S_OK; // success
  435. exit_gracefully:
  436. TraceLeaveResult(hres);
  437. }
  438. STDMETHODIMP CFindMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax)
  439. {
  440. HRESULT hres = E_NOTIMPL;
  441. INT cc;
  442. TraceEnter(TRACE_UI, "CFindMenu::GetCommandString");
  443. // "Find..."? on a DS object, if so then lets load the help text
  444. // for it.
  445. if (IsEqualCLSID(_clsidFindEntry, CLSID_DsFind))
  446. {
  447. if ((idCmd == IDC_DSFIND) && (uFlags == GCS_HELPTEXT))
  448. {
  449. if (!LoadString(g_hInstance, IDS_FINDHELP, (LPTSTR)pszName, ccMax))
  450. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to load help caption for verb");
  451. }
  452. }
  453. hres = S_OK;
  454. exit_gracefully:
  455. TraceLeaveResult(hres);
  456. }
  457. // handle construction
  458. STDAPI CDsFind_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  459. {
  460. CFindMenu *pdf = new CFindMenu(*poi->pclsid);
  461. if (!pdf)
  462. return E_OUTOFMEMORY;
  463. HRESULT hres = pdf->QueryInterface(IID_IUnknown, (void **)ppunk);
  464. pdf->Release();
  465. return hres;
  466. }