Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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