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.

2151 lines
66 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "dspsprt.h"
  4. #include "findfilter.h"
  5. #include "cowsite.h"
  6. #include "cobjsafe.h"
  7. #include "cnctnpt.h"
  8. #include "stdenum.h"
  9. #include "exdisp.h"
  10. #include "exdispid.h"
  11. #include "shldisp.h"
  12. #include "shdispid.h"
  13. #include "dataprv.h"
  14. #include "ids.h"
  15. #include "views.h"
  16. #include "findband.h"
  17. #define WM_DF_SEARCHPROGRESS (WM_USER + 42)
  18. #define WM_DF_ASYNCPROGRESS (WM_USER + 43)
  19. #define WM_DF_SEARCHSTART (WM_USER + 44)
  20. #define WM_DF_SEARCHCOMPLETE (WM_USER + 45)
  21. #define WM_DF_FSNOTIFY (WM_USER + 46)
  22. STDAPI CDocFindCommand_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
  23. typedef struct
  24. {
  25. BSTR bstrName;
  26. VARIANT vValue;
  27. } CMD_CONSTRAINT;
  28. typedef struct
  29. {
  30. LPTSTR pszDotType;
  31. LPTSTR pszDefaultValueMatch;
  32. LPTSTR pszGuid; // If NULL, patch either pszDefaultValueMatch, or pszDotType whichever you find
  33. } TYPE_FIX_ENTRY;
  34. class CFindCmd : public ISearchCommandExt,
  35. public CImpIDispatch,
  36. public CObjectWithSite,
  37. public CObjectSafety,
  38. public IConnectionPointContainer,
  39. public IProvideClassInfo2,
  40. public CSimpleData,
  41. public IRowsetWatchNotify,
  42. public IFindControllerNotify
  43. {
  44. public:
  45. // IUnknown
  46. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  47. STDMETHODIMP_(ULONG) AddRef();
  48. STDMETHODIMP_(ULONG) Release();
  49. // IDispatch
  50. STDMETHOD(GetTypeInfoCount)(UINT * pctinfo);
  51. STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfo);
  52. STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid);
  53. STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
  54. // IConnectionPointContainer
  55. STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints **ppEnum);
  56. STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint **ppCP);
  57. // IProvideClassInfo
  58. STDMETHOD(GetClassInfo)(ITypeInfo **ppTI);
  59. // IProvideClassInfo2
  60. STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID *pGUID);
  61. // IObjectWithSite
  62. STDMETHOD(SetSite)(IUnknown *punkSite);
  63. // ISearchCommandExt
  64. STDMETHOD(ClearResults)(void);
  65. STDMETHOD(NavigateToSearchResults)(void);
  66. STDMETHOD(get_ProgressText)(BSTR *pbs);
  67. STDMETHOD(SaveSearch)(void);
  68. STDMETHOD(RestoreSearch)(void);
  69. STDMETHOD(GetErrorInfo)(BSTR *pbs, int *phr);
  70. STDMETHOD(SearchFor)(int iFor);
  71. STDMETHOD(GetScopeInfo)(BSTR bsScope, int *pdwScopeInfo);
  72. STDMETHOD(RestoreSavedSearch)(VARIANT *pvarFile);
  73. STDMETHOD(Execute)(VARIANT *RecordsAffected, VARIANT *Parameters, long Options);
  74. STDMETHOD(AddConstraint)(BSTR Name, VARIANT Value);
  75. STDMETHOD(GetNextConstraint)(VARIANT_BOOL fReset, DFConstraint **ppdfc);
  76. // IRowsetWatchNotify
  77. STDMETHODIMP OnChange(IRowset *prowset, DBWATCHNOTIFY eChangeReason);
  78. // IFindControllerNotify
  79. STDMETHODIMP DoSortOnColumn(UINT iCol, BOOL fSameCol);
  80. STDMETHODIMP StopSearch(void);
  81. STDMETHODIMP GetItemCount(UINT *pcItems);
  82. STDMETHODIMP SetItemCount(UINT cItems);
  83. STDMETHODIMP ViewDestroyed();
  84. CFindCmd();
  85. HRESULT Init(void);
  86. private:
  87. ~CFindCmd();
  88. HRESULT _GetSearchIDList(LPITEMIDLIST *ppidl);
  89. HRESULT _SetEmptyText(UINT nID);
  90. HRESULT _Clear();
  91. void _SelectResults();
  92. HWND _GetWindow();
  93. struct THREAD_PARAMS {
  94. CFindCmd *pThis;
  95. IFindEnum *penum;
  96. };
  97. struct DEFER_UPDATE_DIR {
  98. struct DEFER_UPDATE_DIR *pdudNext;
  99. LPITEMIDLIST pidl;
  100. BOOL fRecurse;
  101. };
  102. // Internal class to handle notifications from top level browser
  103. class CWBEvents2: public DWebBrowserEvents, DWebBrowserEvents2
  104. {
  105. public:
  106. STDMETHOD(QueryInterface) (REFIID riid, void **ppv);
  107. STDMETHOD_(ULONG, AddRef)(void) { return _pcdfc->AddRef();}
  108. STDMETHOD_(ULONG, Release)(void) { return _pcdfc->Release();}
  109. // (DwebBrowserEvents)IDispatch
  110. STDMETHOD(GetTypeInfoCount)(UINT * pctinfo) { return E_NOTIMPL;}
  111. STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) { return E_NOTIMPL;}
  112. STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return E_NOTIMPL;}
  113. STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
  114. // Some helper functions...
  115. void SetOwner(CFindCmd *pcdfc) { _pcdfc = pcdfc; } // Don't addref as part of larger object... }
  116. void SetWaiting(BOOL fWait) {_fWaitingForNavigate = fWait;}
  117. protected:
  118. // Internal variables...
  119. CFindCmd *_pcdfc; // pointer to top object... could cast, but...
  120. BOOL _fWaitingForNavigate; // Are we waiting for the navigate to search resluts?
  121. };
  122. friend class CWBEvents2;
  123. CWBEvents2 _cwbe;
  124. IConnectionPoint *_pcpBrowser; // hold onto browsers connection point;
  125. ULONG _dwCookie; // Cookie returned by Advise
  126. HRESULT _UpdateFilter(IFindFilter *pfilter);
  127. void _ClearConstraints();
  128. static DWORD CALLBACK _ThreadProc(void *pv);
  129. void _DoSearch(IFindEnum *penum);
  130. HRESULT _Start(BOOL fNavigateIfFail, int iCol, LPCITEMIDLIST pidlUpdate);
  131. HRESULT _Cancel();
  132. HRESULT _Init(THREAD_PARAMS **ppParams, int iCol, LPCITEMIDLIST pidlUpdate);
  133. static HRESULT _FreeThreadParams(THREAD_PARAMS *ptp);
  134. HRESULT _ExecData_Init();
  135. HRESULT _EnsureResultsViewIsCurrent(IUnknown *punk);
  136. HRESULT _ExecData_Release();
  137. BOOL _SetupBrowserCP();
  138. void cdecl _NotifyProgressText(UINT ids,...);
  139. static LRESULT CALLBACK _WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  140. void _PTN_SearchProgress(void);
  141. void _PTN_AsyncProgress(int nPercentComplete, DWORD cAsync);
  142. void _PTN_AsyncToSync(void);
  143. void _PTN_SearchComplete(HRESULT hr, BOOL fAbort);
  144. void _OnChangeNotify(LONG code, LPITEMIDLIST *ppidl);
  145. void _DeferHandleUpdateDir(LPCITEMIDLIST pidl, BOOL bRecurse);
  146. void _ClearDeferUpdateDirList();
  147. void _ClearItemDPA(HDPA hdpa);
  148. HRESULT _SetLastError(HRESULT hr);
  149. void _SearchResultsCLSID(CLSID *pclsid) { *pclsid = _clsidResults; };
  150. IUnknown* _GetObjectToPersist();
  151. HRESULT _ForcedUnadvise(void);
  152. void _PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  153. HRESULT _GetShellView(REFIID riid, void **ppv);
  154. BOOL _FixPersistHandler(LPCTSTR pszBase, LPCTSTR pszDefaultHandler);
  155. void _ProcessTypes(const TYPE_FIX_ENTRY *ptfeTypes, UINT cTypes, TCHAR *pszClass);
  156. void _FixBrokenTypes(void);
  157. // These are the things that the second thread will use during it's processing...
  158. struct {
  159. CRITICAL_SECTION csSearch;
  160. HWND hwndThreadNotify;
  161. HDPA hdpa;
  162. DWORD dwTimeLastNotify;
  163. BOOL fFilesAdded : 1;
  164. BOOL fDirChanged : 1;
  165. BOOL fUpdatePosted : 1;
  166. } _updateParams; // Pass callback params through this object to avoid alloc/free cycle
  167. struct {
  168. IShellFolder *psf;
  169. IShellFolderView *psfv;
  170. IFindFolder *pff;
  171. TCHAR szProgressText[MAX_PATH];
  172. } _execData;
  173. private:
  174. LONG _cRef;
  175. HDSA _hdsaConstraints;
  176. DWORD _cExecInProgress;
  177. BOOL _fAsyncNotifyReceived;
  178. BOOL _fDeferRestore;
  179. BOOL _fDeferRestoreTried;
  180. BOOL _fContinue;
  181. BOOL _fNew;
  182. CConnectionPoint _cpEvents;
  183. OLEDBSimpleProviderListener *_pListener;
  184. HDPA _hdpaItemsToAdd1;
  185. HDPA _hdpaItemsToAdd2;
  186. TCHAR _szProgressText[MAX_PATH+40]; // progress text leave room for chars...
  187. LPITEMIDLIST _pidlUpdate; // Are we processing an updatedir?
  188. LPITEMIDLIST _pidlRestore; // pidl to do restore from...
  189. struct DEFER_UPDATE_DIR *_pdudFirst; // Do we have any defered update dirs?
  190. HRESULT _hrLastError; // the last error reported.
  191. UINT _uStatusMsgIndex; // Files or computers found...
  192. CRITICAL_SECTION _csThread;
  193. DFBSAVEINFO _dfbsi;
  194. CLSID _clsidResults;
  195. };
  196. class CFindConstraint: public DFConstraint, public CImpIDispatch
  197. {
  198. public:
  199. // IUnknown
  200. STDMETHOD(QueryInterface) (REFIID riid, void **ppv);
  201. STDMETHOD_(ULONG, AddRef)(void);
  202. STDMETHOD_(ULONG, Release)(void);
  203. // IDispatch
  204. STDMETHOD(GetTypeInfoCount)(UINT * pctinfo);
  205. STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfo);
  206. STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid);
  207. STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
  208. // DFConstraint
  209. STDMETHOD(get_Name)(BSTR *pbs);
  210. STDMETHOD(get_Value)(VARIANT *pvar);
  211. CFindConstraint(BSTR bstr, VARIANT var);
  212. private:
  213. ~CFindConstraint();
  214. LONG _cRef;
  215. BSTR _bstr;
  216. VARIANT _var;
  217. };
  218. CFindCmd::CFindCmd() : CImpIDispatch(LIBID_Shell32, 1, 0, IID_ISearchCommandExt), CSimpleData(&_pListener)
  219. {
  220. _cRef = 1;
  221. _fAsyncNotifyReceived = 0;
  222. _fContinue = TRUE;
  223. ASSERT(NULL == _pidlRestore);
  224. ASSERT(_cExecInProgress == 0);
  225. InitializeCriticalSection(&_updateParams.csSearch);
  226. InitializeCriticalSection(&_csThread);
  227. _clsidResults = CLSID_DocFindFolder; // default
  228. _cpEvents.SetOwner(SAFECAST(this, ISearchCommandExt *), &DIID_DSearchCommandEvents);
  229. }
  230. HRESULT CFindCmd::Init(void)
  231. {
  232. _hdsaConstraints = DSA_Create(sizeof(CMD_CONSTRAINT), 4);
  233. if (!_hdsaConstraints)
  234. return E_OUTOFMEMORY;
  235. return S_OK;
  236. }
  237. CFindCmd::~CFindCmd()
  238. {
  239. if (_updateParams.hwndThreadNotify)
  240. {
  241. // make sure no outstanding fsnotifies registered.
  242. SHChangeNotifyDeregisterWindow(_updateParams.hwndThreadNotify);
  243. DestroyWindow(_updateParams.hwndThreadNotify);
  244. }
  245. _ClearConstraints();
  246. DSA_Destroy(_hdsaConstraints);
  247. _ExecData_Release();
  248. DeleteCriticalSection(&_updateParams.csSearch);
  249. DeleteCriticalSection(&_csThread);
  250. // Make sure we have removed all outstanding update dirs...
  251. _ClearDeferUpdateDirList();
  252. if (_hdpaItemsToAdd1)
  253. DPA_Destroy(_hdpaItemsToAdd1);
  254. if (_hdpaItemsToAdd2)
  255. DPA_Destroy(_hdpaItemsToAdd2);
  256. ILFree(_pidlRestore);
  257. }
  258. STDMETHODIMP CFindCmd::QueryInterface(REFIID riid, void **ppv)
  259. {
  260. static const QITAB qit[] = {
  261. QITABENT(CFindCmd, ISearchCommandExt),
  262. QITABENTMULTI(CFindCmd, IDispatch, ISearchCommandExt),
  263. QITABENT(CFindCmd, IProvideClassInfo2),
  264. QITABENTMULTI(CFindCmd, IProvideClassInfo,IProvideClassInfo2),
  265. QITABENT(CFindCmd, IObjectWithSite),
  266. QITABENT(CFindCmd, IObjectSafety),
  267. QITABENT(CFindCmd, IConnectionPointContainer),
  268. QITABENT(CFindCmd, OLEDBSimpleProvider),
  269. QITABENT(CFindCmd, IRowsetWatchNotify),
  270. QITABENT(CFindCmd, IFindControllerNotify),
  271. { 0 },
  272. };
  273. return QISearch(this, qit, riid, ppv);
  274. }
  275. STDMETHODIMP_(ULONG) CFindCmd::AddRef()
  276. {
  277. return InterlockedIncrement(&_cRef);
  278. }
  279. STDMETHODIMP_(ULONG) CFindCmd::Release()
  280. {
  281. if (InterlockedDecrement(&_cRef))
  282. return _cRef;
  283. delete this;
  284. return 0;
  285. }
  286. // IDispatch implementation
  287. STDMETHODIMP CFindCmd::GetTypeInfoCount(UINT * pctinfo)
  288. {
  289. return CImpIDispatch::GetTypeInfoCount(pctinfo);
  290. }
  291. STDMETHODIMP CFindCmd::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  292. {
  293. return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo);
  294. }
  295. STDMETHODIMP CFindCmd::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
  296. {
  297. return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  298. }
  299. STDMETHODIMP CFindCmd::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
  300. {
  301. return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  302. }
  303. // ADOCommand implementation, dual interface method callable via script
  304. STDMETHODIMP CFindCmd::AddConstraint(BSTR bstrName, VARIANT vValue)
  305. {
  306. HRESULT hr = E_OUTOFMEMORY;
  307. CMD_CONSTRAINT dfcc = {0};
  308. dfcc.bstrName = SysAllocString(bstrName);
  309. if (dfcc.bstrName)
  310. {
  311. hr = VariantCopy(&dfcc.vValue, &vValue);
  312. if (SUCCEEDED(hr))
  313. {
  314. if (DSA_ERR == DSA_InsertItem(_hdsaConstraints, DSA_APPEND, &dfcc))
  315. {
  316. SysFreeString(dfcc.bstrName);
  317. VariantClear(&dfcc.vValue);
  318. hr = E_OUTOFMEMORY;
  319. }
  320. }
  321. else
  322. {
  323. SysFreeString(dfcc.bstrName);
  324. }
  325. }
  326. return hr;
  327. }
  328. STDMETHODIMP CFindCmd::GetNextConstraint(VARIANT_BOOL fReset, DFConstraint **ppdfc)
  329. {
  330. *ppdfc = NULL;
  331. IFindFilter *pfilter;
  332. HRESULT hr = _execData.pff->GetFindFilter(&pfilter);
  333. if (SUCCEEDED(hr))
  334. {
  335. BSTR bName;
  336. VARIANT var;
  337. VARIANT_BOOL fFound;
  338. hr = pfilter->GetNextConstraint(fReset, &bName, &var, &fFound);
  339. if (SUCCEEDED(hr))
  340. {
  341. if (!fFound)
  342. {
  343. // need a simple way to signal end list, how about an empty name string?
  344. bName = SysAllocString(L"");
  345. }
  346. CFindConstraint *pdfc = new CFindConstraint(bName, var);
  347. if (pdfc)
  348. {
  349. hr = pdfc->QueryInterface(IID_PPV_ARG(DFConstraint, ppdfc));
  350. pdfc->Release();
  351. }
  352. else
  353. {
  354. // error release stuff we allocated.
  355. hr = E_OUTOFMEMORY;
  356. SysFreeString(bName);
  357. VariantClear(&var);
  358. }
  359. }
  360. pfilter->Release();
  361. }
  362. return hr;
  363. }
  364. HRESULT CFindCmd::_UpdateFilter(IFindFilter *pfilter)
  365. {
  366. HRESULT hr = S_OK;
  367. pfilter->ResetFieldsToDefaults();
  368. int cNumParams = DSA_GetItemCount(_hdsaConstraints);
  369. for (int iItem = 0; iItem < cNumParams; iItem++)
  370. {
  371. CMD_CONSTRAINT *pdfcc = (CMD_CONSTRAINT *)DSA_GetItemPtr(_hdsaConstraints, iItem);
  372. if (pdfcc)
  373. {
  374. hr = pfilter->UpdateField(pdfcc->bstrName, pdfcc->vValue);
  375. }
  376. }
  377. // And clear out the constraint list...
  378. _ClearConstraints();
  379. return hr;
  380. }
  381. void CFindCmd::_ClearConstraints()
  382. {
  383. int cNumParams = DSA_GetItemCount(_hdsaConstraints);
  384. for (int iItem = 0; iItem < cNumParams; iItem++)
  385. {
  386. CMD_CONSTRAINT *pdfcc = (CMD_CONSTRAINT *)DSA_GetItemPtr(_hdsaConstraints, iItem);
  387. if (pdfcc)
  388. {
  389. SysFreeString(pdfcc->bstrName);
  390. VariantClear(&pdfcc->vValue);
  391. }
  392. }
  393. DSA_DeleteAllItems(_hdsaConstraints);
  394. }
  395. void cdecl CFindCmd::_NotifyProgressText(UINT ids,...)
  396. {
  397. va_list ArgList;
  398. va_start(ArgList, ids);
  399. LPTSTR psz = _ConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(ids), &ArgList);
  400. va_end(ArgList);
  401. if (psz)
  402. {
  403. LPTSTR pszDst = &_szProgressText[0];
  404. StrCpyN(pszDst, psz, ARRAYSIZE(_szProgressText)-2);
  405. LocalFree(psz);
  406. }
  407. else
  408. {
  409. _szProgressText[0] = 0;
  410. }
  411. _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_PROGRESSTEXT);
  412. }
  413. STDAPI CDocFindCommand_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  414. {
  415. *ppv = NULL;
  416. HRESULT hr;
  417. CFindCmd *pfc = new CFindCmd();
  418. if (pfc)
  419. {
  420. hr = pfc->Init();
  421. if (SUCCEEDED(hr))
  422. hr = pfc->QueryInterface(riid, ppv);
  423. pfc->Release();
  424. }
  425. else
  426. hr = E_OUTOFMEMORY;
  427. return hr;
  428. }
  429. void CFindCmd::_PTN_SearchProgress(void)
  430. {
  431. HRESULT hr = S_OK;
  432. HDPA hdpa = _updateParams.hdpa;
  433. if (hdpa)
  434. {
  435. // Ok lets swap things out from under other thread so that we can process it and still
  436. // let the other thread run...
  437. EnterCriticalSection(&_updateParams.csSearch);
  438. if (_updateParams.hdpa == _hdpaItemsToAdd2)
  439. _updateParams.hdpa = _hdpaItemsToAdd1;
  440. else
  441. _updateParams.hdpa = _hdpaItemsToAdd2;
  442. // say that we don't have any thing here such that other thread will reset up...
  443. _updateParams.fFilesAdded = FALSE;
  444. BOOL fDirChanged = _updateParams.fDirChanged;
  445. _updateParams.fDirChanged = FALSE;
  446. LeaveCriticalSection(&_updateParams.csSearch);
  447. int cItemsToAdd = DPA_GetPtrCount(hdpa);
  448. if (!_execData.pff)
  449. return;
  450. int iItem;
  451. _execData.pff->GetItemCount(&iItem);
  452. int iItemStart = iItem + 1; // needed for notifies 1 based.
  453. if (cItemsToAdd)
  454. {
  455. if (_fContinue)
  456. {
  457. // Are we in an updatedir? If so then need to do merge, else...
  458. if (_pidlUpdate)
  459. {
  460. // see if items in list, already if so we unmark the item
  461. // for delete else if not there maybe add it...
  462. int cItems = iItem;
  463. for (int i = 0; i < cItemsToAdd; i++)
  464. {
  465. LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, i);
  466. FIND_ITEM *pfi;
  467. for (int j = cItems - 1; j >= 0; j--)
  468. {
  469. _execData.pff->GetItem(j, &pfi);
  470. if (pfi && (_execData.pff->GetFolderIndex(pidl) == _execData.pff->GetFolderIndex(&pfi->idl)))
  471. {
  472. if (_execData.psf->CompareIDs(0, pidl, &pfi->idl) == 0)
  473. break;
  474. }
  475. }
  476. if (j == -1)
  477. {
  478. // Not already in the list so add it...
  479. hr = _execData.pff->AddPidl(iItem, pidl, -1, NULL);
  480. if (SUCCEEDED(hr))
  481. iItem++;
  482. }
  483. else
  484. {
  485. // Item still there - remove possible delete flag...
  486. if (pfi)
  487. pfi->dwState &= ~CDFITEM_STATE_MAYBEDELETE;
  488. }
  489. ILFree(pidl); // The AddPidl does a clone of the pidl...
  490. }
  491. if (iItem && _execData.psfv)
  492. {
  493. hr = _execData.psfv->SetObjectCount(iItem, SFVSOC_NOSCROLL);
  494. }
  495. }
  496. else
  497. {
  498. if (_pListener)
  499. _pListener->aboutToInsertRows(iItemStart, cItemsToAdd);
  500. for (int i = 0; i < cItemsToAdd; i++)
  501. {
  502. LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, i);
  503. hr = _execData.pff->AddPidl(iItem, pidl, -1, NULL);
  504. if (SUCCEEDED(hr))
  505. iItem++;
  506. ILFree(pidl); // AddPidl makes a copy
  507. }
  508. if (iItem >= iItemStart)
  509. {
  510. if (_execData.psfv)
  511. hr = _execData.psfv->SetObjectCount(iItem, SFVSOC_NOSCROLL);
  512. _execData.pff->SetItemsChangedSinceSort();
  513. _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_UPDATE);
  514. }
  515. if (_pListener)
  516. {
  517. _pListener->insertedRows(iItemStart, cItemsToAdd);
  518. _pListener->rowsAvailable(iItemStart, cItemsToAdd);
  519. }
  520. }
  521. }
  522. else // _fContinue
  523. {
  524. for (int i = 0; i < cItemsToAdd; i++)
  525. {
  526. ILFree((LPITEMIDLIST)DPA_FastGetPtr(hdpa, i));
  527. }
  528. }
  529. DPA_DeleteAllPtrs(hdpa);
  530. }
  531. if (fDirChanged)
  532. {
  533. _NotifyProgressText(IDS_SEARCHING, _execData.szProgressText);
  534. }
  535. }
  536. _updateParams.dwTimeLastNotify = GetTickCount();
  537. _updateParams.fUpdatePosted = FALSE;
  538. }
  539. void CFindCmd::_PTN_AsyncProgress(int nPercentComplete, DWORD cAsync)
  540. {
  541. if (!_execData.pff)
  542. return;
  543. // Async case try just setting the count...
  544. _execData.pff->SetAsyncCount(cAsync);
  545. if (_execData.psfv)
  546. {
  547. // -1 for the first item means verify visible items only
  548. _execData.pff->ValidateItems(_execData.psfv, -1, -1, FALSE);
  549. _execData.psfv->SetObjectCount(cAsync, SFVSOC_NOSCROLL);
  550. }
  551. _execData.pff->SetItemsChangedSinceSort();
  552. _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_UPDATE);
  553. _NotifyProgressText(IDS_SEARCHINGASYNC, cAsync, nPercentComplete);
  554. }
  555. void CFindCmd::_PTN_AsyncToSync()
  556. {
  557. if (_execData.pff)
  558. _execData.pff->CacheAllAsyncItems();
  559. }
  560. void CFindCmd::_ClearItemDPA(HDPA hdpa)
  561. {
  562. if (hdpa)
  563. {
  564. EnterCriticalSection(&_updateParams.csSearch);
  565. int cItems = DPA_GetPtrCount(hdpa);
  566. for (int i = 0; i < cItems; i++)
  567. {
  568. ILFree((LPITEMIDLIST)DPA_GetPtr(hdpa, i));
  569. }
  570. DPA_DeleteAllPtrs(hdpa);
  571. LeaveCriticalSection(&_updateParams.csSearch);
  572. }
  573. }
  574. void CFindCmd::_PTN_SearchComplete(HRESULT hr, BOOL fAbort)
  575. {
  576. int iItem;
  577. // someone clicked on new button -- cannot set no files found text in listview
  578. // because we'll overwrite enter search criteria to begin
  579. if (!_fNew)
  580. _SetEmptyText(IDS_FINDVIEWEMPTY);
  581. _SetLastError(hr);
  582. // _execData.pff is NULL when Searh is complete by navigating away from the search page
  583. if (!_execData.pff)
  584. {
  585. // do clean up of hdpaToItemsToadd1 and 2
  586. // make sure all items in buffer 1 and 2 are empty
  587. _ClearItemDPA(_hdpaItemsToAdd1);
  588. _ClearItemDPA(_hdpaItemsToAdd2);
  589. }
  590. else
  591. {
  592. // if we have a _pidlUpdate are completing an update
  593. if (_pidlUpdate)
  594. {
  595. int i, cPidf;
  596. UINT uItem;
  597. _execData.pff->GetItemCount(&i);
  598. for (; i-- > 0;)
  599. {
  600. // Pidl at start of structure...
  601. FIND_ITEM *pfi;
  602. _execData.pff->GetItem(i, &pfi);
  603. if (pfi->dwState & CDFITEM_STATE_MAYBEDELETE)
  604. {
  605. _execData.psfv->RemoveObject(&pfi->idl, &uItem);
  606. }
  607. }
  608. ILFree(_pidlUpdate);
  609. _pidlUpdate = NULL;
  610. // clear the update dir flags
  611. _execData.pff->GetFolderListItemCount(&cPidf);
  612. for (i = 0; i < cPidf; i++)
  613. {
  614. FIND_FOLDER_ITEM *pdffli;
  615. if (SUCCEEDED(_execData.pff->GetFolderListItem(i, &pdffli)))
  616. pdffli->fUpdateDir = FALSE;
  617. }
  618. }
  619. // Release our reference count on the searching.
  620. if (_cExecInProgress)
  621. _cExecInProgress--;
  622. // Tell everyone the final count and that we are done...
  623. // But first check if there are any cached up Updatedirs to be processed...
  624. if (_pdudFirst)
  625. {
  626. // first unlink the first one...
  627. struct DEFER_UPDATE_DIR *pdud = _pdudFirst;
  628. _pdudFirst = pdud->pdudNext;
  629. if (_execData.pff->HandleUpdateDir(pdud->pidl, pdud->fRecurse))
  630. {
  631. // Need to spawn sub-search on this...
  632. _Start(FALSE, -1, pdud->pidl);
  633. }
  634. ILFree(pdud->pidl);
  635. LocalFree((HLOCAL)pdud);
  636. }
  637. else
  638. {
  639. if (_execData.psfv)
  640. {
  641. // validate all the items we pulled in already
  642. _execData.pff->ValidateItems(_execData.psfv, 0, -1, TRUE);
  643. }
  644. _execData.pff->GetItemCount(&iItem);
  645. _NotifyProgressText(_uStatusMsgIndex, iItem);
  646. if (!fAbort)
  647. _SelectResults();
  648. }
  649. }
  650. // weird connection point corruption can happen here. somehow the number of sinks is 0 but
  651. // some of the array entries are non null thus causing fault. this problem does not want to
  652. // repro w/ manual testing or debug binaries, only sometimes after an automation run. when
  653. // it happens it is too late to figure out what happened so just patch it here.
  654. if (_cpEvents._HasSinks())
  655. _cpEvents.InvokeDispid(fAbort ? DISPID_SEARCHCOMMAND_ABORT : DISPID_SEARCHCOMMAND_COMPLETE);
  656. }
  657. // see if we need to restart the search based on an update dir
  658. BOOL ShouldRestartSearch(LPCITEMIDLIST pidl)
  659. {
  660. BOOL fRestart = TRUE; // assume we should, non file system pidls
  661. WCHAR szPath[MAX_PATH];
  662. if (SHGetPathFromIDList(pidl, szPath))
  663. {
  664. // Check if this is either a network drive or a remote drive:
  665. if (PathIsRemote(szPath))
  666. {
  667. // If we can find the CI catalogs for the drive on the other machine, then we do
  668. // not want to search.
  669. WCHAR wszCatalog[MAX_PATH], wszMachine[32];
  670. ULONG cchCatalog = ARRAYSIZE(wszCatalog), cchMachine = ARRAYSIZE(wszMachine);
  671. fRestart = (S_OK != LocateCatalogsW(szPath, 0, wszMachine, &cchMachine, wszCatalog, &cchCatalog));
  672. }
  673. else if (-1 != PathGetDriveNumber(szPath))
  674. {
  675. // It is a local dirve...
  676. // Is this machine running the content indexer (CI)?
  677. BOOL fCiRunning, fCiIndexed, fCiPermission;
  678. GetCIStatus(&fCiRunning, &fCiIndexed, &fCiPermission);
  679. fRestart = !fCiRunning || !fCiIndexed; // restart if not running or not fully indexed
  680. }
  681. }
  682. return fRestart;
  683. }
  684. void CFindCmd::_OnChangeNotify(LONG code, LPITEMIDLIST *ppidl)
  685. {
  686. LPITEMIDLIST pidlT;
  687. UINT idsMsg;
  688. UINT cItems;
  689. if (!_execData.pff)
  690. {
  691. _ExecData_Init();
  692. // If we are running async then for now ignore notifications...
  693. // Unless we have cached all of the items...
  694. if (!_execData.pff)
  695. return; // we do not have anything to listen...
  696. }
  697. // see if we want to process the notificiation or not.
  698. switch (code)
  699. {
  700. case SHCNE_RENAMEFOLDER: // With trashcan this is what we see...
  701. case SHCNE_RENAMEITEM: // With trashcan this is what we see...
  702. case SHCNE_DELETE:
  703. case SHCNE_RMDIR:
  704. case SHCNE_UPDATEITEM:
  705. break;
  706. case SHCNE_CREATE:
  707. case SHCNE_MKDIR:
  708. // Process this one out of place
  709. _execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, *ppidl, NULL);
  710. break;
  711. case SHCNE_UPDATEDIR:
  712. TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_UPDATEDIR, pidl=0x%X",*ppidl);
  713. if (ShouldRestartSearch(*ppidl))
  714. {
  715. BOOL bRecurse = (ppidl[1] != NULL);
  716. if (_cExecInProgress)
  717. {
  718. _DeferHandleUpdateDir(*ppidl, bRecurse);
  719. }
  720. else
  721. {
  722. if (_execData.pff->HandleUpdateDir(*ppidl, bRecurse))
  723. {
  724. // Need to spawn sub-search on this...
  725. _Start(FALSE, -1, *ppidl);
  726. }
  727. }
  728. }
  729. return;
  730. default:
  731. return; // we are not interested in this event
  732. }
  733. //
  734. // Now we need to see if the item might be in our list
  735. // First we need to extract off the last part of the id list
  736. // and see if the contained id entry is in our list. If so we
  737. // need to see if can get the defview find the item and update it.
  738. //
  739. _execData.pff->MapToSearchIDList(*ppidl, FALSE, &pidlT);
  740. switch (code)
  741. {
  742. case SHCNE_RMDIR:
  743. TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_RMDIR, pidl=0x%X",*ppidl);
  744. _execData.pff->HandleRMDir(_execData.psfv, *ppidl);
  745. if (pidlT)
  746. {
  747. _execData.psfv->RemoveObject(pidlT, &idsMsg);
  748. }
  749. break;
  750. case SHCNE_DELETE:
  751. TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_DELETE, pidl=0x%X",*ppidl);
  752. if (pidlT)
  753. {
  754. _execData.psfv->RemoveObject(pidlT, &idsMsg);
  755. }
  756. break;
  757. case SHCNE_RENAMEFOLDER:
  758. case SHCNE_RENAMEITEM:
  759. if (pidlT)
  760. {
  761. // If the two items dont have the same parent, we will go ahead
  762. // and remove it...
  763. LPITEMIDLIST pidl1;
  764. if (SUCCEEDED(_execData.pff->GetParentsPIDL(pidlT, &pidl1)))
  765. {
  766. LPITEMIDLIST pidl2 = ILClone(ppidl[1]);
  767. if (pidl2)
  768. {
  769. ILRemoveLastID(pidl2);
  770. if (!ILIsEqual(pidl1, pidl2))
  771. {
  772. _execData.psfv->RemoveObject(pidlT, &idsMsg);
  773. // And maybe add it back to the end... of the list
  774. _execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, ppidl[1], NULL);
  775. }
  776. else
  777. {
  778. // The object is in same folder so must be rename...
  779. // And maybe add it back to the end... of the list
  780. _execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, ppidl[1], pidlT);
  781. }
  782. ILFree(pidl2);
  783. }
  784. ILFree(pidl1);
  785. }
  786. }
  787. else
  788. {
  789. _execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, ppidl[1], NULL);
  790. }
  791. break;
  792. case SHCNE_UPDATEITEM:
  793. TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_UPDATEITEM, pidl=0x%X",*ppidl);
  794. if (pidlT)
  795. _execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, *ppidl, pidlT);
  796. break;
  797. }
  798. // Update the count...
  799. _execData.psfv->GetObjectCount(&cItems);
  800. _NotifyProgressText(_uStatusMsgIndex, cItems);
  801. ILFree(pidlT);
  802. }
  803. // Ok we need to add a defer
  804. void CFindCmd::_DeferHandleUpdateDir(LPCITEMIDLIST pidl, BOOL bRecurse)
  805. {
  806. // See if we already have some items in the list which are lower down in the tree if so we
  807. // can replace it. Or is there one that is higher up, in which case we can ignore it...
  808. struct DEFER_UPDATE_DIR *pdudPrev = NULL;
  809. struct DEFER_UPDATE_DIR *pdud = _pdudFirst;
  810. while (pdud)
  811. {
  812. if (ILIsParent(pdud->pidl, pidl, FALSE))
  813. return; // Already one in the list that will handle this one...
  814. if (ILIsParent(pidl, pdud->pidl, FALSE))
  815. break;
  816. pdudPrev = pdud;
  817. pdud = pdud->pdudNext;
  818. }
  819. // See if we found one that we can replace...
  820. if (pdud)
  821. {
  822. LPITEMIDLIST pidlT = ILClone(pidl);
  823. if (pidlT)
  824. {
  825. ILFree(pdud->pidl);
  826. pdud->pidl = pidlT;
  827. // See if there are others...
  828. pdudPrev = pdud;
  829. pdud = pdud->pdudNext;
  830. while (pdud)
  831. {
  832. if (ILIsParent(pidl, pdud->pidl, FALSE))
  833. {
  834. // Yep lets trash this one.
  835. ILFree(pdud->pidl);
  836. pdudPrev->pdudNext = pdud->pdudNext;
  837. pdud = pdudPrev; // Let it fall through to setup to look at next...
  838. }
  839. pdudPrev = pdud;
  840. pdud = pdud->pdudNext;
  841. }
  842. }
  843. }
  844. else
  845. {
  846. // Nope simply add us in to the start of the list.
  847. pdud = (struct DEFER_UPDATE_DIR*)LocalAlloc(LPTR, sizeof(struct DEFER_UPDATE_DIR));
  848. if (!pdud)
  849. return; // Ooop could not alloc...
  850. pdud->pidl = ILClone(pidl);
  851. if (!pdud->pidl)
  852. {
  853. LocalFree((HLOCAL)pdud);
  854. return;
  855. }
  856. pdud->fRecurse = bRecurse;
  857. pdud->pdudNext = _pdudFirst;
  858. _pdudFirst = pdud;
  859. }
  860. }
  861. void CFindCmd::_ClearDeferUpdateDirList()
  862. {
  863. // Cancel any Pending updatedirs also.
  864. while (_pdudFirst)
  865. {
  866. struct DEFER_UPDATE_DIR *pdud = _pdudFirst;
  867. _pdudFirst = pdud->pdudNext;
  868. ILFree(pdud->pidl);
  869. LocalFree((HLOCAL)pdud);
  870. }
  871. }
  872. LRESULT CALLBACK CFindCmd::_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  873. {
  874. CFindCmd* pThis = (CFindCmd*)GetWindowLongPtr(hwnd, 0);
  875. LRESULT lRes = 0;
  876. switch (uMsg)
  877. {
  878. case WM_DESTROY:
  879. SetWindowLong(hwnd, 0, 0); // make sure we don't deref pThis
  880. break;
  881. case WM_DF_FSNOTIFY:
  882. {
  883. LPITEMIDLIST *ppidl;
  884. LONG lEvent;
  885. LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  886. if (pshcnl)
  887. {
  888. if (pThis)
  889. pThis->_OnChangeNotify(lEvent, ppidl);
  890. SHChangeNotification_Unlock(pshcnl);
  891. }
  892. }
  893. break;
  894. case WM_DF_SEARCHPROGRESS:
  895. pThis->_PTN_SearchProgress();
  896. pThis->Release();
  897. break;
  898. case WM_DF_ASYNCPROGRESS:
  899. pThis->_PTN_AsyncProgress((int)wParam, (DWORD)lParam);
  900. pThis->Release();
  901. break;
  902. case WM_DF_SEARCHSTART:
  903. pThis->_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_START);
  904. pThis->_SetEmptyText(IDS_FINDVIEWEMPTYBUSY);
  905. pThis->Release();
  906. break;
  907. case WM_DF_SEARCHCOMPLETE:
  908. pThis->_PTN_SearchComplete((HRESULT)wParam, (BOOL)lParam);
  909. pThis->Release();
  910. break;
  911. default:
  912. lRes = ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  913. break;
  914. }
  915. return lRes;
  916. }
  917. // test to see if the view is in a mode where many items are displayed
  918. BOOL LotsOfItemsInView(IUnknown *punkSite)
  919. {
  920. BOOL bLotsOfItemsInView = FALSE;
  921. IFolderView * pfv;
  922. HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  923. if (SUCCEEDED(hr))
  924. {
  925. UINT uViewMode;
  926. bLotsOfItemsInView = SUCCEEDED(pfv->GetCurrentViewMode(&uViewMode)) &&
  927. ((FVM_ICON == uViewMode) || (FVM_SMALLICON == uViewMode));
  928. pfv->Release();
  929. }
  930. return bLotsOfItemsInView;
  931. }
  932. void CFindCmd::_DoSearch(IFindEnum *penum)
  933. {
  934. BOOL fAbort = FALSE;
  935. BOOL bLotsOfItems = LotsOfItemsInView(_execData.psfv);
  936. EnterCriticalSection(&_csThread);
  937. // previous thread might have exited but we're still processing search complete message
  938. if (_cExecInProgress > 1)
  939. Sleep(1000); // give it a chance to finish
  940. _updateParams.hdpa = NULL;
  941. _updateParams.fFilesAdded = FALSE;
  942. _updateParams.fDirChanged = FALSE;
  943. _updateParams.fUpdatePosted = FALSE;
  944. _PostMessage(WM_DF_SEARCHSTART, 0, 0);
  945. // Now see if this is an Sync or an Async version of the search...
  946. HRESULT hr = S_OK;
  947. BOOL fQueryIsAsync = penum->FQueryIsAsync();
  948. if (fQueryIsAsync)
  949. {
  950. DBCOUNTITEM dwTotalAsync;
  951. BOOL fDone;
  952. int nPercentComplete;
  953. while (S_OK == (hr = penum->GetAsyncCount(&dwTotalAsync, &nPercentComplete, &fDone)))
  954. {
  955. if (!_fContinue)
  956. {
  957. fAbort = TRUE;
  958. break;
  959. }
  960. _PostMessage(WM_DF_ASYNCPROGRESS, (WPARAM)nPercentComplete, (LPARAM)dwTotalAsync);
  961. // If we are done we can simply let the ending callback tell of the new count...
  962. if (fDone)
  963. break;
  964. // sleep .3 or 1.5 sec
  965. Sleep(bLotsOfItems ? 1500 : 300); // wait between looking again...
  966. }
  967. }
  968. if (!fQueryIsAsync || (fQueryIsAsync == DF_QUERYISMIXED))
  969. {
  970. int state, cItemsSearched = 0, cFoldersSearched = 0, cFoldersSearchedPrev = 0;
  971. _updateParams.hdpa = _hdpaItemsToAdd1; // Assume first one now...
  972. _updateParams.dwTimeLastNotify = GetTickCount();
  973. LPITEMIDLIST pidl;
  974. while (S_OK == (hr = penum->Next(&pidl, &cItemsSearched, &cFoldersSearched, &_fContinue, &state)))
  975. {
  976. if (state == GNF_DONE)
  977. break; // no more
  978. if (!_fContinue)
  979. {
  980. fAbort = TRUE;
  981. break;
  982. }
  983. // See if we should abort
  984. if (state == GNF_MATCH)
  985. {
  986. EnterCriticalSection(&_updateParams.csSearch);
  987. DPA_AppendPtr(_updateParams.hdpa, pidl);
  988. _updateParams.fFilesAdded = TRUE;
  989. LeaveCriticalSection(&_updateParams.csSearch);
  990. }
  991. if (cFoldersSearchedPrev != cFoldersSearched)
  992. {
  993. _updateParams.fDirChanged = TRUE;
  994. cFoldersSearchedPrev = cFoldersSearched;
  995. }
  996. if (!_updateParams.fUpdatePosted &&
  997. (_updateParams.fDirChanged || _updateParams.fFilesAdded))
  998. {
  999. if ((GetTickCount() - _updateParams.dwTimeLastNotify) > 200)
  1000. {
  1001. _updateParams.fUpdatePosted = TRUE;
  1002. _PostMessage(WM_DF_SEARCHPROGRESS, 0, 0);
  1003. }
  1004. }
  1005. }
  1006. _PostMessage(WM_DF_SEARCHPROGRESS, 0, 0);
  1007. }
  1008. if (hr != S_OK)
  1009. {
  1010. fAbort = TRUE;
  1011. }
  1012. _PostMessage(WM_DF_SEARCHCOMPLETE, (WPARAM)hr, (LPARAM)fAbort);
  1013. LeaveCriticalSection(&_csThread);
  1014. }
  1015. DWORD CALLBACK CFindCmd::_ThreadProc(void *pv)
  1016. {
  1017. THREAD_PARAMS *pParams = (THREAD_PARAMS *)pv;
  1018. pParams->pThis->_DoSearch(pParams->penum);
  1019. _FreeThreadParams(pParams);
  1020. return 0;
  1021. }
  1022. HRESULT CFindCmd::_Cancel()
  1023. {
  1024. _ClearDeferUpdateDirList();
  1025. if (DSA_GetItemCount(_hdsaConstraints) == 0)
  1026. {
  1027. _fContinue = FALSE; // Cancel current query if we have a null paramter collection
  1028. return S_OK;
  1029. }
  1030. return E_FAIL;
  1031. }
  1032. HRESULT CFindCmd::_Init(THREAD_PARAMS **ppParams, int iCol, LPCITEMIDLIST pidlUpdate)
  1033. {
  1034. *ppParams = new THREAD_PARAMS;
  1035. if (NULL == *ppParams)
  1036. return E_OUTOFMEMORY;
  1037. // Clear any previous registrations...
  1038. SHChangeNotifyDeregisterWindow(_updateParams.hwndThreadNotify);
  1039. // Prepare to execute the query
  1040. IFindFilter *pfilter;
  1041. HRESULT hr = _execData.pff->GetFindFilter(&pfilter);
  1042. if (SUCCEEDED(hr))
  1043. {
  1044. // We do not need to update the filter if this is done as part of an FSNOTIFY or a Sort...
  1045. if ((iCol >= 0) || pidlUpdate || SUCCEEDED(hr = _UpdateFilter(pfilter)))
  1046. {
  1047. _execData.szProgressText[0] = 0;
  1048. pfilter->DeclareFSNotifyInterest(_updateParams.hwndThreadNotify, WM_DF_FSNOTIFY);
  1049. pfilter->GetStatusMessageIndex(0, &_uStatusMsgIndex);
  1050. DWORD dwFlags;
  1051. hr = pfilter->PrepareToEnumObjects(_GetWindow(), &dwFlags);
  1052. if (SUCCEEDED(hr))
  1053. {
  1054. hr = pfilter->EnumObjects(_execData.psf, pidlUpdate, dwFlags, iCol,
  1055. _execData.szProgressText, SAFECAST(this, IRowsetWatchNotify*), &(*ppParams)->penum);
  1056. }
  1057. }
  1058. pfilter->Release();
  1059. }
  1060. // Fill in the exec params
  1061. (*ppParams)->pThis = this;
  1062. AddRef(); // ExecParams_Free will release this interface addref...
  1063. if (FAILED(hr))
  1064. {
  1065. _FreeThreadParams(*ppParams);
  1066. *ppParams = NULL;
  1067. }
  1068. return hr;
  1069. }
  1070. HRESULT CFindCmd::_FreeThreadParams(THREAD_PARAMS *pParams)
  1071. {
  1072. if (!pParams)
  1073. return S_OK;
  1074. // Don't use atomic release as this a pointer to a class not an interface.
  1075. CFindCmd *pThis = pParams->pThis;
  1076. pParams->pThis = NULL;
  1077. pThis->Release();
  1078. ATOMICRELEASE(pParams->penum);
  1079. delete pParams;
  1080. return S_OK;
  1081. }
  1082. HRESULT CFindCmd::_ExecData_Release()
  1083. {
  1084. ATOMICRELEASE(_execData.psf);
  1085. ATOMICRELEASE(_execData.psfv);
  1086. if (_execData.pff)
  1087. _execData.pff->SetControllerNotifyObject(NULL); // release back pointer to us...
  1088. ATOMICRELEASE(_execData.pff);
  1089. _cExecInProgress = 0; // we must be in process of shutting down at least...
  1090. return S_OK;
  1091. }
  1092. HRESULT CFindCmd::_EnsureResultsViewIsCurrent(IUnknown *punk)
  1093. {
  1094. HRESULT hr = E_FAIL;
  1095. LPITEMIDLIST pidlFolder;
  1096. if (S_OK == SHGetIDListFromUnk(punk, &pidlFolder))
  1097. {
  1098. LPITEMIDLIST pidl;
  1099. if (SUCCEEDED(_GetSearchIDList(&pidl)))
  1100. {
  1101. if (ILIsEqual(pidlFolder, pidl))
  1102. hr = S_OK;
  1103. ILFree(pidl);
  1104. }
  1105. ILFree(pidlFolder);
  1106. }
  1107. return hr;
  1108. }
  1109. // the search results view callback proffeerd itself and we can use that
  1110. // to get a hold of defview and can program it
  1111. HRESULT CFindCmd::_GetShellView(REFIID riid, void **ppv)
  1112. {
  1113. return IUnknown_QueryService(_punkSite, SID_DocFindFolder, riid, ppv);
  1114. }
  1115. HRESULT CFindCmd::_ExecData_Init()
  1116. {
  1117. _ExecData_Release();
  1118. IFolderView *pfv;
  1119. HRESULT hr = _GetShellView(IID_PPV_ARG(IFolderView, &pfv));
  1120. if (SUCCEEDED(hr))
  1121. {
  1122. IShellFolder *psf;
  1123. hr = pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf));
  1124. if (SUCCEEDED(hr))
  1125. {
  1126. IFindFolder *pff;
  1127. hr = psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff));
  1128. if (SUCCEEDED(hr))
  1129. {
  1130. hr = _EnsureResultsViewIsCurrent(psf);
  1131. if (SUCCEEDED(hr))
  1132. {
  1133. IShellFolderView *psfv;
  1134. hr = pfv->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv));
  1135. if (SUCCEEDED(hr))
  1136. {
  1137. IUnknown_Set((IUnknown **)&_execData.pff, pff);
  1138. IUnknown_Set((IUnknown **)&_execData.psf, psf);
  1139. IUnknown_Set((IUnknown **)&_execData.psfv, psfv);
  1140. _execData.pff->SetControllerNotifyObject(SAFECAST(this, IFindControllerNotify*));
  1141. psfv->Release();
  1142. }
  1143. }
  1144. pff->Release();
  1145. }
  1146. psf->Release();
  1147. }
  1148. pfv->Release();
  1149. }
  1150. if (FAILED(hr))
  1151. _ExecData_Release();
  1152. else
  1153. SetShellFolder(_execData.psf);
  1154. return hr;
  1155. }
  1156. BOOL CFindCmd::_SetupBrowserCP()
  1157. {
  1158. if (!_dwCookie)
  1159. {
  1160. _cwbe.SetOwner(this); // make sure our owner is set...
  1161. // register ourself with the Defview to get any events that they may generate...
  1162. IServiceProvider *pspTLB;
  1163. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IServiceProvider, &pspTLB));
  1164. if (SUCCEEDED(hr))
  1165. {
  1166. IConnectionPointContainer *pcpc;
  1167. hr = pspTLB->QueryService(IID_IExpDispSupport, IID_PPV_ARG(IConnectionPointContainer, &pcpc));
  1168. if (SUCCEEDED(hr))
  1169. {
  1170. hr = ConnectToConnectionPoint(SAFECAST(&_cwbe, DWebBrowserEvents*), DIID_DWebBrowserEvents2,
  1171. TRUE, pcpc, &_dwCookie, &_pcpBrowser);
  1172. pcpc->Release();
  1173. }
  1174. pspTLB->Release();
  1175. }
  1176. }
  1177. if (_dwCookie)
  1178. _cwbe.SetWaiting(TRUE);
  1179. return _dwCookie ? TRUE : FALSE;
  1180. }
  1181. HRESULT CFindCmd::_Start(BOOL fNavigateIfFail, int iCol, LPCITEMIDLIST pidlUpdate)
  1182. {
  1183. if (_cExecInProgress)
  1184. return E_UNEXPECTED;
  1185. if (!_hdpaItemsToAdd1)
  1186. {
  1187. _hdpaItemsToAdd1 = DPA_CreateEx(64, GetProcessHeap());
  1188. if (!_hdpaItemsToAdd1)
  1189. return E_OUTOFMEMORY;
  1190. }
  1191. if (!_hdpaItemsToAdd2)
  1192. {
  1193. _hdpaItemsToAdd2 = DPA_CreateEx(64, GetProcessHeap());
  1194. if (!_hdpaItemsToAdd2)
  1195. return E_OUTOFMEMORY;
  1196. }
  1197. if (!_updateParams.hwndThreadNotify)
  1198. {
  1199. _updateParams.hwndThreadNotify = SHCreateWorkerWindow(_WndProc, NULL, 0, 0, 0, this);
  1200. if (!_updateParams.hwndThreadNotify)
  1201. return E_OUTOFMEMORY;
  1202. }
  1203. HRESULT hr = _ExecData_Init();
  1204. if (FAILED(hr))
  1205. {
  1206. if (fNavigateIfFail)
  1207. {
  1208. if (_SetupBrowserCP())
  1209. NavigateToSearchResults();
  1210. }
  1211. // Return S_False so that when we check if this succeeded in finddlg, we wee that it
  1212. // did, and therefore let the animation run. If we return a failure code here, we
  1213. // will stop the animation. This will only hapen when we are navigating to the search
  1214. // results as well as starting the search.
  1215. return S_FALSE;
  1216. }
  1217. THREAD_PARAMS *ptp;
  1218. hr = _Init(&ptp, iCol, pidlUpdate);
  1219. if (SUCCEEDED(hr))
  1220. {
  1221. // See if we should be saving away the selection...
  1222. if (iCol >= 0)
  1223. _execData.pff->RememberSelectedItems();
  1224. // If this is an update then we need to remember our IDList else clear list...
  1225. if (pidlUpdate)
  1226. {
  1227. _pidlUpdate = ILClone(pidlUpdate);
  1228. }
  1229. else
  1230. {
  1231. _Clear(); // tell defview to delete everything
  1232. }
  1233. _execData.pff->SetAsyncEnum(ptp->penum);
  1234. // Start the query
  1235. _cExecInProgress++;
  1236. _fContinue = TRUE;
  1237. _fNew = FALSE;
  1238. if (SHCreateThread(_ThreadProc, ptp, CTF_COINIT, NULL))
  1239. {
  1240. hr = S_OK;
  1241. }
  1242. else
  1243. {
  1244. _cExecInProgress--;
  1245. _FreeThreadParams(ptp);
  1246. _SetEmptyText(IDS_FINDVIEWEMPTY);
  1247. }
  1248. }
  1249. else
  1250. hr = _SetLastError(hr);
  1251. return hr;
  1252. }
  1253. HRESULT CFindCmd::_SetLastError(HRESULT hr)
  1254. {
  1255. if (HRESULT_FACILITY(hr) == FACILITY_SEARCHCOMMAND)
  1256. {
  1257. _hrLastError = hr;
  1258. hr = S_FALSE; // Don't error out script...
  1259. _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_ERROR);
  1260. }
  1261. return hr;
  1262. }
  1263. STDMETHODIMP CFindCmd::Execute(VARIANT *RecordsAffected, VARIANT *Parameters, long Options)
  1264. {
  1265. if (Options == 0)
  1266. return _Cancel();
  1267. _FixBrokenTypes();
  1268. return _Start(TRUE, -1, NULL);
  1269. }
  1270. // IConnectionPointContainer
  1271. STDMETHODIMP CFindCmd::EnumConnectionPoints(IEnumConnectionPoints **ppEnum)
  1272. {
  1273. return CreateInstance_IEnumConnectionPoints(ppEnum, 1, _cpEvents.CastToIConnectionPoint());
  1274. }
  1275. STDMETHODIMP CFindCmd::FindConnectionPoint(REFIID iid, IConnectionPoint **ppCP)
  1276. {
  1277. if (IsEqualIID(iid, DIID_DSearchCommandEvents) ||
  1278. IsEqualIID(iid, IID_IDispatch))
  1279. {
  1280. *ppCP = _cpEvents.CastToIConnectionPoint();
  1281. }
  1282. else
  1283. {
  1284. *ppCP = NULL;
  1285. return E_NOINTERFACE;
  1286. }
  1287. (*ppCP)->AddRef();
  1288. return S_OK;
  1289. }
  1290. // IProvideClassInfo2 methods
  1291. STDMETHODIMP CFindCmd::GetClassInfo(ITypeInfo **ppTI)
  1292. {
  1293. return GetTypeInfoFromLibId(0, LIBID_Shell32, 1, 0, CLSID_DocFindCommand, ppTI);
  1294. }
  1295. STDMETHODIMP CFindCmd::GetGUID(DWORD dwGuidKind, GUID *pGUID)
  1296. {
  1297. if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID)
  1298. {
  1299. *pGUID = DIID_DSearchCommandEvents;
  1300. return S_OK;
  1301. }
  1302. *pGUID = GUID_NULL;
  1303. return E_FAIL;
  1304. }
  1305. STDMETHODIMP CFindCmd::SetSite(IUnknown *punkSite)
  1306. {
  1307. if (!punkSite)
  1308. {
  1309. if (!_cExecInProgress)
  1310. {
  1311. _ExecData_Release();
  1312. }
  1313. _fContinue = FALSE; // Cancel existing queries
  1314. // See if we have a connection point... If so unadvise now...
  1315. if (_dwCookie)
  1316. {
  1317. _pcpBrowser->Unadvise(_dwCookie);
  1318. ATOMICRELEASE(_pcpBrowser);
  1319. _dwCookie = 0;
  1320. }
  1321. // Bug #199671
  1322. // Trident won't call UnAdvise and they except ActiveX Controls
  1323. // to use IOleControl::Close() to do their own UnAdvise, and hope
  1324. // nobody will need events after that. I don't impl IOleControl so
  1325. // we need to do the same thing during IObjectWithSite::SetSite(NULL)
  1326. // and hope someone won't want to reparent us. This is awkward but
  1327. // saves Trident some perf so we will tolerate it.
  1328. EVAL(SUCCEEDED(_cpEvents.UnadviseAll()));
  1329. }
  1330. return CObjectWithSite::SetSite(punkSite);
  1331. }
  1332. void CFindCmd::_SelectResults()
  1333. {
  1334. if (_execData.psfv)
  1335. {
  1336. // If there are any items...
  1337. UINT cItems = 0;
  1338. if (SUCCEEDED(_execData.psfv->GetObjectCount(&cItems)) && cItems > 0)
  1339. {
  1340. IShellView* psv;
  1341. if (SUCCEEDED(_execData.psfv->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
  1342. {
  1343. // If none are selected (don't want to rip the user's selection out of his hand)...
  1344. UINT cSelected = 0;
  1345. if (SUCCEEDED(_execData.psfv->GetSelectedCount(&cSelected)) && cSelected == 0)
  1346. {
  1347. // Retrieve the pidl for the first item in the list...
  1348. LPITEMIDLIST pidlFirst = NULL;
  1349. if (SUCCEEDED(_execData.psfv->GetObject(&pidlFirst, 0)))
  1350. {
  1351. // Give it the focus
  1352. psv->SelectItem(pidlFirst, SVSI_FOCUSED | SVSI_ENSUREVISIBLE);
  1353. }
  1354. }
  1355. // Activate the view.
  1356. psv->UIActivate(SVUIA_ACTIVATE_FOCUS);
  1357. psv->Release();
  1358. }
  1359. }
  1360. }
  1361. }
  1362. STDMETHODIMP CFindCmd::ClearResults(void)
  1363. {
  1364. HRESULT hr = _Clear();
  1365. if (SUCCEEDED(hr))
  1366. {
  1367. _fNew = TRUE;
  1368. _SetEmptyText(IDS_FINDVIEWEMPTYINIT);
  1369. }
  1370. return hr ;
  1371. }
  1372. HRESULT CFindCmd::_Clear()
  1373. {
  1374. // Tell defview to delete everything.
  1375. if (_execData.psfv)
  1376. {
  1377. UINT u;
  1378. _execData.psfv->RemoveObject(NULL, &u);
  1379. }
  1380. // And cleanup our folderList
  1381. if (_execData.pff)
  1382. {
  1383. _execData.pff->ClearItemList();
  1384. _execData.pff->ClearFolderList();
  1385. }
  1386. return S_OK;
  1387. }
  1388. HRESULT CFindCmd::_SetEmptyText(UINT nIDEmptyText)
  1389. {
  1390. IShellFolderViewCB *psfvcb;
  1391. HRESULT hr = IUnknown_QueryService(_execData.psfv, SID_ShellFolderViewCB, IID_PPV_ARG(IShellFolderViewCB, &psfvcb));
  1392. if (SUCCEEDED(hr))
  1393. {
  1394. TCHAR szEmptyText[128];
  1395. LoadString(HINST_THISDLL, nIDEmptyText, szEmptyText, ARRAYSIZE(szEmptyText));
  1396. hr = psfvcb->MessageSFVCB(SFVM_SETEMPTYTEXT, 0, (LPARAM)szEmptyText);
  1397. psfvcb->Release();
  1398. }
  1399. return hr;
  1400. }
  1401. HRESULT CFindCmd::_GetSearchIDList(LPITEMIDLIST *ppidl)
  1402. {
  1403. CLSID clsid;
  1404. _SearchResultsCLSID(&clsid);
  1405. return ILCreateFromCLSID(clsid, ppidl);
  1406. }
  1407. STDMETHODIMP CFindCmd::NavigateToSearchResults(void)
  1408. {
  1409. IShellBrowser *psb;
  1410. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  1411. if (SUCCEEDED(hr))
  1412. {
  1413. LPITEMIDLIST pidl;
  1414. hr = _GetSearchIDList(&pidl);
  1415. if (SUCCEEDED(hr))
  1416. {
  1417. hr = psb->BrowseObject(pidl, SBSP_SAMEBROWSER | SBSP_ABSOLUTE | SBSP_WRITENOHISTORY);
  1418. ILFree(pidl);
  1419. }
  1420. psb->Release();
  1421. }
  1422. return hr;
  1423. }
  1424. IUnknown* CFindCmd::_GetObjectToPersist()
  1425. {
  1426. IOleObject *pole = NULL;
  1427. IShellView *psv;
  1428. HRESULT hr = _GetShellView(IID_PPV_ARG(IShellView, &psv));
  1429. if (SUCCEEDED(hr))
  1430. {
  1431. psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IOleObject, &pole));
  1432. psv->Release();
  1433. }
  1434. return (IUnknown *)pole;
  1435. }
  1436. void CFindCmd::_PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1437. {
  1438. AddRef(); // to be released after processing of the message bellow
  1439. if (!PostMessage(_updateParams.hwndThreadNotify, uMsg, wParam, lParam))
  1440. {
  1441. Release();
  1442. }
  1443. }
  1444. HWND CFindCmd::_GetWindow()
  1445. {
  1446. HWND hwnd;
  1447. return SUCCEEDED(IUnknown_GetWindow(_punkSite, &hwnd)) ? hwnd : NULL;
  1448. }
  1449. STDMETHODIMP CFindCmd::SaveSearch(void)
  1450. {
  1451. IFindFilter *pfilter;
  1452. HRESULT hr = _execData.pff->GetFindFilter(&pfilter);
  1453. if (SUCCEEDED(hr))
  1454. {
  1455. IShellView *psv;
  1456. hr = _GetShellView(IID_PPV_ARG(IShellView, &psv));
  1457. if (SUCCEEDED(hr))
  1458. {
  1459. IUnknown* punk = _GetObjectToPersist(); // NULL is OK
  1460. _execData.pff->Save(pfilter, _GetWindow(), &_dfbsi, psv, punk);
  1461. ATOMICRELEASE(punk);
  1462. psv->Release();
  1463. }
  1464. pfilter->Release();
  1465. }
  1466. return hr;
  1467. }
  1468. STDMETHODIMP CFindCmd::RestoreSearch(void)
  1469. {
  1470. // let script know that a restore happened...
  1471. _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_RESTORE);
  1472. return S_OK;
  1473. }
  1474. STDMETHODIMP CFindCmd::StopSearch(void)
  1475. {
  1476. if (_cExecInProgress)
  1477. return _Cancel();
  1478. return S_OK;
  1479. }
  1480. STDMETHODIMP CFindCmd::GetItemCount(UINT *pcItems)
  1481. {
  1482. if (_execData.psfv)
  1483. {
  1484. return _execData.psfv->GetObjectCount(pcItems);
  1485. }
  1486. return E_FAIL;
  1487. }
  1488. STDMETHODIMP CFindCmd::SetItemCount(UINT cItems)
  1489. {
  1490. if (_execData.psfv)
  1491. {
  1492. return _execData.psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
  1493. }
  1494. return E_FAIL;
  1495. }
  1496. STDMETHODIMP CFindCmd::ViewDestroyed()
  1497. {
  1498. _ExecData_Release();
  1499. return S_OK;
  1500. }
  1501. STDMETHODIMP CFindCmd::get_ProgressText(BSTR *pbs)
  1502. {
  1503. *pbs = SysAllocStringT(_szProgressText);
  1504. return *pbs ? S_OK : E_OUTOFMEMORY;
  1505. }
  1506. //------ error string mappings ------//
  1507. static const UINT error_strings[] =
  1508. {
  1509. SCEE_CONSTRAINT, IDS_DOCFIND_CONSTRAINT,
  1510. SCEE_PATHNOTFOUND, IDS_DOCFIND_PATHNOTFOUND,
  1511. SCEE_INDEXSEARCH, IDS_DOCFIND_SCOPEERROR,
  1512. SCEE_CASESENINDEX, IDS_DOCFIND_CI_NOT_CASE_SEN,
  1513. };
  1514. STDMETHODIMP CFindCmd::GetErrorInfo(BSTR *pbs, int *phr)
  1515. {
  1516. int nCode = HRESULT_CODE(_hrLastError);
  1517. UINT uSeverity = HRESULT_SEVERITY(_hrLastError);
  1518. if (phr)
  1519. *phr = nCode;
  1520. if (pbs)
  1521. {
  1522. UINT nIDString = 0;
  1523. *pbs = NULL;
  1524. for(int i = 0; i < ARRAYSIZE(error_strings); i += 2)
  1525. {
  1526. if (error_strings[i] == (UINT)nCode)
  1527. {
  1528. nIDString = error_strings[i+1];
  1529. break ;
  1530. }
  1531. }
  1532. if (nIDString)
  1533. {
  1534. WCHAR wszMsg[MAX_PATH];
  1535. EVAL(LoadStringW(HINST_THISDLL, nIDString, wszMsg, ARRAYSIZE(wszMsg)));
  1536. *pbs = SysAllocString(wszMsg);
  1537. }
  1538. else
  1539. *pbs = SysAllocString(L"");
  1540. }
  1541. return S_OK;
  1542. }
  1543. STDMETHODIMP CFindCmd::SearchFor(int iFor)
  1544. {
  1545. if (SCE_SEARCHFORFILES == iFor)
  1546. {
  1547. _clsidResults = CLSID_DocFindFolder;
  1548. }
  1549. else if (SCE_SEARCHFORCOMPUTERS == iFor)
  1550. {
  1551. _clsidResults = CLSID_ComputerFindFolder;
  1552. }
  1553. return S_OK;
  1554. }
  1555. STDMETHODIMP CFindCmd::GetScopeInfo(BSTR bsScope, int *pdwScopeInfo)
  1556. {
  1557. *pdwScopeInfo = 0;
  1558. return E_NOTIMPL;
  1559. }
  1560. STDMETHODIMP CFindCmd::RestoreSavedSearch(VARIANT *pvarFile)
  1561. {
  1562. if (pvarFile && pvarFile->vt != VT_EMPTY)
  1563. {
  1564. LPITEMIDLIST pidl = VariantToIDList(pvarFile);
  1565. if (pidl)
  1566. {
  1567. ILFree(_pidlRestore);
  1568. _pidlRestore = pidl ;
  1569. }
  1570. }
  1571. if (_pidlRestore)
  1572. {
  1573. IShellView *psv;
  1574. HRESULT hr = _GetShellView(IID_PPV_ARG(IShellView, &psv));
  1575. if (SUCCEEDED(hr))
  1576. {
  1577. psv->Release();
  1578. if (SUCCEEDED(_ExecData_Init()))
  1579. {
  1580. _execData.pff->RestoreSearchFromSaveFile(_pidlRestore, _execData.psfv);
  1581. _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_RESTORE);
  1582. ILFree(_pidlRestore);
  1583. _pidlRestore = NULL;
  1584. }
  1585. }
  1586. else if (!_fDeferRestoreTried)
  1587. {
  1588. // appears to be race condition to load
  1589. TraceMsg(TF_WARNING, "CFindCmd::MaybeRestoreSearch - _GetShellView failed...");
  1590. _fDeferRestore = TRUE;
  1591. if (!_SetupBrowserCP())
  1592. _fDeferRestore = FALSE;
  1593. }
  1594. }
  1595. return S_OK;
  1596. }
  1597. STDMETHODIMP CFindCmd::OnChange(IRowset *prowset, DBWATCHNOTIFY eChangeReason)
  1598. {
  1599. _fAsyncNotifyReceived = TRUE;
  1600. return S_OK;
  1601. }
  1602. STDMETHODIMP CFindCmd::DoSortOnColumn(UINT iCol, BOOL fSameCol)
  1603. {
  1604. IFindEnum *pdfEnumAsync;
  1605. if (S_OK == _execData.pff->GetAsyncEnum(&pdfEnumAsync))
  1606. {
  1607. // If the search is still running we will restart with the other column else we
  1608. // will make sure all of the items have been cached and let the default processing happen
  1609. if (!fSameCol && _cExecInProgress)
  1610. {
  1611. // We should try to sort on the right column...
  1612. _Start(FALSE, iCol, NULL);
  1613. return S_FALSE; // tell system to not do default processing.
  1614. }
  1615. _execData.pff->CacheAllAsyncItems();
  1616. }
  1617. return S_OK; // let it do default processing.
  1618. }
  1619. // Implemention of our IDispatch to hookup to the top level browsers connnection point...
  1620. STDMETHODIMP CFindCmd::CWBEvents2::QueryInterface(REFIID riid, void **ppv)
  1621. {
  1622. static const QITAB qit[] = {
  1623. QITABENTMULTI(CFindCmd::CWBEvents2, IDispatch, DWebBrowserEvents2),
  1624. QITABENTMULTI2(CFindCmd::CWBEvents2, DIID_DWebBrowserEvents2, DWebBrowserEvents2),
  1625. QITABENTMULTI2(CFindCmd::CWBEvents2, DIID_DWebBrowserEvents, DWebBrowserEvents),
  1626. { 0 },
  1627. };
  1628. return QISearch(this, qit, riid, ppv);
  1629. }
  1630. STDMETHODIMP CFindCmd::CWBEvents2::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
  1631. {
  1632. if (_fWaitingForNavigate)
  1633. {
  1634. if ((dispidMember == DISPID_NAVIGATECOMPLETE) ||
  1635. (dispidMember == DISPID_DOCUMENTCOMPLETE))
  1636. {
  1637. // Assume this is ours... Should maybe check parameters...
  1638. _fWaitingForNavigate = FALSE;
  1639. // Now see if it is a case where we are to restore the search...
  1640. if (_pcdfc->_fDeferRestore)
  1641. {
  1642. _pcdfc->_fDeferRestore = FALSE;
  1643. _pcdfc->_fDeferRestoreTried = TRUE;
  1644. _pcdfc->RestoreSavedSearch(NULL);
  1645. }
  1646. else
  1647. return _pcdfc->_Start(FALSE, -1, NULL);
  1648. }
  1649. }
  1650. return S_OK;
  1651. }
  1652. #define MAX_DEFAULT_VALUE 40 // Longest of all of the below pszDefaultValueMatch strings (plus slop)
  1653. #define MAX_KEY_PH_NAME 70 // "CLSID\{GUID}\PersistentHandler" (plus slop)
  1654. const TYPE_FIX_ENTRY g_tfeTextTypes[] =
  1655. {
  1656. { TEXT(".rtf"), NULL, NULL },
  1657. };
  1658. const TYPE_FIX_ENTRY g_tfeNullTypes[] =
  1659. {
  1660. { TEXT(".mdb"), TEXT("Access.Application.10"), TEXT("{73A4C9C1-D68D-11D0-98BF-00A0C90DC8D9}") },
  1661. { TEXT(".msg"), TEXT("msgfile"), NULL },
  1662. { TEXT(".sc2"), TEXT("SchedulePlus.Application.7"), TEXT("{0482E074-C5B7-101A-82E0-08002B36A333}") },
  1663. { TEXT(".wll"), TEXT("Word.Addin.8"), NULL },
  1664. };
  1665. //
  1666. // rtf is listed twice, once above for TextTypes (to fix when office
  1667. // un-installed) and once here as an OfficeType (to fix when office
  1668. // is re-installed). Uninstalled = TextFilter, Reinstalled = OfficeFilter
  1669. //
  1670. const TYPE_FIX_ENTRY g_tfeOfficeTypes[] =
  1671. {
  1672. { TEXT(".rtf"), TEXT("Word.RTF.8"), TEXT("{00020906-0000-0000-C000-000000000046}") },
  1673. { TEXT(".doc"), TEXT("Word.Document.8"), TEXT("{00020906-0000-0000-C000-000000000046}") },
  1674. { TEXT(".dot"), TEXT("Word.Template.8"), TEXT("{00020906-0000-0000-C000-000000000046}") },
  1675. { TEXT(".pot"), TEXT("PowerPoint.Template.8"), TEXT("{64818D11-4F9B-11CF-86EA-00AA00B929E8}") },
  1676. { TEXT(".pps"), TEXT("PowerPoint.SlideShow.8"), TEXT("{64818D10-4F9B-11CF-86EA-00AA00B929E8}") },
  1677. { TEXT(".ppt"), TEXT("PowerPoint.Show.8"), TEXT("{64818D10-4F9B-11CF-86EA-00AA00B929E8}") },
  1678. { TEXT(".rtf"), TEXT("Word.RTF.8"), TEXT("{00020906-0000-0000-C000-000000000046}") },
  1679. { TEXT(".xlb"), TEXT("Excel.Sheet.8"), TEXT("{00020820-0000-0000-C000-000000000046}") },
  1680. { TEXT(".xlc"), TEXT("Excel.Chart.8"), TEXT("{00020821-0000-0000-C000-000000000046}") },
  1681. { TEXT(".xls"), TEXT("Excel.Sheet.8"), TEXT("{00020820-0000-0000-C000-000000000046}") },
  1682. { TEXT(".xlt"), TEXT("Excel.Template"), TEXT("{00020820-0000-0000-C000-000000000046}") },
  1683. };
  1684. const TYPE_FIX_ENTRY g_tfeHtmlTypes[] =
  1685. {
  1686. { TEXT(".asp"), TEXT("aspfile"), NULL },
  1687. { TEXT(".htx"), TEXT("htxfile"), NULL },
  1688. };
  1689. BOOL CFindCmd::_FixPersistHandler(LPCTSTR pszBase, LPCTSTR pszDefaultHandler)
  1690. {
  1691. TCHAR szPHName[MAX_KEY_PH_NAME];
  1692. LONG lr;
  1693. HKEY hkeyPH;
  1694. HKEY hkeyBase;
  1695. wnsprintf(szPHName,ARRAYSIZE(szPHName), TEXT("%s\\PersistentHandler"), pszBase);
  1696. lr = RegOpenKey(HKEY_CLASSES_ROOT, szPHName, &hkeyPH);
  1697. if (lr == ERROR_SUCCESS)
  1698. {
  1699. // We found an existing PersistHandler key, leave it alone
  1700. RegCloseKey(hkeyPH);
  1701. return TRUE;
  1702. }
  1703. lr = RegOpenKey(HKEY_CLASSES_ROOT, pszBase, &hkeyBase);
  1704. if (lr != ERROR_SUCCESS)
  1705. {
  1706. // We didn't find the base key (normally "CLSID\\{GUID}"), get out
  1707. return FALSE;
  1708. }
  1709. RegCloseKey(hkeyBase);
  1710. lr = RegCreateKey(HKEY_CLASSES_ROOT, szPHName, &hkeyPH);
  1711. if (lr != ERROR_SUCCESS)
  1712. {
  1713. // We couldn't create the ...\PersistHandler key, get out
  1714. return FALSE;
  1715. }
  1716. // Able to create the ...\PersistHandler key, write out the default handler
  1717. lr = RegSetValue(hkeyPH, NULL, REG_SZ, pszDefaultHandler, lstrlen(pszDefaultHandler));
  1718. RegCloseKey(hkeyPH);
  1719. // Success if write succeeded
  1720. return (lr == ERROR_SUCCESS);
  1721. }
  1722. void CFindCmd::_ProcessTypes(
  1723. const TYPE_FIX_ENTRY *ptfeTypes,
  1724. UINT cTypes,
  1725. TCHAR *pszClass)
  1726. {
  1727. UINT iType;
  1728. LONG lr;
  1729. HKEY hkeyType;
  1730. for (iType = 0; iType < cTypes; iType++)
  1731. {
  1732. lr = RegOpenKey(HKEY_CLASSES_ROOT, ptfeTypes[iType].pszDotType, &hkeyType);
  1733. if (lr == ERROR_SUCCESS)
  1734. {
  1735. //
  1736. // If it has a default value to match, repair that (if it exists).
  1737. // If there is no default value to match, just repair the .foo type
  1738. //
  1739. if (ptfeTypes[iType].pszDefaultValueMatch)
  1740. {
  1741. TCHAR szDefaultValue[MAX_DEFAULT_VALUE];
  1742. LONG cb = sizeof(szDefaultValue);
  1743. lr = RegQueryValue(hkeyType, NULL, szDefaultValue, &cb);
  1744. if (lr == ERROR_SUCCESS)
  1745. {
  1746. if (lstrcmp(szDefaultValue,ptfeTypes[iType].pszDefaultValueMatch) == 0)
  1747. {
  1748. if (ptfeTypes[iType].pszGuid == NULL)
  1749. {
  1750. // Fix either the progid or the type, whichever we can
  1751. if (!_FixPersistHandler(ptfeTypes[iType].pszDefaultValueMatch,pszClass))
  1752. {
  1753. _FixPersistHandler(ptfeTypes[iType].pszDotType,pszClass);
  1754. }
  1755. }
  1756. else
  1757. {
  1758. // Fix the persist handler for the guid, since its specified
  1759. TCHAR szPHName[MAX_KEY_PH_NAME];
  1760. wnsprintf(szPHName, ARRAYSIZE(szPHName), TEXT("CLSID\\%s"), ptfeTypes[iType].pszGuid);
  1761. _FixPersistHandler(szPHName, pszClass);
  1762. }
  1763. }
  1764. }
  1765. }
  1766. else
  1767. {
  1768. _FixPersistHandler(ptfeTypes[iType].pszDotType, pszClass);
  1769. }
  1770. RegCloseKey(hkeyType);
  1771. }
  1772. else if (lr == ERROR_FILE_NOT_FOUND)
  1773. {
  1774. //
  1775. // .foo doesn't exist - this can happen because of bad un-install program
  1776. // Create .foo and .foo\PersistentHandler
  1777. //
  1778. lr = RegCreateKey(HKEY_CLASSES_ROOT, ptfeTypes[iType].pszDotType, &hkeyType);
  1779. if (lr == ERROR_SUCCESS)
  1780. {
  1781. _FixPersistHandler(ptfeTypes[iType].pszDotType, pszClass);
  1782. RegCloseKey(hkeyType);
  1783. }
  1784. }
  1785. }
  1786. }
  1787. void CFindCmd::_FixBrokenTypes(void)
  1788. {
  1789. _ProcessTypes(g_tfeNullTypes, ARRAYSIZE(g_tfeNullTypes), TEXT("{098f2470-bae0-11cd-b579-08002b30bfeb}"));
  1790. _ProcessTypes(g_tfeTextTypes, ARRAYSIZE(g_tfeTextTypes), TEXT("{5e941d80-bf96-11cd-b579-08002b30bfeb}"));
  1791. _ProcessTypes(g_tfeOfficeTypes, ARRAYSIZE(g_tfeOfficeTypes), TEXT("{98de59a0-d175-11cd-a7bd-00006b827d94}"));
  1792. _ProcessTypes(g_tfeHtmlTypes, ARRAYSIZE(g_tfeHtmlTypes), TEXT("{eec97550-47a9-11cf-b952-00aa0051fe20}"));
  1793. }
  1794. CFindConstraint::CFindConstraint(BSTR bstr, VARIANT var) : CImpIDispatch(LIBID_Shell32, 1, 0, IID_DFConstraint)
  1795. {
  1796. _cRef = 1;
  1797. _bstr = bstr;
  1798. _var = var;
  1799. }
  1800. CFindConstraint::~CFindConstraint()
  1801. {
  1802. SysFreeString(_bstr);
  1803. VariantClear(&_var);
  1804. }
  1805. STDMETHODIMP CFindConstraint::QueryInterface(REFIID riid, void **ppv)
  1806. {
  1807. static const QITAB qit[] = {
  1808. QITABENT(CFindConstraint, DFConstraint), // IID_DFConstraint
  1809. QITABENTMULTI(CFindConstraint, IDispatch, DFConstraint), // IID_IDispatch
  1810. { 0 },
  1811. };
  1812. return QISearch(this, qit, riid, ppv);
  1813. }
  1814. STDMETHODIMP_(ULONG) CFindConstraint::AddRef()
  1815. {
  1816. return InterlockedIncrement(&_cRef);
  1817. }
  1818. STDMETHODIMP_(ULONG) CFindConstraint::Release()
  1819. {
  1820. if (InterlockedDecrement(&_cRef))
  1821. return _cRef;
  1822. delete this;
  1823. return 0;
  1824. }
  1825. STDMETHODIMP CFindConstraint::GetTypeInfoCount(UINT * pctinfo)
  1826. {
  1827. return CImpIDispatch::GetTypeInfoCount(pctinfo);
  1828. }
  1829. STDMETHODIMP CFindConstraint::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  1830. {
  1831. return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo);
  1832. }
  1833. STDMETHODIMP CFindConstraint::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
  1834. {
  1835. return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  1836. }
  1837. STDMETHODIMP CFindConstraint::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
  1838. {
  1839. return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  1840. }
  1841. STDMETHODIMP CFindConstraint::get_Name(BSTR *pbs)
  1842. {
  1843. *pbs = SysAllocString(_bstr);
  1844. return *pbs? S_OK : E_OUTOFMEMORY;
  1845. }
  1846. STDMETHODIMP CFindConstraint::get_Value(VARIANT *pvar)
  1847. {
  1848. VariantInit(pvar);
  1849. return VariantCopy(pvar, &_var);
  1850. }