Leaked source code of windows server 2003
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.

610 lines
19 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 = {0};
  40. DSQUERYINITPARAMS dqip = {0};
  41. LPFINDSTATE pFindState = (LPFINDSTATE)pThreadData;
  42. TraceEnter(TRACE_UI, "_FindInDsThread");
  43. hresCoInit = CoInitialize(NULL); // can fail, b/c CoInit already preformed
  44. hres = CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ICommonQuery, &pCommonQuery));
  45. FailGracefully(hres, "Failed in CoCreateInstance of CLSID_CommonQuery");
  46. dqip.cbStruct = SIZEOF(dqip);
  47. dqip.pDefaultScope = NULL;
  48. if (pFindState->pScope)
  49. dqip.pDefaultScope = pFindState->pScope;
  50. oqw.cbStruct = SIZEOF(oqw);
  51. oqw.clsidHandler = CLSID_DsQuery;
  52. oqw.pHandlerParameters = &dqip;
  53. if (!pFindState->pScope)
  54. {
  55. oqw.dwFlags |= OQWF_DEFAULTFORM|OQWF_REMOVEFORMS;
  56. oqw.clsidDefaultForm = pFindState->clsidForm;
  57. }
  58. hres = pCommonQuery->OpenQueryWindow(NULL, &oqw, NULL);
  59. FailGracefully(hres, "OpenQueryWindow failed");
  60. exit_gracefully:
  61. LocalFreeStringW(&pFindState->pScope);
  62. LocalFree(pFindState);
  63. DoRelease(pCommonQuery);
  64. if (SUCCEEDED(hresCoInit))
  65. CoUninitialize();
  66. TraceLeave();
  67. DllRelease();
  68. ExitThread(0);
  69. return 0;
  70. }
  71. //
  72. // API for invoking the query UI
  73. //
  74. HRESULT _FindInDs(LPWSTR pScope, LPCLSID pCLSID)
  75. {
  76. HRESULT hres;
  77. LPFINDSTATE pFindState;
  78. HANDLE hThread;
  79. DWORD dwThreadID;
  80. TraceEnter(TRACE_UI, "_FindInDs");
  81. if ((!pScope && !pCLSID) || (pScope && pCLSID))
  82. ExitGracefully(hres, E_INVALIDARG, "Bad arguments for invoking the search");
  83. pFindState = (LPFINDSTATE)LocalAlloc(LPTR, SIZEOF(FINDSTATE));
  84. TraceAssert(pFindState);
  85. if (!pFindState)
  86. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate state block");
  87. // pFindState->pScope = NULL;
  88. // pFindState->clsidForm = { 0 };
  89. if (pScope)
  90. {
  91. Trace(TEXT("Defaulting to scope: %s"), pScope);
  92. hres = LocalAllocStringW(&pFindState->pScope, pScope);
  93. FailGracefully(hres, "Failed to copy scope");
  94. }
  95. if (pCLSID)
  96. {
  97. TraceGUID("Invoking with form: ", *pCLSID);
  98. pFindState->clsidForm = *pCLSID;
  99. }
  100. DllAddRef();
  101. hThread = CreateThread(NULL, 0, _FindInDsThread, (LPVOID)pFindState, 0, &dwThreadID);
  102. TraceAssert(hThread);
  103. if (!hThread)
  104. {
  105. LocalFreeStringW(&pFindState->pScope);
  106. LocalFree((HLOCAL)pFindState);
  107. DllRelease();
  108. ExitGracefully(hres, E_FAIL, "Failed to create thread and issue query on it");
  109. }
  110. CloseHandle(hThread);
  111. hres = S_OK; // success
  112. exit_gracefully:
  113. TraceLeaveResult(hres);
  114. }
  115. // object for invoking the find UI from the search menu in the shell (or off a context menu)
  116. class CFindMenu : public IShellExtInit, IContextMenu
  117. {
  118. private:
  119. LONG _cRef;
  120. CLSID _clsidFindEntry;
  121. LPWSTR _pDsObjectName;
  122. HDSA _hdsaFormList;
  123. public:
  124. CFindMenu(REFCLSID clsidFindEntry);
  125. ~CFindMenu();
  126. // IUnknown
  127. STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
  128. STDMETHOD_(ULONG, AddRef)();
  129. STDMETHOD_(ULONG, Release)();
  130. // IShellExtInit
  131. STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
  132. // IContextMenu
  133. STDMETHODIMP QueryContextMenu(HMENU hShellMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  134. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
  135. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax);
  136. };
  137. CFindMenu::CFindMenu(REFCLSID clsidFindEntry) :
  138. _clsidFindEntry(clsidFindEntry),
  139. _pDsObjectName(NULL),
  140. _hdsaFormList(NULL),
  141. _cRef(1)
  142. {
  143. DllAddRef();
  144. }
  145. INT _FreeFormListCB(LPVOID pItem, LPVOID pData)
  146. {
  147. LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)pItem;
  148. TraceAssert(pFormListItem);
  149. LocalFreeString(&pFormListItem->pCaption);
  150. LocalFreeString(&pFormListItem->pIconPath);
  151. return 1;
  152. }
  153. CFindMenu::~CFindMenu()
  154. {
  155. LocalFreeStringW(&_pDsObjectName);
  156. if (_hdsaFormList)
  157. DSA_DestroyCallback(_hdsaFormList, _FreeFormListCB, NULL);
  158. DllRelease();
  159. }
  160. // QI handling
  161. ULONG CFindMenu::AddRef()
  162. {
  163. return InterlockedIncrement(&_cRef);
  164. }
  165. ULONG CFindMenu::Release()
  166. {
  167. TraceAssert( 0 != _cRef );
  168. ULONG cRef = InterlockedDecrement(&_cRef);
  169. if ( 0 == cRef )
  170. {
  171. delete this;
  172. }
  173. return cRef;
  174. }
  175. HRESULT CFindMenu::QueryInterface(REFIID riid, void **ppv)
  176. {
  177. static const QITAB qit[] =
  178. {
  179. QITABENT(CFindMenu, IShellExtInit), // IID_IShellExtInit
  180. QITABENT(CFindMenu, IContextMenu), // IID_IContextMenu
  181. {0, 0 },
  182. };
  183. return QISearch(this, qit, riid, ppv);
  184. }
  185. // IShellExtInit
  186. STDMETHODIMP CFindMenu::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID)
  187. {
  188. HRESULT hres;
  189. FORMATETC fmte = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  190. STGMEDIUM medium = { TYMED_NULL };
  191. LPDSOBJECTNAMES pDsObjects = NULL;
  192. LPWSTR pDsObjectName;
  193. TraceEnter(TRACE_UI, "CFindMenu::Initialize");
  194. // when building the Start->Find menu we are invoked but we are not passed
  195. // an IDataObject, therefore we know this is the case and we shall just
  196. // build the "In the Directory" form list.
  197. if (!ShowDirectoryUI())
  198. ExitGracefully(hres, E_FAIL, "ShowDirectoryUI returns FALSE, so failing initialize");
  199. if (IsEqualCLSID(_clsidFindEntry, CLSID_DsFind))
  200. {
  201. if (pDataObj && SUCCEEDED(pDataObj->GetData(&fmte, &medium)))
  202. {
  203. pDsObjects = (LPDSOBJECTNAMES)GlobalLock(medium.hGlobal);
  204. pDsObjectName = (LPWSTR)ByteOffset(pDsObjects, pDsObjects->aObjects[0].offsetName);
  205. TraceAssert(pDsObjectName);
  206. hres = LocalAllocStringW(&_pDsObjectName, pDsObjectName);
  207. FailGracefully(hres, "Failed to copy scope path");
  208. }
  209. if (!_pDsObjectName)
  210. ExitGracefully(hres, E_FAIL, "Failed to get root scope for this object");
  211. }
  212. hres = S_OK; // success
  213. exit_gracefully:
  214. #ifdef DSUI_DEBUG
  215. if (SUCCEEDED(hres))
  216. Trace(TEXT("Find rooted at -%s-"), _pDsObjectName ? _pDsObjectName:TEXT("<not defined>"));
  217. #endif
  218. if (pDsObjects)
  219. GlobalUnlock(medium.hGlobal);
  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. // we check for the presense of a DWORD under the policy key,
  343. // if the value is non-NULL (doesn't matter what type)
  344. // then we treat it as a "hide me policy"
  345. DWORD dwType = REG_DWORD, cb = SIZEOF(fHideEntry);
  346. if (ERROR_SUCCESS != RegQueryValueEx(hkPolicy, pszPolicy, NULL, &dwType, (LPBYTE)&fHideEntry, &cb))
  347. {
  348. TraceMsg("Failed to read the policy value");
  349. }
  350. LocalFreeString(&pszPolicy);
  351. }
  352. //
  353. // add the entry to the search menu list?
  354. //
  355. if (!fHideEntry)
  356. {
  357. // OK the GUID for the form parse OK and the policy says it is enabled
  358. // therefore we must attempt to build a find menu entry for this object
  359. if (SUCCEEDED(_LocalQueryMUIString(&fli.pCaption, hKeyForm, TEXT("LocalizedString"))) ||
  360. SUCCEEDED(LocalQueryString(&fli.pCaption, hKeyForm, NULL)))
  361. {
  362. Trace(TEXT("Form title: %s"), fli.pCaption);
  363. if (SUCCEEDED(LocalQueryString(&fli.pIconPath, hKeyForm, TEXT("Icon"))))
  364. {
  365. fli.idIcon = PathParseIconLocation(fli.pIconPath);
  366. Trace(TEXT("Icon is: %s, resource %d"), fli.pIconPath, fli.idIcon);
  367. }
  368. if (-1 == DSA_AppendItem(_hdsaFormList, &fli))
  369. {
  370. _FreeFormListCB(&fli, NULL);
  371. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate FORMLISTITEM structure");
  372. }
  373. }
  374. }
  375. }
  376. }
  377. }
  378. // we now (hopefully) have a DS full of the items we want to display on
  379. // the menu, so lets try and construct the menu around us.
  380. for (i = 0 ; i < DSA_GetItemCount(_hdsaFormList) ; i++, iItems++)
  381. {
  382. LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)DSA_GetItemPtr(_hdsaFormList, i);
  383. TraceAssert(pFormListItem);
  384. _SetMenuItemIcon(hMenu, i, idCmdFirst+i, TRUE, pFormListItem->pIconPath, pFormListItem->idIcon, pFormListItem->pCaption, NULL);
  385. }
  386. }
  387. else
  388. {
  389. // when we are just a normal verb hanging off an objects context menu
  390. // then lets just load the string we want to display and show it.
  391. if (!LoadString(GLOBAL_HINSTANCE, IDS_FIND, szBuffer, ARRAYSIZE(szBuffer)))
  392. ExitGracefully(hres, E_FAIL, "Failed to load resource for menu item");
  393. InsertMenu(hMenu, indexMenu, MF_BYPOSITION|MF_STRING, idCmdFirst+IDC_DSFIND, szBuffer);
  394. iItems++;
  395. }
  396. hres = S_OK;
  397. exit_gracefully:
  398. if (SUCCEEDED(hres))
  399. hres = ResultFromShort(iItems);
  400. if (hKey)
  401. RegCloseKey(hKey);
  402. if (hKeyForm)
  403. RegCloseKey(hKeyForm);
  404. if (hkPolicy)
  405. RegCloseKey(hkPolicy);
  406. LocalFreeString(&pBuffer);
  407. TraceLeaveValue(hres);
  408. }
  409. STDMETHODIMP CFindMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
  410. {
  411. HRESULT hres = E_FAIL;
  412. INT id = LOWORD(lpcmi->lpVerb);
  413. TraceEnter(TRACE_UI, "CFindMenu::InvokeCommand");
  414. if (!HIWORD(lpcmi->lpVerb))
  415. {
  416. // if we have a DSA and the verb is inside the DSA then lets invoke the
  417. // query UI with the correct form displayed, otherwise we can default to
  418. // using the scope we have (which can also be NULL)
  419. if (IsEqualCLSID(_clsidFindEntry, CLSID_DsStartFind) &&
  420. _hdsaFormList && (id < DSA_GetItemCount(_hdsaFormList)))
  421. {
  422. LPFORMLISTITEM pFormListItem = (LPFORMLISTITEM)DSA_GetItemPtr(_hdsaFormList, id);
  423. TraceAssert(pFormListItem);
  424. TraceGUID("Invoking query form: ", pFormListItem->clsidForm);
  425. hres = _FindInDs(NULL, &pFormListItem->clsidForm);
  426. FailGracefully(hres, "FindInDs failed when invoking with a query form");
  427. }
  428. else
  429. {
  430. Trace(TEXT("Scope is: %s"), _pDsObjectName ? _pDsObjectName:TEXT("<none>"));
  431. hres = _FindInDs(_pDsObjectName, NULL);
  432. FailGracefully(hres, "FindInDs Failed when invoking with a scope");
  433. }
  434. }
  435. hres = S_OK; // success
  436. exit_gracefully:
  437. TraceLeaveResult(hres);
  438. }
  439. STDMETHODIMP CFindMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax)
  440. {
  441. HRESULT hres = E_NOTIMPL;
  442. INT cc;
  443. TraceEnter(TRACE_UI, "CFindMenu::GetCommandString");
  444. // "Find..."? on a DS object, if so then lets load the help text
  445. // for it.
  446. if (IsEqualCLSID(_clsidFindEntry, CLSID_DsFind))
  447. {
  448. if ((idCmd == IDC_DSFIND) && (uFlags == GCS_HELPTEXT))
  449. {
  450. if (!LoadString(g_hInstance, IDS_FINDHELP, (LPTSTR)pszName, ccMax))
  451. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to load help caption for verb");
  452. }
  453. }
  454. hres = S_OK;
  455. exit_gracefully:
  456. TraceLeaveResult(hres);
  457. }
  458. // handle construction
  459. STDAPI CDsFind_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  460. {
  461. CFindMenu *pdf = new CFindMenu(*poi->pclsid);
  462. if (!pdf)
  463. return E_OUTOFMEMORY;
  464. HRESULT hres = pdf->QueryInterface(IID_IUnknown, (void **)ppunk);
  465. pdf->Release();
  466. return hres;
  467. }