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.

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