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.

5396 lines
165 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include <regstr.h>
  4. #include <varutil.h>
  5. #include "ids.h"
  6. #include "findhlp.h"
  7. #include "pidl.h"
  8. #include "shitemid.h"
  9. #include "defview.h"
  10. #include "fstreex.h"
  11. #include "views.h"
  12. #include "cowsite.h"
  13. #include "exdisp.h"
  14. #include "shguidp.h"
  15. #include "prop.h" // COLUMN_INFO
  16. #include <limits.h>
  17. #include "stgutil.h"
  18. #include "netview.h"
  19. #include "basefvcb.h"
  20. #include "findfilter.h"
  21. #include "defvphst.h"
  22. #include "perhist.h"
  23. #include "adoint.h"
  24. #include "dspsprt.h"
  25. #include "defcm.h"
  26. #include "enumidlist.h"
  27. #include "contextmenu.h"
  28. // findband.cpp
  29. STDAPI GetCIStatus(BOOL *pbRunning, BOOL *pbIndexed, BOOL *pbPermission);
  30. STDAPI CatalogUptodate(LPCWSTR pszCatalog, LPCWSTR pszMachine);
  31. class CFindFolder;
  32. class CFindLVRange : public ILVRange
  33. {
  34. public:
  35. // IUnknown
  36. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  37. STDMETHODIMP_(ULONG) AddRef(void);
  38. STDMETHODIMP_(ULONG) Release(void);
  39. // ILVRange
  40. STDMETHODIMP IncludeRange(LONG iBegin, LONG iEnd);
  41. STDMETHODIMP ExcludeRange(LONG iBegin, LONG iEnd);
  42. STDMETHODIMP InvertRange(LONG iBegin, LONG iEnd);
  43. STDMETHODIMP InsertItem(LONG iItem);
  44. STDMETHODIMP RemoveItem(LONG iItem);
  45. STDMETHODIMP Clear();
  46. STDMETHODIMP IsSelected(LONG iItem);
  47. STDMETHODIMP IsEmpty();
  48. STDMETHODIMP NextSelected(LONG iItem, LONG *piItem);
  49. STDMETHODIMP NextUnSelected(LONG iItem, LONG *piItem);
  50. STDMETHODIMP CountIncluded(LONG *pcIncluded);
  51. // Helperfunctions...
  52. void SetOwner(CFindFolder *pff, DWORD dwMask)
  53. {
  54. // don't AddRef -- we're a member variable of the object punk points to
  55. _pff = pff;
  56. _dwMask = dwMask;
  57. _cIncluded = 0;
  58. }
  59. void IncrementIncludedCount() {_cIncluded++;}
  60. void DecrementIncludedCount() {_cIncluded--;}
  61. protected:
  62. CFindFolder *_pff;
  63. DWORD _dwMask; // The mask we use to know which "selection" bit we are tracking...
  64. LONG _cIncluded; // count included... (selected)
  65. };
  66. class CFindFolder : public IFindFolder,
  67. public IShellFolder2,
  68. public IShellIcon,
  69. public IShellIconOverlay,
  70. public IPersistFolder2
  71. {
  72. public:
  73. CFindFolder(IFindFilter *pff);
  74. // IUnknown
  75. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  76. STDMETHODIMP_(ULONG) AddRef(void);
  77. STDMETHODIMP_(ULONG) Release(void);
  78. // IShellFolder
  79. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
  80. STDMETHODIMP EnumObjects(THIS_ HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
  81. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
  82. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  83. { return BindToObject(pidl, pbc, riid, ppv); }
  84. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  85. STDMETHODIMP CreateViewObject(HWND hwnd, REFIID riid, void **ppv);
  86. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut);
  87. STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv);
  88. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
  89. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST *ppidlOut);
  90. // IShellFolder2
  91. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
  92. STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum);
  93. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
  94. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState);
  95. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
  96. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  97. STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid);
  98. // IFindFolder
  99. STDMETHODIMP GetFindFilter(IFindFilter **pfilter);
  100. STDMETHODIMP AddPidl(int i, LPCITEMIDLIST pidl, DWORD dwItemID, FIND_ITEM **ppItem);
  101. STDMETHODIMP GetItem(int iItem, FIND_ITEM **ppItem);
  102. STDMETHODIMP DeleteItem(int iItem);
  103. STDMETHODIMP GetItemCount(INT *pcItems);
  104. STDMETHODIMP ValidateItems(IUnknown *punkView, int iItemFirst, int cItems, BOOL bSearchComplete);
  105. STDMETHODIMP GetFolderListItemCount(INT *pcCount);
  106. STDMETHODIMP GetFolderListItem(int iItem, FIND_FOLDER_ITEM **ppItem);
  107. STDMETHODIMP GetFolder(int iFolder, REFIID riid, void **ppv);
  108. STDMETHODIMP_(UINT) GetFolderIndex(LPCITEMIDLIST pidl);
  109. STDMETHODIMP SetItemsChangedSinceSort();
  110. STDMETHODIMP ClearItemList();
  111. STDMETHODIMP ClearFolderList();
  112. STDMETHODIMP AddFolder(LPITEMIDLIST pidl, BOOL fCheckForDup, int * piFolder);
  113. STDMETHODIMP SetAsyncEnum(IFindEnum *pfenum);
  114. STDMETHODIMP GetAsyncEnum(IFindEnum **ppfenum);
  115. STDMETHODIMP CacheAllAsyncItems();
  116. STDMETHODIMP_(BOOL) AllAsyncItemsCached();
  117. STDMETHODIMP SetAsyncCount(DBCOUNTITEM cCount);
  118. STDMETHODIMP ClearSaveStateList();
  119. STDMETHODIMP GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState);
  120. STDMETHODIMP MapToSearchIDList(LPCITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl);
  121. STDMETHODIMP GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent);
  122. STDMETHODIMP SetControllerNotifyObject(IFindControllerNotify *pfcn);
  123. STDMETHODIMP GetControllerNotifyObject(IFindControllerNotify **ppfcn);
  124. STDMETHODIMP RememberSelectedItems();
  125. STDMETHODIMP SaveFolderList(IStream *pstm);
  126. STDMETHODIMP RestoreFolderList(IStream *pstm);
  127. STDMETHODIMP SaveItemList(IStream *pstm);
  128. STDMETHODIMP RestoreItemList(IStream *pstm, int *pcItems);
  129. STDMETHODIMP RestoreSearchFromSaveFile(LPCITEMIDLIST pidlSaveFile, IShellFolderView *psfv);
  130. STDMETHODIMP_(BOOL) HandleUpdateDir(LPCITEMIDLIST pidl, BOOL fCheckSubDirs);
  131. STDMETHODIMP_(void) HandleRMDir(IShellFolderView *psfv, LPCITEMIDLIST pidl);
  132. STDMETHODIMP_(void) UpdateOrMaybeAddPidl(IShellFolderView *psfv, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlOld);
  133. STDMETHODIMP_(void) Save(IFindFilter* pfilter, HWND hwnd, DFBSAVEINFO * pSaveInfo, IShellView* psv, IUnknown * pObject);
  134. STDMETHODIMP OpenContainingFolder(IUnknown *punkSite);
  135. STDMETHODIMP AddDataToIDList(LPCITEMIDLIST pidl, int iFolder, LPCITEMIDLIST pidlFolder, UINT uFlags, UINT uRow, DWORD dwItemID, ULONG ulRank, LPITEMIDLIST *ppidl);
  136. // IShellIcon
  137. STDMETHODIMP GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex);
  138. // IShellIconOverlay
  139. STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex);
  140. STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex);
  141. // IPersist
  142. STDMETHODIMP GetClassID(CLSID *pClassID);
  143. // IPersistFolder
  144. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  145. // IPersistFolder2
  146. STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl);
  147. friend class CFindFolderViewCB;
  148. friend class CFindLVRange;
  149. HRESULT Init();
  150. private:
  151. ~CFindFolder();
  152. HRESULT _CompareFolderIndexes(int iFolder1, int iFolder2);
  153. void _AddFIND_ITEMToSaveStateList(FIND_ITEM *pesfi);
  154. LPITEMIDLIST _GetFullPidlForItem(LPCITEMIDLIST pidl);
  155. HRESULT _UpdateItemList();
  156. static int CALLBACK _SortForDataObj(void *p1, void *p2, LPARAM lparam);
  157. static ULONG _Rank(LPCITEMIDLIST pidl);
  158. static DWORD _ItemID(LPCITEMIDLIST pidl);
  159. static PCHIDDENDOCFINDDATA _HiddenData(LPCITEMIDLIST pidl);
  160. FIND_FOLDER_ITEM *_FolderListItem(int iFolder);
  161. FIND_FOLDER_ITEM *_FolderListItem(LPCITEMIDLIST pidl);
  162. static BOOL _MapColIndex(UINT *piColumn);
  163. HRESULT _PrepareHIDA(UINT cidl, LPCITEMIDLIST *apidl, HDPA *phdpa);
  164. HRESULT _GetDetailsFolder();
  165. HRESULT _QueryItemShellFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf);
  166. HRESULT _QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv);
  167. HRESULT _Folder(FIND_FOLDER_ITEM *pffli, REFIID riid, void **ppv);
  168. HRESULT _FolderFromItem(LPCITEMIDLIST pidl, REFIID riid, void **ppv);
  169. HRESULT _GetFolderName(LPCITEMIDLIST pidl, DWORD gdnFlags, LPTSTR psz, UINT cch);
  170. int _CompareByCachedSCID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  171. int _CompareNames(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwFlags);
  172. HRESULT _GetItemDisplayName(LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR wzName, UINT cch);
  173. HRESULT _GetFolderIDList(int iFolder, LPITEMIDLIST *ppidlParent);
  174. LONG _cRef;
  175. CFindLVRange _dflvrSel; // manage selection and cut range information...
  176. CFindLVRange _dflvrCut;
  177. HDPA _hdpaItems; // all of the items in the results
  178. HDPA _hdpaPidf; // folders that the items came from
  179. IFindFilter *_pfilter;
  180. BOOL _fItemsChangedSinceSort;// Has the list changed since the last sort?
  181. BOOL _fAllAsyncItemsCached; // Have we already cached all of the items?
  182. BOOL _fSearchComplete;
  183. BOOL _fInRefresh; // true if received prerefresh callback but postrefresh
  184. LPITEMIDLIST _pidl;
  185. IFindEnum *_pDFEnumAsync; // we have an async one, will need to call back for PIDLS and the like
  186. DBCOUNTITEM _cAsyncItems; // Count of async items
  187. int _iGetIDList; // index of the last IDlist we retrieved in callback.
  188. HDSA _hdsaSaveStateForIDs; // Async - Remember which items are selected when we sort
  189. int _cSaveStateSelected; // Number of items in selection list which are selected
  190. IFindControllerNotify *_pfcn; // Sometimes need to let the "Controller" object know about things
  191. CRITICAL_SECTION _csSearch;
  192. int _iCompareFolderCache1, _iCompareFolderCache2, _iCompareFolderCacheResult;
  193. IShellFolder2 *_psfDetails;
  194. SHCOLUMNID _scidCached; // Cached SCID for sorting the columns
  195. UINT _uiColumnCached; // The index to the cached column for _scidCached
  196. #if DEBUG
  197. DWORD _GUIThreadID; // Items can only be added to _hdpaItems on the UI thread.
  198. #endif
  199. };
  200. class CFindFolderViewCB : public CBaseShellFolderViewCB
  201. {
  202. public:
  203. STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  204. HRESULT SetShellView(IShellView *psv);
  205. CFindFolderViewCB(CFindFolder* pff);
  206. // IServiceProvider override
  207. STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv);
  208. // Web View Tasks
  209. static HRESULT _OnOpenContainingFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  210. private:
  211. ~CFindFolderViewCB();
  212. HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP);
  213. HRESULT OnReArrange(DWORD pv, LPARAM lp);
  214. HRESULT OnGETWORKINGDIR(DWORD pv, UINT wP, LPTSTR lP);
  215. HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP);
  216. HRESULT OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream**lP);
  217. HRESULT OnGETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST *ppidl);
  218. HRESULT OnGetItemIconIndex(DWORD pv, WPARAM iItem, int *piIcon);
  219. HRESULT OnSetItemIconOverlay(DWORD pv, WPARAM iItem, int dwOverlayState);
  220. HRESULT OnGetItemIconOverlay(DWORD pv, WPARAM iItem, int * pdwOverlayState);
  221. HRESULT OnSETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST pidl);
  222. HRESULT OnGetIndexForItemIDList(DWORD pv, int * piItem, LPITEMIDLIST pidl);
  223. HRESULT OnDeleteItem(DWORD pv, LPCITEMIDLIST pidl);
  224. HRESULT OnODFindItem(DWORD pv, int * piItem, NM_FINDITEM* pnmfi);
  225. HRESULT OnODCacheHint(DWORD pv, NMLVCACHEHINT* pnmlvc);
  226. HRESULT OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP);
  227. HRESULT OnGetEmptyText(DWORD pv, UINT cchTextMax, LPTSTR pszText);
  228. HRESULT OnSetEmptyText(DWORD pv, UINT res, LPCTSTR pszText);
  229. HRESULT OnHwndMain(DWORD pv, HWND hwndMain);
  230. HRESULT OnIsOwnerData(DWORD pv, DWORD *pdwFlags);
  231. HRESULT OnSetISFV(DWORD pv, IShellFolderView* pisfv);
  232. HRESULT OnWindowCreated(DWORD pv, HWND hwnd);
  233. HRESULT OnWindowDestroy(DWORD pv, HWND wP);
  234. HRESULT OnGetODRangeObject(DWORD pv, WPARAM wWhich, ILVRange **pplvr);
  235. HRESULT OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP);
  236. HRESULT OnGetIPersistHistory(DWORD pv, IPersistHistory **ppph);
  237. HRESULT OnRefresh(DWORD pv, BOOL fPreRefresh);
  238. HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *shtd);
  239. HRESULT OnSortListData(DWORD pv, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
  240. HRESULT _ProfferService(BOOL bProffer);
  241. HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
  242. HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
  243. HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
  244. HRESULT OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme);
  245. CFindFolder* _pff;
  246. UINT _iColSort; // Which column are we sorting by
  247. BOOL _fIgnoreSelChange; // Sort in process
  248. UINT _iFocused; // Which item has the focus?
  249. UINT _cSelected; // Count of items selected
  250. IProfferService* _pps; // ProfferService site.
  251. DWORD _dwServiceCookie; // ProfferService cookie.
  252. TCHAR _szEmptyText[128]; // empty results list text.
  253. friend class CFindLVRange;
  254. };
  255. // Class to save and restore find state on the travel log
  256. class CFindPersistHistory : public CDefViewPersistHistory
  257. {
  258. public:
  259. CFindPersistHistory();
  260. // IPersist
  261. STDMETHODIMP GetClassID(CLSID *pClassID);
  262. // IPersistHistory
  263. STDMETHODIMP LoadHistory(IStream *pStream, IBindCtx *pbc);
  264. STDMETHODIMP SaveHistory(IStream *pStream);
  265. protected:
  266. IFindFolder* _GetDocFindFolder();
  267. };
  268. // {a5df1ea0-5702-11d1-83fa-00a0c90dc849}
  269. const IID IID_IFindFolder = {0xa5df1ea0, 0x5702, 0x11d1, {0x83, 0xfa, 0x00, 0xa0, 0xc9, 0x0d, 0xc8, 0x49}};
  270. // {5B8DCBF0-B096-11d1-9217-00403393B8F0}
  271. const IID IID_IFindControllerNotify = {0x5b8dcbf0, 0xb096, 0x11d1, {0x92, 0x17, 0x0, 0x40, 0x33, 0x93, 0xb8, 0xf0}};
  272. // Listview doesn't support more than 100000000 items, so if our
  273. // client returns more than that, just stop after that point.
  274. //
  275. // Instead of 100000000, we use the next lower 64K boundary. This keeps
  276. // us away from strange boundary cases (where a +1 might push us over the
  277. // top), and it keeps the Alpha happy.
  278. //
  279. #define MAX_LISTVIEWITEMS (100000000 & ~0xFFFF)
  280. #define SANE_ITEMCOUNT(c) ((int)min(c, MAX_LISTVIEWITEMS))
  281. // Unicode descriptor:
  282. //
  283. // Structure written at the end of NT-generated find stream serves dual purpose.
  284. // 1. Contains an NT-specific signature to identify stream as NT-generated.
  285. // Appears as "NTFF" (NT Find File) in ASCII dump of file.
  286. // 2. Contains an offset to the unicode-formatted criteria section.
  287. //
  288. // The following diagram shows the find criteria/results stream format including
  289. // the NT-specific unicode criteria and descriptor.
  290. //
  291. // +-----------------------------------------+ --------------
  292. // | DFHEADER structure | . .
  293. // +-----------------------------------------+ . .
  294. // | DF Criteria records (ANSI) | Win95 .
  295. // +-----------------------------------------+ . .
  296. // | DF Results (PIDL) [optional] | . NT
  297. // +-----------------------------------------+ ------- .
  298. // +----->| DF Criteria records (Unicode) [NT only] | .
  299. // | +-----------------------------------------+ .
  300. // | | Unicode Descriptor | .
  301. // | +--------------------+ ----------------------------------
  302. // | / \
  303. // | / \
  304. // | +-----------------+---------+
  305. // +---| Offset (64-bit) | "NTFF" |
  306. // +-----------------+---------+
  307. //
  308. //
  309. const DWORD c_NTsignature = 0x4646544E; // "NTFF" in ASCII file dump.
  310. typedef struct
  311. {
  312. ULARGE_INTEGER oUnicodeCriteria; // Offset of unicode find criteria.
  313. DWORD NTsignature; // Signature of NT-generated find file.
  314. } DFC_UNICODE_DESC;
  315. enum
  316. {
  317. IDFCOL_NAME = 0, // default col from guy we are delegating to
  318. IDFCOL_PATH,
  319. IDFCOL_RANK,
  320. };
  321. const COLUMN_INFO c_find_cols[] =
  322. {
  323. DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL),
  324. DEFINE_COL_STR_ENTRY(SCID_DIRECTORY, 30, IDS_PATH_COL),
  325. DEFINE_COL_STR_MENU_ENTRY(SCID_RANK, 10, IDS_RANK_COL),
  326. };
  327. class CFindMenuBase : public IContextMenuCB , public CObjectWithSite
  328. {
  329. public:
  330. CFindMenuBase();
  331. // IUnknown
  332. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  333. STDMETHODIMP_(ULONG) AddRef(void);
  334. STDMETHODIMP_(ULONG) Release(void);
  335. // IContextMenuCB
  336. STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) PURE;
  337. protected:
  338. virtual ~CFindMenuBase();
  339. private:
  340. LONG _cRef;
  341. };
  342. CFindMenuBase::CFindMenuBase() : _cRef(1)
  343. {
  344. }
  345. CFindMenuBase::~CFindMenuBase()
  346. {
  347. }
  348. STDMETHODIMP CFindMenuBase::QueryInterface(REFIID riid, void **ppv)
  349. {
  350. static const QITAB qit[] = {
  351. QITABENT(CFindMenuBase, IContextMenuCB), // IID_IContextMenuCB
  352. QITABENT(CFindMenuBase, IObjectWithSite), // IID_IObjectWithSite
  353. { 0 },
  354. };
  355. return QISearch(this, qit, riid, ppv);
  356. }
  357. ULONG CFindMenuBase::AddRef()
  358. {
  359. return InterlockedIncrement(&_cRef);
  360. }
  361. ULONG CFindMenuBase::Release()
  362. {
  363. if (InterlockedDecrement(&_cRef))
  364. return _cRef;
  365. delete this;
  366. return 0;
  367. }
  368. class CFindMenuCB : public CFindMenuBase
  369. {
  370. public:
  371. // IContextMenuCB
  372. STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  373. };
  374. STDMETHODIMP CFindMenuCB::CallBack(IShellFolder *psf, HWND hwnd,
  375. IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  376. {
  377. HRESULT hr = S_OK;
  378. switch (uMsg)
  379. {
  380. case DFM_MERGECONTEXTMENU:
  381. if (!(wParam & CMF_VERBSONLY))
  382. {
  383. LPQCMINFO pqcm = (LPQCMINFO)lParam;
  384. if (!pdtobj)
  385. {
  386. UINT idStart = pqcm->idCmdFirst;
  387. UINT idBGMain = 0, idBGPopup = 0;
  388. IFindFolder *pff;
  389. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff))))
  390. {
  391. IFindFilter *pfilter;
  392. if (SUCCEEDED(pff->GetFindFilter(&pfilter)))
  393. {
  394. pfilter->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup);
  395. CDefFolderMenu_MergeMenu(HINST_THISDLL, idBGMain, idBGPopup, pqcm);
  396. DeleteMenu(pqcm->hmenu, idStart+SFVIDM_EDIT_PASTE, MF_BYCOMMAND);
  397. DeleteMenu(pqcm->hmenu, idStart+SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND);
  398. DeleteMenu(pqcm->hmenu, idStart+SFVIDM_MISC_REFRESH, MF_BYCOMMAND);
  399. IFindControllerNotify *pdcn;
  400. if (S_OK == pff->GetControllerNotifyObject(&pdcn))
  401. {
  402. pdcn->Release();
  403. }
  404. else
  405. {
  406. DeleteMenu(pqcm->hmenu, idStart+FSIDM_SAVESEARCH, MF_BYCOMMAND);
  407. }
  408. pfilter->Release();
  409. }
  410. pff->Release();
  411. }
  412. }
  413. }
  414. break;
  415. case DFM_INVOKECOMMAND:
  416. {
  417. // Check if this is from item context menu
  418. if (pdtobj)
  419. {
  420. switch(wParam)
  421. {
  422. case DFM_CMD_LINK:
  423. hr = SHCreateLinks(hwnd, NULL, pdtobj, SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL);
  424. break;
  425. case DFM_CMD_DELETE:
  426. // convert to DFM_INVOKCOMMANDEX to get flags bits
  427. hr = DeleteFilesInDataObject(hwnd, 0, pdtobj, 0);
  428. break;
  429. case DFM_CMD_PROPERTIES:
  430. // We need to pass an empty IDlist to combine with.
  431. hr = SHLaunchPropSheet(CFSFolder_PropertiesThread, pdtobj,
  432. (LPCTSTR)lParam, NULL, (void *)&c_idlDesktop);
  433. break;
  434. default:
  435. // Note: Fixing the working of Translator Key is not worth fixing. Hence Punted.
  436. // if GetAttributesOf did not specify the SFGAO_ bit
  437. // that corresponds to this default DFM_CMD, then we should
  438. // fail it here instead of returning S_FALSE. Otherwise,
  439. // accelerator keys (cut/copy/paste/etc) will get here, and
  440. // defcm tries to do the command with mixed results.
  441. // if GetAttributesOf did not specify SFGAO_CANLINK
  442. // or SFGAO_CANDELETE or SFGAO_HASPROPERTIES, then the above
  443. // implementations of these DFM_CMD commands are wrong...
  444. // Let the defaults happen for this object
  445. hr = S_FALSE;
  446. break;
  447. }
  448. }
  449. else
  450. {
  451. switch (wParam)
  452. {
  453. case FSIDM_SAVESEARCH:
  454. {
  455. IFindFolder *pff;
  456. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff))))
  457. {
  458. IFindControllerNotify *pdcn;
  459. if (S_OK == pff->GetControllerNotifyObject(&pdcn))
  460. {
  461. pdcn->SaveSearch();
  462. pdcn->Release();
  463. }
  464. pff->Release();
  465. }
  466. }
  467. break;
  468. default:
  469. hr = S_FALSE; // one of view menu items, use the default code.
  470. break;
  471. }
  472. }
  473. }
  474. break;
  475. case DFM_GETHELPTEXT: // ansi version
  476. case DFM_GETHELPTEXTW:
  477. {
  478. UINT id = LOWORD(wParam) + IDS_MH_FSIDM_FIRST;
  479. if (uMsg == DFM_GETHELPTEXTW)
  480. LoadStringW(HINST_THISDLL, id, (LPWSTR)lParam, HIWORD(wParam));
  481. else
  482. LoadStringA(HINST_THISDLL, id, (LPSTR)lParam, HIWORD(wParam));
  483. }
  484. break;
  485. default:
  486. hr = E_NOTIMPL;
  487. break;
  488. }
  489. return hr;
  490. }
  491. class CFindFolderContextMenuItemCB : public CFindMenuBase
  492. {
  493. public:
  494. // IContextMenuCB
  495. STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  496. private:
  497. CFindFolderContextMenuItemCB(IFindFolder* pff);
  498. ~CFindFolderContextMenuItemCB();
  499. friend HRESULT CFindItem_Create(HWND hwnd, IFindFolder *pff, IContextMenu **ppcm);
  500. STDMETHODIMP _GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode);
  501. IFindFolder *_pff;
  502. };
  503. CFindFolderContextMenuItemCB::CFindFolderContextMenuItemCB(IFindFolder* pff) : _pff(pff)
  504. {
  505. _pff->AddRef();
  506. }
  507. CFindFolderContextMenuItemCB::~CFindFolderContextMenuItemCB()
  508. {
  509. _pff->Release();
  510. }
  511. STDMETHODIMP CFindFolderContextMenuItemCB::_GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode)
  512. {
  513. HRESULT hr;
  514. if (idCmd == FSIDM_OPENCONTAININGFOLDER)
  515. {
  516. if (bUnicode)
  517. StrCpyNW((LPWSTR)pszName, L"OpenContainingFolder", cchMax);
  518. else
  519. StrCpyNA((LPSTR)pszName, "OpenContainingFolder", cchMax);
  520. hr = S_OK;
  521. }
  522. else
  523. {
  524. hr = E_NOTIMPL;
  525. }
  526. return hr;
  527. }
  528. STDMETHODIMP CFindFolderContextMenuItemCB::CallBack(IShellFolder *psf, HWND hwnd,
  529. IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  530. {
  531. HRESULT hr = S_OK;
  532. switch (uMsg)
  533. {
  534. case DFM_MERGECONTEXTMENU:
  535. if (!(wParam & CMF_VERBSONLY))
  536. {
  537. LPQCMINFO pqcm = (LPQCMINFO)lParam;
  538. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DOCFIND_ITEM_MERGE, 0, pqcm);
  539. }
  540. break;
  541. case DFM_INVOKECOMMAND:
  542. switch (wParam)
  543. {
  544. case FSIDM_OPENCONTAININGFOLDER:
  545. _pff->OpenContainingFolder(_punkSite);
  546. break;
  547. default:
  548. hr = E_FAIL; // not our command
  549. break;
  550. }
  551. break;
  552. case DFM_GETHELPTEXT:
  553. case DFM_GETHELPTEXTW:
  554. // probably need to implement these...
  555. case DFM_GETVERBA:
  556. case DFM_GETVERBW:
  557. hr = _GetVerb((UINT_PTR)(LOWORD(wParam)), (LPSTR)lParam, (UINT)(HIWORD(wParam)), uMsg == DFM_GETVERBW);
  558. break;
  559. default:
  560. hr = E_NOTIMPL;
  561. break;
  562. }
  563. return hr;
  564. }
  565. STDAPI CFindItem_Create(HWND hwnd, IFindFolder* pff, IContextMenu **ppcm)
  566. {
  567. *ppcm = NULL;
  568. HRESULT hr;
  569. // We want a quick IContextMenu implementation -- an empty defcm looks easiest
  570. IContextMenuCB* pcmcb = new CFindFolderContextMenuItemCB(pff);
  571. if (pcmcb)
  572. {
  573. hr = CDefFolderMenu_CreateEx(NULL, hwnd, 0, NULL, NULL, pcmcb, NULL, NULL, ppcm);
  574. pcmcb->Release();
  575. }
  576. else
  577. hr = E_OUTOFMEMORY;
  578. return hr;
  579. }
  580. void cdecl DocFind_SetStatusText(HWND hwndStatus, int iField, UINT ids,...)
  581. {
  582. TCHAR sz2[MAX_PATH+32]; // leave slop for message + max path name
  583. va_list ArgList;
  584. if (hwndStatus)
  585. {
  586. va_start(ArgList, ids);
  587. LPTSTR psz = _ConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(ids), &ArgList);
  588. if (psz)
  589. {
  590. StrCpyN(sz2, psz, ARRAYSIZE(sz2));
  591. LocalFree(psz);
  592. }
  593. else
  594. {
  595. sz2[0] = 0;
  596. }
  597. va_end(ArgList);
  598. if (iField < 0)
  599. {
  600. SendMessage(hwndStatus, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)(LPTSTR)sz2);
  601. }
  602. else
  603. {
  604. SendMessage(hwndStatus, SB_SETTEXT, iField, (LPARAM)(LPTSTR)sz2);
  605. }
  606. UpdateWindow(hwndStatus);
  607. }
  608. }
  609. HRESULT CFindFolder::AddFolder(LPITEMIDLIST pidl, BOOL fCheckForDup, int *piFolder)
  610. {
  611. *piFolder = -1;
  612. if (fCheckForDup)
  613. {
  614. EnterCriticalSection(&_csSearch);
  615. for (int i = DPA_GetPtrCount(_hdpaPidf) - 1; i >= 0; i--)
  616. {
  617. FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
  618. if (pffli && ILIsEqual(&pffli->idl, pidl))
  619. {
  620. LeaveCriticalSection(&_csSearch);
  621. *piFolder = i;
  622. return S_OK;
  623. }
  624. }
  625. LeaveCriticalSection(&_csSearch);
  626. }
  627. int cb = ILGetSize(pidl);
  628. FIND_FOLDER_ITEM *pffli;;
  629. HRESULT hr = SHLocalAlloc(sizeof(*pffli) - sizeof(pffli->idl) + cb, &pffli);
  630. if (SUCCEEDED(hr))
  631. {
  632. // pddfli->psf = NULL;
  633. // pffli->fUpdateDir = FALSE;
  634. memcpy(&pffli->idl, pidl, cb);
  635. EnterCriticalSection(&_csSearch);
  636. // Now add this item to our DPA...
  637. *piFolder = DPA_AppendPtr(_hdpaPidf, pffli);
  638. LeaveCriticalSection(&_csSearch);
  639. if (-1 != *piFolder)
  640. {
  641. // If this is a network ID list then register a path -> pidl mapping, therefore
  642. // avoiding having to create simple ID lists, which don't work correctly when
  643. // being compared against real ID lists.
  644. if (IsIDListInNameSpace(pidl, &CLSID_NetworkPlaces))
  645. {
  646. TCHAR szPath[ MAX_PATH ];
  647. SHGetPathFromIDList(pidl, szPath);
  648. NPTRegisterNameToPidlTranslation(szPath, _ILNext(pidl)); // skip the My Net Places entry
  649. }
  650. }
  651. else
  652. {
  653. LocalFree((HLOCAL)pffli);
  654. hr = E_OUTOFMEMORY;
  655. }
  656. }
  657. return hr;
  658. }
  659. typedef struct
  660. {
  661. DWORD dwState; // State of the item;
  662. DWORD dwItemID; // Only used for Async support...
  663. } FIND_ITEM_SAVE_STATE;
  664. void CFindFolder::_AddFIND_ITEMToSaveStateList(FIND_ITEM *pesfi)
  665. {
  666. FIND_ITEM_SAVE_STATE essi;
  667. essi.dwState = pesfi->dwState & CDFITEM_STATE_MASK;
  668. essi.dwItemID = _ItemID(&pesfi->idl);
  669. DSA_AppendItem(_hdsaSaveStateForIDs, (void *)&essi);
  670. if (essi.dwState & LVIS_SELECTED)
  671. _cSaveStateSelected++;
  672. }
  673. HRESULT CFindFolder::RememberSelectedItems()
  674. {
  675. EnterCriticalSection(&_csSearch);
  676. // Currently has list of pidls...
  677. for (int i = DPA_GetPtrCount(_hdpaItems); i-- > 0;)
  678. {
  679. // Pidl at start of structure...
  680. FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_hdpaItems, i);
  681. if (pesfi)
  682. {
  683. if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED))
  684. _AddFIND_ITEMToSaveStateList(pesfi);
  685. }
  686. }
  687. LeaveCriticalSection(&_csSearch);
  688. return S_OK;
  689. }
  690. STDMETHODIMP CFindFolder::ClearItemList()
  691. {
  692. // Clear out any async enumerators we may have
  693. SetAsyncEnum(NULL);
  694. _cAsyncItems = 0; // clear out our count of items...
  695. _pfilter->ReleaseQuery();
  696. // Also tell the filter to release everything...
  697. EnterCriticalSection(&_csSearch);
  698. if (_hdpaItems)
  699. {
  700. // Currently has list of pidls...
  701. for (int i = DPA_GetPtrCount(_hdpaItems) - 1; i >= 0; i--)
  702. {
  703. // Pidl at start of structure...
  704. FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_hdpaItems, i);
  705. if (pesfi)
  706. LocalFree((HLOCAL)pesfi);
  707. }
  708. _fSearchComplete = FALSE;
  709. DPA_DeleteAllPtrs(_hdpaItems);
  710. }
  711. LeaveCriticalSection(&_csSearch);
  712. return S_OK;
  713. }
  714. STDMETHODIMP CFindFolder::ClearFolderList()
  715. {
  716. EnterCriticalSection(&_csSearch);
  717. if (_hdpaPidf)
  718. {
  719. for (int i = DPA_GetPtrCount(_hdpaPidf) - 1; i >= 0; i--)
  720. {
  721. FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
  722. if (pffli)
  723. {
  724. // Release the IShellFolder if we have one
  725. if (pffli->psf)
  726. pffli->psf->Release();
  727. // And delete the item
  728. LocalFree((HLOCAL)pffli);
  729. }
  730. }
  731. DPA_DeleteAllPtrs(_hdpaPidf);
  732. }
  733. LeaveCriticalSection(&_csSearch);
  734. return S_OK;
  735. }
  736. CFindFolder::CFindFolder(IFindFilter *pff) : _cRef(1), _iGetIDList(-1), _pfilter(pff), _iCompareFolderCache1(-1), _uiColumnCached(-1)
  737. {
  738. ASSERT(_pidl == NULL);
  739. _pfilter->AddRef();
  740. // initialize our LV selection objects...
  741. _dflvrSel.SetOwner(this, LVIS_SELECTED);
  742. _dflvrCut.SetOwner(this, LVIS_CUT);
  743. InitializeCriticalSection(&_csSearch);
  744. #if DEBUG
  745. _GUIThreadID = GetCurrentThreadId();
  746. #endif
  747. }
  748. CFindFolder::~CFindFolder()
  749. {
  750. ASSERT(_cRef==0);
  751. // We will need to call our function to Free our items in our
  752. // Folder list. We will use the same function that we use to
  753. // clear it when we do a new search
  754. ClearItemList();
  755. ClearFolderList();
  756. ClearSaveStateList();
  757. EnterCriticalSection(&_csSearch);
  758. DPA_Destroy(_hdpaPidf);
  759. DPA_Destroy(_hdpaItems);
  760. _hdpaPidf = NULL;
  761. _hdpaItems = NULL;
  762. LeaveCriticalSection(&_csSearch);
  763. DSA_Destroy(_hdsaSaveStateForIDs);
  764. _pfilter->Release();
  765. if (_psfDetails)
  766. _psfDetails->Release();
  767. DeleteCriticalSection(&_csSearch);
  768. }
  769. HRESULT CFindFolder::Init()
  770. {
  771. // Create the heap for the folder lists.
  772. _hdpaPidf = DPA_CreateEx(64, GetProcessHeap());
  773. // Create the DPA and DSA for the item list.
  774. _hdpaItems = DPA_CreateEx(64, GetProcessHeap());
  775. _hdsaSaveStateForIDs = DSA_Create(sizeof(FIND_ITEM_SAVE_STATE), 16);
  776. return _hdsaSaveStateForIDs && _hdpaItems && _hdpaPidf ? S_OK : E_OUTOFMEMORY;
  777. }
  778. STDMETHODIMP CFindFolder::AddDataToIDList(LPCITEMIDLIST pidl, int iFolder, LPCITEMIDLIST pidlFolder, UINT uFlags, UINT uRow, DWORD dwItemID, ULONG ulRank, LPITEMIDLIST *ppidl)
  779. {
  780. HRESULT hr;
  781. LPITEMIDLIST pidlToFree;
  782. if (pidlFolder)
  783. {
  784. pidlToFree = NULL;
  785. hr = S_OK;
  786. }
  787. else
  788. {
  789. hr = _GetFolderIDList(iFolder, &pidlToFree);
  790. pidlFolder = pidlToFree;
  791. }
  792. if (SUCCEEDED(hr))
  793. {
  794. HIDDENDOCFINDDATA *phfd;
  795. int cb = ILGetSize(pidlFolder);
  796. int cbTotal = sizeof(*phfd) - sizeof(phfd->idlParent) + cb;
  797. hr = SHLocalAlloc(cbTotal, &phfd);
  798. if (SUCCEEDED(hr))
  799. {
  800. phfd->hid.cb = (WORD)cbTotal;
  801. phfd->hid.wVersion = 0;
  802. phfd->hid.id = IDLHID_DOCFINDDATA;
  803. phfd->iFolder = (WORD)iFolder; // index to the folder DPA
  804. phfd->wFlags = (WORD)uFlags;
  805. phfd->uRow = uRow; // Which row in the CI;
  806. phfd->dwItemID = dwItemID; // Only used for Async support...
  807. phfd->ulRank = ulRank; // The rank returned by CI...
  808. memcpy(&phfd->idlParent, pidlFolder, cb);
  809. hr = ILCloneWithHiddenID(pidl, &phfd->hid, ppidl);
  810. LocalFree(phfd);
  811. }
  812. ILFree(pidlToFree);
  813. }
  814. return hr;
  815. }
  816. HRESULT CreateFindWithFilter(IFindFilter *pff, REFIID riid, void **ppv)
  817. {
  818. *ppv = NULL;
  819. HRESULT hr = E_OUTOFMEMORY;
  820. CFindFolder *pfindfldr = new CFindFolder(pff);
  821. if (pfindfldr)
  822. {
  823. hr = pfindfldr->Init();
  824. if (SUCCEEDED(hr))
  825. hr = pfindfldr->QueryInterface(riid, ppv);
  826. pfindfldr->Release();
  827. }
  828. return hr;
  829. }
  830. STDAPI CDocFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  831. {
  832. *ppv = NULL;
  833. IFindFilter *pff;
  834. HRESULT hr = CreateNameSpaceFindFilter(&pff);
  835. if (SUCCEEDED(hr))
  836. {
  837. hr = CreateFindWithFilter(pff, riid, ppv);
  838. pff->Release();
  839. }
  840. return hr;
  841. }
  842. STDAPI CComputerFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  843. {
  844. *ppv = NULL;
  845. IFindFilter *pff;
  846. HRESULT hr = CreateDefaultComputerFindFilter(&pff);
  847. if (pff)
  848. {
  849. hr = CreateFindWithFilter(pff, riid, ppv);
  850. pff->Release();
  851. }
  852. return hr;
  853. }
  854. HRESULT CFindFolder::MapToSearchIDList(LPCITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl)
  855. {
  856. *ppidl = NULL;
  857. LPITEMIDLIST pidlParent;
  858. LPCITEMIDLIST pidlChild;
  859. if (SUCCEEDED(SplitIDList(pidl, &pidlParent, &pidlChild)))
  860. {
  861. EnterCriticalSection(&_csSearch);
  862. // loop through our DPA list and see if we can find a matach
  863. for (int i = 0; i < DPA_GetPtrCount(_hdpaPidf); i++)
  864. {
  865. FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
  866. if (pffli && ILIsEqual(pidlParent, &pffli->idl))
  867. {
  868. // We found the right one
  869. // so no lets transform the ID into one of our own
  870. // to return. Note: we must catch the case where the
  871. // original one passed in was a simple pidl and do
  872. // the appropriate thing.
  873. //
  874. LPITEMIDLIST pidlToFree = NULL; // might need to cleanup in one case
  875. // If this is not a FS folder, just clone it.
  876. IShellFolder *psf;
  877. if (fMapToReal && SUCCEEDED(_Folder(pffli, IID_PPV_ARG(IShellFolder, &psf))))
  878. {
  879. if (SUCCEEDED(SHGetRealIDL(psf, pidlChild, &pidlToFree)))
  880. {
  881. pidlChild = pidlToFree; // use this below...
  882. }
  883. psf->Release();
  884. }
  885. // create the doc find version of the pidl witht the
  886. // extra hidden items embedded
  887. AddDataToIDList(pidlChild, i, pidlParent, DFDF_NONE, 0, 0, 0, ppidl);
  888. ILFree(pidlToFree); // may be NULL
  889. break; // done with this loop
  890. }
  891. }
  892. LeaveCriticalSection(&_csSearch);
  893. ILFree(pidlParent);
  894. }
  895. return *ppidl ? S_OK : S_FALSE;
  896. }
  897. // Called before saving folder list. Needed especially in Asynch search,
  898. // (CI). We lazily pull item data from RowSet only when list view asks
  899. // for it. When we are leaving the search folder, we pull all items
  900. // creating all necessary folder lists. This ensure when saving folder
  901. // list, all are included.
  902. // remark : Fix bug#338714.
  903. HRESULT CFindFolder::_UpdateItemList()
  904. {
  905. USHORT cb = 0;
  906. int cItems;
  907. if (SUCCEEDED(GetItemCount(&cItems)))
  908. {
  909. for (int i = 0; i < cItems; i++)
  910. {
  911. FIND_ITEM *pesfi;
  912. if (DB_S_ENDOFROWSET == GetItem(i, &pesfi))
  913. break;
  914. }
  915. }
  916. return S_OK;
  917. }
  918. // IFindFolder
  919. HRESULT CFindFolder::SaveFolderList(IStream *pstm)
  920. {
  921. // We First pull all the items from RowSet (in Asynch case)
  922. _UpdateItemList();
  923. EnterCriticalSection(&_csSearch);
  924. // Now loop through our DPA list and see if we can find a matach
  925. for (int i = 0; i < DPA_GetPtrCount(_hdpaPidf); i++)
  926. {
  927. FIND_FOLDER_ITEM *pffli = _FolderListItem(i);
  928. if (EVAL(pffli))
  929. ILSaveToStream(pstm, &pffli->idl);
  930. else
  931. break;
  932. }
  933. LeaveCriticalSection(&_csSearch);
  934. // Now out a zero size item..
  935. USHORT cb = 0;
  936. pstm->Write(&cb, sizeof(cb), NULL);
  937. return TRUE;
  938. }
  939. // IFindFolder, Restore results out to file.
  940. HRESULT CFindFolder::RestoreFolderList(IStream *pstm)
  941. {
  942. // loop through and all all of the folders to our list...
  943. LPITEMIDLIST pidl = NULL;
  944. HRESULT hr;
  945. for (;;)
  946. {
  947. hr = ILLoadFromStream(pstm, &pidl); // frees [in,out] pidl for us
  948. if (pidl == NULL)
  949. break; // end of the list
  950. else
  951. {
  952. int i;
  953. AddFolder(pidl, FALSE, &i);
  954. }
  955. }
  956. ILFree(pidl); // don't forget to free last pidl
  957. return hr;
  958. }
  959. HRESULT CFindFolder::SaveItemList(IStream *pstm)
  960. {
  961. // We First serialize all of our PIDLS for each item in our list
  962. int cItems;
  963. if (SUCCEEDED(GetItemCount(&cItems)))
  964. {
  965. // And Save the items that are in the list
  966. for (int i = 0; i < cItems; i++)
  967. {
  968. FIND_ITEM *pesfi;
  969. HRESULT hr = GetItem(i, &pesfi);
  970. if (hr == DB_S_ENDOFROWSET)
  971. break;
  972. if (SUCCEEDED(hr) && pesfi)
  973. ILSaveToStream(pstm, &pesfi->idl);
  974. }
  975. }
  976. USHORT cb = 0;
  977. pstm->Write(&cb, sizeof(cb), NULL); // a Trailing NULL size to say end of pidl list...
  978. return S_OK;
  979. }
  980. HRESULT CFindFolder::RestoreItemList(IStream *pstm, int *pcItems)
  981. {
  982. // And the pidls that are associated with the object
  983. int cItems = 0;
  984. LPITEMIDLIST pidl = NULL; // don't free previous one
  985. FIND_ITEM *pesfi;
  986. for (;;)
  987. {
  988. if (FAILED(ILLoadFromStream(pstm, &pidl)) || (pidl == NULL))
  989. break;
  990. if (FAILED(AddPidl(cItems, pidl, (UINT)-1, &pesfi)) || !pesfi)
  991. break;
  992. cItems++;
  993. }
  994. ILFree(pidl); // Free the last one read in
  995. *pcItems = cItems;
  996. return S_OK;
  997. }
  998. HRESULT CFindFolder::_GetFolderIDList(int iFolder, LPITEMIDLIST *ppidlParent)
  999. {
  1000. *ppidlParent = NULL;
  1001. HRESULT hr = E_FAIL;
  1002. EnterCriticalSection(&_csSearch);
  1003. FIND_FOLDER_ITEM *pffli = _FolderListItem(iFolder);
  1004. if (pffli)
  1005. hr = SHILClone(&pffli->idl, ppidlParent);
  1006. LeaveCriticalSection(&_csSearch);
  1007. return hr;
  1008. }
  1009. HRESULT CFindFolder::GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent)
  1010. {
  1011. return _GetFolderIDList(GetFolderIndex(pidl), ppidlParent);
  1012. }
  1013. HRESULT CFindFolder::SetControllerNotifyObject(IFindControllerNotify *pfcn)
  1014. {
  1015. IUnknown_Set((IUnknown **)&_pfcn, pfcn);
  1016. return S_OK;
  1017. }
  1018. HRESULT CFindFolder::GetControllerNotifyObject(IFindControllerNotify **ppfcn)
  1019. {
  1020. *ppfcn = _pfcn;
  1021. if (_pfcn)
  1022. _pfcn->AddRef();
  1023. return _pfcn ? S_OK : S_FALSE;
  1024. }
  1025. STDMETHODIMP_(ULONG) CFindFolder::AddRef()
  1026. {
  1027. return InterlockedIncrement(&_cRef);
  1028. }
  1029. STDMETHODIMP_(ULONG) CFindFolder::Release()
  1030. {
  1031. if (InterlockedDecrement(&_cRef))
  1032. return _cRef;
  1033. delete this;
  1034. return 0;
  1035. }
  1036. STDMETHODIMP CFindFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwzDisplayName,
  1037. ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
  1038. {
  1039. return E_NOTIMPL;
  1040. }
  1041. STDMETHODIMP CFindFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
  1042. {
  1043. // We do not want the def view to enumerate us, instead we
  1044. // will tell defview to call us...
  1045. *ppenum = NULL; // No enumerator
  1046. return S_FALSE; // no enumerator (not error)
  1047. }
  1048. STDMETHODIMP CFindFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  1049. {
  1050. IShellFolder *psf;
  1051. HRESULT hr = _QueryItemShellFolder(pidl, &psf);
  1052. if (SUCCEEDED(hr))
  1053. {
  1054. hr = psf->BindToObject(pidl, pbc, riid, ppv);
  1055. psf->Release();
  1056. }
  1057. return hr;
  1058. }
  1059. // Little helper function for bellow
  1060. HRESULT CFindFolder::_CompareFolderIndexes(int iFolder1, int iFolder2)
  1061. {
  1062. HRESULT hr = E_INVALIDARG;
  1063. EnterCriticalSection(&_csSearch);
  1064. FIND_FOLDER_ITEM *pffli1 = _FolderListItem(iFolder1);
  1065. FIND_FOLDER_ITEM *pffli2 = _FolderListItem(iFolder2);
  1066. if (pffli1 && pffli2)
  1067. {
  1068. // Check our 1-level deep cache. Since its is common for there to be multiple
  1069. // items in the same folder, during a sort operation, we often compare the
  1070. // same two folders repeatedly.
  1071. if ((_iCompareFolderCache1 != iFolder1) || (_iCompareFolderCache2 != iFolder2))
  1072. {
  1073. TCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
  1074. SHGetPathFromIDList(&pffli1->idl, szPath1);
  1075. SHGetPathFromIDList(&pffli2->idl, szPath2);
  1076. _iCompareFolderCacheResult = lstrcmpi(szPath1, szPath2);
  1077. _iCompareFolderCache1 = iFolder1;
  1078. _iCompareFolderCache2 = iFolder2;
  1079. }
  1080. hr = ResultFromShort(_iCompareFolderCacheResult);
  1081. }
  1082. LeaveCriticalSection(&_csSearch);
  1083. return hr;
  1084. }
  1085. PCHIDDENDOCFINDDATA CFindFolder::_HiddenData(LPCITEMIDLIST pidl)
  1086. {
  1087. return (PCHIDDENDOCFINDDATA)ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
  1088. }
  1089. UINT CFindFolder::GetFolderIndex(LPCITEMIDLIST pidl)
  1090. {
  1091. PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA)ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
  1092. return phdfd ? phdfd->iFolder : -1;
  1093. }
  1094. FIND_FOLDER_ITEM *CFindFolder::_FolderListItem(int iFolder)
  1095. {
  1096. return (FIND_FOLDER_ITEM *)DPA_GetPtr(_hdpaPidf, iFolder);
  1097. }
  1098. FIND_FOLDER_ITEM *CFindFolder::_FolderListItem(LPCITEMIDLIST pidl)
  1099. {
  1100. return _FolderListItem(GetFolderIndex(pidl));
  1101. }
  1102. ULONG CFindFolder::_Rank(LPCITEMIDLIST pidl)
  1103. {
  1104. PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl);
  1105. // Could be mixed if so put ones without rank at the end...
  1106. return phdfd && (phdfd->wFlags & DFDF_EXTRADATA) ? phdfd->ulRank : 0;
  1107. }
  1108. DWORD CFindFolder::_ItemID(LPCITEMIDLIST pidl)
  1109. {
  1110. PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl);
  1111. return phdfd && (phdfd->wFlags & DFDF_EXTRADATA) ? phdfd->dwItemID : -1;
  1112. }
  1113. HRESULT CFindFolder::_GetItemDisplayName(LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR wzName, UINT cch)
  1114. {
  1115. // Get the IShellFolder:
  1116. IShellFolder *psf;
  1117. HRESULT hr = _QueryItemShellFolder(pidl, &psf);
  1118. if (SUCCEEDED(hr))
  1119. {
  1120. // Get the display name:
  1121. hr = DisplayNameOf(psf, pidl, dwFlags, wzName, cch);
  1122. psf->Release();
  1123. }
  1124. return hr;
  1125. }
  1126. // Given the 2 pidls, we extract the display name using DisplayNameOf and then
  1127. // if all goes well, we compare the two.
  1128. int CFindFolder::_CompareNames(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwFlags)
  1129. {
  1130. int iRetVal = 0;
  1131. WCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  1132. // Get the name for 1
  1133. HRESULT hr = _GetItemDisplayName(pidl1, dwFlags, szName1, ARRAYSIZE(szName1));
  1134. if (SUCCEEDED(hr))
  1135. {
  1136. // Get the name for 2
  1137. hr = _GetItemDisplayName(pidl2, dwFlags, szName2, ARRAYSIZE(szName2));
  1138. if (SUCCEEDED(hr))
  1139. {
  1140. // Compare and set value
  1141. iRetVal = StrCmpLogicalW(szName1, szName2);
  1142. }
  1143. }
  1144. return iRetVal;
  1145. }
  1146. int CFindFolder::_CompareByCachedSCID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1147. {
  1148. int iRetVal = 0;
  1149. // If sort on name, we will skip this and use the code below.
  1150. if (!IsEqualSCID(_scidCached, SCID_NAME))
  1151. {
  1152. iRetVal = CompareBySCID(this, &_scidCached, pidl1, pidl2);
  1153. }
  1154. // If they are still the same, sort them alphabetically by the name:
  1155. // When we want to sort by name (either becuase we are sorting the
  1156. // name column, or because 2 items are identical in other regards) we
  1157. // want to display name vs the GetDetailsOf name for 2 reasons:
  1158. // 1. Some folders like IE's History don't support GetDetailsEx.
  1159. // 2. Recycle bin returns the file name, not the displayable name;
  1160. // so we would end up with "DC###..." instead of "New Folder".
  1161. if (iRetVal == 0)
  1162. {
  1163. iRetVal = _CompareNames(pidl1, pidl2, SHGDN_INFOLDER | SHGDN_NORMAL);
  1164. if (iRetVal == 0) // the display names are the same, could they be in different folders?
  1165. {
  1166. iRetVal = _CompareNames(pidl1, pidl2, SHGDN_FORPARSING);
  1167. }
  1168. }
  1169. return iRetVal;
  1170. }
  1171. STDMETHODIMP CFindFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1172. {
  1173. HRESULT hr = E_INVALIDARG;
  1174. ASSERT(pidl1 == ILFindLastID(pidl1));
  1175. UINT iInputColumn = ((DWORD)lParam & SHCIDS_COLUMNMASK);
  1176. UINT iMappedColumn = iInputColumn;
  1177. if (_MapColIndex(&iMappedColumn))
  1178. {
  1179. if (IDFCOL_PATH == iMappedColumn)
  1180. {
  1181. UINT iFolder1 = GetFolderIndex(pidl1);
  1182. UINT iFolder2 = GetFolderIndex(pidl2);
  1183. if (iFolder1 != iFolder2)
  1184. return _CompareFolderIndexes(iFolder1, iFolder2);
  1185. }
  1186. else
  1187. {
  1188. ASSERT(iMappedColumn == IDFCOL_RANK);
  1189. ULONG ulRank1 = _Rank(pidl1);
  1190. ULONG ulRank2 = _Rank(pidl2);
  1191. if (ulRank1 < ulRank2)
  1192. return ResultFromShort(-1);
  1193. if (ulRank1 > ulRank2)
  1194. return ResultFromShort(1);
  1195. }
  1196. }
  1197. // Check the SCID cache and update it if necessary.
  1198. if (_uiColumnCached != iInputColumn)
  1199. {
  1200. hr = MapColumnToSCID(iInputColumn, &_scidCached);
  1201. if (SUCCEEDED(hr))
  1202. {
  1203. _uiColumnCached = iInputColumn;
  1204. }
  1205. }
  1206. // Check if one is a folder and not the other. put folders before files.
  1207. int iRes = CompareFolderness(this, pidl1, pidl2);
  1208. if (iRes == 0)
  1209. {
  1210. iRes = _CompareByCachedSCID(pidl1, pidl2);
  1211. }
  1212. return ResultFromShort(iRes);
  1213. }
  1214. STDMETHODIMP CFindFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  1215. {
  1216. *ppv = NULL;
  1217. HRESULT hr = E_NOINTERFACE;
  1218. if (IsEqualIID(riid, IID_IShellView))
  1219. {
  1220. IShellFolderViewCB* psfvcb = new CFindFolderViewCB(this);
  1221. if (psfvcb)
  1222. {
  1223. SFV_CREATE sSFV = {0};
  1224. sSFV.cbSize = sizeof(sSFV);
  1225. sSFV.pshf = this;
  1226. sSFV.psfvcb = psfvcb;
  1227. hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
  1228. psfvcb->Release();
  1229. }
  1230. else
  1231. {
  1232. hr = E_OUTOFMEMORY;
  1233. }
  1234. }
  1235. else if (IsEqualIID(riid, IID_IContextMenu))
  1236. {
  1237. IContextMenuCB *pcmcb = new CFindMenuCB();
  1238. if (pcmcb)
  1239. {
  1240. hr = CDefFolderMenu_CreateEx(NULL, hwnd,
  1241. 0, NULL, this, pcmcb, NULL, NULL, (IContextMenu * *)ppv);
  1242. pcmcb->Release();
  1243. }
  1244. else
  1245. hr = E_OUTOFMEMORY;
  1246. }
  1247. return hr;
  1248. }
  1249. STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
  1250. {
  1251. HRESULT hr;
  1252. if (cidl == 0)
  1253. {
  1254. // defview asks to see if any items can be renamed this way, lame
  1255. *prgfInOut = SFGAO_CANRENAME;
  1256. hr = S_OK;
  1257. }
  1258. else
  1259. {
  1260. ASSERT(*apidl == ILFindLastID(*apidl))
  1261. IShellFolder *psf;
  1262. hr = _QueryItemShellFolder(apidl[0], &psf);
  1263. if (SUCCEEDED(hr))
  1264. {
  1265. hr = psf->GetAttributesOf(cidl, apidl, prgfInOut);
  1266. psf->Release();
  1267. }
  1268. }
  1269. return hr;
  1270. }
  1271. //
  1272. // To be called back from within CDefFolderMenuE - Currently only used
  1273. //
  1274. // Some helper functions
  1275. STDMETHODIMP CFindFolder::SetItemsChangedSinceSort()
  1276. {
  1277. _fItemsChangedSinceSort = TRUE;
  1278. _iCompareFolderCache1 = -1; // an invalid folder index value
  1279. return S_OK;
  1280. }
  1281. STDMETHODIMP CFindFolder::GetItemCount(INT *pcItems)
  1282. {
  1283. ASSERT(pcItems);
  1284. DBCOUNTITEM cItems = 0;
  1285. EnterCriticalSection(&_csSearch);
  1286. if (_hdpaItems)
  1287. cItems = DPA_GetPtrCount(_hdpaItems);
  1288. LeaveCriticalSection(&_csSearch);
  1289. // If async, then we may not have grown our dpa yet... but in mixed case we have so take
  1290. // max of the two...
  1291. if (_pDFEnumAsync)
  1292. {
  1293. if (_cAsyncItems > cItems)
  1294. cItems = _cAsyncItems;
  1295. }
  1296. *pcItems = SANE_ITEMCOUNT(cItems);
  1297. return S_OK;
  1298. };
  1299. STDMETHODIMP CFindFolder::GetItem(int iItem, FIND_ITEM **ppItem)
  1300. {
  1301. HRESULT hr = E_FAIL; // just to init, use anything
  1302. FIND_ITEM *pesfi;
  1303. IFindEnum *pidfenum;
  1304. GetAsyncEnum(&pidfenum);
  1305. DWORD dwItemID = (UINT)-1;
  1306. EnterCriticalSection(&_csSearch);
  1307. int i = DPA_GetPtrCount(_hdpaItems);
  1308. pesfi = (FIND_ITEM *) DPA_GetPtr(_hdpaItems, iItem);
  1309. LeaveCriticalSection(&_csSearch);
  1310. // Mondo hack to better handle Async searching (ROWSET), we are not sure if we
  1311. // can trust the PIDL of the row as new rows may have been inserted...
  1312. // Only do this if we are not looking at the previous item..
  1313. if (pesfi && pidfenum && !_fSearchComplete && (iItem != _iGetIDList))
  1314. {
  1315. PCHIDDENDOCFINDDATA phdfd = _HiddenData(&pesfi->idl);
  1316. // As we can now have mixed results only blow away if this is an async guy...
  1317. if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA))
  1318. {
  1319. pidfenum->GetItemID(iItem, &dwItemID);
  1320. if (dwItemID != phdfd->dwItemID)
  1321. {
  1322. // Overload, pass NULL to ADDPIDL to tell system to free that item
  1323. if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED))
  1324. _AddFIND_ITEMToSaveStateList(pesfi);
  1325. AddPidl(iItem, 0, NULL, NULL);
  1326. pesfi = NULL;
  1327. }
  1328. }
  1329. }
  1330. _iGetIDList = iItem; // remember the last one we retrieved...
  1331. if (!pesfi && (iItem >= 0))
  1332. {
  1333. // See if this is the async case
  1334. if (pidfenum)
  1335. {
  1336. LPITEMIDLIST pidlT;
  1337. hr = pidfenum->GetItemIDList(SANE_ITEMCOUNT(iItem), &pidlT);
  1338. if (SUCCEEDED(hr) && hr != DB_S_ENDOFROWSET)
  1339. {
  1340. AddPidl(iItem, pidlT, dwItemID, &pesfi);
  1341. // See if this item should show up as selected...
  1342. if (dwItemID == (UINT)-1)
  1343. pidfenum->GetItemID(iItem, &dwItemID);
  1344. GetStateFromSaveStateList(dwItemID, &pesfi->dwState);
  1345. }
  1346. }
  1347. }
  1348. *ppItem = pesfi;
  1349. if (hr != DB_S_ENDOFROWSET)
  1350. hr = pesfi ? S_OK : E_FAIL;
  1351. return hr;
  1352. }
  1353. STDMETHODIMP CFindFolder::DeleteItem(int iItem)
  1354. {
  1355. HRESULT hr = E_FAIL;
  1356. if (!_fInRefresh)
  1357. {
  1358. FIND_ITEM *pesfi;
  1359. hr = E_INVALIDARG;
  1360. // make sure the item is in dpa (if using cI)
  1361. if (SUCCEEDED(GetItem(iItem, &pesfi)) && pesfi)
  1362. {
  1363. EnterCriticalSection(&_csSearch);
  1364. DPA_DeletePtr(_hdpaItems, iItem);
  1365. LeaveCriticalSection(&_csSearch);
  1366. PCHIDDENDOCFINDDATA phdfd = _HiddenData(&pesfi->idl);
  1367. if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA))
  1368. {
  1369. //we are deleting async item...
  1370. _cAsyncItems--;
  1371. }
  1372. if (pesfi->dwState &= LVIS_SELECTED)
  1373. {
  1374. // Need to update the count of items selected...
  1375. _dflvrSel.DecrementIncludedCount();
  1376. }
  1377. LocalFree((HLOCAL)pesfi);
  1378. hr = S_OK;
  1379. }
  1380. }
  1381. return hr;
  1382. }
  1383. // evil window crawling code to get the listview from defview
  1384. HWND ListviewFromView(HWND hwnd)
  1385. {
  1386. HWND hwndLV;
  1387. do
  1388. {
  1389. hwndLV = FindWindowEx(hwnd, NULL, WC_LISTVIEW, NULL);
  1390. }
  1391. while ((hwndLV == NULL) && (hwnd = GetWindow(hwnd, GW_CHILD)));
  1392. return hwndLV;
  1393. }
  1394. HWND ListviewFromViewUnk(IUnknown *punkView)
  1395. {
  1396. HWND hwnd;
  1397. if (SUCCEEDED(IUnknown_GetWindow(punkView, &hwnd)))
  1398. {
  1399. hwnd = ListviewFromView(hwnd);
  1400. }
  1401. return hwnd;
  1402. }
  1403. STDMETHODIMP CFindFolder::ValidateItems(IUnknown *punkView, int iItem, int cItems, BOOL bSearchComplete)
  1404. {
  1405. IFindEnum *pidfenum;
  1406. if (S_OK != GetAsyncEnum(&pidfenum) || _fAllAsyncItemsCached)
  1407. return S_OK; // nothing to validate.
  1408. DWORD dwItemID = (UINT)-1;
  1409. int cItemsInList;
  1410. GetItemCount(&cItemsInList);
  1411. // force reload of rows
  1412. pidfenum->Reset();
  1413. HWND hwndLV = ListviewFromViewUnk(punkView);
  1414. int iLVFirst = ListView_GetTopIndex(hwndLV);
  1415. int cLVItems = ListView_GetCountPerPage(hwndLV);
  1416. if (iItem == -1)
  1417. {
  1418. iItem = iLVFirst;
  1419. cItems = cLVItems;
  1420. }
  1421. // to avoid failing to update an item...
  1422. if (bSearchComplete)
  1423. _iGetIDList = -1;
  1424. while ((iItem < cItemsInList) && cItems)
  1425. {
  1426. EnterCriticalSection(&_csSearch);
  1427. FIND_ITEM *pesfi = (FIND_ITEM *) DPA_GetPtr(_hdpaItems, iItem);
  1428. LeaveCriticalSection(&_csSearch);
  1429. if (!pesfi) // Assume that if we have not gotten this one we are in the clear...
  1430. break;
  1431. PCHIDDENDOCFINDDATA phdfd = _HiddenData(&pesfi->idl);
  1432. if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA))
  1433. {
  1434. pidfenum->GetItemID(iItem, &dwItemID);
  1435. if (dwItemID != _ItemID(&pesfi->idl))
  1436. {
  1437. FIND_ITEM *pItem; // dummy to make GetItem happy
  1438. // Oops don't match,
  1439. if (InRange(iItem, iLVFirst, iLVFirst+cLVItems))
  1440. {
  1441. if (SUCCEEDED(GetItem(iItem, &pItem)))
  1442. {
  1443. ListView_RedrawItems(hwndLV, iItem, iItem);
  1444. }
  1445. }
  1446. else
  1447. {
  1448. AddPidl(iItem, NULL, 0, NULL);
  1449. }
  1450. }
  1451. }
  1452. else
  1453. {
  1454. break; // stop after we reach first non ci item
  1455. }
  1456. iItem++;
  1457. cItems--;
  1458. }
  1459. _fSearchComplete = bSearchComplete;
  1460. return S_OK;
  1461. }
  1462. STDMETHODIMP CFindFolder::AddPidl(int i, LPCITEMIDLIST pidl, DWORD dwItemID, FIND_ITEM **ppcdfi)
  1463. {
  1464. HRESULT hr = S_OK;
  1465. ASSERT(GetCurrentThreadId() == _GUIThreadID);
  1466. if (NULL == pidl)
  1467. {
  1468. EnterCriticalSection(&_csSearch);
  1469. FIND_ITEM* pesfi = (FIND_ITEM*)DPA_GetPtr(_hdpaItems, i);
  1470. if (pesfi)
  1471. {
  1472. LocalFree((HLOCAL)pesfi);
  1473. DPA_SetPtr(_hdpaItems, i, NULL);
  1474. }
  1475. LeaveCriticalSection(&_csSearch);
  1476. if (ppcdfi)
  1477. *ppcdfi = NULL;
  1478. }
  1479. else
  1480. {
  1481. int cb = ILGetSize(pidl);
  1482. FIND_ITEM *pesfi;
  1483. hr = SHLocalAlloc(sizeof(*pesfi) - sizeof(pesfi->idl) + cb, &pesfi);
  1484. if (SUCCEEDED(hr))
  1485. {
  1486. // pesfi->dwMask = 0;
  1487. // pesfi->dwState = 0;
  1488. pesfi->iIcon = -1;
  1489. memcpy(&pesfi->idl, pidl, cb);
  1490. EnterCriticalSection(&_csSearch);
  1491. BOOL bRet = DPA_SetPtr(_hdpaItems, i, (void *)pesfi);
  1492. LeaveCriticalSection(&_csSearch);
  1493. if (bRet)
  1494. {
  1495. if (ppcdfi)
  1496. *ppcdfi = pesfi;
  1497. }
  1498. else
  1499. {
  1500. LocalFree((HLOCAL)pesfi);
  1501. pesfi = NULL;
  1502. hr = E_OUTOFMEMORY;
  1503. }
  1504. }
  1505. }
  1506. return hr;
  1507. }
  1508. STDMETHODIMP CFindFolder::SetAsyncEnum(IFindEnum *pdfEnumAsync)
  1509. {
  1510. if (_pDFEnumAsync)
  1511. _pDFEnumAsync->Release();
  1512. _pDFEnumAsync = pdfEnumAsync;
  1513. if (pdfEnumAsync)
  1514. pdfEnumAsync->AddRef();
  1515. return S_OK;
  1516. }
  1517. STDMETHODIMP CFindFolder::CacheAllAsyncItems()
  1518. {
  1519. if (_fAllAsyncItemsCached)
  1520. return S_OK; // Allready done it...
  1521. IFindEnum *pidfenum;
  1522. if (S_OK != GetAsyncEnum(&pidfenum))
  1523. return S_FALSE; // nothing to do...
  1524. // Probably the easiest thing to do is to simply walk through all of the items...
  1525. int maxItems = SANE_ITEMCOUNT(_cAsyncItems);
  1526. for (int i = 0; i < maxItems; i++)
  1527. {
  1528. FIND_ITEM *pesfi;
  1529. GetItem(i, &pesfi);
  1530. }
  1531. _fAllAsyncItemsCached = TRUE;
  1532. return S_OK;
  1533. }
  1534. BOOL CFindFolder::AllAsyncItemsCached()
  1535. {
  1536. return _fAllAsyncItemsCached;
  1537. }
  1538. STDMETHODIMP CFindFolder::GetAsyncEnum(IFindEnum **ppdfEnumAsync)
  1539. {
  1540. *ppdfEnumAsync = _pDFEnumAsync; // unreferecned!
  1541. return *ppdfEnumAsync ? S_OK : S_FALSE;
  1542. }
  1543. STDMETHODIMP CFindFolder::SetAsyncCount(DBCOUNTITEM cCount)
  1544. {
  1545. _cAsyncItems = cCount;
  1546. _fAllAsyncItemsCached = FALSE;
  1547. return S_OK;
  1548. }
  1549. STDMETHODIMP CFindFolder::ClearSaveStateList()
  1550. {
  1551. DSA_DeleteAllItems(_hdsaSaveStateForIDs);
  1552. _cSaveStateSelected = 0;
  1553. return S_OK;
  1554. }
  1555. STDMETHODIMP CFindFolder::GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState)
  1556. {
  1557. for (int i = DSA_GetItemCount(_hdsaSaveStateForIDs); i-- > 0;)
  1558. {
  1559. // Pidl at start of structure...
  1560. FIND_ITEM_SAVE_STATE *pessi = (FIND_ITEM_SAVE_STATE*)DSA_GetItemPtr(_hdsaSaveStateForIDs, i);
  1561. if (pessi->dwItemID == dwItemID)
  1562. {
  1563. *pdwState = pessi->dwState;
  1564. if (pessi->dwState & LVIS_SELECTED)
  1565. {
  1566. // Remember the counts of items that we have touched...
  1567. _dflvrSel.IncrementIncludedCount();
  1568. _cSaveStateSelected--;
  1569. }
  1570. // Any items we retrieve we can get rid of...
  1571. DSA_DeleteItem(_hdsaSaveStateForIDs, i);
  1572. return S_OK;
  1573. }
  1574. }
  1575. return S_FALSE;
  1576. }
  1577. STDMETHODIMP CFindFolder::GetFolderListItemCount(INT *pcItemCount)
  1578. {
  1579. *pcItemCount = 0;
  1580. EnterCriticalSection(&_csSearch);
  1581. if (_hdpaPidf)
  1582. *pcItemCount = DPA_GetPtrCount(_hdpaPidf);
  1583. LeaveCriticalSection(&_csSearch);
  1584. return S_OK;
  1585. }
  1586. STDMETHODIMP CFindFolder::GetFolderListItem(int iItem, FIND_FOLDER_ITEM **ppdffi)
  1587. {
  1588. EnterCriticalSection(&_csSearch);
  1589. *ppdffi = (FIND_FOLDER_ITEM *)DPA_GetPtr(_hdpaPidf, iItem);
  1590. LeaveCriticalSection(&_csSearch);
  1591. return *ppdffi ? S_OK : E_FAIL;
  1592. }
  1593. class CFindMenuWrap : public CContextMenuForwarder
  1594. {
  1595. public:
  1596. // IContextMenu overrides
  1597. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
  1598. protected:
  1599. CFindMenuWrap(IDataObject* pdo, IContextMenu* pcmArray);
  1600. ~CFindMenuWrap();
  1601. friend HRESULT DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2, REFIID riid, void** ppv);
  1602. private:
  1603. IDataObject * _pdtobj;
  1604. };
  1605. CFindMenuWrap::CFindMenuWrap(IDataObject* pdo, IContextMenu* pcmArray) : CContextMenuForwarder(pcmArray)
  1606. {
  1607. _pdtobj = pdo;
  1608. _pdtobj->AddRef();
  1609. }
  1610. CFindMenuWrap::~CFindMenuWrap()
  1611. {
  1612. _pdtobj->Release();
  1613. }
  1614. STDMETHODIMP CFindMenuWrap::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  1615. {
  1616. BOOL fIsLink = FALSE;
  1617. // "link" has to create a link on the desktop, not in the folder (since we're not a real folder...)
  1618. if (IS_INTRESOURCE(lpici->lpVerb))
  1619. {
  1620. WCHAR szCommandString[64];
  1621. if (SUCCEEDED(ContextMenu_GetCommandStringVerb(_pcm, LOWORD((UINT_PTR)lpici->lpVerb), szCommandString, ARRAYSIZE(szCommandString))))
  1622. {
  1623. fIsLink = !StrCmpIW(szCommandString, L"link");
  1624. }
  1625. }
  1626. else
  1627. {
  1628. fIsLink = !StrCmpIA(lpici->lpVerb, "link");
  1629. }
  1630. if (fIsLink)
  1631. {
  1632. // Note: old code used to check pdtobj, but we don't create this
  1633. // object unless we get one of them, so why check?
  1634. ASSERT(_pdtobj);
  1635. return SHCreateLinks(lpici->hwnd, NULL, _pdtobj,
  1636. SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL);
  1637. }
  1638. return CContextMenuForwarder::InvokeCommand(lpici);
  1639. }
  1640. HRESULT DFWrapIContextMenu(HWND hwnd, IShellFolder *psf, LPCITEMIDLIST pidl,
  1641. IContextMenu* pcmExtra, void **ppvInOut)
  1642. {
  1643. IContextMenu *pcmWrap = NULL;
  1644. IContextMenu *pcmFree = (IContextMenu*)*ppvInOut;
  1645. IDataObject* pdo;
  1646. HRESULT hr = psf->GetUIObjectOf(hwnd, 1, &pidl, IID_X_PPV_ARG(IDataObject, NULL, &pdo));
  1647. if (SUCCEEDED(hr))
  1648. {
  1649. hr = DFWrapIContextMenus(pdo, pcmFree, pcmExtra, IID_PPV_ARG(IContextMenu, &pcmWrap));
  1650. pdo->Release();
  1651. }
  1652. pcmFree->Release();
  1653. *ppvInOut = pcmWrap;
  1654. return hr;
  1655. }
  1656. HRESULT DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2, REFIID riid, void** ppv)
  1657. {
  1658. *ppv = NULL;
  1659. IContextMenu * pcmArray;
  1660. IContextMenu* rgpcm[2] = {pcm2, pcm1};
  1661. HRESULT hr = Create_ContextMenuOnContextMenuArray(rgpcm, ARRAYSIZE(rgpcm), IID_PPV_ARG(IContextMenu, &pcmArray));
  1662. if (SUCCEEDED(hr))
  1663. {
  1664. CFindMenuWrap * p = new CFindMenuWrap(pdo, pcmArray);
  1665. if (p)
  1666. {
  1667. hr = p->QueryInterface(riid, ppv);
  1668. p->Release();
  1669. }
  1670. else
  1671. {
  1672. hr = E_OUTOFMEMORY;
  1673. }
  1674. pcmArray->Release();
  1675. }
  1676. return hr;
  1677. }
  1678. STDMETHODIMP CFindFolder::QueryInterface(REFIID riid, void **ppv)
  1679. {
  1680. static const QITAB qit[] = {
  1681. QITABENT(CFindFolder, IShellFolder2), //IID_ISHELLFolder2
  1682. QITABENTMULTI(CFindFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
  1683. QITABENT(CFindFolder, IFindFolder), //IID_IFindFolder
  1684. QITABENT(CFindFolder, IShellIcon), //IID_IShellIcon
  1685. QITABENT(CFindFolder, IPersistFolder2), //IID_IPersistFolder2
  1686. QITABENTMULTI(CFindFolder, IPersistFolder, IPersistFolder2), //IID_IPersistFolder
  1687. QITABENTMULTI(CFindFolder, IPersist, IPersistFolder2), //IID_IPersist
  1688. QITABENT(CFindFolder, IShellIconOverlay), //IID_IShellIconOverlay
  1689. { 0 },
  1690. };
  1691. return QISearch(this, qit, riid, ppv);
  1692. }
  1693. // IPersistFolder2 implementation
  1694. STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassID)
  1695. {
  1696. *pClassID = CLSID_DocFindFolder;
  1697. return S_OK;
  1698. }
  1699. STDMETHODIMP CFindFolder::Initialize(LPCITEMIDLIST pidl)
  1700. {
  1701. if (_pidl)
  1702. ILFree(_pidl);
  1703. return SHILClone(pidl, &_pidl);
  1704. }
  1705. STDMETHODIMP CFindFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  1706. {
  1707. return GetCurFolderImpl(_pidl, ppidl);
  1708. }
  1709. // helper function to sort the selected ID list by something that
  1710. // makes file operations work reasonably OK, when both an object and it's
  1711. // parent is in the list...
  1712. //
  1713. int CALLBACK CFindFolder::_SortForDataObj(void *p1, void *p2, LPARAM lparam)
  1714. {
  1715. // Since I do recursion, If I get the Folder index number from the
  1716. // last element of each and sort by them such that the higher numbers
  1717. // come first, should solve the problem fine...
  1718. LPITEMIDLIST pidl1 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)p1);
  1719. LPITEMIDLIST pidl2 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)p2);
  1720. CFindFolder *pff = (CFindFolder *)lparam;
  1721. return pff->GetFolderIndex(pidl2) - pff->GetFolderIndex(pidl1);
  1722. }
  1723. LPITEMIDLIST CFindFolder::_GetFullPidlForItem(LPCITEMIDLIST pidl)
  1724. {
  1725. LPITEMIDLIST pidlRet = NULL;
  1726. LPITEMIDLIST pidlParent;
  1727. if (S_OK == GetParentsPIDL(pidl, &pidlParent))
  1728. {
  1729. pidlRet = ILCombine(pidlParent, pidl);
  1730. ILFree(pidlParent);
  1731. }
  1732. return pidlRet;
  1733. }
  1734. // we generate a non flat HIDA. this is so clients that
  1735. // use this HIDA will bind to the folder that the results came
  1736. // from instead of this folder that has runtime state that won't
  1737. // be present if we rebind
  1738. HRESULT CFindFolder::_PrepareHIDA(UINT cidl, LPCITEMIDLIST * apidl, HDPA *phdpa)
  1739. {
  1740. HRESULT hr = E_OUTOFMEMORY;
  1741. *phdpa = DPA_Create(0);
  1742. if (*phdpa)
  1743. {
  1744. if (DPA_Grow(*phdpa, cidl))
  1745. {
  1746. for (UINT i = 0; i < cidl; i++)
  1747. {
  1748. LPITEMIDLIST pidl = _GetFullPidlForItem(apidl[i]);
  1749. if (pidl)
  1750. DPA_InsertPtr(*phdpa, i, pidl);
  1751. }
  1752. // In order to make file manipulation functions work properly we
  1753. // need to sort the elements to make sure if an element and one
  1754. // of it's parents are in the list, that the element comes
  1755. // before it's parents...
  1756. DPA_Sort(*phdpa, _SortForDataObj, (LPARAM)this);
  1757. hr = S_OK;
  1758. }
  1759. }
  1760. return hr;
  1761. }
  1762. STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
  1763. REFIID riid, UINT * prgfInOut, void **ppv)
  1764. {
  1765. HRESULT hr = E_INVALIDARG;
  1766. *ppv = NULL;
  1767. // if just one item we can deletate to real folder
  1768. if (cidl == 1)
  1769. {
  1770. // Note we may have been passed in a complex item so find the last
  1771. ASSERT(ILIsEmpty(_ILNext(*apidl))); // should be a single level PIDL!
  1772. IShellFolder *psf;
  1773. hr = _QueryItemShellFolder(apidl[0], &psf);
  1774. if (SUCCEEDED(hr))
  1775. {
  1776. hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
  1777. // if we are doing context menu, then we will wrap this
  1778. // interface in a wrapper object, that we can then pick
  1779. // off commands like link to process specially
  1780. if (SUCCEEDED(hr))
  1781. {
  1782. if (IsEqualIID(riid, IID_IContextMenu))
  1783. {
  1784. // we also let the net/file guy add in a context menu if they want to
  1785. IContextMenu* pcmExtra = NULL;
  1786. _pfilter->GetItemContextMenu(hwnd, SAFECAST(this, IFindFolder*), &pcmExtra);
  1787. hr = DFWrapIContextMenu(hwnd, psf, apidl[0], pcmExtra, ppv);
  1788. ATOMICRELEASE(pcmExtra);
  1789. }
  1790. else if (IsEqualIID(riid, IID_IQueryInfo)) // && SHGetAttributes(psf, apidl[0], SFGAO_FILESYSTEM))
  1791. {
  1792. WrapInfotip(SAFECAST(this, IShellFolder2 *), apidl[0], &SCID_DIRECTORY, (IUnknown *)*ppv);
  1793. }
  1794. }
  1795. psf->Release();
  1796. }
  1797. }
  1798. else if (cidl > 1)
  1799. {
  1800. if (IsEqualIID(riid, IID_IContextMenu))
  1801. {
  1802. // Try to create a menu object that we process ourself
  1803. // Yes, do context menu.
  1804. HKEY ahkeys[MAX_ASSOC_KEYS] = {0};
  1805. DWORD ckeys = 0;
  1806. LPITEMIDLIST pidlFull = _GetFullPidlForItem(apidl[0]);
  1807. if (pidlFull)
  1808. {
  1809. // Get the hkeyProgID and hkeyBaseProgID from the first item.
  1810. ckeys = SHGetAssocKeysForIDList(pidlFull, ahkeys, ARRAYSIZE(ahkeys));
  1811. ILFree(pidlFull);
  1812. }
  1813. IContextMenuCB *pcmcb = new CFindMenuCB();
  1814. if (pcmcb)
  1815. {
  1816. hr = CDefFolderMenu_Create2Ex(NULL, hwnd,
  1817. cidl, apidl, this, pcmcb,
  1818. ckeys, ahkeys,
  1819. (IContextMenu **)ppv);
  1820. pcmcb->Release();
  1821. }
  1822. SHRegCloseKeys(ahkeys, ckeys);
  1823. }
  1824. else if (IsEqualIID(riid, IID_IDataObject))
  1825. {
  1826. HDPA hdpa;
  1827. hr = _PrepareHIDA(cidl, apidl, &hdpa);
  1828. if (SUCCEEDED(hr))
  1829. {
  1830. hr = SHCreateFileDataObject(&c_idlDesktop, cidl, (LPCITEMIDLIST*)DPA_GetPtrPtr(hdpa),
  1831. NULL, (IDataObject **)ppv);
  1832. DPA_FreeIDArray(hdpa);
  1833. }
  1834. }
  1835. }
  1836. return hr;
  1837. }
  1838. STDMETHODIMP CFindFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwRes, LPSTRRET pStrRet)
  1839. {
  1840. IShellFolder *psf;
  1841. HRESULT hr = _QueryItemShellFolder(pidl, &psf);
  1842. if (SUCCEEDED(hr))
  1843. {
  1844. if ((dwRes & SHGDN_INFOLDER) && (dwRes & SHGDN_FORPARSING) && !(dwRes & SHGDN_FORADDRESSBAR))
  1845. {
  1846. // The thumbnail cache uses this as a hit test... in search view we can have files with the same name.
  1847. dwRes &= ~SHGDN_INFOLDER;
  1848. }
  1849. hr = psf->GetDisplayNameOf(pidl, dwRes, pStrRet);
  1850. psf->Release();
  1851. }
  1852. return hr;
  1853. }
  1854. STDMETHODIMP CFindFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
  1855. DWORD dwRes, LPITEMIDLIST *ppidlOut)
  1856. {
  1857. if (ppidlOut)
  1858. *ppidlOut = NULL;
  1859. IShellFolder *psf;
  1860. HRESULT hr = _QueryItemShellFolder(pidl, &psf);
  1861. if (SUCCEEDED(hr))
  1862. {
  1863. LPITEMIDLIST pidlRenamed;
  1864. hr = psf->SetNameOf(hwnd, pidl, PathFindFileName(pszName), dwRes, ppidlOut ? &pidlRenamed : NULL);
  1865. if (SUCCEEDED(hr) && ppidlOut)
  1866. {
  1867. hr = AddDataToIDList(pidlRenamed, GetFolderIndex(pidl), NULL, DFDF_NONE, 0, 0, 0, ppidlOut);
  1868. ILFree(pidlRenamed);
  1869. }
  1870. psf->Release();
  1871. }
  1872. return hr;
  1873. }
  1874. STDMETHODIMP CFindFolder::GetDefaultSearchGUID(GUID *pGuid)
  1875. {
  1876. return _pfilter->GetDefaultSearchGUID(SAFECAST(this, IShellFolder2*), pGuid);
  1877. }
  1878. STDMETHODIMP CFindFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
  1879. {
  1880. return _pfilter->EnumSearches(SAFECAST(this, IShellFolder2*), ppenum);
  1881. }
  1882. HRESULT CFindFolder::_Folder(FIND_FOLDER_ITEM *pffli, REFIID riid, void **ppv)
  1883. {
  1884. HRESULT hr;
  1885. if (pffli->psf)
  1886. hr = S_OK;
  1887. else
  1888. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, &pffli->idl, &pffli->psf));
  1889. if (SUCCEEDED(hr))
  1890. hr = pffli->psf->QueryInterface(riid, ppv);
  1891. return hr;
  1892. }
  1893. HRESULT CFindFolder::GetFolder(int iFolder, REFIID riid, void **ppv)
  1894. {
  1895. *ppv = NULL;
  1896. HRESULT hr = E_FAIL;
  1897. EnterCriticalSection(&_csSearch);
  1898. FIND_FOLDER_ITEM *pffli = _FolderListItem(iFolder);
  1899. if (pffli)
  1900. hr = _Folder(pffli, riid, ppv);
  1901. LeaveCriticalSection(&_csSearch);
  1902. return hr;
  1903. }
  1904. HRESULT CFindFolder::_FolderFromItem(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  1905. {
  1906. *ppv = NULL;
  1907. HRESULT hr = E_FAIL;
  1908. PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl);
  1909. if (phdfd)
  1910. {
  1911. hr = SHBindToObject(NULL, riid, &phdfd->idlParent, ppv);
  1912. }
  1913. return hr;
  1914. }
  1915. HRESULT CFindFolder::_QueryItemShellFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf)
  1916. {
  1917. *ppsf = NULL;
  1918. HRESULT hr = E_FAIL;
  1919. EnterCriticalSection(&_csSearch);
  1920. FIND_FOLDER_ITEM *pffli = _FolderListItem(pidl);
  1921. if (pffli)
  1922. {
  1923. if (pffli->psf)
  1924. hr = S_OK;
  1925. else
  1926. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, &pffli->idl, &pffli->psf));
  1927. if (SUCCEEDED(hr))
  1928. {
  1929. *ppsf = pffli->psf;
  1930. (*ppsf)->AddRef();
  1931. }
  1932. }
  1933. LeaveCriticalSection(&_csSearch);
  1934. if (FAILED(hr))
  1935. {
  1936. hr = _FolderFromItem(pidl, IID_PPV_ARG(IShellFolder, ppsf));
  1937. }
  1938. return hr;
  1939. }
  1940. HRESULT CFindFolder::_QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  1941. {
  1942. *ppv = NULL;
  1943. HRESULT hr = E_FAIL;
  1944. EnterCriticalSection(&_csSearch);
  1945. FIND_FOLDER_ITEM *pffli = _FolderListItem(pidl);
  1946. if (pffli)
  1947. hr = _Folder(pffli, riid, ppv);
  1948. LeaveCriticalSection(&_csSearch);
  1949. if (FAILED(hr))
  1950. {
  1951. hr = _FolderFromItem(pidl, riid, ppv);
  1952. }
  1953. return hr;
  1954. }
  1955. HRESULT CFindFolder::_GetDetailsFolder()
  1956. {
  1957. HRESULT hr;
  1958. if (_psfDetails)
  1959. hr = S_OK; // in cache
  1960. else
  1961. {
  1962. IFindFilter *pfilter;
  1963. hr = GetFindFilter(&pfilter);
  1964. if (SUCCEEDED(hr))
  1965. {
  1966. hr = pfilter->GetColumnsFolder(&_psfDetails);
  1967. pfilter->Release();
  1968. }
  1969. }
  1970. return hr;
  1971. }
  1972. STDMETHODIMP CFindFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  1973. {
  1974. HRESULT hr = _GetDetailsFolder();
  1975. if (SUCCEEDED(hr))
  1976. hr = _psfDetails->GetDefaultColumn(dwRes, pSort, pDisplay);
  1977. return hr;
  1978. }
  1979. BOOL CFindFolder::_MapColIndex(UINT *piColumn)
  1980. {
  1981. switch (*piColumn)
  1982. {
  1983. case IDFCOL_NAME: // 0
  1984. return FALSE;
  1985. case IDFCOL_PATH: // 1
  1986. case IDFCOL_RANK: // 2
  1987. return TRUE;
  1988. default: // >= 3
  1989. *piColumn -= IDFCOL_RANK;
  1990. return FALSE;
  1991. }
  1992. }
  1993. STDMETHODIMP CFindFolder::GetDefaultColumnState(UINT iColumn, DWORD *pdwState)
  1994. {
  1995. HRESULT hr;
  1996. if (_MapColIndex(&iColumn))
  1997. {
  1998. *pdwState = c_find_cols[iColumn].csFlags;
  1999. hr = S_OK;
  2000. }
  2001. else
  2002. {
  2003. hr = _GetDetailsFolder();
  2004. if (SUCCEEDED(hr))
  2005. {
  2006. hr = _psfDetails->GetDefaultColumnState(iColumn, pdwState);
  2007. *pdwState &= ~SHCOLSTATE_SLOW; // virtual lv and defview
  2008. }
  2009. }
  2010. return hr;
  2011. }
  2012. HRESULT CFindFolder::_GetFolderName(LPCITEMIDLIST pidl, DWORD gdnFlags, LPTSTR psz, UINT cch)
  2013. {
  2014. LPITEMIDLIST pidlFolder;
  2015. HRESULT hr = GetParentsPIDL(pidl, &pidlFolder);
  2016. if (SUCCEEDED(hr))
  2017. {
  2018. hr = SHGetNameAndFlags(pidlFolder, gdnFlags, psz, cch, NULL);
  2019. ILFree(pidlFolder);
  2020. }
  2021. return hr;
  2022. }
  2023. STDMETHODIMP CFindFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  2024. {
  2025. HRESULT hr;
  2026. if (IsEqualSCID(*pscid, SCID_RANK))
  2027. {
  2028. hr = InitVariantFromUINT(pv, _Rank(pidl));
  2029. }
  2030. else
  2031. {
  2032. IShellFolder2 *psf;
  2033. hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellFolder2, &psf));
  2034. if (SUCCEEDED(hr))
  2035. {
  2036. hr = psf->GetDetailsEx(pidl, pscid, pv);
  2037. psf->Release();
  2038. }
  2039. if (FAILED(hr))
  2040. {
  2041. if (IsEqualSCID(*pscid, SCID_DIRECTORY))
  2042. {
  2043. TCHAR szTemp[MAX_PATH];
  2044. hr = _GetFolderName(pidl, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szTemp, ARRAYSIZE(szTemp));
  2045. if (SUCCEEDED(hr))
  2046. {
  2047. hr = InitVariantFromStr(pv, szTemp);
  2048. }
  2049. }
  2050. }
  2051. }
  2052. return hr;
  2053. }
  2054. // Figure out what the correct column index is to match the scid we are given
  2055. // where the returned index is relative to the folder passed in.
  2056. int MapSCIDToColumnForFolder(IShellFolder2 *psf, SHCOLUMNID scidIn)
  2057. {
  2058. SHCOLUMNID scidNew;
  2059. for (UINT i = 0; SUCCEEDED(psf->MapColumnToSCID(i, &scidNew)); i++)
  2060. {
  2061. if (IsEqualSCID(scidNew, scidIn))
  2062. {
  2063. return i; // found
  2064. }
  2065. }
  2066. return -1; // not found
  2067. }
  2068. STDMETHODIMP CFindFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
  2069. {
  2070. HRESULT hr;
  2071. if (_MapColIndex(&iColumn))
  2072. {
  2073. if (pidl)
  2074. {
  2075. TCHAR szTemp[MAX_PATH];
  2076. szTemp[0] = 0;
  2077. if (IDFCOL_PATH == iColumn)
  2078. {
  2079. _GetFolderName(pidl, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szTemp, ARRAYSIZE(szTemp));
  2080. }
  2081. else
  2082. {
  2083. ASSERT(IDFCOL_RANK == iColumn);
  2084. ULONG uRank = _Rank(pidl);
  2085. if (uRank)
  2086. AddCommas(uRank, szTemp, ARRAYSIZE(szTemp));
  2087. }
  2088. hr = StringToStrRet(szTemp, &pdi->str);
  2089. }
  2090. else
  2091. {
  2092. hr = GetDetailsOfInfo(c_find_cols, ARRAYSIZE(c_find_cols), iColumn, pdi);
  2093. }
  2094. }
  2095. else
  2096. {
  2097. if (pidl)
  2098. {
  2099. IShellFolder2 *psf;
  2100. hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellFolder2, &psf));
  2101. if (SUCCEEDED(hr))
  2102. {
  2103. // We cannot simply ask for GetDetailsOf because some folders map different
  2104. // column numbers to differnt values.
  2105. // Translate the column index to the SHCOLUMNID relative to this folder.
  2106. SHCOLUMNID colId;
  2107. hr = _GetDetailsFolder();
  2108. if (SUCCEEDED(hr))
  2109. hr = _psfDetails->MapColumnToSCID(iColumn, &colId);
  2110. // Use the SCID to get the correct column index...
  2111. if (SUCCEEDED(hr))
  2112. {
  2113. // Get the column index for the SCID with respect to the other folder
  2114. int newIndex = MapSCIDToColumnForFolder(psf, colId);
  2115. if (newIndex != -1)
  2116. {
  2117. // Found the correct column index, so use it to get the data
  2118. hr = psf->GetDetailsOf(pidl, newIndex, pdi);
  2119. }
  2120. else
  2121. {
  2122. // Failed to find the correct column index.
  2123. hr = E_FAIL;
  2124. }
  2125. }
  2126. psf->Release();
  2127. }
  2128. }
  2129. else
  2130. {
  2131. hr = _GetDetailsFolder();
  2132. if (SUCCEEDED(hr))
  2133. hr = _psfDetails->GetDetailsOf(NULL, iColumn, pdi);
  2134. }
  2135. }
  2136. return hr;
  2137. }
  2138. STDMETHODIMP CFindFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  2139. {
  2140. HRESULT hr;
  2141. if (_MapColIndex(&iColumn))
  2142. {
  2143. hr = MapColumnToSCIDImpl(c_find_cols, ARRAYSIZE(c_find_cols), iColumn, pscid);
  2144. }
  2145. else
  2146. {
  2147. hr = _GetDetailsFolder();
  2148. if (SUCCEEDED(hr))
  2149. hr = _psfDetails->MapColumnToSCID(iColumn, pscid);
  2150. }
  2151. return hr;
  2152. }
  2153. STDMETHODIMP CFindFolder::GetFindFilter(IFindFilter **ppfilter)
  2154. {
  2155. return _pfilter->QueryInterface(IID_PPV_ARG(IFindFilter, ppfilter));
  2156. }
  2157. // IShellIcon::GetIconOf
  2158. STDMETHODIMP CFindFolder::GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex)
  2159. {
  2160. IShellIcon * psiItem;
  2161. HRESULT hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellIcon, &psiItem));
  2162. if (SUCCEEDED(hr))
  2163. {
  2164. hr = psiItem->GetIconOf(pidl, flags, piIndex);
  2165. psiItem->Release();
  2166. }
  2167. return hr;
  2168. }
  2169. // IShellIconOverlay
  2170. STDMETHODIMP CFindFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex)
  2171. {
  2172. IShellIconOverlay * psioItem;
  2173. HRESULT hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellIconOverlay, &psioItem));
  2174. if (SUCCEEDED(hr))
  2175. {
  2176. hr = psioItem->GetOverlayIndex(pidl, pIndex);
  2177. psioItem->Release();
  2178. }
  2179. return hr;
  2180. }
  2181. STDMETHODIMP CFindFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex)
  2182. {
  2183. return E_NOTIMPL;
  2184. }
  2185. STDMETHODIMP CFindFolder::RestoreSearchFromSaveFile(LPCITEMIDLIST pidlSaveFile, IShellFolderView *psfv)
  2186. {
  2187. // See if we can restore most of the search from here...
  2188. IStream *pstm;
  2189. HRESULT hr = StgBindToObject(pidlSaveFile, STGM_READ | STGM_SHARE_DENY_WRITE, IID_PPV_ARG(IStream, &pstm));
  2190. if (SUCCEEDED(hr))
  2191. {
  2192. ULONG cbRead;
  2193. DFHEADER dfh;
  2194. // Note: in theory I should test the size read by the size of the
  2195. // smaller headers, but if the number of bytes read is smaller than
  2196. // the few new things added then there is nothing to restore anyway...
  2197. // Note: Win95/NT4 incorrectly failed newer versions of this structure.
  2198. // Which is bogus since the struct was backward compatible (that's what
  2199. // the offsets are for). We fix for NT5 and beyond, but downlevel
  2200. // systems are forever broken. Hopefully this feature is rarely enough
  2201. // used (and never mailed) that nobody will notice we're broken.
  2202. if (SUCCEEDED(pstm->Read(&dfh, sizeof(dfh), &cbRead)) &&
  2203. (sizeof(dfh) == cbRead) && (DOCFIND_SIG == dfh.wSig))
  2204. {
  2205. DFC_UNICODE_DESC desc;
  2206. LARGE_INTEGER dlibMove = {0, 0};
  2207. WORD fCharType = 0;
  2208. // Check the stream's signature to see if it was generated by Win95 or NT.
  2209. dlibMove.QuadPart = -(LONGLONG)sizeof(desc);
  2210. pstm->Seek(dlibMove, STREAM_SEEK_END, NULL);
  2211. pstm->Read(&desc, sizeof(desc), &cbRead);
  2212. if (cbRead > 0 && desc.NTsignature == c_NTsignature)
  2213. {
  2214. // NT-generated stream. Read in Unicode criteria.
  2215. fCharType = DFC_FMT_UNICODE;
  2216. dlibMove.QuadPart = desc.oUnicodeCriteria.QuadPart;
  2217. }
  2218. else
  2219. {
  2220. // Win95-generated stream. Read in ANSI criteria.
  2221. fCharType = DFC_FMT_ANSI;
  2222. dlibMove.LowPart = dfh.oCriteria;
  2223. }
  2224. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  2225. _pfilter->RestoreCriteria(pstm, dfh.cCriteria, fCharType);
  2226. // Now read in the results
  2227. dlibMove.LowPart = dfh.oResults;
  2228. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  2229. if (dfh.wVer > 1)
  2230. {
  2231. // only restore this way if version 2 data....
  2232. // Now Restore away the folder list
  2233. RestoreFolderList(pstm);
  2234. int cItems = 0;
  2235. RestoreItemList(pstm, &cItems);
  2236. if (cItems > 0)
  2237. psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
  2238. }
  2239. }
  2240. else
  2241. hr = E_FAIL;
  2242. pstm->Release();
  2243. }
  2244. return hr;
  2245. }
  2246. // a form of this code is duplicated in browseui searchext.cpp
  2247. //
  2248. BOOL RealFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile)
  2249. {
  2250. // First create the top level browser...
  2251. IWebBrowser2 *pwb2;
  2252. HRESULT hr = CoCreateInstance(CLSID_ShellBrowserWindow, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pwb2));
  2253. if (SUCCEEDED(hr))
  2254. {
  2255. VARIANT varClsid;
  2256. hr = InitBSTRVariantFromGUID(&varClsid, CLSID_FileSearchBand);
  2257. if (SUCCEEDED(hr))
  2258. {
  2259. VARIANT varEmpty = {0};
  2260. // show a search bar
  2261. hr = pwb2->ShowBrowserBar(&varClsid, &varEmpty, &varEmpty);
  2262. if (SUCCEEDED(hr))
  2263. {
  2264. // Grab the band's IUnknown from browser property.
  2265. VARIANT varFsb;
  2266. hr = pwb2->GetProperty(varClsid.bstrVal, &varFsb);
  2267. if (SUCCEEDED(hr))
  2268. {
  2269. // QI for IFileSearchBand, which we'll use to program the search band's
  2270. // search type (files or folders), inititial scope, and/or saved query file.
  2271. IFileSearchBand* pfsb;
  2272. if (SUCCEEDED(QueryInterfaceVariant(varFsb, IID_PPV_ARG(IFileSearchBand, &pfsb))))
  2273. {
  2274. BSTR bstrSearch;
  2275. hr = BSTRFromCLSID(SRCID_SFileSearch, &bstrSearch);
  2276. if (SUCCEEDED(hr))
  2277. {
  2278. VARIANT varQueryFile = {0}, varScope = {0};
  2279. // assign initial scope
  2280. if (pidlFolder)
  2281. InitVariantFromIDList(&varScope, pidlFolder);
  2282. // assign query file from which to restore search
  2283. else if (pidlSaveFile)
  2284. InitVariantFromIDList(&varQueryFile, pidlSaveFile);
  2285. pfsb->SetSearchParameters(&bstrSearch, VARIANT_TRUE, &varScope, &varQueryFile);
  2286. VariantClear(&varScope);
  2287. VariantClear(&varQueryFile);
  2288. SysFreeString(bstrSearch);
  2289. }
  2290. pfsb->Release();
  2291. }
  2292. VariantClear(&varFsb);
  2293. }
  2294. if (SUCCEEDED(hr))
  2295. hr = pwb2->put_Visible(TRUE);
  2296. }
  2297. VariantClear(&varClsid); // frees bstrFileSearchBand too
  2298. }
  2299. pwb2->Release();
  2300. }
  2301. return hr;
  2302. }
  2303. HRESULT CFindFolder::OpenContainingFolder(IUnknown *punkSite)
  2304. {
  2305. IFolderView *pfv;
  2306. HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  2307. if (SUCCEEDED(hr))
  2308. {
  2309. IEnumIDList *penum;
  2310. hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IEnumIDList, &penum));
  2311. if (S_OK == hr)
  2312. {
  2313. LPITEMIDLIST pidl;
  2314. ULONG c;
  2315. while (S_OK == penum->Next(1, &pidl, &c))
  2316. {
  2317. // Now get the parent of it.
  2318. LPITEMIDLIST pidlParent;
  2319. if (SUCCEEDED(GetParentsPIDL(pidl, &pidlParent)))
  2320. {
  2321. SHOpenFolderAndSelectItems(pidlParent, 1, (LPCITEMIDLIST *)&pidl, 0);
  2322. ILFree(pidlParent);
  2323. }
  2324. ILFree(pidl);
  2325. }
  2326. penum->Release();
  2327. }
  2328. pfv->Release();
  2329. }
  2330. return hr;
  2331. }
  2332. // Save away the current search to a file on the desktop.
  2333. // For now the name will be automatically generated.
  2334. //
  2335. void CFindFolder::Save(IFindFilter* pfilter, HWND hwnd, DFBSAVEINFO * pSaveInfo, IShellView* psv, IUnknown *pObject)
  2336. {
  2337. TCHAR szFilePath[MAX_PATH];
  2338. IStream * pstm;
  2339. DFHEADER dfh;
  2340. TCHAR szTemp[MAX_PATH];
  2341. SHORT cb;
  2342. LARGE_INTEGER dlibMove = {0, 0};
  2343. ULARGE_INTEGER libCurPos;
  2344. FOLDERSETTINGS fs;
  2345. //
  2346. // See if the search already has a file name associated with it. If so
  2347. // we will save it in it, else we will create a new file on the desktop
  2348. if (pfilter->FFilterChanged() == S_FALSE)
  2349. {
  2350. // Lets blow away the save file
  2351. ILFree(pSaveInfo->pidlSaveFile);
  2352. pSaveInfo->pidlSaveFile = NULL;
  2353. }
  2354. // If it still looks like we want to continue to use a save file then
  2355. // continue.
  2356. if (pSaveInfo->pidlSaveFile)
  2357. {
  2358. SHGetPathFromIDList(pSaveInfo->pidlSaveFile, szFilePath);
  2359. }
  2360. else
  2361. {
  2362. // First get the path name to the Desktop.
  2363. SHGetSpecialFolderPath(NULL, szFilePath, CSIDL_PERSONAL, TRUE);
  2364. // and update the title
  2365. // we now do this before getting a filename because we generate
  2366. // the file name from the title
  2367. LPTSTR pszTitle;
  2368. pfilter->GenerateTitle(&pszTitle, TRUE);
  2369. if (pszTitle)
  2370. {
  2371. // Now add on the extension.
  2372. lstrcpyn(szTemp, pszTitle, MAX_PATH - (lstrlen(szFilePath) + 1 + 4 + 1+3));
  2373. lstrcat(szTemp, TEXT(".fnd"));
  2374. LocalFree(pszTitle); // And free the title string.
  2375. }
  2376. else
  2377. {
  2378. szTemp[0] = 0;
  2379. }
  2380. // Now loop through and replace all of the invalid characters with _'s
  2381. // we special case a few of the characters...
  2382. for (LPTSTR lpsz = szTemp; *lpsz; lpsz = CharNext(lpsz))
  2383. {
  2384. if (PathGetCharType(*lpsz) & (GCT_INVALID|GCT_WILD|GCT_SEPARATOR))
  2385. {
  2386. switch (*lpsz)
  2387. {
  2388. case TEXT(':'):
  2389. *lpsz = TEXT('-');
  2390. break;
  2391. case TEXT('*'):
  2392. *lpsz = TEXT('@');
  2393. break;
  2394. case TEXT('?'):
  2395. *lpsz = TEXT('!');
  2396. break;
  2397. default:
  2398. *lpsz = TEXT('_');
  2399. }
  2400. }
  2401. }
  2402. TCHAR szShortName[12];
  2403. LoadString(HINST_THISDLL, IDS_FIND_SHORT_NAME, szShortName, ARRAYSIZE(szShortName));
  2404. if (!PathYetAnotherMakeUniqueName(szFilePath, szFilePath, szShortName, szTemp))
  2405. return;
  2406. }
  2407. // Now lets bring up the save as dialog...
  2408. TCHAR szFilter[MAX_PATH];
  2409. TCHAR szTitle[MAX_PATH];
  2410. TCHAR szFilename[MAX_PATH];
  2411. OPENFILENAME ofn = { 0 };
  2412. LoadString(g_hinst, IDS_FINDFILESFILTER, szFilter, ARRAYSIZE(szFilter));
  2413. LoadString(g_hinst, IDS_FINDSAVERESULTSTITLE, szTitle, ARRAYSIZE(szTitle));
  2414. //Strip out the # and make them Nulls for SaveAs Dialog
  2415. LPTSTR psz = szFilter;
  2416. while (*psz)
  2417. {
  2418. if (*psz == TEXT('#'))
  2419. *psz = 0;
  2420. psz++;
  2421. }
  2422. lstrcpy(szFilename, PathFindFileName(szFilePath));
  2423. PathRemoveFileSpec(szFilePath);
  2424. ofn.lStructSize = sizeof(ofn);
  2425. ofn.hwndOwner = hwnd;
  2426. ofn.hInstance = g_hinst;
  2427. ofn.lpstrFilter = szFilter;
  2428. ofn.lpstrFile = szFilename;
  2429. ofn.nMaxFile = MAX_PATH;
  2430. ofn.lpstrInitialDir = szFilePath;
  2431. ofn.lpstrTitle = szTitle;
  2432. ofn.lpstrDefExt = TEXT("fnd");
  2433. ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST |
  2434. OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
  2435. ofn.lpTemplateName = NULL;
  2436. ofn.lpfnHook= NULL;
  2437. ofn.lCustData = NULL;
  2438. if (!GetSaveFileName(&ofn))
  2439. return;
  2440. if (FAILED(SHCreateStreamOnFile(szFilename, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, &pstm)))
  2441. return;
  2442. // remember the file that we saved away to...
  2443. ILFree(pSaveInfo->pidlSaveFile);
  2444. SHParseDisplayName(szFilename, NULL, &pSaveInfo->pidlSaveFile, 0, NULL);
  2445. // Now setup and write out header information
  2446. ZeroMemory(&dfh, sizeof(dfh));
  2447. dfh.wSig = DOCFIND_SIG;
  2448. dfh.wVer = DF_CURFILEVER;
  2449. dfh.dwFlags = pSaveInfo->dwFlags;
  2450. dfh.wSortOrder = (WORD)pSaveInfo->SortMode;
  2451. dfh.wcbItem = sizeof(DFITEM);
  2452. dfh.oCriteria = sizeof(dfh);
  2453. // dfh.cCriteria = sizeof(s_aIndexes) / sizeof(SHORT);
  2454. // dfh.oResults =;
  2455. // Not used anymore...
  2456. dfh.cResults = -1;
  2457. // Note: Later we may convert this to DOCFILE where the
  2458. // criteria is stored as properties.
  2459. // Get the current Folder Settings
  2460. if (SUCCEEDED(psv->GetCurrentInfo(&fs)))
  2461. dfh.ViewMode = fs.ViewMode;
  2462. else
  2463. dfh.ViewMode = FVM_DETAILS;
  2464. // Now call the filter object to save out his own set of criterias
  2465. dlibMove.LowPart = dfh.oCriteria;
  2466. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  2467. HRESULT hr = pfilter->SaveCriteria(pstm, DFC_FMT_ANSI);
  2468. if (SUCCEEDED(hr))
  2469. dfh.cCriteria = GetScode(hr);
  2470. // Now setup to output the results
  2471. dlibMove.LowPart = 0;
  2472. pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos); // Get current pos
  2473. dfh.oResults = libCurPos.LowPart;
  2474. //
  2475. // Now Let our file folder serialize his results out here also...
  2476. // But only if the option is set to do so...
  2477. //
  2478. cb = 0;
  2479. // Write out a Trailing NULL for Folder list
  2480. pstm->Write(&cb, sizeof(cb), NULL);
  2481. // And item list.
  2482. pstm->Write(&cb, sizeof(cb), NULL);
  2483. // END of DFHEADER_WIN95 information
  2484. // BEGIN of NT5 information:
  2485. // Now setup to output the history stream
  2486. if (pObject)
  2487. {
  2488. dlibMove.LowPart = 0;
  2489. pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos); // Get current pos
  2490. dfh.oHistory = libCurPos.LowPart;
  2491. if (FAILED(SavePersistHistory(pObject, pstm)))
  2492. {
  2493. // On failure we might as well just pretend we didn't save this bit of data.
  2494. // Do we need an error message -- the ui won't be right when relaunched...
  2495. //
  2496. dfh.oHistory = 0;
  2497. dlibMove.LowPart = libCurPos.LowPart;
  2498. pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL);
  2499. }
  2500. }
  2501. // In NT the below was done AT THE END OF THE STREAM instead of
  2502. // revving the DFHEADER struct. (Okay, DFHEADEREX, since Win95
  2503. // already broke DFHEADER back compat by in improper version check)
  2504. // This could have been done by putting a propery signatured
  2505. // DFHEADEREX that had proper versioning so we could add information
  2506. // to. Unfortunately another hardcoded struct was tacked on to
  2507. // the end of the stream... Next time, please fix the problem
  2508. // instead of work around it.
  2509. //
  2510. // What this boils down to is we cannot put any information
  2511. // after the DFC_UNICODE_DESC section, so might as well
  2512. // always do this SaveCriteria section last...
  2513. //
  2514. // See comment at top of file for DFC_UNICODE_DESC.
  2515. //
  2516. DFC_UNICODE_DESC desc;
  2517. //
  2518. // Get the current location in stream. This is the offset where
  2519. // we'll write the unicode find criteria. Save this
  2520. // value (along with NT-specific signature) in the descriptor
  2521. //
  2522. dlibMove.LowPart = 0;
  2523. pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos);
  2524. desc.oUnicodeCriteria.QuadPart = libCurPos.QuadPart;
  2525. desc.NTsignature = c_NTsignature;
  2526. // Append the Unicode version of the find criteria.
  2527. hr = pfilter->SaveCriteria(pstm, DFC_FMT_UNICODE);
  2528. // Append the unicode criteria descriptor to the end of the file.
  2529. pstm->Write(&desc, sizeof(desc), NULL);
  2530. //
  2531. // don't put any code between the above DFC_UNICDE_DESC section
  2532. // and this back-patch of the dfh header...
  2533. //
  2534. // Finally output the header information at the start of the file
  2535. // and close the file
  2536. //
  2537. pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
  2538. pstm->Write(&dfh, sizeof(dfh), NULL);
  2539. pstm->Release();
  2540. SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL);
  2541. SHChangeNotify(SHCNE_FREESPACE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL);
  2542. }
  2543. // Broke out from class to share with old and new code
  2544. BOOL CFindFolder::HandleUpdateDir(LPCITEMIDLIST pidl, BOOL fCheckSubDirs)
  2545. {
  2546. // 1. Start walk through list of dirs. Find list of directories effected
  2547. // and mark them
  2548. // 2. Walk the list of items that we have and mark each of the items that
  2549. // that are in our list of directories and then do a search...
  2550. BOOL fCurrentItemsMayBeImpacted = FALSE;
  2551. FIND_FOLDER_ITEM *pffli;
  2552. INT cPidf;
  2553. // First see which directories are effected...
  2554. GetFolderListItemCount(&cPidf);
  2555. for (int iPidf = 0; iPidf < cPidf; iPidf++)
  2556. {
  2557. if (SUCCEEDED(GetFolderListItem(iPidf, &pffli))
  2558. && !pffli->fUpdateDir) // We may have already impacted these...
  2559. {
  2560. pffli->fUpdateDir = ILIsParent(pidl, &pffli->idl, FALSE);
  2561. fCurrentItemsMayBeImpacted |= pffli->fUpdateDir;
  2562. }
  2563. }
  2564. if (fCurrentItemsMayBeImpacted)
  2565. {
  2566. // Now we need to walk through the whole list and remove any entries
  2567. // that are no longer there...
  2568. //
  2569. int iItem;
  2570. if (SUCCEEDED(GetItemCount(&iItem)))
  2571. {
  2572. for (--iItem; iItem >= 0; iItem--)
  2573. {
  2574. FIND_ITEM *pesfi;
  2575. if (SUCCEEDED(GetItem(iItem, &pesfi)) && pesfi)
  2576. {
  2577. UINT iFolder = GetFolderIndex(&pesfi->idl);
  2578. // See if item may be impacted...
  2579. if (SUCCEEDED(GetFolderListItem(iFolder, &pffli)) && pffli->fUpdateDir)
  2580. pesfi->dwState |= CDFITEM_STATE_MAYBEDELETE;
  2581. }
  2582. }
  2583. }
  2584. }
  2585. return fCurrentItemsMayBeImpacted;
  2586. }
  2587. void CFindFolder::UpdateOrMaybeAddPidl(IShellFolderView *psfv, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlOld)
  2588. {
  2589. HRESULT hr;
  2590. // First see if we should try to do an update...
  2591. if (pidlOld)
  2592. {
  2593. LPITEMIDLIST pidlT;
  2594. if (S_OK == MapToSearchIDList(pidl, TRUE, &pidlT))
  2595. {
  2596. SetItemsChangedSinceSort();
  2597. UINT iItem;
  2598. // cast needed for bad interface def
  2599. hr = psfv->UpdateObject((LPITEMIDLIST)pidlOld, (LPITEMIDLIST)pidlT, &iItem);
  2600. ILFree(pidlT); // In either case simply blow away our generated pidl...
  2601. if (SUCCEEDED(hr))
  2602. return;
  2603. }
  2604. }
  2605. IShellFolder *psf;
  2606. LPCITEMIDLIST pidlChild;
  2607. if (SUCCEEDED(SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
  2608. {
  2609. BOOL fMatch = FALSE;
  2610. // See if this item matches the filter...
  2611. IFindFilter *pfilter;
  2612. if (SUCCEEDED(GetFindFilter(&pfilter)))
  2613. {
  2614. fMatch = pfilter->MatchFilter(psf, pidlChild) != 0;
  2615. pfilter->Release();
  2616. }
  2617. psf->Release();
  2618. if (fMatch)
  2619. {
  2620. LPITEMIDLIST pidlT;
  2621. if (S_OK != MapToSearchIDList(pidl, TRUE, &pidlT))
  2622. {
  2623. fMatch = FALSE;
  2624. // The folder has not been added before now...
  2625. TCHAR szPath[MAX_PATH];
  2626. SHGetPathFromIDList(pidl, szPath);
  2627. if (!IsFileInBitBucket(szPath))
  2628. {
  2629. PathRemoveFileSpec(szPath);
  2630. LPITEMIDLIST pidlFolder;
  2631. if (SUCCEEDED(SHParseDisplayName(szPath, NULL, &pidlFolder, 0, NULL)))
  2632. {
  2633. int iFolder;
  2634. hr = AddFolder(pidlFolder, TRUE, &iFolder);
  2635. if (SUCCEEDED(hr))
  2636. {
  2637. fMatch = (S_OK == MapToSearchIDList(pidl, TRUE, &pidlT));
  2638. }
  2639. ILFree(pidlFolder);
  2640. }
  2641. }
  2642. }
  2643. if (fMatch)
  2644. {
  2645. // There are times we get notified twice. To handle this
  2646. // see if the item is already in our list. If so punt...
  2647. SetItemsChangedSinceSort();
  2648. UINT iItem;
  2649. if (FAILED(psfv->UpdateObject(pidlT, pidlT, &iItem)))
  2650. {
  2651. // item not in the view yet... so we need to add it
  2652. if (SUCCEEDED(GetItemCount((INT *)&iItem)))
  2653. {
  2654. // Normal case would be here to add the object
  2655. // We need to add this to our dpa and dsa...
  2656. FIND_ITEM *pesfi;
  2657. AddPidl(iItem, pidlT, (UINT)-1, &pesfi);
  2658. if (pesfi)
  2659. psfv->SetObjectCount(++iItem, SFVSOC_NOSCROLL);
  2660. }
  2661. }
  2662. ILFree(pidlT);
  2663. }
  2664. else
  2665. {
  2666. ASSERT(NULL == pidlT);
  2667. }
  2668. }
  2669. }
  2670. }
  2671. void CFindFolder::HandleRMDir(IShellFolderView *psfv, LPCITEMIDLIST pidl)
  2672. {
  2673. BOOL fCurrentItemsMayBeImpacted = FALSE;
  2674. FIND_FOLDER_ITEM *pffli;
  2675. INT cItems;
  2676. FIND_ITEM *pesfi;
  2677. // First see which directories are effected...
  2678. GetFolderListItemCount(&cItems);
  2679. for (int iItem = 0; iItem < cItems; iItem++)
  2680. {
  2681. if (SUCCEEDED(GetFolderListItem(iItem, &pffli)))
  2682. {
  2683. pffli->fDeleteDir = ILIsParent(pidl, &pffli->idl, FALSE);
  2684. fCurrentItemsMayBeImpacted |= pffli->fDeleteDir;
  2685. }
  2686. else
  2687. {
  2688. #ifdef DEBUG
  2689. INT cItem;
  2690. GetFolderListItemCount(&cItem);
  2691. TraceMsg(TF_WARNING, "NULL pffli in _handleRMDir (iItem == %d, ItemCount()==%d)!!!", iItem, cItems);
  2692. #endif
  2693. }
  2694. }
  2695. if (fCurrentItemsMayBeImpacted)
  2696. {
  2697. // Now we need to walk through the whole list and remove any entries
  2698. // that are no longer there...
  2699. if (SUCCEEDED(GetItemCount(&iItem)))
  2700. {
  2701. for (--iItem; iItem >= 0; iItem--)
  2702. {
  2703. if (FAILED(GetItem(iItem, &pesfi)) || pesfi == NULL)
  2704. continue;
  2705. // See if item may be impacted...
  2706. UINT iFolder = GetFolderIndex(&pesfi->idl);
  2707. if (SUCCEEDED(GetFolderListItem(iFolder, &pffli))
  2708. && pffli->fDeleteDir)
  2709. {
  2710. psfv->RemoveObject(&pesfi->idl, (UINT*)&cItems);
  2711. }
  2712. }
  2713. }
  2714. }
  2715. }
  2716. // export used for Start.Search-> cascade menu
  2717. STDAPI_(IContextMenu *) SHFind_InitMenuPopup(HMENU hmenu, HWND hwnd, UINT idCmdFirst, UINT idCmdLast)
  2718. {
  2719. IContextMenu * pcm = NULL;
  2720. HKEY hkFind = SHGetShellKey(SHELLKEY_HKLM_EXPLORER, TEXT("FindExtensions"), FALSE);
  2721. if (hkFind)
  2722. {
  2723. if (SUCCEEDED(CDefFolderMenu_CreateHKeyMenu(hwnd, hkFind, &pcm)))
  2724. {
  2725. int iItems = GetMenuItemCount(hmenu);
  2726. // nuke all old entries
  2727. while (iItems--)
  2728. {
  2729. DeleteMenu(hmenu, iItems, MF_BYPOSITION);
  2730. }
  2731. pcm->QueryContextMenu(hmenu, 0, idCmdFirst, idCmdLast, CMF_NODEFAULT|CMF_INCLUDESTATIC|CMF_FINDHACK);
  2732. iItems = GetMenuItemCount(hmenu);
  2733. if (!iItems)
  2734. {
  2735. TraceMsg(TF_DOCFIND, "no menus in find extension, blowing away context menu");
  2736. pcm->Release();
  2737. pcm = NULL;
  2738. }
  2739. }
  2740. RegCloseKey(hkFind);
  2741. }
  2742. return pcm;
  2743. }
  2744. void _SetObjectCount(IShellView *psv, int cItems, DWORD dwFlags)
  2745. {
  2746. IShellFolderView *psfv;
  2747. if (SUCCEEDED(psv->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
  2748. {
  2749. psfv->SetObjectCount(cItems, dwFlags);
  2750. psfv->Release();
  2751. }
  2752. }
  2753. typedef struct
  2754. {
  2755. PFNLVCOMPARE pfnCompare;
  2756. LPARAM lParamSort;
  2757. } FIND_SORT_INFO;
  2758. int CALLBACK _FindCompareItems(void *p1, void *p2, LPARAM lParam)
  2759. {
  2760. FIND_SORT_INFO *pfsi = (FIND_SORT_INFO*)lParam;
  2761. return pfsi->pfnCompare(PtrToInt(p1), PtrToInt(p2), pfsi->lParamSort);
  2762. }
  2763. HRESULT CFindFolderViewCB::OnSortListData(DWORD pv, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
  2764. {
  2765. EnterCriticalSection(&_pff->_csSearch);
  2766. // First mark the focused item in the list so we can find it later...
  2767. FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, _iFocused); // indirect
  2768. if (pesfi)
  2769. pesfi->dwState |= LVIS_FOCUSED;
  2770. int cItems = DPA_GetPtrCount(_pff->_hdpaItems);
  2771. HDPA hdpaForSorting = NULL;
  2772. if (cItems)
  2773. {
  2774. hdpaForSorting = DPA_Create(cItems);
  2775. }
  2776. if (hdpaForSorting)
  2777. {
  2778. for (int i = 0; i< cItems; i++)
  2779. {
  2780. DPA_SetPtr(hdpaForSorting, i, IntToPtr(i));
  2781. }
  2782. // sort out items
  2783. FIND_SORT_INFO fsi;
  2784. fsi.pfnCompare = pfnCompare;
  2785. fsi.lParamSort = lParamSort;
  2786. DPA_Sort(hdpaForSorting, _FindCompareItems, (LPARAM)&fsi);
  2787. for (i = 0; i < cItems; i++)
  2788. {
  2789. int iIndex = PtrToInt(DPA_FastGetPtr(hdpaForSorting, i));
  2790. // Move the items from _hdpaItems to hdpaForSorting in sorted order
  2791. DPA_SetPtr(hdpaForSorting, i, DPA_FastGetPtr(_pff->_hdpaItems, iIndex));
  2792. }
  2793. // Now switch the two HDPA to get the sorted list in the member variable
  2794. DPA_Destroy(_pff->_hdpaItems);
  2795. _pff->_hdpaItems = hdpaForSorting;
  2796. }
  2797. // Now find the focused item and scroll it into place...
  2798. IShellView *psv;
  2799. if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
  2800. {
  2801. int iFocused = -1;
  2802. // Tell the view we need to reshuffle....
  2803. // Gross, this one defaults to invalidate all which for this one is fine...
  2804. _SetObjectCount(psv, cItems, SFVSOC_INVALIDATE_ALL); // Invalidate all
  2805. for (int iEnd = cItems - 1; iEnd >= 0; iEnd--)
  2806. {
  2807. pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iEnd); // indirect
  2808. if (pesfi && pesfi->dwState & LVIS_FOCUSED)
  2809. iFocused = iEnd;
  2810. }
  2811. // Now handle the focused item...
  2812. if (iFocused != -1)
  2813. {
  2814. _pff->_iGetIDList = iFocused; // remember the last one we retrieved...
  2815. pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iFocused); // indirect
  2816. if (pesfi)
  2817. {
  2818. // flags depend on first one and also if selected?
  2819. psv->SelectItem(&pesfi->idl, SVSI_FOCUSED | SVSI_ENSUREVISIBLE | SVSI_SELECT);
  2820. pesfi->dwState &= ~LVIS_FOCUSED; // don't keep it around to get lost later...
  2821. }
  2822. }
  2823. _iFocused = iFocused;
  2824. _fIgnoreSelChange = FALSE;
  2825. psv->Release();
  2826. }
  2827. LeaveCriticalSection(&_pff->_csSearch);
  2828. return S_OK;
  2829. }
  2830. HRESULT CFindFolderViewCB::OnMergeMenu(DWORD pv, QCMINFO*lP)
  2831. {
  2832. DebugMsg(DM_TRACE, TEXT("sh TR - DF_FSNCallBack DVN_MERGEMENU"));
  2833. UINT idCmdFirst = lP->idCmdFirst;
  2834. UINT idBGMain = 0, idBGPopup = 0;
  2835. _pff->_pfilter->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup);
  2836. CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, idBGPopup, lP);
  2837. // Lets remove some menu items that are not useful to us.
  2838. HMENU hmenu = lP->hmenu;
  2839. DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTE, MF_BYCOMMAND);
  2840. DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND);
  2841. // DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTESPECIAL, MF_BYCOMMAND);
  2842. // This is sortof bogus but if after the merge one of the
  2843. // menus has no items in it, remove the menu.
  2844. for (int i = GetMenuItemCount(hmenu) - 1; i >= 0; i--)
  2845. {
  2846. HMENU hmenuSub = GetSubMenu(hmenu, i);
  2847. if (hmenuSub && (GetMenuItemCount(hmenuSub) == 0))
  2848. {
  2849. DeleteMenu(hmenu, i, MF_BYPOSITION);
  2850. }
  2851. }
  2852. return S_OK;
  2853. }
  2854. HRESULT CFindFolderViewCB::OnGETWORKINGDIR(DWORD pv, UINT wP, LPTSTR lP)
  2855. {
  2856. HRESULT hr = E_FAIL;
  2857. IShellFolderView *psfv;
  2858. if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
  2859. {
  2860. LPCITEMIDLIST *ppidls; // pointer to a list of pidls.
  2861. UINT cpidls = 0; // Count of pidls that were returned.
  2862. psfv->GetSelectedObjects(&ppidls, &cpidls);
  2863. if (cpidls > 0)
  2864. {
  2865. LPITEMIDLIST pidl;
  2866. if (SUCCEEDED(_pff->GetParentsPIDL(ppidls[0], &pidl)))
  2867. {
  2868. SHGetPathFromIDList(pidl, lP);
  2869. ILFree(pidl);
  2870. }
  2871. LocalFree((void *)ppidls); // const -> non const
  2872. hr = S_OK;
  2873. }
  2874. else
  2875. {
  2876. hr = E_FAIL;
  2877. }
  2878. psfv->Release();
  2879. }
  2880. return hr;
  2881. }
  2882. HRESULT CFindFolderViewCB::OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream **ppstm)
  2883. {
  2884. return _pff->_pfilter->GetColSaveStream(wP, ppstm);
  2885. }
  2886. HRESULT CFindFolderViewCB::OnGETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST *ppidl)
  2887. {
  2888. FIND_ITEM *pesfi;
  2889. if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
  2890. {
  2891. *ppidl = &pesfi->idl; // return alias!
  2892. return S_OK;
  2893. }
  2894. *ppidl = NULL;
  2895. return E_FAIL;
  2896. }
  2897. // in defviewx.c
  2898. STDAPI SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage);
  2899. HRESULT CFindFolderViewCB::OnGetItemIconIndex(DWORD pv, WPARAM iItem, int *piIcon)
  2900. {
  2901. FIND_ITEM *pesfi;
  2902. *piIcon = -1;
  2903. if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
  2904. {
  2905. if (pesfi->iIcon == -1)
  2906. {
  2907. IShellFolder* psf = (IShellFolder*)_pff;
  2908. SHGetIconFromPIDL(psf, NULL, &pesfi->idl, 0, &pesfi->iIcon);
  2909. }
  2910. *piIcon = pesfi->iIcon;
  2911. return S_OK;
  2912. }
  2913. return E_FAIL;
  2914. }
  2915. HRESULT CFindFolderViewCB::OnSetItemIconOverlay(DWORD pv, WPARAM iItem, int iOverlayIndex)
  2916. {
  2917. HRESULT hr = E_FAIL;
  2918. FIND_ITEM *pesfi;
  2919. if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
  2920. {
  2921. pesfi->dwMask |= ESFITEM_ICONOVERLAYSET;
  2922. pesfi->dwState |= INDEXTOOVERLAYMASK(iOverlayIndex) & LVIS_OVERLAYMASK;
  2923. hr = S_OK;
  2924. }
  2925. return hr;
  2926. }
  2927. HRESULT CFindFolderViewCB::OnGetItemIconOverlay(DWORD pv, WPARAM iItem, int * piOverlayIndex)
  2928. {
  2929. HRESULT hr = E_FAIL;
  2930. *piOverlayIndex = SFV_ICONOVERLAY_DEFAULT;
  2931. FIND_ITEM *pesfi;
  2932. if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
  2933. {
  2934. if (pesfi->dwMask & ESFITEM_ICONOVERLAYSET)
  2935. {
  2936. *piOverlayIndex = OVERLAYMASKTO1BASEDINDEX(pesfi->dwState & LVIS_OVERLAYMASK);
  2937. }
  2938. else
  2939. *piOverlayIndex = SFV_ICONOVERLAY_UNSET;
  2940. hr = S_OK;
  2941. }
  2942. return hr;
  2943. }
  2944. HRESULT CFindFolderViewCB::OnSETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST pidl)
  2945. {
  2946. FIND_ITEM *pesfi;
  2947. _pff->_iGetIDList = (int) iItem; // remember the last one we retrieved...
  2948. if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi)
  2949. {
  2950. FIND_ITEM *pesfiNew;
  2951. if (SUCCEEDED(_pff->AddPidl((int) iItem, pidl, 0, &pesfiNew) && pesfiNew))
  2952. {
  2953. pesfiNew->dwState = pesfi->dwState;
  2954. LocalFree((HLOCAL)pesfi); // Free the old one...
  2955. }
  2956. return S_OK;
  2957. }
  2958. return E_FAIL;
  2959. }
  2960. BOOL DF_ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2961. {
  2962. BOOL bRet = (pidl1 == pidl2);
  2963. if (!bRet)
  2964. {
  2965. PCHIDDENDOCFINDDATA phdfd1 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl1, IDLHID_DOCFINDDATA);
  2966. PCHIDDENDOCFINDDATA phdfd2 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl2, IDLHID_DOCFINDDATA);
  2967. if (phdfd1 && phdfd2)
  2968. bRet = (phdfd1->iFolder == phdfd2->iFolder) && ILIsEqual(pidl1, pidl2);
  2969. }
  2970. return bRet;
  2971. }
  2972. HRESULT CFindFolderViewCB::OnGetIndexForItemIDList(DWORD pv, int * piItem, LPITEMIDLIST pidl)
  2973. {
  2974. int cItems;
  2975. // Try to short circuit searching for pidls...
  2976. if (SUCCEEDED(_pff->GetItemCount(&cItems)) && _pff->_iGetIDList < cItems)
  2977. {
  2978. FIND_ITEM *pesfi;
  2979. if (SUCCEEDED(_pff->GetItem(_pff->_iGetIDList, &pesfi)) && pesfi)
  2980. {
  2981. if (DF_ILIsEqual(&pesfi->idl, pidl))
  2982. {
  2983. // Yep it was ours so return the index quickly..
  2984. *piItem = _pff->_iGetIDList;
  2985. return S_OK;
  2986. }
  2987. }
  2988. }
  2989. // Otherwise let it search the old fashion way...
  2990. return E_FAIL;
  2991. }
  2992. HRESULT CFindFolderViewCB::OnDeleteItem(DWORD pv, LPCITEMIDLIST pidl)
  2993. {
  2994. // We simply need to remove this item from our list. The
  2995. // underlying listview will decrement the count on their end...
  2996. FIND_ITEM *pesfi;
  2997. int iItem;
  2998. int cItems;
  2999. BOOL bFound;
  3000. if (!pidl)
  3001. {
  3002. _pff->SetAsyncEnum(NULL);
  3003. return S_OK; // special case telling us all items deleted...
  3004. }
  3005. bFound = FALSE;
  3006. if (SUCCEEDED(_pff->GetItem(_pff->_iGetIDList, &pesfi))
  3007. && pesfi
  3008. && (DF_ILIsEqual(&pesfi->idl, pidl)))
  3009. {
  3010. iItem = _pff->_iGetIDList;
  3011. bFound = TRUE;
  3012. }
  3013. else
  3014. {
  3015. if (SUCCEEDED(_pff->GetItemCount(&cItems)))
  3016. {
  3017. for (iItem = 0; iItem < cItems; iItem++)
  3018. {
  3019. if (SUCCEEDED(_pff->GetItem(iItem, &pesfi)) && pesfi && (DF_ILIsEqual(&pesfi->idl, pidl)))
  3020. {
  3021. bFound = TRUE;
  3022. break;
  3023. }
  3024. }
  3025. }
  3026. }
  3027. if (bFound)
  3028. {
  3029. _pff->DeleteItem(iItem);
  3030. }
  3031. return S_OK;
  3032. }
  3033. HRESULT CFindFolderViewCB::OnODFindItem(DWORD pv, int * piItem, NM_FINDITEM* pnmfi)
  3034. {
  3035. // We have to do the subsearch ourself to find the correct item...
  3036. // As the listview has no information saved in it...
  3037. int iItem = pnmfi->iStart;
  3038. int cItem;
  3039. UINT flags = pnmfi->lvfi.flags;
  3040. if (FAILED(_pff->GetItemCount(&cItem)))
  3041. return E_FAIL;
  3042. if ((flags & LVFI_STRING) == 0)
  3043. return E_FAIL; // Not sure what type of search this is...
  3044. int cbString = lstrlen(pnmfi->lvfi.psz);
  3045. for (int j = cItem; j-- != 0;)
  3046. {
  3047. if (iItem >= cItem)
  3048. {
  3049. if (flags & LVFI_WRAP)
  3050. iItem = 0;
  3051. else
  3052. break;
  3053. }
  3054. // Now we need to get the Display name for this item...
  3055. FIND_ITEM *pesfi;
  3056. TCHAR szPath[MAX_PATH];
  3057. IShellFolder* psf = (IShellFolder*)_pff;
  3058. if (SUCCEEDED(_pff->GetItem(iItem, &pesfi)) && pesfi &&
  3059. SUCCEEDED(DisplayNameOf(psf, &pesfi->idl, NULL, szPath, ARRAYSIZE(szPath))))
  3060. {
  3061. if (flags & (LVFI_PARTIAL|LVFI_SUBSTRING))
  3062. {
  3063. if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
  3064. pnmfi->lvfi.psz, cbString, szPath, cbString) == 2)
  3065. {
  3066. *piItem = iItem;
  3067. return S_OK;
  3068. }
  3069. }
  3070. else if (lstrcmpi(pnmfi->lvfi.psz, szPath) == 0)
  3071. {
  3072. *piItem = iItem;
  3073. return S_OK;
  3074. }
  3075. }
  3076. ++iItem;
  3077. }
  3078. return E_FAIL;
  3079. }
  3080. HRESULT CFindFolderViewCB::OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA *lP)
  3081. {
  3082. // Try to remember which item is focused...
  3083. if (lP->uNewState & LVIS_FOCUSED)
  3084. _iFocused = wPh;
  3085. return S_OK;
  3086. }
  3087. HRESULT CFindFolderViewCB::OnSetEmptyText(DWORD pv, UINT res, LPCTSTR pszText)
  3088. {
  3089. if (pszText && 0 == lstrcmp(_szEmptyText, pszText))
  3090. return S_OK;
  3091. lstrcpyn(_szEmptyText, pszText ? pszText : TEXT(""), ARRAYSIZE(_szEmptyText));
  3092. HWND hwndLV = ListviewFromViewUnk(_punkSite);
  3093. if (hwndLV)
  3094. SendMessage(hwndLV, LVM_RESETEMPTYTEXT, 0, 0);
  3095. return S_OK;
  3096. }
  3097. HRESULT CFindFolderViewCB::OnGetEmptyText(DWORD pv, UINT cchTextMax, LPTSTR pszText)
  3098. {
  3099. if (_szEmptyText[0])
  3100. {
  3101. lstrcpyn(pszText, _szEmptyText, cchTextMax);
  3102. }
  3103. else
  3104. {
  3105. LoadString(HINST_THISDLL, IDS_FINDVIEWEMPTYINIT, pszText, cchTextMax);
  3106. }
  3107. return S_OK;
  3108. }
  3109. HRESULT CFindFolderViewCB::OnReArrange(DWORD pv, LPARAM lparam)
  3110. {
  3111. UINT nCol = (UINT)lparam;
  3112. // See if there is any controller object registered that may want to take over this...
  3113. // if we are in a mixed query and we have already fetched the async items, simply sort
  3114. // the dpa's...
  3115. IFindEnum *pidfenum;
  3116. if (S_OK == _pff->GetAsyncEnum(&pidfenum))
  3117. {
  3118. if (!((pidfenum->FQueryIsAsync() == DF_QUERYISMIXED) && _pff->_fAllAsyncItemsCached))
  3119. {
  3120. if (_pff->_pfcn)
  3121. {
  3122. // if they return S_FALSE it implies that they handled it and they do not
  3123. // want the default processing to happen...
  3124. if (_pff->_pfcn->DoSortOnColumn(nCol, _iColSort == nCol) == S_FALSE)
  3125. {
  3126. _iColSort = nCol;
  3127. return S_OK;
  3128. }
  3129. }
  3130. else
  3131. {
  3132. // If we are running in the ROWSET way, we may want to have the ROWSET do the work...
  3133. // pass one we spawn off a new search with the right column sorted
  3134. if (_iColSort != nCol)
  3135. {
  3136. _iColSort = nCol;
  3137. }
  3138. // Warning the call above may release our AsyncEnum and generate a new one so
  3139. // Don't rely on it's existence here...
  3140. return S_OK;
  3141. }
  3142. }
  3143. // we must pull in all the results from ci
  3144. if (pidfenum->FQueryIsAsync() && !_pff->_fAllAsyncItemsCached)
  3145. _pff->CacheAllAsyncItems();
  3146. #ifdef DEBUG
  3147. #define MAX_LISTVIEWITEMS (100000000 & ~0xFFFF)
  3148. #define SANE_ITEMCOUNT(c) ((int)min(c, MAX_LISTVIEWITEMS))
  3149. if (pidfenum->FQueryIsAsync())
  3150. {
  3151. ASSERT(DPA_GetPtrCount(_pff->_hdpaItems) >= SANE_ITEMCOUNT(_pff->_cAsyncItems));
  3152. for (int i = 0; i < SANE_ITEMCOUNT(_pff->_cAsyncItems); i++)
  3153. {
  3154. FIND_ITEM *pesfi = (FIND_ITEM *)DPA_GetPtr(_pff->_hdpaItems, i);
  3155. ASSERT(pesfi);
  3156. if (!pesfi)
  3157. {
  3158. ASSERT(SUCCEEDED(_pff->GetItem(i, &pesfi)));
  3159. }
  3160. }
  3161. }
  3162. #endif
  3163. }
  3164. // Use the common sort.
  3165. return E_FAIL;
  3166. }
  3167. HRESULT CFindFolderViewCB::OnWindowCreated(DWORD pv, HWND hwnd)
  3168. {
  3169. _ProfferService(TRUE); // register our service w/ top level container
  3170. return S_OK;
  3171. }
  3172. HRESULT CFindFolderViewCB::_ProfferService(BOOL bProffer)
  3173. {
  3174. HRESULT hr = E_FAIL;
  3175. if (bProffer)
  3176. {
  3177. // shouldn't be redundantly registering our service
  3178. ASSERT(NULL == _pps);
  3179. ASSERT(-1 == _dwServiceCookie);
  3180. IProfferService* pps;
  3181. hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IProfferService, &pps));
  3182. if (SUCCEEDED(hr))
  3183. {
  3184. hr = pps->ProfferService(SID_DocFindFolder, this, &_dwServiceCookie);
  3185. if (SUCCEEDED(hr))
  3186. {
  3187. pps->AddRef();
  3188. _pps = pps;
  3189. }
  3190. pps->Release();
  3191. }
  3192. }
  3193. else
  3194. {
  3195. if (NULL == _pps)
  3196. {
  3197. hr = S_OK;
  3198. }
  3199. else
  3200. {
  3201. hr = _pps->RevokeService(_dwServiceCookie);
  3202. if (SUCCEEDED(hr))
  3203. {
  3204. ATOMICRELEASE(_pps);
  3205. _dwServiceCookie = -1;
  3206. }
  3207. }
  3208. }
  3209. return hr;
  3210. }
  3211. HRESULT CFindFolderViewCB::OnWindowDestroy(DWORD pv, HWND wP)
  3212. {
  3213. _ProfferService(FALSE); // unregister our service w/ top level container
  3214. if (_pff->_pfcn)
  3215. _pff->_pfcn->StopSearch();
  3216. // The search may have a circular set of pointers. So call the
  3217. // delete items and folders here to remove these back references...
  3218. _pff->ClearItemList();
  3219. _pff->ClearFolderList();
  3220. IFindControllerNotify *pfcn;
  3221. if (_pff->GetControllerNotifyObject(&pfcn) == S_OK)
  3222. {
  3223. pfcn->ViewDestroyed();
  3224. pfcn->Release();
  3225. }
  3226. return S_OK;
  3227. }
  3228. HRESULT CFindFolderViewCB::OnIsOwnerData(DWORD pv, DWORD *pdwFlags)
  3229. {
  3230. *pdwFlags |= FWF_OWNERDATA; // we want virtual defview support
  3231. return S_OK;
  3232. }
  3233. HRESULT CFindFolderViewCB::OnGetODRangeObject(DWORD pv, WPARAM wWhich, ILVRange **plvr)
  3234. {
  3235. HRESULT hr = E_FAIL;
  3236. switch (wWhich)
  3237. {
  3238. case LVSR_SELECTION:
  3239. hr = _pff->_dflvrSel.QueryInterface(IID_PPV_ARG(ILVRange, plvr));
  3240. break;
  3241. case LVSR_CUT:
  3242. hr = _pff->_dflvrCut.QueryInterface(IID_PPV_ARG(ILVRange, plvr));
  3243. break;
  3244. }
  3245. return hr;
  3246. }
  3247. HRESULT CFindFolderViewCB::OnODCacheHint(DWORD pv, NMLVCACHEHINT* pnmlvc)
  3248. {
  3249. // The listview is giving us a hint of the items it is about to do something in a range
  3250. // so make sure we have pidls for each of the items in the range...
  3251. int iTo;
  3252. _pff->GetItemCount(&iTo);
  3253. if (iTo >= pnmlvc->iTo)
  3254. iTo = pnmlvc->iTo;
  3255. else
  3256. iTo--;
  3257. for (int i = pnmlvc->iFrom; i <= iTo; i++)
  3258. {
  3259. FIND_ITEM *pesfi;
  3260. if (FAILED(_pff->GetItem(i, &pesfi)))
  3261. break;
  3262. }
  3263. return S_OK;
  3264. }
  3265. HRESULT CFindFolderViewCB::OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP)
  3266. {
  3267. *lP = FVM_DETAILS; // match the advanced mode of SC (+ Win2K parity)
  3268. return S_OK;
  3269. }
  3270. HRESULT CFindFolderViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
  3271. {
  3272. ZeroMemory(pData, sizeof(*pData));
  3273. pData->dwLayout = SFVMWVL_DETAILS | SFVMWVL_FILES;
  3274. return S_OK;
  3275. }
  3276. HRESULT CFindFolderViewCB::_OnOpenContainingFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  3277. {
  3278. CFindFolderViewCB* pThis = (CFindFolderViewCB*)(void*)pv;
  3279. return pThis->_pff->OpenContainingFolder(pThis->_punkSite);
  3280. }
  3281. const WVTASKITEM c_FindTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_SEARCH, IDS_HEADER_FIND_TT);
  3282. const WVTASKITEM c_FindTaskList[] =
  3283. {
  3284. WVTI_ENTRY_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_OPENCONTAININGFOLDER, IDS_TASK_OPENCONTAININGFOLDER, 0, IDS_TASK_OPENCONTAININGFOLDER_TT, IDI_TASK_OPENCONTAININGFOLDER, NULL, CFindFolderViewCB::_OnOpenContainingFolder),
  3285. };
  3286. HRESULT CFindFolderViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
  3287. {
  3288. ZeroMemory(pData, sizeof(*pData));
  3289. Create_IUIElement(&c_FindTaskHeader, &(pData->pSpecialTaskHeader));
  3290. LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_NETWORK) };
  3291. CreateIEnumIDListOnCSIDLs(_pidl, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &pData->penumOtherPlaces);
  3292. return S_OK;
  3293. }
  3294. HRESULT CFindFolderViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
  3295. {
  3296. ZeroMemory(pTasks, sizeof(*pTasks));
  3297. Create_IEnumUICommand((IUnknown*)(void*)this, c_FindTaskList, ARRAYSIZE(c_FindTaskList), &pTasks->penumSpecialTasks);
  3298. return S_OK;
  3299. }
  3300. HRESULT CFindFolderViewCB::OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme)
  3301. {
  3302. ZeroMemory(pTheme, sizeof(*pTheme));
  3303. pTheme->pszThemeID = L"search";
  3304. return S_OK;
  3305. }
  3306. HRESULT CFindFolderViewCB::OnGetIPersistHistory(DWORD pv, IPersistHistory **ppph)
  3307. {
  3308. // If they call us with ppph == NULL they simply want to know if we support
  3309. // the history so return S_OK;
  3310. if (ppph == NULL)
  3311. return S_OK;
  3312. // get the persist history from us and we hold folder and view objects
  3313. *ppph = NULL;
  3314. CFindPersistHistory *pdfph = new CFindPersistHistory();
  3315. if (!pdfph)
  3316. return E_OUTOFMEMORY;
  3317. HRESULT hr = pdfph->QueryInterface(IID_PPV_ARG(IPersistHistory, ppph));
  3318. pdfph->Release();
  3319. return hr;
  3320. }
  3321. HRESULT CFindFolderViewCB::OnRefresh(DWORD pv, BOOL fPreRefresh)
  3322. {
  3323. EnterCriticalSection(&_pff->_csSearch);
  3324. _pff->_fInRefresh = BOOLIFY(fPreRefresh);
  3325. // If we have old results tell defview the new count now...
  3326. if (!fPreRefresh && _pff->_hdpaItems)
  3327. {
  3328. IShellFolderView *psfv;
  3329. UINT cItems = DPA_GetPtrCount(_pff->_hdpaItems);
  3330. if (cItems && _punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
  3331. {
  3332. psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
  3333. psfv->Release();
  3334. }
  3335. }
  3336. LeaveCriticalSection(&_pff->_csSearch);
  3337. return S_OK;
  3338. }
  3339. HRESULT CFindFolderViewCB::OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *phtd)
  3340. {
  3341. if (IsOS(OS_ANYSERVER))
  3342. {
  3343. StrCpyW(phtd->wszHelpFile, L"find.chm");
  3344. }
  3345. else
  3346. {
  3347. StrCpyW(phtd->wszHelpTopic, L"hcp://services/subsite?node=Unmapped/Search");
  3348. }
  3349. return S_OK;
  3350. }
  3351. STDMETHODIMP CFindFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  3352. {
  3353. switch (uMsg)
  3354. {
  3355. HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
  3356. HANDLE_MSG(0, SFVM_GETWORKINGDIR, OnGETWORKINGDIR);
  3357. HANDLE_MSG(0, SFVM_GETCOLSAVESTREAM, OnGETCOLSAVESTREAM);
  3358. HANDLE_MSG(0, SFVM_GETITEMIDLIST, OnGETITEMIDLIST);
  3359. HANDLE_MSG(0, SFVM_SETITEMIDLIST, OnSETITEMIDLIST);
  3360. HANDLE_MSG(0, SFVM_SELCHANGE, OnSelChange);
  3361. HANDLE_MSG(0, SFVM_INDEXOFITEMIDLIST, OnGetIndexForItemIDList);
  3362. HANDLE_MSG(0, SFVM_DELETEITEM, OnDeleteItem);
  3363. HANDLE_MSG(0, SFVM_ODFINDITEM, OnODFindItem);
  3364. HANDLE_MSG(0, SFVM_ARRANGE, OnReArrange);
  3365. HANDLE_MSG(0, SFVM_GETEMPTYTEXT, OnGetEmptyText);
  3366. HANDLE_MSG(0, SFVM_SETEMPTYTEXT, OnSetEmptyText);
  3367. HANDLE_MSG(0, SFVM_GETITEMICONINDEX, OnGetItemIconIndex);
  3368. HANDLE_MSG(0, SFVM_SETICONOVERLAY, OnSetItemIconOverlay);
  3369. HANDLE_MSG(0, SFVM_GETICONOVERLAY, OnGetItemIconOverlay);
  3370. HANDLE_MSG(0, SFVM_FOLDERSETTINGSFLAGS, OnIsOwnerData);
  3371. HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
  3372. HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWindowDestroy);
  3373. HANDLE_MSG(0, SFVM_GETODRANGEOBJECT, OnGetODRangeObject);
  3374. HANDLE_MSG(0, SFVM_ODCACHEHINT, OnODCacheHint);
  3375. HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE);
  3376. HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
  3377. HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
  3378. HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
  3379. HANDLE_MSG(0, SFVM_GETWEBVIEWTHEME, OnGetWebViewTheme);
  3380. HANDLE_MSG(0, SFVM_GETIPERSISTHISTORY, OnGetIPersistHistory);
  3381. HANDLE_MSG(0, SFVM_REFRESH, OnRefresh);
  3382. HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
  3383. HANDLE_MSG(0, SFVM_SORTLISTDATA, OnSortListData);
  3384. default:
  3385. return E_FAIL;
  3386. }
  3387. return S_OK;
  3388. }
  3389. CFindFolderViewCB::CFindFolderViewCB(CFindFolder* pff) :
  3390. CBaseShellFolderViewCB(pff->_pidl, 0), _pff(pff), _fIgnoreSelChange(FALSE),
  3391. _iColSort((UINT)-1), _iFocused((UINT)-1), _cSelected(0), _pps(NULL), _dwServiceCookie(-1)
  3392. {
  3393. _pff->AddRef();
  3394. }
  3395. CFindFolderViewCB::~CFindFolderViewCB()
  3396. {
  3397. _pff->Release();
  3398. ASSERT(NULL == _pps);
  3399. ASSERT(_dwServiceCookie == -1);
  3400. }
  3401. // give the find command code access to defview via this QS that we proffered
  3402. HRESULT CFindFolderViewCB::QueryService(REFGUID guidService, REFIID riid, void **ppv)
  3403. {
  3404. HRESULT hr = E_NOTIMPL;
  3405. *ppv = NULL;
  3406. if (guidService == SID_DocFindFolder)
  3407. {
  3408. hr = IUnknown_QueryService(_punkSite, SID_DefView, riid, ppv);
  3409. }
  3410. return hr;
  3411. }
  3412. CFindPersistHistory::CFindPersistHistory()
  3413. {
  3414. }
  3415. STDAPI CFindPersistHistory_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  3416. {
  3417. HRESULT hr;
  3418. CFindPersistHistory *pdfph = new CFindPersistHistory();
  3419. if (pdfph)
  3420. {
  3421. hr = pdfph->QueryInterface(riid, ppv);
  3422. pdfph->Release();
  3423. }
  3424. else
  3425. {
  3426. *ppv = NULL;
  3427. hr = E_OUTOFMEMORY;
  3428. }
  3429. return hr;
  3430. }
  3431. // Functions to support persisting the document into the history stream...
  3432. STDMETHODIMP CFindPersistHistory::GetClassID(CLSID *pClassID)
  3433. {
  3434. *pClassID = CLSID_DocFindPersistHistory;
  3435. return S_OK;
  3436. }
  3437. IFindFolder *CFindPersistHistory::_GetDocFindFolder()
  3438. {
  3439. IFindFolder *pdff = NULL;
  3440. // the _punksite is to the defview so we can simply QI for frame...
  3441. IFolderView *pfv;
  3442. if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
  3443. {
  3444. pfv->GetFolder(IID_PPV_ARG(IFindFolder, &pdff));
  3445. pfv->Release();
  3446. }
  3447. return pdff;
  3448. }
  3449. STDMETHODIMP CFindPersistHistory::LoadHistory(IStream *pstm, IBindCtx *pbc)
  3450. {
  3451. int cItems = 0;
  3452. IFindFolder *pdff = _GetDocFindFolder();
  3453. if (pdff)
  3454. {
  3455. pdff->RestoreFolderList(pstm);
  3456. pdff->RestoreItemList(pstm, &cItems);
  3457. pdff->Release();
  3458. }
  3459. IShellFolderView *psfv;
  3460. if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv))))
  3461. {
  3462. psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
  3463. psfv->Release();
  3464. }
  3465. // call our base class to allow it to restore it's stuff as well.
  3466. return CDefViewPersistHistory::LoadHistory(pstm, pbc);
  3467. }
  3468. STDMETHODIMP CFindPersistHistory::SaveHistory(IStream *pstm)
  3469. {
  3470. IFindFolder *pdff = _GetDocFindFolder();
  3471. if (pdff)
  3472. {
  3473. pdff->SaveFolderList(pstm);
  3474. pdff->SaveItemList(pstm);
  3475. pdff->Release();
  3476. }
  3477. // Let base class save out as well
  3478. return CDefViewPersistHistory::SaveHistory(pstm);
  3479. }
  3480. // use to manage the selection states for an owner data listview...
  3481. STDMETHODIMP_(ULONG) CFindLVRange::AddRef()
  3482. {
  3483. return _pff->AddRef();
  3484. }
  3485. STDMETHODIMP_(ULONG) CFindLVRange::Release()
  3486. {
  3487. return _pff->Release();
  3488. }
  3489. STDMETHODIMP CFindLVRange::QueryInterface(REFIID riid, void **ppv)
  3490. {
  3491. static const QITAB qit[] = {
  3492. QITABENT(CFindLVRange, ILVRange), // IID_ILVRange
  3493. { 0 },
  3494. };
  3495. return QISearch(this, qit, riid, ppv);
  3496. }
  3497. // ILVRange methods
  3498. STDMETHODIMP CFindLVRange::IncludeRange(LONG iBegin, LONG iEnd)
  3499. {
  3500. // Including the range must load the elements as we need the object ptr...
  3501. FIND_ITEM *pesfi;
  3502. int iTotal;
  3503. _pff->GetItemCount(&iTotal);
  3504. if (iEnd > iTotal)
  3505. iEnd = iTotal-1;
  3506. for (long i = iBegin; i <= iEnd;i++)
  3507. {
  3508. if (SUCCEEDED(_pff->GetItem(i, &pesfi)) && pesfi)
  3509. {
  3510. if ((pesfi->dwState & _dwMask) == 0)
  3511. {
  3512. _cIncluded++;
  3513. pesfi->dwState |= _dwMask;
  3514. }
  3515. }
  3516. }
  3517. return S_OK;
  3518. }
  3519. STDMETHODIMP CFindLVRange::ExcludeRange(LONG iBegin, LONG iEnd)
  3520. {
  3521. // Excluding the range is OK to not load the elements as this would be to deslect all...
  3522. EnterCriticalSection(&_pff->_csSearch);
  3523. if (iEnd >= DPA_GetPtrCount(_pff->_hdpaItems))
  3524. iEnd = DPA_GetPtrCount(_pff->_hdpaItems) - 1;
  3525. for (long i = iBegin; i <= iEnd; i++)
  3526. {
  3527. FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_pff->_hdpaItems, i);
  3528. if (pesfi)
  3529. {
  3530. if (pesfi->dwState & _dwMask)
  3531. {
  3532. _cIncluded--;
  3533. pesfi->dwState &= ~_dwMask;
  3534. }
  3535. }
  3536. }
  3537. LeaveCriticalSection(&_pff->_csSearch);
  3538. return S_OK;
  3539. }
  3540. STDMETHODIMP CFindLVRange::InvertRange(LONG iBegin, LONG iEnd)
  3541. {
  3542. // Including the range must load the elements as we need the object ptr...
  3543. int iTotal;
  3544. _pff->GetItemCount(&iTotal);
  3545. if (iEnd > iTotal)
  3546. iEnd = iTotal-1;
  3547. for (long i = iBegin; i <= iEnd;i++)
  3548. {
  3549. FIND_ITEM *pesfi;
  3550. if (SUCCEEDED(_pff->GetItem(i, &pesfi)) && pesfi)
  3551. {
  3552. if ((pesfi->dwState & _dwMask) == 0)
  3553. {
  3554. _cIncluded++;
  3555. pesfi->dwState |= _dwMask;
  3556. }
  3557. else
  3558. {
  3559. _cIncluded--;
  3560. pesfi->dwState &= ~_dwMask;
  3561. }
  3562. }
  3563. }
  3564. return S_OK;
  3565. }
  3566. STDMETHODIMP CFindLVRange::InsertItem(LONG iItem)
  3567. {
  3568. // We already maintain the list anyway...
  3569. return S_OK;
  3570. }
  3571. STDMETHODIMP CFindLVRange::RemoveItem(LONG iItem)
  3572. {
  3573. // We maintain the list so don't do anything...
  3574. return S_OK;
  3575. }
  3576. STDMETHODIMP CFindLVRange::Clear()
  3577. {
  3578. // If there are things selected, need to unselect them now...
  3579. if (_cIncluded)
  3580. ExcludeRange(0, LONG_MAX);
  3581. _cIncluded = 0;
  3582. _pff->ClearSaveStateList();
  3583. return S_OK;
  3584. }
  3585. STDMETHODIMP CFindLVRange::IsSelected(LONG iItem)
  3586. {
  3587. // Don't force the items to be generated if they were not before...
  3588. HRESULT hr = S_FALSE;
  3589. EnterCriticalSection(&_pff->_csSearch);
  3590. FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem);
  3591. if (pesfi)
  3592. hr = pesfi->dwState & _dwMask ? S_OK : S_FALSE;
  3593. LeaveCriticalSection(&_pff->_csSearch);
  3594. // Assume not selected if we don't have the item yet...
  3595. return hr;
  3596. }
  3597. STDMETHODIMP CFindLVRange::IsEmpty()
  3598. {
  3599. return _cIncluded ? S_FALSE : S_OK;
  3600. }
  3601. STDMETHODIMP CFindLVRange::NextSelected(LONG iItem, LONG *piItem)
  3602. {
  3603. EnterCriticalSection(&_pff->_csSearch);
  3604. LONG cItems = DPA_GetPtrCount(_pff->_hdpaItems);
  3605. while (iItem < cItems)
  3606. {
  3607. FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem);
  3608. if (pesfi && (pesfi->dwState & _dwMask))
  3609. {
  3610. *piItem = iItem;
  3611. LeaveCriticalSection(&_pff->_csSearch);
  3612. return S_OK;
  3613. }
  3614. iItem++;
  3615. }
  3616. LeaveCriticalSection(&_pff->_csSearch);
  3617. *piItem = -1;
  3618. return S_FALSE;
  3619. }
  3620. STDMETHODIMP CFindLVRange::NextUnSelected(LONG iItem, LONG *piItem)
  3621. {
  3622. EnterCriticalSection(&_pff->_csSearch);
  3623. LONG cItems = DPA_GetPtrCount(_pff->_hdpaItems);
  3624. while (iItem < cItems)
  3625. {
  3626. FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem);
  3627. if (!pesfi || ((pesfi->dwState & _dwMask) == 0))
  3628. {
  3629. *piItem = iItem;
  3630. LeaveCriticalSection(&_pff->_csSearch);
  3631. return S_OK;
  3632. }
  3633. iItem++;
  3634. }
  3635. LeaveCriticalSection(&_pff->_csSearch);
  3636. *piItem = -1;
  3637. return S_FALSE;
  3638. }
  3639. STDMETHODIMP CFindLVRange::CountIncluded(LONG *pcIncluded)
  3640. {
  3641. *pcIncluded = _cIncluded;
  3642. // Sortof Gross, but if looking at selection then also include the list of items
  3643. // that are selected in our save list...
  3644. if (_dwMask & LVIS_SELECTED)
  3645. *pcIncluded += _pff->_cSaveStateSelected;
  3646. return S_OK;
  3647. }
  3648. // Define OleDBEnum translation structure...
  3649. typedef struct _dfodbet // DFET for short
  3650. {
  3651. struct _dfodbet *pdfetNext;
  3652. LPWSTR pwszFrom;
  3653. int cbFrom;
  3654. LPWSTR pwszTo;
  3655. } DFODBET;
  3656. class CContentIndexEnum : public IFindEnum, public IShellService
  3657. {
  3658. public:
  3659. // IUnknown
  3660. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  3661. STDMETHODIMP_(ULONG) AddRef(void);
  3662. STDMETHODIMP_(ULONG) Release(void);
  3663. // IFindEnum
  3664. STDMETHODIMP Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState);
  3665. STDMETHODIMP Skip(int celt);
  3666. STDMETHODIMP Reset();
  3667. STDMETHODIMP StopSearch();
  3668. STDMETHODIMP_(BOOL) FQueryIsAsync();
  3669. STDMETHODIMP GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone);
  3670. STDMETHODIMP GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl);
  3671. STDMETHODIMP GetItemID(UINT iItem, DWORD *puWorkID);
  3672. STDMETHODIMP SortOnColumn(UINT iCOl, BOOL fAscending);
  3673. // IShellService
  3674. STDMETHODIMP SetOwner(IUnknown* punkOwner);
  3675. CContentIndexEnum(IFindFilter *pfilter, IFindFolder *pff, DWORD grfFlags,
  3676. int iColSort, LPTSTR pszProgressText, IRowsetWatchNotify *prwn);
  3677. HRESULT DoQuery(LPWSTR *apwszPaths, UINT *pcPaths);
  3678. private:
  3679. ~CContentIndexEnum();
  3680. void HandleWatchNotify(DBWATCHNOTIFY eChangeReason);
  3681. HRESULT _BuildAndSetCommandTree(int iCol, BOOL fReverse);
  3682. HRESULT _SetCmdProp(ICommand *pCommand);
  3683. HRESULT _MapColumns(IUnknown *punk, DBORDINAL cCols, DBBINDING *pBindings, const DBID * pDbCols, HACCESSOR &hAccessor);
  3684. void _ReleaseAccessor();
  3685. HRESULT _CacheRowSet(UINT iItem);
  3686. BOOL _TranslateFolder(LPCWSTR pszParent, LPWSTR pszResult);
  3687. void _ClearFolderState();
  3688. LONG _cRef;
  3689. IFindFilter *_pfilter;
  3690. IRowsetWatchNotify *_prwn;
  3691. IFindFolder *_pff;
  3692. int _iColSort;
  3693. DWORD _grfFlags;
  3694. DWORD _grfWarnings;
  3695. LPTSTR _pszProgressText;
  3696. TCHAR _szCurrentDir[MAX_PATH];
  3697. IShellFolder *_psfCurrentDir;
  3698. LPITEMIDLIST _pidlFolder;
  3699. int _iFolder;
  3700. HRESULT _hrCurrent;
  3701. ICommand *_pCommand;
  3702. IRowsetLocate *_pRowset;
  3703. IRowsetAsynch *_pRowsetAsync;
  3704. HACCESSOR _hAccessor;
  3705. HACCESSOR _hAccessorWorkID;
  3706. HROW _ahrow[100]; // Cache 100 hrows out for now
  3707. UINT _ihrowFirst; // The index of which row is cached out first
  3708. DBCOUNTITEM _cRows; // number of hrows in _ahrow
  3709. DFODBET *_pdfetFirst; // Name translation list.
  3710. };
  3711. STDAPI CreateOleDBEnum(IFindFilter *pfilter, IShellFolder *psf,
  3712. LPWSTR *apwszPaths, UINT *pcPaths, DWORD grfFlags, int iColSort,
  3713. LPTSTR pszProgressText, IRowsetWatchNotify *prwn, IFindEnum **ppdfenum)
  3714. {
  3715. *ppdfenum = NULL;
  3716. HRESULT hr = E_OUTOFMEMORY;
  3717. IFindFolder *pff;
  3718. psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff));
  3719. CContentIndexEnum* pdfenum = new CContentIndexEnum(pfilter, pff, grfFlags, iColSort, pszProgressText, prwn);
  3720. if (pdfenum)
  3721. {
  3722. hr = pdfenum->DoQuery(apwszPaths, pcPaths);
  3723. if (hr == S_OK) // We only continue to use this if query returne S_OK...
  3724. *ppdfenum = (IFindEnum*)pdfenum;
  3725. else
  3726. {
  3727. pdfenum->Release(); // release the memory we allocated
  3728. }
  3729. }
  3730. if (pff)
  3731. pff->Release();
  3732. return hr;
  3733. }
  3734. const DBID c_aDbCols[] =
  3735. {
  3736. {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_NAME}},
  3737. {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_PATH}},
  3738. {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_ATTRIBUTES}},
  3739. {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_SIZE}},
  3740. {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_WRITETIME}},
  3741. {{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR) PROPID_QUERY_RANK}},
  3742. };
  3743. const DBID c_aDbWorkIDCols[] =
  3744. {
  3745. {{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR)PROPID_QUERY_WORKID}}
  3746. };
  3747. const LPCWSTR c_awszColSortNames[] = {
  3748. L"FileName[a],Path[a]",
  3749. L"Path[a],FileName[a]",
  3750. L"Size[a]",
  3751. NULL,
  3752. L"Write[a]",
  3753. L"Rank[d]"
  3754. };
  3755. const ULONG c_cDbCols = ARRAYSIZE(c_aDbCols);
  3756. const DBID c_dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0};
  3757. const GUID c_guidQueryExt = DBPROPSET_QUERYEXT;
  3758. const GUID c_guidRowsetProps = {0xc8b522be,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
  3759. CContentIndexEnum::CContentIndexEnum(IFindFilter *pfilter, IFindFolder *pff,
  3760. DWORD grfFlags, int iColSort, LPTSTR pszProgressText, IRowsetWatchNotify *prwn) :
  3761. _cRef(1), _ihrowFirst((UINT)-1), _pfilter(pfilter),
  3762. _pff(pff), _prwn(prwn), _grfFlags(grfFlags),
  3763. _grfWarnings(DFW_DEFAULT), _iColSort(iColSort), _pszProgressText(pszProgressText)
  3764. {
  3765. _szCurrentDir[0] = 0;
  3766. ASSERT(_pRowset == 0);
  3767. ASSERT(_pRowsetAsync == 0);
  3768. ASSERT(_pCommand == 0);
  3769. ASSERT(_hAccessor == 0);
  3770. ASSERT(_hAccessorWorkID ==0);
  3771. ASSERT(_cRows == 0);
  3772. if (_pfilter)
  3773. {
  3774. _pfilter->AddRef();
  3775. _pfilter->GetWarningFlags(&_grfWarnings);
  3776. }
  3777. if (_pff)
  3778. _pff->AddRef();
  3779. if (_prwn)
  3780. _prwn->AddRef();
  3781. }
  3782. void CContentIndexEnum::_ClearFolderState()
  3783. {
  3784. ATOMICRELEASE(_psfCurrentDir);
  3785. ILFree(_pidlFolder);
  3786. _pidlFolder = NULL;
  3787. _iFolder = -1;
  3788. _szCurrentDir[0] = 0;
  3789. }
  3790. CContentIndexEnum::~CContentIndexEnum()
  3791. {
  3792. ATOMICRELEASE(_pfilter);
  3793. ATOMICRELEASE(_pff);
  3794. ATOMICRELEASE(_prwn);
  3795. _ClearFolderState();
  3796. if (_pRowset)
  3797. {
  3798. ATOMICRELEASE(_pRowsetAsync);
  3799. // Release any cached rows.
  3800. _CacheRowSet((UINT)-1);
  3801. if (_hAccessor || _hAccessorWorkID)
  3802. _ReleaseAccessor();
  3803. _pRowset->Release();
  3804. }
  3805. ATOMICRELEASE(_pCommand);
  3806. // Release any name translations we may have allocated.
  3807. DFODBET *pdfet = _pdfetFirst;
  3808. while (pdfet)
  3809. {
  3810. DFODBET *pdfetT = pdfet;
  3811. pdfet = pdfet->pdfetNext; // First setup to look at the next item before we free stuff...
  3812. LocalFree((HLOCAL)pdfetT->pwszFrom);
  3813. LocalFree((HLOCAL)pdfetT->pwszTo);
  3814. LocalFree((HLOCAL)pdfetT);
  3815. }
  3816. }
  3817. HRESULT CContentIndexEnum::QueryInterface(REFIID riid, void **ppv)
  3818. {
  3819. static const QITAB qit[] = {
  3820. QITABENTMULTI(CContentIndexEnum, IUnknown, IFindEnum), // IID_IUNKNOWN
  3821. QITABENT(CContentIndexEnum, IShellService), // IID_IShellService
  3822. { 0 },
  3823. };
  3824. return QISearch(this, qit, riid, ppv);
  3825. }
  3826. ULONG CContentIndexEnum::AddRef()
  3827. {
  3828. return InterlockedIncrement(&_cRef);
  3829. }
  3830. ULONG CContentIndexEnum::Release()
  3831. {
  3832. if (InterlockedDecrement(&_cRef))
  3833. return _cRef;
  3834. delete this;
  3835. return 0;
  3836. }
  3837. HRESULT CContentIndexEnum::Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState)
  3838. {
  3839. return E_PENDING; // as good a return as any to say that we are async...
  3840. }
  3841. HRESULT CContentIndexEnum::Skip(int celt)
  3842. {
  3843. return E_NOTIMPL;
  3844. }
  3845. HRESULT CContentIndexEnum::Reset()
  3846. {
  3847. // overload Reset to mean dump the rowset cache!!!
  3848. _CacheRowSet(-1);
  3849. // still return failiure
  3850. return E_NOTIMPL;
  3851. }
  3852. HRESULT CContentIndexEnum::StopSearch()
  3853. {
  3854. // Lets see if we can find one that works...
  3855. HRESULT hr = _pCommand->Cancel();
  3856. if (FAILED(hr))
  3857. hr = _pRowsetAsync->Stop();
  3858. if (FAILED(hr))
  3859. {
  3860. IDBAsynchStatus *pdbas;
  3861. if (SUCCEEDED(_pRowset->QueryInterface(IID_PPV_ARG(IDBAsynchStatus, &pdbas))))
  3862. {
  3863. hr = pdbas->Abort(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN);
  3864. pdbas->Release();
  3865. }
  3866. }
  3867. return hr;
  3868. }
  3869. BOOL CContentIndexEnum::FQueryIsAsync()
  3870. {
  3871. return TRUE;
  3872. }
  3873. HRESULT CContentIndexEnum::GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone)
  3874. {
  3875. if (!_pRowsetAsync)
  3876. return E_FAIL;
  3877. BOOL fMore;
  3878. DBCOUNTITEM dwDen, dwNum;
  3879. HRESULT hr = _pRowsetAsync->RatioFinished(&dwDen, &dwNum, pdwTotalAsync, &fMore);
  3880. if (SUCCEEDED(hr))
  3881. {
  3882. *pfQueryDone = dwDen == dwNum;
  3883. *pnPercentComplete = dwDen ? (int)((dwNum * 100) / dwDen) : 100;
  3884. }
  3885. else
  3886. *pfQueryDone = TRUE; // in case that is all they are looking at...
  3887. return hr;
  3888. }
  3889. // modify pszPath until you can parse it, return result in *ppidl
  3890. HRESULT _StripToParseableName(LPTSTR pszPath, LPITEMIDLIST *ppidl)
  3891. {
  3892. *ppidl = NULL;
  3893. HRESULT hr = E_FAIL;
  3894. PathRemoveBackslash(pszPath);
  3895. while (PathRemoveFileSpec(pszPath) && FAILED(hr))
  3896. {
  3897. hr = SHParseDisplayName(pszPath, NULL, ppidl, 0, NULL);
  3898. }
  3899. return hr;
  3900. }
  3901. // we could not get pidl for this item for some reason. we have to put
  3902. // it in the list of bad items so that we can tell ci not to give it to
  3903. // us the next time we do search
  3904. void _ExcludeFromFutureSearch(LPCTSTR pszParent)
  3905. {
  3906. HKEY hkey;
  3907. TCHAR szParent[MAX_PATH];
  3908. StrCpyN(szParent, pszParent, ARRAYSIZE(szParent));
  3909. if (RegCreateKeyExW(HKEY_CURRENT_USER, CI_SPECIAL_FOLDERS, 0, L"", 0, KEY_WRITE | KEY_QUERY_VALUE, NULL, &hkey, NULL) == ERROR_SUCCESS)
  3910. {
  3911. LPITEMIDLIST pidlT;
  3912. if (SUCCEEDED(_StripToParseableName(szParent, &pidlT)))
  3913. {
  3914. ILFree(pidlT);
  3915. DWORD dwInsert = 0; // init to zero in case query info bellow fails
  3916. int iEnd;
  3917. TCHAR sz[MAX_PATH], szName[10];
  3918. RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwInsert, NULL, NULL, NULL, NULL);
  3919. // start from the end as there is a high chance we added this at the end
  3920. for (int i = dwInsert - 1; i >= 0; i--)
  3921. {
  3922. DWORD cb = sizeof(sz);
  3923. wsprintf(szName, L"%d", i);
  3924. if (RegQueryValueEx(hkey, szName, NULL, NULL, (BYTE *)sz, &cb) == ERROR_SUCCESS)
  3925. {
  3926. LPTSTR pszTemp = StrStrI(sz + 1, szParent); // +1 to pass " that's at the beginning of the string
  3927. if (pszTemp && pszTemp == sz + 1)
  3928. {
  3929. dwInsert = i; // overwrite this value
  3930. break;
  3931. }
  3932. else
  3933. {
  3934. iEnd = lstrlen(sz);
  3935. if (EVAL(iEnd > 1))
  3936. {
  3937. int iBackslash = iEnd - 3;
  3938. ASSERT(sz[iBackslash] == L'\\');
  3939. sz[iBackslash] = L'\0';
  3940. pszTemp = StrStrI(szParent, sz + 1);
  3941. sz[iBackslash] = L'\\';
  3942. if (pszTemp && pszTemp == szParent)
  3943. {
  3944. dwInsert = -1;
  3945. break;
  3946. }
  3947. }
  3948. }
  3949. }
  3950. }
  3951. if (dwInsert != -1)
  3952. {
  3953. wsprintf(szName, L"%d", dwInsert);
  3954. PathAppend(szParent, TEXT("*"));
  3955. PathQuoteSpaces(szParent);
  3956. RegSetValueEx(hkey, szName, 0, REG_SZ, (BYTE *)szParent, (lstrlen(szParent) + 1) * sizeof(szParent[0]));
  3957. }
  3958. }
  3959. RegCloseKey(hkey);
  3960. }
  3961. }
  3962. // If it is a UNC it might be one we need to translate, to handle the case that
  3963. // content index does not support redirected drives.
  3964. BOOL CContentIndexEnum::_TranslateFolder(LPCTSTR pszParent, LPTSTR pszResult)
  3965. {
  3966. BOOL fTranslated = FALSE;
  3967. StrCpyW(pszResult, pszParent); // default to the same
  3968. if (PathIsUNC(pszParent))
  3969. {
  3970. for (DFODBET *pdfet = _pdfetFirst; pdfet; pdfet = pdfet->pdfetNext)
  3971. {
  3972. if ((StrCmpNIW(pszParent, pdfet->pwszFrom, pdfet->cbFrom) == 0)
  3973. && (pszParent[pdfet->cbFrom] == L'\\'))
  3974. {
  3975. // Ok we have a translation to use.
  3976. fTranslated = TRUE;
  3977. StrCpyW(pszResult, pdfet->pwszTo);
  3978. // need + 1 here or we'll get something like w:\\winnt! bogus path, that is.
  3979. StrCatW(pszResult, &pszParent[pdfet->cbFrom + 1]);
  3980. }
  3981. }
  3982. }
  3983. return fTranslated;
  3984. }
  3985. HRESULT CContentIndexEnum::GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl)
  3986. {
  3987. *ppidl = NULL;
  3988. HRESULT hr = _CacheRowSet(iItem);
  3989. if (S_OK != hr)
  3990. {
  3991. return E_FAIL; // we could not get the item someone asked for, so error...
  3992. }
  3993. PROPVARIANT* data[c_cDbCols];
  3994. hr = _pRowset->GetData(_ahrow[iItem - _ihrowFirst], _hAccessor, &data);
  3995. if (S_OK == hr)
  3996. {
  3997. // data[0].pwszVal is the file name
  3998. // data[1].pwszVal is the full path (including file name)
  3999. // data[2].ulVal is the attribute
  4000. // data[3].ulVal is the size in byte
  4001. // data[4].filetime is the last write time in UTC
  4002. // data[5].ulVal is the rank of the item...
  4003. WIN32_FIND_DATA fd = {0};
  4004. fd.dwFileAttributes = data[2]->ulVal;
  4005. fd.nFileSizeLow = data[3]->ulVal;
  4006. fd.ftLastWriteTime = data[4]->filetime;
  4007. ASSERT(ShowSuperHidden() || !IsSuperHidden(fd.dwFileAttributes)); // query should exclude these
  4008. StrCpyW(fd.cFileName, data[0]->pwszVal);
  4009. WCHAR szParent[MAX_PATH];
  4010. StrCpyW(szParent, data[1]->pwszVal); // full path
  4011. PathRemoveFileSpec(szParent); // strip to parent folder path
  4012. WCHAR szTranslatedParent[MAX_PATH];
  4013. BOOL fTranslated = _TranslateFolder(szParent, szTranslatedParent);
  4014. if (lstrcmp(szParent, _szCurrentDir) != 0)
  4015. {
  4016. _ClearFolderState(); // our previous "current folder" state is now invalid
  4017. hr = SHParseDisplayName(szTranslatedParent, NULL, &_pidlFolder, 0, NULL);
  4018. if (SUCCEEDED(hr))
  4019. {
  4020. hr = _pff->AddFolder(_pidlFolder, TRUE, &_iFolder);
  4021. if (SUCCEEDED(hr))
  4022. {
  4023. hr = _pff->GetFolder(_iFolder, IID_PPV_ARG(IShellFolder, &_psfCurrentDir));
  4024. if (SUCCEEDED(hr))
  4025. {
  4026. // on succesful init of this folder save the cache key
  4027. lstrcpy(_szCurrentDir, szParent);
  4028. }
  4029. }
  4030. }
  4031. else if (hr != E_OUTOFMEMORY && !fTranslated)
  4032. {
  4033. _ExcludeFromFutureSearch(szParent);
  4034. }
  4035. _hrCurrent = hr; // save error state for next time around
  4036. if (FAILED(hr))
  4037. _ClearFolderState();
  4038. }
  4039. else
  4040. {
  4041. hr = _hrCurrent;
  4042. }
  4043. if (SUCCEEDED(hr))
  4044. {
  4045. // success implies the state of these variables
  4046. ASSERT((NULL != _psfCurrentDir) && (NULL != _pidlFolder) && (_iFolder > 0));
  4047. DWORD dwItemID;
  4048. GetItemID(iItem, &dwItemID);
  4049. LPITEMIDLIST pidl;
  4050. hr = SHSimpleIDListFromFindData2(_psfCurrentDir, &fd, &pidl);
  4051. if (SUCCEEDED(hr))
  4052. {
  4053. hr = _pff->AddDataToIDList(pidl, _iFolder, _pidlFolder, DFDF_EXTRADATA, iItem, dwItemID, data[5]->ulVal, ppidl);
  4054. ILFree(pidl);
  4055. }
  4056. }
  4057. else
  4058. {
  4059. // failure implies these should be clear
  4060. ASSERT((NULL == _psfCurrentDir) && (NULL == _pidlFolder));
  4061. LPITEMIDLIST pidlFull;
  4062. if (SUCCEEDED(_StripToParseableName(szTranslatedParent, &pidlFull)))
  4063. {
  4064. LPCITEMIDLIST pidlChild;
  4065. if (SUCCEEDED(SplitIDList(pidlFull, &_pidlFolder, &pidlChild)))
  4066. {
  4067. hr = _pff->AddFolder(_pidlFolder, TRUE, &_iFolder);
  4068. if (SUCCEEDED(hr))
  4069. {
  4070. hr = _pff->GetFolder(_iFolder, IID_PPV_ARG(IShellFolder, &_psfCurrentDir));
  4071. if (SUCCEEDED(hr))
  4072. {
  4073. hr = _pff->AddDataToIDList(pidlChild, _iFolder, _pidlFolder, DFDF_NONE, 0, 0, 0, ppidl);
  4074. if (SUCCEEDED(hr))
  4075. {
  4076. // on succesful init of this folder save the cache key
  4077. lstrcpy(_szCurrentDir, szTranslatedParent);
  4078. PathRemoveFileSpec(_szCurrentDir);
  4079. }
  4080. }
  4081. }
  4082. }
  4083. ILFree(pidlFull);
  4084. if (FAILED(hr))
  4085. _ClearFolderState();
  4086. }
  4087. }
  4088. }
  4089. return hr;
  4090. }
  4091. HRESULT CContentIndexEnum::GetItemID(UINT iItem, DWORD *puItemID)
  4092. {
  4093. *puItemID = (UINT)-1;
  4094. HRESULT hr = _CacheRowSet(iItem);
  4095. if (S_OK == hr)
  4096. {
  4097. PROPVARIANT* data[1];
  4098. hr = _pRowset->GetData(_ahrow[iItem - _ihrowFirst], _hAccessorWorkID, &data);
  4099. if (S_OK == hr)
  4100. {
  4101. // Only one data column so this is easy...
  4102. // The ULVal is the thing we are after...
  4103. *puItemID = data[0]->ulVal;
  4104. }
  4105. }
  4106. return hr;
  4107. }
  4108. HRESULT CContentIndexEnum::SortOnColumn(UINT iCol, BOOL fAscending)
  4109. {
  4110. // Ok We need to generate the Sort String...
  4111. return _BuildAndSetCommandTree(iCol, fAscending);
  4112. }
  4113. HRESULT CContentIndexEnum::SetOwner(IUnknown* punkOwner)
  4114. {
  4115. // Used to set the docfind folder and from that the filter.
  4116. ATOMICRELEASE(_pfilter);
  4117. ATOMICRELEASE(_pff);
  4118. if (punkOwner)
  4119. {
  4120. punkOwner->QueryInterface(IID_PPV_ARG(IFindFolder, &_pff));
  4121. if (_pff)
  4122. _pff->GetFindFilter(&_pfilter);
  4123. }
  4124. return S_OK;
  4125. }
  4126. HRESULT CContentIndexEnum::_MapColumns(IUnknown *punk, DBORDINAL cCols,
  4127. DBBINDING *pBindings, const DBID *pDbCols,
  4128. HACCESSOR &hAccessor)
  4129. {
  4130. DBORDINAL aMappedColumnIDs[c_cDbCols];
  4131. IColumnsInfo *pColumnsInfo;
  4132. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IColumnsInfo, &pColumnsInfo));
  4133. if (SUCCEEDED(hr))
  4134. {
  4135. hr = pColumnsInfo->MapColumnIDs(cCols, pDbCols, aMappedColumnIDs);
  4136. if (SUCCEEDED(hr))
  4137. {
  4138. for (ULONG i = 0; i < cCols; i++)
  4139. pBindings[i].iOrdinal = aMappedColumnIDs[i];
  4140. IAccessor *pIAccessor;
  4141. hr = punk->QueryInterface(IID_PPV_ARG(IAccessor, &pIAccessor));
  4142. if (SUCCEEDED(hr))
  4143. {
  4144. hAccessor = 0;
  4145. hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cCols, pBindings, 0, &hAccessor, 0);
  4146. pIAccessor->Release();
  4147. }
  4148. }
  4149. pColumnsInfo->Release();
  4150. }
  4151. return hr;
  4152. }
  4153. void CContentIndexEnum::_ReleaseAccessor()
  4154. {
  4155. IAccessor *pIAccessor;
  4156. HRESULT hr = _pRowset->QueryInterface(IID_PPV_ARG(IAccessor, &pIAccessor));
  4157. if (SUCCEEDED(hr))
  4158. {
  4159. if (_hAccessor)
  4160. pIAccessor->ReleaseAccessor(_hAccessor, 0);
  4161. if (_hAccessorWorkID)
  4162. pIAccessor->ReleaseAccessor(_hAccessorWorkID, 0);
  4163. pIAccessor->Release();
  4164. }
  4165. }
  4166. HRESULT CContentIndexEnum::_CacheRowSet(UINT iItem)
  4167. {
  4168. HRESULT hr = S_OK;
  4169. if (!_pRowset)
  4170. return E_FAIL;
  4171. if (!_cRows || !InRange(iItem, _ihrowFirst, _ihrowFirst+(UINT)_cRows-1) || (iItem == (UINT)-1))
  4172. {
  4173. // Release the last cached element we had.
  4174. if (_cRows != 0)
  4175. _pRowset->ReleaseRows(ARRAYSIZE(_ahrow), _ahrow, 0, 0, 0);
  4176. // See if we are simply releasing our cached data...
  4177. _cRows = 0;
  4178. _ihrowFirst = (UINT)-1;
  4179. if (iItem == (UINT)-1)
  4180. return S_OK;
  4181. // Ok try to read in the next on...
  4182. BYTE bBookMark = (BYTE) DBBMK_FIRST;
  4183. HROW *rghRows = (HROW *)_ahrow;
  4184. // change this to fetch 100 or so rows at the time -- huge perf improvment
  4185. hr = _pRowset->GetRowsAt(0, 0, sizeof(bBookMark), &bBookMark, iItem, ARRAYSIZE(_ahrow), &_cRows, &rghRows);
  4186. if (FAILED(hr))
  4187. return hr;
  4188. _ihrowFirst = iItem;
  4189. if ((DB_S_ENDOFROWSET == hr) || (_cRows == 0))
  4190. {
  4191. if (_cRows == 0)
  4192. _ihrowFirst = -1;
  4193. else
  4194. hr = S_OK; // we got some items and caller expects S_OK so change DB_S_ENDOFROWSET to noerror
  4195. }
  4196. }
  4197. return hr;
  4198. }
  4199. void CContentIndexEnum::HandleWatchNotify(DBWATCHNOTIFY eChangeReason)
  4200. {
  4201. // For now we will simply Acknoledge the change...
  4202. if (_prwn)
  4203. _prwn->OnChange(NULL, eChangeReason);
  4204. }
  4205. HRESULT CContentIndexEnum::_SetCmdProp(ICommand *pCommand)
  4206. {
  4207. #define MAX_PROPS 8
  4208. DBPROPSET aPropSet[MAX_PROPS];
  4209. DBPROP aProp[MAX_PROPS];
  4210. ULONG cProps = 0;
  4211. HRESULT hr;
  4212. // asynchronous query
  4213. aProp[cProps].dwPropertyID = DBPROP_IRowsetAsynch;
  4214. aProp[cProps].dwOptions = 0;
  4215. aProp[cProps].dwStatus = 0;
  4216. aProp[cProps].colid = c_dbcolNull;
  4217. aProp[cProps].vValue.vt = VT_BOOL;
  4218. aProp[cProps].vValue.boolVal = VARIANT_TRUE;
  4219. aPropSet[cProps].rgProperties = &aProp[cProps];
  4220. aPropSet[cProps].cProperties = 1;
  4221. aPropSet[cProps].guidPropertySet = c_guidRowsetProps;
  4222. cProps++;
  4223. // don't timeout queries
  4224. aProp[cProps].dwPropertyID = DBPROP_COMMANDTIMEOUT;
  4225. aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
  4226. aProp[cProps].dwStatus = 0;
  4227. aProp[cProps].colid = c_dbcolNull;
  4228. aProp[cProps].vValue.vt = VT_I4;
  4229. aProp[cProps].vValue.lVal = 0;
  4230. aPropSet[cProps].rgProperties = &aProp[cProps];
  4231. aPropSet[cProps].cProperties = 1;
  4232. aPropSet[cProps].guidPropertySet = c_guidRowsetProps;
  4233. cProps++;
  4234. // We can handle PROPVARIANTs
  4235. aProp[cProps].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
  4236. aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
  4237. aProp[cProps].dwStatus = 0;
  4238. aProp[cProps].colid = c_dbcolNull;
  4239. aProp[cProps].vValue.vt = VT_BOOL;
  4240. aProp[cProps].vValue.boolVal = VARIANT_TRUE;
  4241. aPropSet[cProps].rgProperties = &aProp[cProps];
  4242. aPropSet[cProps].cProperties = 1;
  4243. aPropSet[cProps].guidPropertySet = c_guidQueryExt;
  4244. cProps++;
  4245. ICommandProperties * pCmdProp = 0;
  4246. hr = pCommand->QueryInterface(IID_PPV_ARG(ICommandProperties, &pCmdProp));
  4247. if (SUCCEEDED(hr))
  4248. {
  4249. hr = pCmdProp->SetProperties(cProps, aPropSet);
  4250. pCmdProp->Release();
  4251. }
  4252. return hr;
  4253. }
  4254. // create the query command string
  4255. HRESULT CContentIndexEnum::_BuildAndSetCommandTree(int iCol, BOOL fReverse)
  4256. {
  4257. LPWSTR pwszRestrictions = NULL;
  4258. DWORD dwGQRFlags;
  4259. HRESULT hr = _pfilter->GenerateQueryRestrictions(&pwszRestrictions, &dwGQRFlags);
  4260. if (SUCCEEDED(hr))
  4261. {
  4262. ULONG ulDialect;
  4263. hr = _pfilter->GetQueryLanguageDialect(&ulDialect);
  4264. if (SUCCEEDED(hr))
  4265. {
  4266. // NOTE: hard coded to our current list of columns
  4267. WCHAR wszSort[80]; // use this to sort by different columns...
  4268. wszSort[0] = 0;
  4269. if ((iCol >= 0) && (iCol < ARRAYSIZE(c_awszColSortNames)) && c_awszColSortNames[iCol])
  4270. {
  4271. // Sort order is hardcoded for ascending.
  4272. StrCpyW(wszSort, c_awszColSortNames[iCol]);
  4273. StrCatW(wszSort, L",Path[a],FileName[a]");
  4274. }
  4275. DBCOMMANDTREE *pTree = NULL;
  4276. hr = CITextToFullTreeEx(pwszRestrictions, ulDialect,
  4277. L"FileName,Path,Attrib,Size,Write,Rank,WorkID",
  4278. wszSort[0] ? wszSort : NULL, 0, &pTree, 0, 0, LOCALE_USER_DEFAULT);
  4279. if (FAILED(hr))
  4280. {
  4281. // Map this to one that I know about
  4282. // Note: We will only do this if we require CI else we will try to fallback to old search...
  4283. // Note we are running into problems where CI says we are contained in a Catalog even if
  4284. // CI process is not running... So try to avoid this if possible
  4285. if (dwGQRFlags & GQR_REQUIRES_CI)
  4286. hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_CONSTRAINT);
  4287. }
  4288. if (SUCCEEDED(hr))
  4289. {
  4290. ICommandTree *pCmdTree;
  4291. hr = _pCommand->QueryInterface(IID_PPV_ARG(ICommandTree, &pCmdTree));
  4292. if (SUCCEEDED(hr))
  4293. {
  4294. hr = pCmdTree->SetCommandTree(&pTree, DBCOMMANDREUSE_NONE, FALSE);
  4295. pCmdTree->Release();
  4296. }
  4297. }
  4298. }
  4299. }
  4300. LocalFree((HLOCAL)pwszRestrictions);
  4301. return hr;
  4302. }
  4303. #define cbP (sizeof (PROPVARIANT *))
  4304. // [in, out] apwszPaths this is modified
  4305. // [in, out] pcPaths
  4306. HRESULT CContentIndexEnum::DoQuery(LPWSTR *apwszPaths, UINT *pcPaths)
  4307. {
  4308. UINT nPaths = *pcPaths;
  4309. WCHAR** aScopes = NULL;
  4310. WCHAR** aScopesOrig = NULL;
  4311. ULONG* aDepths = NULL;
  4312. WCHAR** aCatalogs = NULL;
  4313. WCHAR** aMachines = NULL;
  4314. WCHAR wszPath[MAX_PATH];
  4315. LPWSTR pwszPath = wszPath;
  4316. LPWSTR pszMachineAlloc = NULL, pszCatalogAlloc = NULL;
  4317. LPWSTR pwszMachine, pwszCatalog;
  4318. UINT i, iPath = 0;
  4319. DWORD dwQueryRestrictions;
  4320. // Initiailize all of our query values back to unused
  4321. _hAccessor = NULL;
  4322. _hAccessorWorkID = NULL;
  4323. _pRowset = NULL;
  4324. _pRowsetAsync = NULL;
  4325. _pCommand = NULL;
  4326. // Get array of search paths...
  4327. #define MAX_MACHINE_NAME_LEN 32
  4328. BOOL fIsCIRunning, fCiIndexed, fCiPermission;
  4329. GetCIStatus(&fIsCIRunning, &fCiIndexed, &fCiPermission);
  4330. // First pass see if we have anything that make use at all of CI if not lets simply bail and let
  4331. // old code walk the list...
  4332. HRESULT hr = _pfilter->GenerateQueryRestrictions(NULL, &dwQueryRestrictions);
  4333. if (FAILED(hr))
  4334. goto Abort;
  4335. if ((dwQueryRestrictions & GQR_MAKES_USE_OF_CI) == 0)
  4336. {
  4337. hr = S_FALSE;
  4338. goto Abort;
  4339. }
  4340. // allocate the arrays that we need to pass to CIMakeICommand and
  4341. // the buffers needed for the machine name and catalog name
  4342. aDepths = (ULONG*)LocalAlloc(LPTR, nPaths * sizeof(ULONG));
  4343. aScopes = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
  4344. aScopesOrig = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
  4345. aCatalogs = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
  4346. aMachines = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*));
  4347. pszMachineAlloc = pwszMachine = (LPWSTR)LocalAlloc(LPTR, nPaths * MAX_MACHINE_NAME_LEN * sizeof(WCHAR));
  4348. pszCatalogAlloc = pwszCatalog = (LPWSTR)LocalAlloc(LPTR, nPaths * MAX_PATH * sizeof(WCHAR));
  4349. if (!aDepths || !aScopes || !aScopesOrig || !aCatalogs ||
  4350. !aMachines || !pszMachineAlloc || !pszCatalogAlloc)
  4351. {
  4352. hr = E_OUTOFMEMORY;
  4353. goto Abort;
  4354. }
  4355. // This following loop does two things,
  4356. // 1. Check if all the scopes are indexed, if any one scope is not,
  4357. // fail the call and we'll do the win32 find.
  4358. // 2. Prepare the arrays of parameters that we need to pass to
  4359. // CIMakeICommand().
  4360. //
  4361. // NOTE: Reinerf says this code looks busted for nPaths > 1. See bug 199254 for comments.
  4362. for (i = 0; i < nPaths; i++)
  4363. {
  4364. ULONG cchMachine = MAX_MACHINE_NAME_LEN;
  4365. ULONG cchCatalog = MAX_PATH;
  4366. WCHAR wszUNCPath[MAX_PATH];
  4367. BOOL fRemapped = FALSE;
  4368. // if CI is not running we can still do ci queries on a remote drive (if it is indexed)
  4369. // so we cannot just bail if ci is not running on user's machine
  4370. if (!fIsCIRunning && !PathIsRemote(apwszPaths[i]))
  4371. continue; // do grep on this one
  4372. hr = LocateCatalogsW(apwszPaths[i], 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog);
  4373. if (hr != S_OK)
  4374. {
  4375. // see if by chance this is a network redirected drive. If so we CI does not handle
  4376. // these. See if we can remap to UNC path to ask again...
  4377. if (!PathIsUNC(apwszPaths[i]))
  4378. {
  4379. DWORD nLength = ARRAYSIZE(wszUNCPath);
  4380. // this api takes TCHAR, but we only compile this part for WINNT...
  4381. DWORD dwType = SHWNetGetConnection(apwszPaths[i], wszUNCPath, &nLength);
  4382. if ((dwType == NO_ERROR) || (dwType == ERROR_CONNECTION_UNAVAIL))
  4383. {
  4384. fRemapped = TRUE;
  4385. LPWSTR pwsz = PathSkipRootW(apwszPaths[i]);
  4386. if (pwsz)
  4387. PathAppendW(wszUNCPath, pwsz);
  4388. cchMachine = MAX_MACHINE_NAME_LEN; // reset in params
  4389. cchCatalog = MAX_PATH;
  4390. hr = LocateCatalogsW(wszUNCPath, 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog);
  4391. }
  4392. }
  4393. }
  4394. if (hr != S_OK)
  4395. {
  4396. continue; // this one is not indexed.
  4397. }
  4398. if (S_FALSE == CatalogUptodate(pwszCatalog, pwszMachine))
  4399. {
  4400. // not up todate
  4401. if (dwQueryRestrictions & GQR_REQUIRES_CI)
  4402. {
  4403. // ci not up to date and we must use it..
  4404. // inform the user that results may not be complete
  4405. if (!(_grfWarnings & DFW_IGNORE_INDEXNOTCOMPLETE))
  4406. {
  4407. hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXNOTCOMPLETE);
  4408. goto Abort;
  4409. }
  4410. //else use ci although index is not complete
  4411. }
  4412. else
  4413. {
  4414. // ci is not upto date so just use grep for this drive so user can get
  4415. // complete results
  4416. pwszCatalog[0] = 0;
  4417. pwszMachine[0] = 0;
  4418. continue;
  4419. }
  4420. }
  4421. aDepths[iPath] = (_grfFlags & DFOO_INCLUDESUBDIRS) ? QUERY_DEEP : QUERY_SHALLOW;
  4422. aScopesOrig[iPath] = apwszPaths[i];
  4423. if (fRemapped)
  4424. {
  4425. aScopes[iPath] = StrDupW(wszUNCPath);
  4426. if (aScopes[iPath] == NULL)
  4427. {
  4428. hr = E_OUTOFMEMORY;
  4429. goto Abort;
  4430. }
  4431. }
  4432. else
  4433. {
  4434. aScopes[iPath] = apwszPaths[i];
  4435. }
  4436. aCatalogs[iPath] = pwszCatalog;
  4437. aMachines[iPath] = pwszMachine;
  4438. pwszCatalog += MAX_PATH; // advance the catalog and machine name buffer
  4439. pwszMachine += MAX_MACHINE_NAME_LEN;
  4440. iPath++; // next item in this list
  4441. }
  4442. if (iPath == 0)
  4443. {
  4444. // no catalogs found; - We should check to see if by chance the user specified a query that
  4445. // is CI based if so error apapropriately...
  4446. hr = (dwQueryRestrictions & GQR_REQUIRES_CI) ? MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXSEARCH) : S_FALSE;
  4447. goto Abort;
  4448. }
  4449. // Get ICommand.
  4450. hr = CIMakeICommand(&_pCommand, iPath, aDepths, aScopes, aCatalogs, aMachines);
  4451. if (SUCCEEDED(hr))
  4452. {
  4453. // create the query command string - Assume default sort...
  4454. hr = _BuildAndSetCommandTree(_iColSort, FALSE);
  4455. if (SUCCEEDED(hr))
  4456. {
  4457. if ((dwQueryRestrictions & GQR_REQUIRES_CI) && (nPaths != iPath))
  4458. {
  4459. // check warning flags to see if we should ignore and continue
  4460. if (0 == (_grfWarnings & DFW_IGNORE_CISCOPEMISMATCH))
  4461. {
  4462. hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_SCOPEMISMATCH);
  4463. }
  4464. }
  4465. if (SUCCEEDED(hr))
  4466. {
  4467. // Get IRowset.
  4468. _SetCmdProp(_pCommand);
  4469. hr = _pCommand->Execute(0, IID_IRowsetLocate, 0, 0, (IUnknown **)&_pRowset);
  4470. if (SUCCEEDED(hr))
  4471. {
  4472. // we have the IRowset.
  4473. // Real work to get the Accessor
  4474. DBBINDING aPropMainCols[c_cDbCols] =
  4475. {
  4476. { 0,cbP*0,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  4477. { 0,cbP*1,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  4478. { 0,cbP*2,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  4479. { 0,cbP*3,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  4480. { 0,cbP*4,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  4481. { 0,cbP*5,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }
  4482. };
  4483. hr = _MapColumns(_pRowset, c_cDbCols, aPropMainCols, c_aDbCols, _hAccessor);
  4484. if (SUCCEEDED(hr))
  4485. {
  4486. // OK lets also get the accessor for the WorkID...
  4487. hr = _MapColumns(_pRowset, ARRAYSIZE(c_aDbWorkIDCols), aPropMainCols, c_aDbWorkIDCols, _hAccessorWorkID);
  4488. if (SUCCEEDED(hr))
  4489. {
  4490. hr = _pRowset->QueryInterface(IID_PPV_ARG(IRowsetAsynch, &_pRowsetAsync));
  4491. }
  4492. }
  4493. }
  4494. }
  4495. }
  4496. }
  4497. if (FAILED(hr))
  4498. goto Abort;
  4499. // If we got here than at least some of our paths are indexed
  4500. // we may need to compress the list down of the ones we did not handle...
  4501. *pcPaths = (nPaths - iPath); // Let caller know how many we did not process
  4502. // need to move all the ones we did not process to the start of the list...
  4503. // we always process this list here as we may need to allocate translation lists to be used to
  4504. // translate the some UNCS back to the mapped drive the user passed in.
  4505. UINT j = 0, iInsert = 0;
  4506. iPath--; // make it easy to detect
  4507. for (i = 0; i < nPaths; i++)
  4508. {
  4509. if (aScopesOrig[j] == apwszPaths[i])
  4510. {
  4511. if (aScopesOrig[j] != aScopes[j])
  4512. {
  4513. // There is a translation in place.
  4514. DFODBET *pdfet = (DFODBET*)LocalAlloc(LPTR, sizeof(*pdfet));
  4515. if (pdfet)
  4516. {
  4517. pdfet->pdfetNext = _pdfetFirst;
  4518. _pdfetFirst = pdfet;
  4519. pdfet->pwszFrom = aScopes[j];
  4520. pdfet->cbFrom = lstrlenW(pdfet->pwszFrom);
  4521. pdfet->pwszTo = aScopesOrig[j];
  4522. aScopes[j] = aScopesOrig[j]; // Make sure loop below does not delete pwszFrom
  4523. apwszPaths[i] = NULL; // Likewise for pswsTo...
  4524. }
  4525. }
  4526. if (apwszPaths[i])
  4527. {
  4528. LocalFree((HLOCAL)apwszPaths[i]);
  4529. apwszPaths[i] = NULL;
  4530. }
  4531. if (j < iPath)
  4532. j++;
  4533. }
  4534. else
  4535. {
  4536. apwszPaths[iInsert++] = apwszPaths[i]; // move to right place
  4537. }
  4538. }
  4539. iPath++; // setup to go through cleanupcode...
  4540. // Fall through to cleanup code...
  4541. Abort:
  4542. // Warning... Since a failure return from this function will
  4543. // release this class, most all of the allocated items up till the failure should
  4544. // be released... Also cleanup any paths we may have allocated...
  4545. for (i = 0; i < iPath; i++)
  4546. {
  4547. if (aScopesOrig[i] != aScopes[i])
  4548. LocalFree(aScopes[i]);
  4549. }
  4550. if (aDepths)
  4551. LocalFree(aDepths);
  4552. if (aScopes)
  4553. LocalFree(aScopes);
  4554. if (aScopesOrig)
  4555. LocalFree(aScopesOrig);
  4556. if (aCatalogs)
  4557. LocalFree(aCatalogs);
  4558. if (aMachines)
  4559. LocalFree(aMachines);
  4560. if (pszMachineAlloc)
  4561. LocalFree(pszMachineAlloc);
  4562. if (pszCatalogAlloc)
  4563. LocalFree(pszCatalogAlloc);
  4564. return hr;
  4565. }
  4566. // This is the main external entry point to start a search. This will
  4567. // create a new thread to process the
  4568. STDAPI_(BOOL) SHFindComputer(LPCITEMIDLIST, LPCITEMIDLIST)
  4569. {
  4570. IContextMenu *pcm;
  4571. HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IContextMenu, &pcm));
  4572. if (SUCCEEDED(hr))
  4573. {
  4574. CMINVOKECOMMANDINFO ici = {0};
  4575. ici.cbSize = sizeof(ici);
  4576. ici.lpParameters = "{996E1EB1-B524-11d1-9120-00A0C98BA67D}"; // Search Guid of Find Computers
  4577. ici.nShow = SW_NORMAL;
  4578. hr = pcm->InvokeCommand(&ici);
  4579. pcm->Release();
  4580. }
  4581. return SUCCEEDED(hr);
  4582. }
  4583. BOOL _IsComputerPidl(LPCITEMIDLIST pidl)
  4584. {
  4585. CLSID clsid;
  4586. if (SUCCEEDED(GetCLSIDFromIDList(pidl, &clsid)))
  4587. {
  4588. return (IsEqualCLSID(clsid, CLSID_NetworkPlaces)
  4589. || IsEqualCLSID(clsid, CLSID_NetworkRoot)
  4590. || IsEqualCLSID(clsid, CLSID_NetworkDomain));
  4591. }
  4592. return FALSE;
  4593. }
  4594. // This is the main external entry point to start a search. This will
  4595. // create a new thread to process the
  4596. //
  4597. STDAPI_(BOOL) SHFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile)
  4598. {
  4599. // are we allowed?
  4600. if (SHRestricted(REST_NOFIND))
  4601. return FALSE;
  4602. // We Need a hack to allow Find to work for cases like
  4603. // Rest of network and workgroups to map to find computer instead
  4604. // This is rather gross, but what the heck. It is also assumed that
  4605. // the pidl is of the type that we know about (either File or network)
  4606. if (pidlFolder && _IsComputerPidl(pidlFolder))
  4607. {
  4608. return SHFindComputer(pidlFolder, pidlSaveFile);
  4609. }
  4610. return RealFindFiles(pidlFolder, pidlSaveFile);
  4611. }