#include "shellprv.h" #pragma hdrstop #include #include #include "ids.h" #include "findhlp.h" #include "pidl.h" #include "shitemid.h" #include "defview.h" #include "fstreex.h" #include "views.h" #include "cowsite.h" #include "exdisp.h" #include "shguidp.h" #include "prop.h" // COLUMN_INFO #include #include "stgutil.h" #include "netview.h" #include "basefvcb.h" #include "findfilter.h" #include "defvphst.h" #include "perhist.h" #include "adoint.h" #include "dspsprt.h" #include "defcm.h" #include "enumidlist.h" #include "contextmenu.h" // findband.cpp STDAPI GetCIStatus(BOOL *pbRunning, BOOL *pbIndexed, BOOL *pbPermission); STDAPI CatalogUptodate(LPCWSTR pszCatalog, LPCWSTR pszMachine); class CFindFolder; class CFindLVRange : public ILVRange { public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // ILVRange STDMETHODIMP IncludeRange(LONG iBegin, LONG iEnd); STDMETHODIMP ExcludeRange(LONG iBegin, LONG iEnd); STDMETHODIMP InvertRange(LONG iBegin, LONG iEnd); STDMETHODIMP InsertItem(LONG iItem); STDMETHODIMP RemoveItem(LONG iItem); STDMETHODIMP Clear(); STDMETHODIMP IsSelected(LONG iItem); STDMETHODIMP IsEmpty(); STDMETHODIMP NextSelected(LONG iItem, LONG *piItem); STDMETHODIMP NextUnSelected(LONG iItem, LONG *piItem); STDMETHODIMP CountIncluded(LONG *pcIncluded); // Helperfunctions... void SetOwner(CFindFolder *pff, DWORD dwMask) { // don't AddRef -- we're a member variable of the object punk points to _pff = pff; _dwMask = dwMask; _cIncluded = 0; } void IncrementIncludedCount() {_cIncluded++;} void DecrementIncludedCount() {_cIncluded--;} protected: CFindFolder *_pff; DWORD _dwMask; // The mask we use to know which "selection" bit we are tracking... LONG _cIncluded; // count included... (selected) }; class CFindFolder : public IFindFolder, public IShellFolder2, public IShellIcon, public IShellIconOverlay, public IPersistFolder2 { public: CFindFolder(IFindFilter *pff); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IShellFolder STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes); STDMETHODIMP EnumObjects(THIS_ HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList); STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv); STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { return BindToObject(pidl, pbc, riid, ppv); } STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject(HWND hwnd, REFIID riid, void **ppv); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv); STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName); STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST *ppidlOut); // IShellFolder2 STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid); STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum); STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay); STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState); STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv); STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails); STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid); // IFindFolder STDMETHODIMP GetFindFilter(IFindFilter **pfilter); STDMETHODIMP AddPidl(int i, LPCITEMIDLIST pidl, DWORD dwItemID, FIND_ITEM **ppItem); STDMETHODIMP GetItem(int iItem, FIND_ITEM **ppItem); STDMETHODIMP DeleteItem(int iItem); STDMETHODIMP GetItemCount(INT *pcItems); STDMETHODIMP ValidateItems(IUnknown *punkView, int iItemFirst, int cItems, BOOL bSearchComplete); STDMETHODIMP GetFolderListItemCount(INT *pcCount); STDMETHODIMP GetFolderListItem(int iItem, FIND_FOLDER_ITEM **ppItem); STDMETHODIMP GetFolder(int iFolder, REFIID riid, void **ppv); STDMETHODIMP_(UINT) GetFolderIndex(LPCITEMIDLIST pidl); STDMETHODIMP SetItemsChangedSinceSort(); STDMETHODIMP ClearItemList(); STDMETHODIMP ClearFolderList(); STDMETHODIMP AddFolder(LPITEMIDLIST pidl, BOOL fCheckForDup, int * piFolder); STDMETHODIMP SetAsyncEnum(IFindEnum *pfenum); STDMETHODIMP GetAsyncEnum(IFindEnum **ppfenum); STDMETHODIMP CacheAllAsyncItems(); STDMETHODIMP_(BOOL) AllAsyncItemsCached(); STDMETHODIMP SetAsyncCount(DBCOUNTITEM cCount); STDMETHODIMP ClearSaveStateList(); STDMETHODIMP GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState); STDMETHODIMP MapToSearchIDList(LPCITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl); STDMETHODIMP GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent); STDMETHODIMP SetControllerNotifyObject(IFindControllerNotify *pfcn); STDMETHODIMP GetControllerNotifyObject(IFindControllerNotify **ppfcn); STDMETHODIMP RememberSelectedItems(); STDMETHODIMP SaveFolderList(IStream *pstm); STDMETHODIMP RestoreFolderList(IStream *pstm); STDMETHODIMP SaveItemList(IStream *pstm); STDMETHODIMP RestoreItemList(IStream *pstm, int *pcItems); STDMETHODIMP RestoreSearchFromSaveFile(LPCITEMIDLIST pidlSaveFile, IShellFolderView *psfv); STDMETHODIMP_(BOOL) HandleUpdateDir(LPCITEMIDLIST pidl, BOOL fCheckSubDirs); STDMETHODIMP_(void) HandleRMDir(IShellFolderView *psfv, LPCITEMIDLIST pidl); STDMETHODIMP_(void) UpdateOrMaybeAddPidl(IShellFolderView *psfv, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlOld); STDMETHODIMP_(void) Save(IFindFilter* pfilter, HWND hwnd, DFBSAVEINFO * pSaveInfo, IShellView* psv, IUnknown * pObject); STDMETHODIMP OpenContainingFolder(IUnknown *punkSite); STDMETHODIMP AddDataToIDList(LPCITEMIDLIST pidl, int iFolder, LPCITEMIDLIST pidlFolder, UINT uFlags, UINT uRow, DWORD dwItemID, ULONG ulRank, LPITEMIDLIST *ppidl); // IShellIcon STDMETHODIMP GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex); // IShellIconOverlay STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex); STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex); // IPersist STDMETHODIMP GetClassID(CLSID *pClassID); // IPersistFolder STDMETHODIMP Initialize(LPCITEMIDLIST pidl); // IPersistFolder2 STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl); friend class CFindFolderViewCB; friend class CFindLVRange; HRESULT Init(); private: ~CFindFolder(); HRESULT _CompareFolderIndexes(int iFolder1, int iFolder2); void _AddFIND_ITEMToSaveStateList(FIND_ITEM *pesfi); LPITEMIDLIST _GetFullPidlForItem(LPCITEMIDLIST pidl); HRESULT _UpdateItemList(); static int CALLBACK _SortForDataObj(void *p1, void *p2, LPARAM lparam); static ULONG _Rank(LPCITEMIDLIST pidl); static DWORD _ItemID(LPCITEMIDLIST pidl); static PCHIDDENDOCFINDDATA _HiddenData(LPCITEMIDLIST pidl); FIND_FOLDER_ITEM *_FolderListItem(int iFolder); FIND_FOLDER_ITEM *_FolderListItem(LPCITEMIDLIST pidl); static BOOL _MapColIndex(UINT *piColumn); HRESULT _PrepareHIDA(UINT cidl, LPCITEMIDLIST *apidl, HDPA *phdpa); HRESULT _GetDetailsFolder(); HRESULT _QueryItemShellFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf); HRESULT _QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv); HRESULT _Folder(FIND_FOLDER_ITEM *pffli, REFIID riid, void **ppv); HRESULT _FolderFromItem(LPCITEMIDLIST pidl, REFIID riid, void **ppv); HRESULT _GetFolderName(LPCITEMIDLIST pidl, DWORD gdnFlags, LPTSTR psz, UINT cch); int _CompareByCachedSCID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); int _CompareNames(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwFlags); HRESULT _GetItemDisplayName(LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR wzName, UINT cch); HRESULT _GetFolderIDList(int iFolder, LPITEMIDLIST *ppidlParent); LONG _cRef; CFindLVRange _dflvrSel; // manage selection and cut range information... CFindLVRange _dflvrCut; HDPA _hdpaItems; // all of the items in the results HDPA _hdpaPidf; // folders that the items came from IFindFilter *_pfilter; BOOL _fItemsChangedSinceSort;// Has the list changed since the last sort? BOOL _fAllAsyncItemsCached; // Have we already cached all of the items? BOOL _fSearchComplete; BOOL _fInRefresh; // true if received prerefresh callback but postrefresh LPITEMIDLIST _pidl; IFindEnum *_pDFEnumAsync; // we have an async one, will need to call back for PIDLS and the like DBCOUNTITEM _cAsyncItems; // Count of async items int _iGetIDList; // index of the last IDlist we retrieved in callback. HDSA _hdsaSaveStateForIDs; // Async - Remember which items are selected when we sort int _cSaveStateSelected; // Number of items in selection list which are selected IFindControllerNotify *_pfcn; // Sometimes need to let the "Controller" object know about things CRITICAL_SECTION _csSearch; BOOL _fcsSearch; int _iCompareFolderCache1, _iCompareFolderCache2, _iCompareFolderCacheResult; IShellFolder2 *_psfDetails; SHCOLUMNID _scidCached; // Cached SCID for sorting the columns UINT _uiColumnCached; // The index to the cached column for _scidCached #if DEBUG DWORD _GUIThreadID; // Items can only be added to _hdpaItems on the UI thread. #endif }; class CFindFolderViewCB : public CBaseShellFolderViewCB { public: STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); HRESULT SetShellView(IShellView *psv); CFindFolderViewCB(CFindFolder* pff); // IServiceProvider override STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv); // Web View Tasks static HRESULT _OnOpenContainingFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc); private: ~CFindFolderViewCB(); HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP); HRESULT OnReArrange(DWORD pv, LPARAM lp); HRESULT OnGETWORKINGDIR(DWORD pv, UINT wP, LPTSTR lP); HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP); HRESULT OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream**lP); HRESULT OnGETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST *ppidl); HRESULT OnGetItemIconIndex(DWORD pv, WPARAM iItem, int *piIcon); HRESULT OnSetItemIconOverlay(DWORD pv, WPARAM iItem, int dwOverlayState); HRESULT OnGetItemIconOverlay(DWORD pv, WPARAM iItem, int * pdwOverlayState); HRESULT OnSETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST pidl); HRESULT OnGetIndexForItemIDList(DWORD pv, int * piItem, LPITEMIDLIST pidl); HRESULT OnDeleteItem(DWORD pv, LPCITEMIDLIST pidl); HRESULT OnODFindItem(DWORD pv, int * piItem, NM_FINDITEM* pnmfi); HRESULT OnODCacheHint(DWORD pv, NMLVCACHEHINT* pnmlvc); HRESULT OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP); HRESULT OnGetEmptyText(DWORD pv, UINT cchTextMax, LPTSTR pszText); HRESULT OnSetEmptyText(DWORD pv, UINT res, LPCTSTR pszText); HRESULT OnHwndMain(DWORD pv, HWND hwndMain); HRESULT OnIsOwnerData(DWORD pv, DWORD *pdwFlags); HRESULT OnSetISFV(DWORD pv, IShellFolderView* pisfv); HRESULT OnWindowCreated(DWORD pv, HWND hwnd); HRESULT OnWindowDestroy(DWORD pv, HWND wP); HRESULT OnGetODRangeObject(DWORD pv, WPARAM wWhich, ILVRange **pplvr); HRESULT OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP); HRESULT OnGetIPersistHistory(DWORD pv, IPersistHistory **ppph); HRESULT OnRefresh(DWORD pv, BOOL fPreRefresh); HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *shtd); HRESULT OnSortListData(DWORD pv, PFNLVCOMPARE pfnCompare, LPARAM lParamSort); HRESULT _ProfferService(BOOL bProffer); HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData); HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData); HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks); HRESULT OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme); CFindFolder* _pff; UINT _iColSort; // Which column are we sorting by BOOL _fIgnoreSelChange; // Sort in process UINT _iFocused; // Which item has the focus? UINT _cSelected; // Count of items selected IProfferService* _pps; // ProfferService site. DWORD _dwServiceCookie; // ProfferService cookie. TCHAR _szEmptyText[128]; // empty results list text. friend class CFindLVRange; }; // Class to save and restore find state on the travel log class CFindPersistHistory : public CDefViewPersistHistory { public: CFindPersistHistory(); // IPersist STDMETHODIMP GetClassID(CLSID *pClassID); // IPersistHistory STDMETHODIMP LoadHistory(IStream *pStream, IBindCtx *pbc); STDMETHODIMP SaveHistory(IStream *pStream); protected: IFindFolder* _GetDocFindFolder(); }; // {a5df1ea0-5702-11d1-83fa-00a0c90dc849} const IID IID_IFindFolder = {0xa5df1ea0, 0x5702, 0x11d1, {0x83, 0xfa, 0x00, 0xa0, 0xc9, 0x0d, 0xc8, 0x49}}; // {5B8DCBF0-B096-11d1-9217-00403393B8F0} const IID IID_IFindControllerNotify = {0x5b8dcbf0, 0xb096, 0x11d1, {0x92, 0x17, 0x0, 0x40, 0x33, 0x93, 0xb8, 0xf0}}; // Listview doesn't support more than 100000000 items, so if our // client returns more than that, just stop after that point. // // Instead of 100000000, we use the next lower 64K boundary. This keeps // us away from strange boundary cases (where a +1 might push us over the // top), and it keeps the Alpha happy. // #define MAX_LISTVIEWITEMS (100000000 & ~0xFFFF) #define SANE_ITEMCOUNT(c) ((int)min(c, MAX_LISTVIEWITEMS)) // Unicode descriptor: // // Structure written at the end of NT-generated find stream serves dual purpose. // 1. Contains an NT-specific signature to identify stream as NT-generated. // Appears as "NTFF" (NT Find File) in ASCII dump of file. // 2. Contains an offset to the unicode-formatted criteria section. // // The following diagram shows the find criteria/results stream format including // the NT-specific unicode criteria and descriptor. // // +-----------------------------------------+ -------------- // | DFHEADER structure | . . // +-----------------------------------------+ . . // | DF Criteria records (ANSI) | Win95 . // +-----------------------------------------+ . . // | DF Results (PIDL) [optional] | . NT // +-----------------------------------------+ ------- . // +----->| DF Criteria records (Unicode) [NT only] | . // | +-----------------------------------------+ . // | | Unicode Descriptor | . // | +--------------------+ ---------------------------------- // | / \ // | / \ // | +-----------------+---------+ // +---| Offset (64-bit) | "NTFF" | // +-----------------+---------+ // // const DWORD c_NTsignature = 0x4646544E; // "NTFF" in ASCII file dump. typedef struct { ULARGE_INTEGER oUnicodeCriteria; // Offset of unicode find criteria. DWORD NTsignature; // Signature of NT-generated find file. } DFC_UNICODE_DESC; enum { IDFCOL_NAME = 0, // default col from guy we are delegating to IDFCOL_PATH, IDFCOL_RANK, }; const COLUMN_INFO c_find_cols[] = { DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL), DEFINE_COL_STR_ENTRY(SCID_DIRECTORY, 30, IDS_PATH_COL), DEFINE_COL_STR_MENU_ENTRY(SCID_RANK, 10, IDS_RANK_COL), }; class CFindMenuBase : public IContextMenuCB , public CObjectWithSite { public: CFindMenuBase(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IContextMenuCB STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) PURE; protected: virtual ~CFindMenuBase(); private: LONG _cRef; }; CFindMenuBase::CFindMenuBase() : _cRef(1) { } CFindMenuBase::~CFindMenuBase() { } STDMETHODIMP CFindMenuBase::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFindMenuBase, IContextMenuCB), // IID_IContextMenuCB QITABENT(CFindMenuBase, IObjectWithSite), // IID_IObjectWithSite { 0 }, }; return QISearch(this, qit, riid, ppv); } ULONG CFindMenuBase::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CFindMenuBase::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } class CFindMenuCB : public CFindMenuBase { public: // IContextMenuCB STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); }; STDMETHODIMP CFindMenuCB::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; switch (uMsg) { case DFM_MERGECONTEXTMENU: if (!(wParam & CMF_VERBSONLY)) { LPQCMINFO pqcm = (LPQCMINFO)lParam; if (!pdtobj) { UINT idStart = pqcm->idCmdFirst; UINT idBGMain = 0, idBGPopup = 0; IFindFolder *pff; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff)))) { IFindFilter *pfilter; if (SUCCEEDED(pff->GetFindFilter(&pfilter))) { pfilter->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup); CDefFolderMenu_MergeMenu(HINST_THISDLL, idBGMain, idBGPopup, pqcm); DeleteMenu(pqcm->hmenu, idStart+SFVIDM_EDIT_PASTE, MF_BYCOMMAND); DeleteMenu(pqcm->hmenu, idStart+SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND); DeleteMenu(pqcm->hmenu, idStart+SFVIDM_MISC_REFRESH, MF_BYCOMMAND); IFindControllerNotify *pdcn; if (S_OK == pff->GetControllerNotifyObject(&pdcn)) { pdcn->Release(); } else { DeleteMenu(pqcm->hmenu, idStart+FSIDM_SAVESEARCH, MF_BYCOMMAND); } pfilter->Release(); } pff->Release(); } } } break; case DFM_INVOKECOMMAND: { // Check if this is from item context menu if (pdtobj) { switch(wParam) { case DFM_CMD_LINK: hr = SHCreateLinks(hwnd, NULL, pdtobj, SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL); break; case DFM_CMD_DELETE: // convert to DFM_INVOKCOMMANDEX to get flags bits hr = DeleteFilesInDataObject(hwnd, 0, pdtobj, 0); break; case DFM_CMD_PROPERTIES: // We need to pass an empty IDlist to combine with. hr = SHLaunchPropSheet(CFSFolder_PropertiesThread, pdtobj, (LPCTSTR)lParam, NULL, (void *)&c_idlDesktop); break; default: // Note: Fixing the working of Translator Key is not worth fixing. Hence Punted. // if GetAttributesOf did not specify the SFGAO_ bit // that corresponds to this default DFM_CMD, then we should // fail it here instead of returning S_FALSE. Otherwise, // accelerator keys (cut/copy/paste/etc) will get here, and // defcm tries to do the command with mixed results. // if GetAttributesOf did not specify SFGAO_CANLINK // or SFGAO_CANDELETE or SFGAO_HASPROPERTIES, then the above // implementations of these DFM_CMD commands are wrong... // Let the defaults happen for this object hr = S_FALSE; break; } } else { switch (wParam) { case FSIDM_SAVESEARCH: { IFindFolder *pff; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff)))) { IFindControllerNotify *pdcn; if (S_OK == pff->GetControllerNotifyObject(&pdcn)) { pdcn->SaveSearch(); pdcn->Release(); } pff->Release(); } } break; default: hr = S_FALSE; // one of view menu items, use the default code. break; } } } break; case DFM_GETHELPTEXT: // ansi version case DFM_GETHELPTEXTW: { UINT id = LOWORD(wParam) + IDS_MH_FSIDM_FIRST; if (uMsg == DFM_GETHELPTEXTW) LoadStringW(HINST_THISDLL, id, (LPWSTR)lParam, HIWORD(wParam)); else LoadStringA(HINST_THISDLL, id, (LPSTR)lParam, HIWORD(wParam)); } break; default: hr = E_NOTIMPL; break; } return hr; } class CFindFolderContextMenuItemCB : public CFindMenuBase { public: // IContextMenuCB STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); private: CFindFolderContextMenuItemCB(IFindFolder* pff); ~CFindFolderContextMenuItemCB(); friend HRESULT CFindItem_Create(HWND hwnd, IFindFolder *pff, IContextMenu **ppcm); STDMETHODIMP _GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode); IFindFolder *_pff; }; CFindFolderContextMenuItemCB::CFindFolderContextMenuItemCB(IFindFolder* pff) : _pff(pff) { _pff->AddRef(); } CFindFolderContextMenuItemCB::~CFindFolderContextMenuItemCB() { _pff->Release(); } STDMETHODIMP CFindFolderContextMenuItemCB::_GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode) { HRESULT hr; if (idCmd == FSIDM_OPENCONTAININGFOLDER) { if (bUnicode) hr = StringCchCopyW((LPWSTR)pszName, cchMax, L"OpenContainingFolder"); else hr = StringCchCopyA((LPSTR)pszName, cchMax, "OpenContainingFolder"); } else { hr = E_NOTIMPL; } return hr; } STDMETHODIMP CFindFolderContextMenuItemCB::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; switch (uMsg) { case DFM_MERGECONTEXTMENU: if (!(wParam & CMF_VERBSONLY)) { LPQCMINFO pqcm = (LPQCMINFO)lParam; CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DOCFIND_ITEM_MERGE, 0, pqcm); } break; case DFM_INVOKECOMMAND: switch (wParam) { case FSIDM_OPENCONTAININGFOLDER: _pff->OpenContainingFolder(_punkSite); break; default: hr = E_FAIL; // not our command break; } break; case DFM_GETHELPTEXT: case DFM_GETHELPTEXTW: // probably need to implement these... case DFM_GETVERBA: case DFM_GETVERBW: hr = _GetVerb((UINT_PTR)(LOWORD(wParam)), (LPSTR)lParam, (UINT)(HIWORD(wParam)), uMsg == DFM_GETVERBW); break; default: hr = E_NOTIMPL; break; } return hr; } STDAPI CFindItem_Create(HWND hwnd, IFindFolder* pff, IContextMenu **ppcm) { *ppcm = NULL; HRESULT hr; // We want a quick IContextMenu implementation -- an empty defcm looks easiest IContextMenuCB* pcmcb = new CFindFolderContextMenuItemCB(pff); if (pcmcb) { hr = CDefFolderMenu_CreateEx(NULL, hwnd, 0, NULL, NULL, pcmcb, NULL, NULL, ppcm); pcmcb->Release(); } else hr = E_OUTOFMEMORY; return hr; } HRESULT CFindFolder::AddFolder(LPITEMIDLIST pidl, BOOL fCheckForDup, int *piFolder) { *piFolder = -1; if (fCheckForDup) { EnterCriticalSection(&_csSearch); for (int i = DPA_GetPtrCount(_hdpaPidf) - 1; i >= 0; i--) { FIND_FOLDER_ITEM *pffli = _FolderListItem(i); if (pffli && ILIsEqual(&pffli->idl, pidl)) { LeaveCriticalSection(&_csSearch); *piFolder = i; return S_OK; } } LeaveCriticalSection(&_csSearch); } int cb = ILGetSize(pidl); FIND_FOLDER_ITEM *pffli;; HRESULT hr = SHLocalAlloc(sizeof(*pffli) - sizeof(pffli->idl) + cb, &pffli); if (SUCCEEDED(hr)) { // pddfli->psf = NULL; // pffli->fUpdateDir = FALSE; memcpy(&pffli->idl, pidl, cb); EnterCriticalSection(&_csSearch); // Now add this item to our DPA... *piFolder = DPA_AppendPtr(_hdpaPidf, pffli); LeaveCriticalSection(&_csSearch); if (-1 != *piFolder) { // If this is a network ID list then register a path -> pidl mapping, therefore // avoiding having to create simple ID lists, which don't work correctly when // being compared against real ID lists. if (IsIDListInNameSpace(pidl, &CLSID_NetworkPlaces)) { TCHAR szPath[ MAX_PATH ]; SHGetPathFromIDList(pidl, szPath); NPTRegisterNameToPidlTranslation(szPath, _ILNext(pidl)); // skip the My Net Places entry } } else { LocalFree((HLOCAL)pffli); hr = E_OUTOFMEMORY; } } return hr; } typedef struct { DWORD dwState; // State of the item; DWORD dwItemID; // Only used for Async support... } FIND_ITEM_SAVE_STATE; void CFindFolder::_AddFIND_ITEMToSaveStateList(FIND_ITEM *pesfi) { FIND_ITEM_SAVE_STATE essi; essi.dwState = pesfi->dwState & CDFITEM_STATE_MASK; essi.dwItemID = _ItemID(&pesfi->idl); DSA_AppendItem(_hdsaSaveStateForIDs, (void *)&essi); if (essi.dwState & LVIS_SELECTED) _cSaveStateSelected++; } HRESULT CFindFolder::RememberSelectedItems() { EnterCriticalSection(&_csSearch); // Currently has list of pidls... for (int i = DPA_GetPtrCount(_hdpaItems); i-- > 0;) { // Pidl at start of structure... FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_hdpaItems, i); if (pesfi) { if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED)) _AddFIND_ITEMToSaveStateList(pesfi); } } LeaveCriticalSection(&_csSearch); return S_OK; } STDMETHODIMP CFindFolder::ClearItemList() { // Clear out any async enumerators we may have SetAsyncEnum(NULL); _cAsyncItems = 0; // clear out our count of items... _pfilter->ReleaseQuery(); // Also tell the filter to release everything... EnterCriticalSection(&_csSearch); if (_hdpaItems) { // Currently has list of pidls... for (int i = DPA_GetPtrCount(_hdpaItems) - 1; i >= 0; i--) { // Pidl at start of structure... FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_hdpaItems, i); if (pesfi) LocalFree((HLOCAL)pesfi); } _fSearchComplete = FALSE; DPA_DeleteAllPtrs(_hdpaItems); } LeaveCriticalSection(&_csSearch); return S_OK; } STDMETHODIMP CFindFolder::ClearFolderList() { EnterCriticalSection(&_csSearch); if (_hdpaPidf) { for (int i = DPA_GetPtrCount(_hdpaPidf) - 1; i >= 0; i--) { FIND_FOLDER_ITEM *pffli = _FolderListItem(i); if (pffli) { // Release the IShellFolder if we have one if (pffli->psf) pffli->psf->Release(); // And delete the item LocalFree((HLOCAL)pffli); } } DPA_DeleteAllPtrs(_hdpaPidf); } LeaveCriticalSection(&_csSearch); return S_OK; } CFindFolder::CFindFolder(IFindFilter *pff) : _cRef(1), _iGetIDList(-1), _pfilter(pff), _iCompareFolderCache1(-1), _uiColumnCached(-1) { ASSERT(_pidl == NULL); _pfilter->AddRef(); // initialize our LV selection objects... _dflvrSel.SetOwner(this, LVIS_SELECTED); _dflvrCut.SetOwner(this, LVIS_CUT); #if DEBUG _GUIThreadID = GetCurrentThreadId(); #endif } CFindFolder::~CFindFolder() { ASSERT(_cRef==0); // We will need to call our function to Free our items in our // Folder list. We will use the same function that we use to // clear it when we do a new search ClearItemList(); ClearFolderList(); ClearSaveStateList(); EnterCriticalSection(&_csSearch); DPA_Destroy(_hdpaPidf); DPA_Destroy(_hdpaItems); _hdpaPidf = NULL; _hdpaItems = NULL; LeaveCriticalSection(&_csSearch); DSA_Destroy(_hdsaSaveStateForIDs); _pfilter->Release(); if (_psfDetails) _psfDetails->Release(); if (_fcsSearch) { DeleteCriticalSection(&_csSearch); } } HRESULT CFindFolder::Init() { if (InitializeCriticalSectionAndSpinCount(&_csSearch, 0)) { _fcsSearch = TRUE; } // Create the heap for the folder lists. _hdpaPidf = DPA_CreateEx(64, GetProcessHeap()); // Create the DPA and DSA for the item list. _hdpaItems = DPA_CreateEx(64, GetProcessHeap()); _hdsaSaveStateForIDs = DSA_Create(sizeof(FIND_ITEM_SAVE_STATE), 16); return _fcsSearch && _hdsaSaveStateForIDs && _hdpaItems && _hdpaPidf ? S_OK : E_OUTOFMEMORY; } STDMETHODIMP CFindFolder::AddDataToIDList(LPCITEMIDLIST pidl, int iFolder, LPCITEMIDLIST pidlFolder, UINT uFlags, UINT uRow, DWORD dwItemID, ULONG ulRank, LPITEMIDLIST *ppidl) { HRESULT hr; LPITEMIDLIST pidlToFree; if (pidlFolder) { pidlToFree = NULL; hr = S_OK; } else { hr = _GetFolderIDList(iFolder, &pidlToFree); pidlFolder = pidlToFree; } if (SUCCEEDED(hr)) { HIDDENDOCFINDDATA *phfd; int cb = ILGetSize(pidlFolder); int cbTotal = sizeof(*phfd) - sizeof(phfd->idlParent) + cb; hr = SHLocalAlloc(cbTotal, &phfd); if (SUCCEEDED(hr)) { phfd->hid.cb = (WORD)cbTotal; phfd->hid.wVersion = 0; phfd->hid.id = IDLHID_DOCFINDDATA; phfd->iFolder = (WORD)iFolder; // index to the folder DPA phfd->wFlags = (WORD)uFlags; phfd->uRow = uRow; // Which row in the CI; phfd->dwItemID = dwItemID; // Only used for Async support... phfd->ulRank = ulRank; // The rank returned by CI... memcpy(&phfd->idlParent, pidlFolder, cb); hr = ILCloneWithHiddenID(pidl, &phfd->hid, ppidl); LocalFree(phfd); } ILFree(pidlToFree); } return hr; } HRESULT CreateFindWithFilter(IFindFilter *pff, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_OUTOFMEMORY; CFindFolder *pfindfldr = new CFindFolder(pff); if (pfindfldr) { hr = pfindfldr->Init(); if (SUCCEEDED(hr)) hr = pfindfldr->QueryInterface(riid, ppv); pfindfldr->Release(); } return hr; } STDAPI CDocFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { *ppv = NULL; IFindFilter *pff; HRESULT hr = CreateNameSpaceFindFilter(&pff); if (SUCCEEDED(hr)) { hr = CreateFindWithFilter(pff, riid, ppv); pff->Release(); } return hr; } STDAPI CComputerFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { *ppv = NULL; IFindFilter *pff; HRESULT hr = CreateDefaultComputerFindFilter(&pff); if (pff) { hr = CreateFindWithFilter(pff, riid, ppv); pff->Release(); } return hr; } HRESULT CFindFolder::MapToSearchIDList(LPCITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl) { *ppidl = NULL; LPITEMIDLIST pidlParent; LPCITEMIDLIST pidlChild; if (SUCCEEDED(SplitIDList(pidl, &pidlParent, &pidlChild))) { EnterCriticalSection(&_csSearch); // loop through our DPA list and see if we can find a matach for (int i = 0; i < DPA_GetPtrCount(_hdpaPidf); i++) { FIND_FOLDER_ITEM *pffli = _FolderListItem(i); if (pffli && ILIsEqual(pidlParent, &pffli->idl)) { // We found the right one // so no lets transform the ID into one of our own // to return. Note: we must catch the case where the // original one passed in was a simple pidl and do // the appropriate thing. // LPITEMIDLIST pidlToFree = NULL; // might need to cleanup in one case // If this is not a FS folder, just clone it. IShellFolder *psf; if (fMapToReal && SUCCEEDED(_Folder(pffli, IID_PPV_ARG(IShellFolder, &psf)))) { if (SUCCEEDED(SHGetRealIDL(psf, pidlChild, &pidlToFree))) { pidlChild = pidlToFree; // use this below... } psf->Release(); } // create the doc find version of the pidl witht the // extra hidden items embedded AddDataToIDList(pidlChild, i, pidlParent, DFDF_NONE, 0, 0, 0, ppidl); ILFree(pidlToFree); // may be NULL break; // done with this loop } } LeaveCriticalSection(&_csSearch); ILFree(pidlParent); } return *ppidl ? S_OK : S_FALSE; } // Called before saving folder list. Needed especially in Asynch search, // (CI). We lazily pull item data from RowSet only when list view asks // for it. When we are leaving the search folder, we pull all items // creating all necessary folder lists. This ensure when saving folder // list, all are included. // remark : Fix bug#338714. HRESULT CFindFolder::_UpdateItemList() { USHORT cb = 0; int cItems; if (SUCCEEDED(GetItemCount(&cItems))) { for (int i = 0; i < cItems; i++) { FIND_ITEM *pesfi; if (DB_S_ENDOFROWSET == GetItem(i, &pesfi)) break; } } return S_OK; } // IFindFolder HRESULT CFindFolder::SaveFolderList(IStream *pstm) { // We First pull all the items from RowSet (in Asynch case) _UpdateItemList(); EnterCriticalSection(&_csSearch); // Now loop through our DPA list and see if we can find a matach for (int i = 0; i < DPA_GetPtrCount(_hdpaPidf); i++) { FIND_FOLDER_ITEM *pffli = _FolderListItem(i); if (EVAL(pffli)) ILSaveToStream(pstm, &pffli->idl); else break; } LeaveCriticalSection(&_csSearch); // Now out a zero size item.. USHORT cb = 0; pstm->Write(&cb, sizeof(cb), NULL); return TRUE; } // IFindFolder, Restore results out to file. HRESULT CFindFolder::RestoreFolderList(IStream *pstm) { // loop through and all all of the folders to our list... LPITEMIDLIST pidl = NULL; HRESULT hr; for (;;) { hr = ILLoadFromStream(pstm, &pidl); // frees [in,out] pidl for us if (pidl == NULL) break; // end of the list else { int i; AddFolder(pidl, FALSE, &i); } } ILFree(pidl); // don't forget to free last pidl return hr; } HRESULT CFindFolder::SaveItemList(IStream *pstm) { // We First serialize all of our PIDLS for each item in our list int cItems; if (SUCCEEDED(GetItemCount(&cItems))) { // And Save the items that are in the list for (int i = 0; i < cItems; i++) { FIND_ITEM *pesfi; HRESULT hr = GetItem(i, &pesfi); if (hr == DB_S_ENDOFROWSET) break; if (SUCCEEDED(hr) && pesfi) ILSaveToStream(pstm, &pesfi->idl); } } USHORT cb = 0; pstm->Write(&cb, sizeof(cb), NULL); // a Trailing NULL size to say end of pidl list... return S_OK; } HRESULT CFindFolder::RestoreItemList(IStream *pstm, int *pcItems) { // And the pidls that are associated with the object int cItems = 0; LPITEMIDLIST pidl = NULL; // don't free previous one FIND_ITEM *pesfi; for (;;) { if (FAILED(ILLoadFromStream(pstm, &pidl)) || (pidl == NULL)) break; if (FAILED(AddPidl(cItems, pidl, (UINT)-1, &pesfi)) || !pesfi) break; cItems++; } ILFree(pidl); // Free the last one read in *pcItems = cItems; return S_OK; } HRESULT CFindFolder::_GetFolderIDList(int iFolder, LPITEMIDLIST *ppidlParent) { *ppidlParent = NULL; HRESULT hr = E_FAIL; EnterCriticalSection(&_csSearch); FIND_FOLDER_ITEM *pffli = _FolderListItem(iFolder); if (pffli) hr = SHILClone(&pffli->idl, ppidlParent); LeaveCriticalSection(&_csSearch); return hr; } HRESULT CFindFolder::GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent) { return _GetFolderIDList(GetFolderIndex(pidl), ppidlParent); } HRESULT CFindFolder::SetControllerNotifyObject(IFindControllerNotify *pfcn) { IUnknown_Set((IUnknown **)&_pfcn, pfcn); return S_OK; } HRESULT CFindFolder::GetControllerNotifyObject(IFindControllerNotify **ppfcn) { *ppfcn = _pfcn; if (_pfcn) _pfcn->AddRef(); return _pfcn ? S_OK : S_FALSE; } STDMETHODIMP_(ULONG) CFindFolder::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CFindFolder::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDMETHODIMP CFindFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwzDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes) { return E_NOTIMPL; } STDMETHODIMP CFindFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum) { // We do not want the def view to enumerate us, instead we // will tell defview to call us... *ppenum = NULL; // No enumerator return S_FALSE; // no enumerator (not error) } STDMETHODIMP CFindFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { IShellFolder *psf; HRESULT hr = _QueryItemShellFolder(pidl, &psf); if (SUCCEEDED(hr)) { hr = psf->BindToObject(pidl, pbc, riid, ppv); psf->Release(); } return hr; } // Little helper function for bellow HRESULT CFindFolder::_CompareFolderIndexes(int iFolder1, int iFolder2) { HRESULT hr = E_INVALIDARG; EnterCriticalSection(&_csSearch); FIND_FOLDER_ITEM *pffli1 = _FolderListItem(iFolder1); FIND_FOLDER_ITEM *pffli2 = _FolderListItem(iFolder2); if (pffli1 && pffli2) { // Check our 1-level deep cache. Since its is common for there to be multiple // items in the same folder, during a sort operation, we often compare the // same two folders repeatedly. if ((_iCompareFolderCache1 != iFolder1) || (_iCompareFolderCache2 != iFolder2)) { TCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; SHGetPathFromIDList(&pffli1->idl, szPath1); SHGetPathFromIDList(&pffli2->idl, szPath2); _iCompareFolderCacheResult = lstrcmpi(szPath1, szPath2); _iCompareFolderCache1 = iFolder1; _iCompareFolderCache2 = iFolder2; } hr = ResultFromShort(_iCompareFolderCacheResult); } LeaveCriticalSection(&_csSearch); return hr; } PCHIDDENDOCFINDDATA CFindFolder::_HiddenData(LPCITEMIDLIST pidl) { return (PCHIDDENDOCFINDDATA)ILFindHiddenID(pidl, IDLHID_DOCFINDDATA); } UINT CFindFolder::GetFolderIndex(LPCITEMIDLIST pidl) { PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA)ILFindHiddenID(pidl, IDLHID_DOCFINDDATA); return phdfd ? phdfd->iFolder : -1; } FIND_FOLDER_ITEM *CFindFolder::_FolderListItem(int iFolder) { return (FIND_FOLDER_ITEM *)DPA_GetPtr(_hdpaPidf, iFolder); } FIND_FOLDER_ITEM *CFindFolder::_FolderListItem(LPCITEMIDLIST pidl) { return _FolderListItem(GetFolderIndex(pidl)); } ULONG CFindFolder::_Rank(LPCITEMIDLIST pidl) { PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl); // Could be mixed if so put ones without rank at the end... return phdfd && (phdfd->wFlags & DFDF_EXTRADATA) ? phdfd->ulRank : 0; } DWORD CFindFolder::_ItemID(LPCITEMIDLIST pidl) { PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl); return phdfd && (phdfd->wFlags & DFDF_EXTRADATA) ? phdfd->dwItemID : -1; } HRESULT CFindFolder::_GetItemDisplayName(LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR wzName, UINT cch) { // Get the IShellFolder: IShellFolder *psf; HRESULT hr = _QueryItemShellFolder(pidl, &psf); if (SUCCEEDED(hr)) { // Get the display name: hr = DisplayNameOf(psf, pidl, dwFlags, wzName, cch); psf->Release(); } return hr; } // Given the 2 pidls, we extract the display name using DisplayNameOf and then // if all goes well, we compare the two. int CFindFolder::_CompareNames(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwFlags) { int iRetVal = 0; WCHAR szName1[MAX_PATH], szName2[MAX_PATH]; // Get the name for 1 HRESULT hr = _GetItemDisplayName(pidl1, dwFlags, szName1, ARRAYSIZE(szName1)); if (SUCCEEDED(hr)) { // Get the name for 2 hr = _GetItemDisplayName(pidl2, dwFlags, szName2, ARRAYSIZE(szName2)); if (SUCCEEDED(hr)) { // Compare and set value iRetVal = StrCmpLogicalW(szName1, szName2); } } return iRetVal; } int CFindFolder::_CompareByCachedSCID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { int iRetVal = 0; // If sort on name, we will skip this and use the code below. if (!IsEqualSCID(_scidCached, SCID_NAME)) { iRetVal = CompareBySCID(this, &_scidCached, pidl1, pidl2); } // If they are still the same, sort them alphabetically by the name: // When we want to sort by name (either becuase we are sorting the // name column, or because 2 items are identical in other regards) we // want to display name vs the GetDetailsOf name for 2 reasons: // 1. Some folders like IE's History don't support GetDetailsEx. // 2. Recycle bin returns the file name, not the displayable name; // so we would end up with "DC###..." instead of "New Folder". if (iRetVal == 0) { iRetVal = _CompareNames(pidl1, pidl2, SHGDN_INFOLDER | SHGDN_NORMAL); if (iRetVal == 0) // the display names are the same, could they be in different folders? { iRetVal = _CompareNames(pidl1, pidl2, SHGDN_FORPARSING); } } return iRetVal; } STDMETHODIMP CFindFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = E_INVALIDARG; ASSERT(pidl1 == ILFindLastID(pidl1)); UINT iInputColumn = ((DWORD)lParam & SHCIDS_COLUMNMASK); UINT iMappedColumn = iInputColumn; if (_MapColIndex(&iMappedColumn)) { if (IDFCOL_PATH == iMappedColumn) { UINT iFolder1 = GetFolderIndex(pidl1); UINT iFolder2 = GetFolderIndex(pidl2); if (iFolder1 != iFolder2) return _CompareFolderIndexes(iFolder1, iFolder2); } else { ASSERT(iMappedColumn == IDFCOL_RANK); ULONG ulRank1 = _Rank(pidl1); ULONG ulRank2 = _Rank(pidl2); if (ulRank1 < ulRank2) return ResultFromShort(-1); if (ulRank1 > ulRank2) return ResultFromShort(1); } } // Check the SCID cache and update it if necessary. if (_uiColumnCached != iInputColumn) { hr = MapColumnToSCID(iInputColumn, &_scidCached); if (SUCCEEDED(hr)) { _uiColumnCached = iInputColumn; } } // Check if one is a folder and not the other. put folders before files. int iRes = CompareFolderness(this, pidl1, pidl2); if (iRes == 0) { iRes = _CompareByCachedSCID(pidl1, pidl2); } return ResultFromShort(iRes); } STDMETHODIMP CFindFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_NOINTERFACE; if (IsEqualIID(riid, IID_IShellView)) { IShellFolderViewCB* psfvcb = new CFindFolderViewCB(this); if (psfvcb) { SFV_CREATE sSFV = {0}; sSFV.cbSize = sizeof(sSFV); sSFV.pshf = this; sSFV.psfvcb = psfvcb; hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv); psfvcb->Release(); } else { hr = E_OUTOFMEMORY; } } else if (IsEqualIID(riid, IID_IContextMenu)) { IContextMenuCB *pcmcb = new CFindMenuCB(); if (pcmcb) { hr = CDefFolderMenu_CreateEx(NULL, hwnd, 0, NULL, this, pcmcb, NULL, NULL, (IContextMenu * *)ppv); pcmcb->Release(); } else hr = E_OUTOFMEMORY; } return hr; } STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut) { HRESULT hr; if (cidl == 0) { // defview asks to see if any items can be renamed this way, lame *prgfInOut = SFGAO_CANRENAME; hr = S_OK; } else { ASSERT(*apidl == ILFindLastID(*apidl)) IShellFolder *psf; hr = _QueryItemShellFolder(apidl[0], &psf); if (SUCCEEDED(hr)) { hr = psf->GetAttributesOf(cidl, apidl, prgfInOut); psf->Release(); } } return hr; } // // To be called back from within CDefFolderMenuE - Currently only used // // Some helper functions STDMETHODIMP CFindFolder::SetItemsChangedSinceSort() { _fItemsChangedSinceSort = TRUE; _iCompareFolderCache1 = -1; // an invalid folder index value return S_OK; } STDMETHODIMP CFindFolder::GetItemCount(INT *pcItems) { ASSERT(pcItems); DBCOUNTITEM cItems = 0; EnterCriticalSection(&_csSearch); if (_hdpaItems) cItems = DPA_GetPtrCount(_hdpaItems); LeaveCriticalSection(&_csSearch); // If async, then we may not have grown our dpa yet... but in mixed case we have so take // max of the two... if (_pDFEnumAsync) { if (_cAsyncItems > cItems) cItems = _cAsyncItems; } *pcItems = SANE_ITEMCOUNT(cItems); return S_OK; }; STDMETHODIMP CFindFolder::GetItem(int iItem, FIND_ITEM **ppItem) { HRESULT hr = E_FAIL; // just to init, use anything FIND_ITEM *pesfi; IFindEnum *pidfenum; GetAsyncEnum(&pidfenum); DWORD dwItemID = (UINT)-1; EnterCriticalSection(&_csSearch); int i = DPA_GetPtrCount(_hdpaItems); pesfi = (FIND_ITEM *) DPA_GetPtr(_hdpaItems, iItem); LeaveCriticalSection(&_csSearch); // Mondo hack to better handle Async searching (ROWSET), we are not sure if we // can trust the PIDL of the row as new rows may have been inserted... // Only do this if we are not looking at the previous item.. if (pesfi && pidfenum && !_fSearchComplete && (iItem != _iGetIDList)) { PCHIDDENDOCFINDDATA phdfd = _HiddenData(&pesfi->idl); // As we can now have mixed results only blow away if this is an async guy... if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA)) { pidfenum->GetItemID(iItem, &dwItemID); if (dwItemID != phdfd->dwItemID) { // Overload, pass NULL to ADDPIDL to tell system to free that item if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED)) _AddFIND_ITEMToSaveStateList(pesfi); AddPidl(iItem, 0, NULL, NULL); pesfi = NULL; } } } _iGetIDList = iItem; // remember the last one we retrieved... if (!pesfi && (iItem >= 0)) { // See if this is the async case if (pidfenum) { LPITEMIDLIST pidlT; hr = pidfenum->GetItemIDList(SANE_ITEMCOUNT(iItem), &pidlT); if (SUCCEEDED(hr) && hr != DB_S_ENDOFROWSET) { AddPidl(iItem, pidlT, dwItemID, &pesfi); // See if this item should show up as selected... if (dwItemID == (UINT)-1) pidfenum->GetItemID(iItem, &dwItemID); GetStateFromSaveStateList(dwItemID, &pesfi->dwState); } } } *ppItem = pesfi; if (hr != DB_S_ENDOFROWSET) hr = pesfi ? S_OK : E_FAIL; return hr; } STDMETHODIMP CFindFolder::DeleteItem(int iItem) { HRESULT hr = E_FAIL; if (!_fInRefresh) { FIND_ITEM *pesfi; hr = E_INVALIDARG; // make sure the item is in dpa (if using cI) if (SUCCEEDED(GetItem(iItem, &pesfi)) && pesfi) { EnterCriticalSection(&_csSearch); DPA_DeletePtr(_hdpaItems, iItem); LeaveCriticalSection(&_csSearch); PCHIDDENDOCFINDDATA phdfd = _HiddenData(&pesfi->idl); if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA)) { //we are deleting async item... _cAsyncItems--; } if (pesfi->dwState &= LVIS_SELECTED) { // Need to update the count of items selected... _dflvrSel.DecrementIncludedCount(); } LocalFree((HLOCAL)pesfi); hr = S_OK; } } return hr; } // evil window crawling code to get the listview from defview HWND ListviewFromView(HWND hwnd) { HWND hwndLV; do { hwndLV = FindWindowEx(hwnd, NULL, WC_LISTVIEW, NULL); } while ((hwndLV == NULL) && (hwnd = GetWindow(hwnd, GW_CHILD))); return hwndLV; } HWND ListviewFromViewUnk(IUnknown *punkView) { HWND hwnd; if (SUCCEEDED(IUnknown_GetWindow(punkView, &hwnd))) { hwnd = ListviewFromView(hwnd); } return hwnd; } STDMETHODIMP CFindFolder::ValidateItems(IUnknown *punkView, int iItem, int cItems, BOOL bSearchComplete) { IFindEnum *pidfenum; if (S_OK != GetAsyncEnum(&pidfenum) || _fAllAsyncItemsCached) return S_OK; // nothing to validate. DWORD dwItemID = (UINT)-1; int cItemsInList; GetItemCount(&cItemsInList); // force reload of rows pidfenum->Reset(); HWND hwndLV = ListviewFromViewUnk(punkView); int iLVFirst = ListView_GetTopIndex(hwndLV); int cLVItems = ListView_GetCountPerPage(hwndLV); if (iItem == -1) { iItem = iLVFirst; cItems = cLVItems; } // to avoid failing to update an item... if (bSearchComplete) _iGetIDList = -1; while ((iItem < cItemsInList) && cItems) { EnterCriticalSection(&_csSearch); FIND_ITEM *pesfi = (FIND_ITEM *) DPA_GetPtr(_hdpaItems, iItem); LeaveCriticalSection(&_csSearch); if (!pesfi) // Assume that if we have not gotten this one we are in the clear... break; PCHIDDENDOCFINDDATA phdfd = _HiddenData(&pesfi->idl); if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA)) { pidfenum->GetItemID(iItem, &dwItemID); if (dwItemID != _ItemID(&pesfi->idl)) { FIND_ITEM *pItem; // dummy to make GetItem happy // Oops don't match, if (InRange(iItem, iLVFirst, iLVFirst+cLVItems)) { if (SUCCEEDED(GetItem(iItem, &pItem))) { ListView_RedrawItems(hwndLV, iItem, iItem); } } else { AddPidl(iItem, NULL, 0, NULL); } } } else { break; // stop after we reach first non ci item } iItem++; cItems--; } _fSearchComplete = bSearchComplete; return S_OK; } STDMETHODIMP CFindFolder::AddPidl(int i, LPCITEMIDLIST pidl, DWORD dwItemID, FIND_ITEM **ppcdfi) { HRESULT hr = S_OK; ASSERT(GetCurrentThreadId() == _GUIThreadID); if (NULL == pidl) { EnterCriticalSection(&_csSearch); FIND_ITEM* pesfi = (FIND_ITEM*)DPA_GetPtr(_hdpaItems, i); if (pesfi) { LocalFree((HLOCAL)pesfi); DPA_SetPtr(_hdpaItems, i, NULL); } LeaveCriticalSection(&_csSearch); if (ppcdfi) *ppcdfi = NULL; } else { int cb = ILGetSize(pidl); FIND_ITEM *pesfi; hr = SHLocalAlloc(sizeof(*pesfi) - sizeof(pesfi->idl) + cb, &pesfi); if (SUCCEEDED(hr)) { // pesfi->dwMask = 0; // pesfi->dwState = 0; pesfi->iIcon = -1; memcpy(&pesfi->idl, pidl, cb); EnterCriticalSection(&_csSearch); BOOL bRet = DPA_SetPtr(_hdpaItems, i, (void *)pesfi); LeaveCriticalSection(&_csSearch); if (bRet) { if (ppcdfi) *ppcdfi = pesfi; } else { LocalFree((HLOCAL)pesfi); pesfi = NULL; hr = E_OUTOFMEMORY; } } } return hr; } STDMETHODIMP CFindFolder::SetAsyncEnum(IFindEnum *pdfEnumAsync) { if (_pDFEnumAsync) _pDFEnumAsync->Release(); _pDFEnumAsync = pdfEnumAsync; if (pdfEnumAsync) pdfEnumAsync->AddRef(); return S_OK; } STDMETHODIMP CFindFolder::CacheAllAsyncItems() { if (_fAllAsyncItemsCached) return S_OK; // Allready done it... IFindEnum *pidfenum; if (S_OK != GetAsyncEnum(&pidfenum)) return S_FALSE; // nothing to do... // Probably the easiest thing to do is to simply walk through all of the items... int maxItems = SANE_ITEMCOUNT(_cAsyncItems); for (int i = 0; i < maxItems; i++) { FIND_ITEM *pesfi; GetItem(i, &pesfi); } _fAllAsyncItemsCached = TRUE; return S_OK; } BOOL CFindFolder::AllAsyncItemsCached() { return _fAllAsyncItemsCached; } STDMETHODIMP CFindFolder::GetAsyncEnum(IFindEnum **ppdfEnumAsync) { *ppdfEnumAsync = _pDFEnumAsync; // unreferecned! return *ppdfEnumAsync ? S_OK : S_FALSE; } STDMETHODIMP CFindFolder::SetAsyncCount(DBCOUNTITEM cCount) { _cAsyncItems = cCount; _fAllAsyncItemsCached = FALSE; return S_OK; } STDMETHODIMP CFindFolder::ClearSaveStateList() { DSA_DeleteAllItems(_hdsaSaveStateForIDs); _cSaveStateSelected = 0; return S_OK; } STDMETHODIMP CFindFolder::GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState) { for (int i = DSA_GetItemCount(_hdsaSaveStateForIDs); i-- > 0;) { // Pidl at start of structure... FIND_ITEM_SAVE_STATE *pessi = (FIND_ITEM_SAVE_STATE*)DSA_GetItemPtr(_hdsaSaveStateForIDs, i); if (pessi->dwItemID == dwItemID) { *pdwState = pessi->dwState; if (pessi->dwState & LVIS_SELECTED) { // Remember the counts of items that we have touched... _dflvrSel.IncrementIncludedCount(); _cSaveStateSelected--; } // Any items we retrieve we can get rid of... DSA_DeleteItem(_hdsaSaveStateForIDs, i); return S_OK; } } return S_FALSE; } STDMETHODIMP CFindFolder::GetFolderListItemCount(INT *pcItemCount) { *pcItemCount = 0; EnterCriticalSection(&_csSearch); if (_hdpaPidf) *pcItemCount = DPA_GetPtrCount(_hdpaPidf); LeaveCriticalSection(&_csSearch); return S_OK; } STDMETHODIMP CFindFolder::GetFolderListItem(int iItem, FIND_FOLDER_ITEM **ppdffi) { EnterCriticalSection(&_csSearch); *ppdffi = (FIND_FOLDER_ITEM *)DPA_GetPtr(_hdpaPidf, iItem); LeaveCriticalSection(&_csSearch); return *ppdffi ? S_OK : E_FAIL; } class CFindMenuWrap : public CContextMenuForwarder { public: // IContextMenu overrides STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici); protected: CFindMenuWrap(IDataObject* pdo, IContextMenu* pcmArray); ~CFindMenuWrap(); friend HRESULT DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2, REFIID riid, void** ppv); private: IDataObject * _pdtobj; }; CFindMenuWrap::CFindMenuWrap(IDataObject* pdo, IContextMenu* pcmArray) : CContextMenuForwarder(pcmArray) { _pdtobj = pdo; _pdtobj->AddRef(); } CFindMenuWrap::~CFindMenuWrap() { _pdtobj->Release(); } STDMETHODIMP CFindMenuWrap::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) { BOOL fIsLink = FALSE; // "link" has to create a link on the desktop, not in the folder (since we're not a real folder...) if (IS_INTRESOURCE(lpici->lpVerb)) { WCHAR szCommandString[64]; if (SUCCEEDED(ContextMenu_GetCommandStringVerb(_pcm, LOWORD((UINT_PTR)lpici->lpVerb), szCommandString, ARRAYSIZE(szCommandString)))) { fIsLink = !StrCmpIW(szCommandString, L"link"); } } else { fIsLink = !StrCmpIA(lpici->lpVerb, "link"); } if (fIsLink) { // Note: old code used to check pdtobj, but we don't create this // object unless we get one of them, so why check? ASSERT(_pdtobj); return SHCreateLinks(lpici->hwnd, NULL, _pdtobj, SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL); } return CContextMenuForwarder::InvokeCommand(lpici); } HRESULT DFWrapIContextMenu(HWND hwnd, IShellFolder *psf, LPCITEMIDLIST pidl, IContextMenu* pcmExtra, void **ppvInOut) { IContextMenu *pcmWrap = NULL; IContextMenu *pcmFree = (IContextMenu*)*ppvInOut; IDataObject* pdo; HRESULT hr = psf->GetUIObjectOf(hwnd, 1, &pidl, IID_X_PPV_ARG(IDataObject, NULL, &pdo)); if (SUCCEEDED(hr)) { hr = DFWrapIContextMenus(pdo, pcmFree, pcmExtra, IID_PPV_ARG(IContextMenu, &pcmWrap)); pdo->Release(); } pcmFree->Release(); *ppvInOut = pcmWrap; return hr; } HRESULT DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2, REFIID riid, void** ppv) { *ppv = NULL; IContextMenu * pcmArray; IContextMenu* rgpcm[2] = {pcm2, pcm1}; HRESULT hr = Create_ContextMenuOnContextMenuArray(rgpcm, ARRAYSIZE(rgpcm), IID_PPV_ARG(IContextMenu, &pcmArray)); if (SUCCEEDED(hr)) { CFindMenuWrap * p = new CFindMenuWrap(pdo, pcmArray); if (p) { hr = p->QueryInterface(riid, ppv); p->Release(); } else { hr = E_OUTOFMEMORY; } pcmArray->Release(); } return hr; } STDMETHODIMP CFindFolder::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFindFolder, IShellFolder2), //IID_ISHELLFolder2 QITABENTMULTI(CFindFolder, IShellFolder, IShellFolder2), // IID_IShellFolder QITABENT(CFindFolder, IFindFolder), //IID_IFindFolder QITABENT(CFindFolder, IShellIcon), //IID_IShellIcon QITABENT(CFindFolder, IPersistFolder2), //IID_IPersistFolder2 QITABENTMULTI(CFindFolder, IPersistFolder, IPersistFolder2), //IID_IPersistFolder QITABENTMULTI(CFindFolder, IPersist, IPersistFolder2), //IID_IPersist QITABENT(CFindFolder, IShellIconOverlay), //IID_IShellIconOverlay { 0 }, }; return QISearch(this, qit, riid, ppv); } // IPersistFolder2 implementation STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_DocFindFolder; return S_OK; } STDMETHODIMP CFindFolder::Initialize(LPCITEMIDLIST pidl) { if (_pidl) ILFree(_pidl); return SHILClone(pidl, &_pidl); } STDMETHODIMP CFindFolder::GetCurFolder(LPITEMIDLIST *ppidl) { return GetCurFolderImpl(_pidl, ppidl); } // helper function to sort the selected ID list by something that // makes file operations work reasonably OK, when both an object and it's // parent is in the list... // int CALLBACK CFindFolder::_SortForDataObj(void *p1, void *p2, LPARAM lparam) { // Since I do recursion, If I get the Folder index number from the // last element of each and sort by them such that the higher numbers // come first, should solve the problem fine... LPITEMIDLIST pidl1 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)p1); LPITEMIDLIST pidl2 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)p2); CFindFolder *pff = (CFindFolder *)lparam; return pff->GetFolderIndex(pidl2) - pff->GetFolderIndex(pidl1); } LPITEMIDLIST CFindFolder::_GetFullPidlForItem(LPCITEMIDLIST pidl) { LPITEMIDLIST pidlRet = NULL; LPITEMIDLIST pidlParent; if (S_OK == GetParentsPIDL(pidl, &pidlParent)) { pidlRet = ILCombine(pidlParent, pidl); ILFree(pidlParent); } return pidlRet; } // we generate a non flat HIDA. this is so clients that // use this HIDA will bind to the folder that the results came // from instead of this folder that has runtime state that won't // be present if we rebind HRESULT CFindFolder::_PrepareHIDA(UINT cidl, LPCITEMIDLIST * apidl, HDPA *phdpa) { HRESULT hr = E_OUTOFMEMORY; *phdpa = DPA_Create(0); if (*phdpa) { if (DPA_Grow(*phdpa, cidl)) { for (UINT i = 0; i < cidl; i++) { LPITEMIDLIST pidl = _GetFullPidlForItem(apidl[i]); if (pidl) DPA_InsertPtr(*phdpa, i, pidl); } // In order to make file manipulation functions work properly we // need to sort the elements to make sure if an element and one // of it's parents are in the list, that the element comes // before it's parents... DPA_Sort(*phdpa, _SortForDataObj, (LPARAM)this); hr = S_OK; } } return hr; } STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv) { HRESULT hr = E_INVALIDARG; *ppv = NULL; // if just one item we can deletate to real folder if (cidl == 1) { // Note we may have been passed in a complex item so find the last ASSERT(ILIsEmpty(_ILNext(*apidl))); // should be a single level PIDL! IShellFolder *psf; hr = _QueryItemShellFolder(apidl[0], &psf); if (SUCCEEDED(hr)) { hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv); // if we are doing context menu, then we will wrap this // interface in a wrapper object, that we can then pick // off commands like link to process specially if (SUCCEEDED(hr)) { if (IsEqualIID(riid, IID_IContextMenu)) { // we also let the net/file guy add in a context menu if they want to IContextMenu* pcmExtra = NULL; _pfilter->GetItemContextMenu(hwnd, SAFECAST(this, IFindFolder*), &pcmExtra); hr = DFWrapIContextMenu(hwnd, psf, apidl[0], pcmExtra, ppv); ATOMICRELEASE(pcmExtra); } else if (IsEqualIID(riid, IID_IQueryInfo)) // && SHGetAttributes(psf, apidl[0], SFGAO_FILESYSTEM)) { WrapInfotip(SAFECAST(this, IShellFolder2 *), apidl[0], &SCID_DIRECTORY, (IUnknown *)*ppv); } } psf->Release(); } } else if (cidl > 1) { if (IsEqualIID(riid, IID_IContextMenu)) { // Try to create a menu object that we process ourself // Yes, do context menu. HKEY ahkeys[MAX_ASSOC_KEYS] = {0}; DWORD ckeys = 0; LPITEMIDLIST pidlFull = _GetFullPidlForItem(apidl[0]); if (pidlFull) { // Get the hkeyProgID and hkeyBaseProgID from the first item. ckeys = SHGetAssocKeysForIDList(pidlFull, ahkeys, ARRAYSIZE(ahkeys)); ILFree(pidlFull); } IContextMenuCB *pcmcb = new CFindMenuCB(); if (pcmcb) { hr = CDefFolderMenu_Create2Ex(NULL, hwnd, cidl, apidl, this, pcmcb, ckeys, ahkeys, (IContextMenu **)ppv); pcmcb->Release(); } SHRegCloseKeys(ahkeys, ckeys); } else if (IsEqualIID(riid, IID_IDataObject)) { HDPA hdpa; hr = _PrepareHIDA(cidl, apidl, &hdpa); if (SUCCEEDED(hr)) { hr = SHCreateFileDataObject(&c_idlDesktop, cidl, (LPCITEMIDLIST*)DPA_GetPtrPtr(hdpa), NULL, (IDataObject **)ppv); DPA_FreeIDArray(hdpa); } } } return hr; } STDMETHODIMP CFindFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwRes, LPSTRRET pStrRet) { IShellFolder *psf; HRESULT hr = _QueryItemShellFolder(pidl, &psf); if (SUCCEEDED(hr)) { if ((dwRes & SHGDN_INFOLDER) && (dwRes & SHGDN_FORPARSING) && !(dwRes & SHGDN_FORADDRESSBAR)) { // The thumbnail cache uses this as a hit test... in search view we can have files with the same name. dwRes &= ~SHGDN_INFOLDER; } hr = psf->GetDisplayNameOf(pidl, dwRes, pStrRet); psf->Release(); } return hr; } STDMETHODIMP CFindFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwRes, LPITEMIDLIST *ppidlOut) { if (ppidlOut) *ppidlOut = NULL; IShellFolder *psf; HRESULT hr = _QueryItemShellFolder(pidl, &psf); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlRenamed; hr = psf->SetNameOf(hwnd, pidl, PathFindFileName(pszName), dwRes, ppidlOut ? &pidlRenamed : NULL); if (SUCCEEDED(hr) && ppidlOut) { hr = AddDataToIDList(pidlRenamed, GetFolderIndex(pidl), NULL, DFDF_NONE, 0, 0, 0, ppidlOut); ILFree(pidlRenamed); } psf->Release(); } return hr; } STDMETHODIMP CFindFolder::GetDefaultSearchGUID(GUID *pGuid) { return _pfilter->GetDefaultSearchGUID(SAFECAST(this, IShellFolder2*), pGuid); } STDMETHODIMP CFindFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum) { return _pfilter->EnumSearches(SAFECAST(this, IShellFolder2*), ppenum); } HRESULT CFindFolder::_Folder(FIND_FOLDER_ITEM *pffli, REFIID riid, void **ppv) { HRESULT hr; if (pffli->psf) hr = S_OK; else hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, &pffli->idl, &pffli->psf)); if (SUCCEEDED(hr)) hr = pffli->psf->QueryInterface(riid, ppv); return hr; } HRESULT CFindFolder::GetFolder(int iFolder, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_FAIL; EnterCriticalSection(&_csSearch); FIND_FOLDER_ITEM *pffli = _FolderListItem(iFolder); if (pffli) hr = _Folder(pffli, riid, ppv); LeaveCriticalSection(&_csSearch); return hr; } HRESULT CFindFolder::_FolderFromItem(LPCITEMIDLIST pidl, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_FAIL; PCHIDDENDOCFINDDATA phdfd = _HiddenData(pidl); if (phdfd) { hr = SHBindToObject(NULL, riid, &phdfd->idlParent, ppv); } return hr; } HRESULT CFindFolder::_QueryItemShellFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf) { *ppsf = NULL; HRESULT hr = E_FAIL; EnterCriticalSection(&_csSearch); FIND_FOLDER_ITEM *pffli = _FolderListItem(pidl); if (pffli) { if (pffli->psf) hr = S_OK; else hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, &pffli->idl, &pffli->psf)); if (SUCCEEDED(hr)) { *ppsf = pffli->psf; (*ppsf)->AddRef(); } } LeaveCriticalSection(&_csSearch); if (FAILED(hr)) { hr = _FolderFromItem(pidl, IID_PPV_ARG(IShellFolder, ppsf)); } return hr; } HRESULT CFindFolder::_QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_FAIL; EnterCriticalSection(&_csSearch); FIND_FOLDER_ITEM *pffli = _FolderListItem(pidl); if (pffli) hr = _Folder(pffli, riid, ppv); LeaveCriticalSection(&_csSearch); if (FAILED(hr)) { hr = _FolderFromItem(pidl, riid, ppv); } return hr; } HRESULT CFindFolder::_GetDetailsFolder() { HRESULT hr; if (_psfDetails) hr = S_OK; // in cache else { IFindFilter *pfilter; hr = GetFindFilter(&pfilter); if (SUCCEEDED(hr)) { hr = pfilter->GetColumnsFolder(&_psfDetails); pfilter->Release(); } } return hr; } STDMETHODIMP CFindFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { HRESULT hr = _GetDetailsFolder(); if (SUCCEEDED(hr)) hr = _psfDetails->GetDefaultColumn(dwRes, pSort, pDisplay); return hr; } BOOL CFindFolder::_MapColIndex(UINT *piColumn) { switch (*piColumn) { case IDFCOL_NAME: // 0 return FALSE; case IDFCOL_PATH: // 1 case IDFCOL_RANK: // 2 return TRUE; default: // >= 3 *piColumn -= IDFCOL_RANK; return FALSE; } } STDMETHODIMP CFindFolder::GetDefaultColumnState(UINT iColumn, DWORD *pdwState) { HRESULT hr; if (_MapColIndex(&iColumn)) { *pdwState = c_find_cols[iColumn].csFlags; hr = S_OK; } else { hr = _GetDetailsFolder(); if (SUCCEEDED(hr)) { hr = _psfDetails->GetDefaultColumnState(iColumn, pdwState); *pdwState &= ~SHCOLSTATE_SLOW; // virtual lv and defview } } return hr; } HRESULT CFindFolder::_GetFolderName(LPCITEMIDLIST pidl, DWORD gdnFlags, LPTSTR psz, UINT cch) { LPITEMIDLIST pidlFolder; HRESULT hr = GetParentsPIDL(pidl, &pidlFolder); if (SUCCEEDED(hr)) { hr = SHGetNameAndFlags(pidlFolder, gdnFlags, psz, cch, NULL); ILFree(pidlFolder); } return hr; } STDMETHODIMP CFindFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { HRESULT hr; if (IsEqualSCID(*pscid, SCID_RANK)) { hr = InitVariantFromUINT(pv, _Rank(pidl)); } else { IShellFolder2 *psf; hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellFolder2, &psf)); if (SUCCEEDED(hr)) { hr = psf->GetDetailsEx(pidl, pscid, pv); psf->Release(); } if (FAILED(hr)) { if (IsEqualSCID(*pscid, SCID_DIRECTORY)) { TCHAR szTemp[MAX_PATH]; hr = _GetFolderName(pidl, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szTemp, ARRAYSIZE(szTemp)); if (SUCCEEDED(hr)) { hr = InitVariantFromStr(pv, szTemp); } } } } return hr; } // Figure out what the correct column index is to match the scid we are given // where the returned index is relative to the folder passed in. int MapSCIDToColumnForFolder(IShellFolder2 *psf, SHCOLUMNID scidIn) { SHCOLUMNID scidNew; for (UINT i = 0; SUCCEEDED(psf->MapColumnToSCID(i, &scidNew)); i++) { if (IsEqualSCID(scidNew, scidIn)) { return i; // found } } return -1; // not found } STDMETHODIMP CFindFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi) { HRESULT hr; if (_MapColIndex(&iColumn)) { if (pidl) { TCHAR szTemp[MAX_PATH]; szTemp[0] = 0; if (IDFCOL_PATH == iColumn) { _GetFolderName(pidl, SHGDN_FORADDRESSBAR | SHGDN_FORPARSING, szTemp, ARRAYSIZE(szTemp)); } else { ASSERT(IDFCOL_RANK == iColumn); ULONG uRank = _Rank(pidl); if (uRank) AddCommas(uRank, szTemp, ARRAYSIZE(szTemp)); } hr = StringToStrRet(szTemp, &pdi->str); } else { hr = GetDetailsOfInfo(c_find_cols, ARRAYSIZE(c_find_cols), iColumn, pdi); } } else { if (pidl) { IShellFolder2 *psf; hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellFolder2, &psf)); if (SUCCEEDED(hr)) { // We cannot simply ask for GetDetailsOf because some folders map different // column numbers to differnt values. // Translate the column index to the SHCOLUMNID relative to this folder. SHCOLUMNID colId; hr = _GetDetailsFolder(); if (SUCCEEDED(hr)) hr = _psfDetails->MapColumnToSCID(iColumn, &colId); // Use the SCID to get the correct column index... if (SUCCEEDED(hr)) { // Get the column index for the SCID with respect to the other folder int newIndex = MapSCIDToColumnForFolder(psf, colId); if (newIndex != -1) { // Found the correct column index, so use it to get the data hr = psf->GetDetailsOf(pidl, newIndex, pdi); } else { // Failed to find the correct column index. hr = E_FAIL; } } psf->Release(); } } else { hr = _GetDetailsFolder(); if (SUCCEEDED(hr)) hr = _psfDetails->GetDetailsOf(NULL, iColumn, pdi); } } return hr; } STDMETHODIMP CFindFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) { HRESULT hr; if (_MapColIndex(&iColumn)) { hr = MapColumnToSCIDImpl(c_find_cols, ARRAYSIZE(c_find_cols), iColumn, pscid); } else { hr = _GetDetailsFolder(); if (SUCCEEDED(hr)) hr = _psfDetails->MapColumnToSCID(iColumn, pscid); } return hr; } STDMETHODIMP CFindFolder::GetFindFilter(IFindFilter **ppfilter) { return _pfilter->QueryInterface(IID_PPV_ARG(IFindFilter, ppfilter)); } // IShellIcon::GetIconOf STDMETHODIMP CFindFolder::GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex) { IShellIcon * psiItem; HRESULT hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellIcon, &psiItem)); if (SUCCEEDED(hr)) { hr = psiItem->GetIconOf(pidl, flags, piIndex); psiItem->Release(); } return hr; } // IShellIconOverlay STDMETHODIMP CFindFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex) { IShellIconOverlay * psioItem; HRESULT hr = _QueryItemInterface(pidl, IID_PPV_ARG(IShellIconOverlay, &psioItem)); if (SUCCEEDED(hr)) { hr = psioItem->GetOverlayIndex(pidl, pIndex); psioItem->Release(); } return hr; } STDMETHODIMP CFindFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex) { return E_NOTIMPL; } STDMETHODIMP CFindFolder::RestoreSearchFromSaveFile(LPCITEMIDLIST pidlSaveFile, IShellFolderView *psfv) { // See if we can restore most of the search from here... IStream *pstm; HRESULT hr = StgBindToObject(pidlSaveFile, STGM_READ | STGM_SHARE_DENY_WRITE, IID_PPV_ARG(IStream, &pstm)); if (SUCCEEDED(hr)) { ULONG cbRead; DFHEADER dfh; // Note: in theory I should test the size read by the size of the // smaller headers, but if the number of bytes read is smaller than // the few new things added then there is nothing to restore anyway... // Note: Win95/NT4 incorrectly failed newer versions of this structure. // Which is bogus since the struct was backward compatible (that's what // the offsets are for). We fix for NT5 and beyond, but downlevel // systems are forever broken. Hopefully this feature is rarely enough // used (and never mailed) that nobody will notice we're broken. if (SUCCEEDED(pstm->Read(&dfh, sizeof(dfh), &cbRead)) && (sizeof(dfh) == cbRead) && (DOCFIND_SIG == dfh.wSig)) { DFC_UNICODE_DESC desc; LARGE_INTEGER dlibMove = {0, 0}; WORD fCharType = 0; // Check the stream's signature to see if it was generated by Win95 or NT. dlibMove.QuadPart = -(LONGLONG)sizeof(desc); pstm->Seek(dlibMove, STREAM_SEEK_END, NULL); pstm->Read(&desc, sizeof(desc), &cbRead); if (cbRead > 0 && desc.NTsignature == c_NTsignature) { // NT-generated stream. Read in Unicode criteria. fCharType = DFC_FMT_UNICODE; dlibMove.QuadPart = desc.oUnicodeCriteria.QuadPart; } else { // Win95-generated stream. Read in ANSI criteria. fCharType = DFC_FMT_ANSI; dlibMove.LowPart = dfh.oCriteria; } pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL); _pfilter->RestoreCriteria(pstm, dfh.cCriteria, fCharType); // Now read in the results dlibMove.LowPart = dfh.oResults; pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL); if (dfh.wVer > 1) { // only restore this way if version 2 data.... // Now Restore away the folder list RestoreFolderList(pstm); int cItems = 0; RestoreItemList(pstm, &cItems); if (cItems > 0) psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL); } } else hr = E_FAIL; pstm->Release(); } return hr; } // a form of this code is duplicated in browseui searchext.cpp // BOOL RealFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile) { // First create the top level browser... IWebBrowser2 *pwb2; HRESULT hr = CoCreateInstance(CLSID_ShellBrowserWindow, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pwb2)); if (SUCCEEDED(hr)) { VARIANT varClsid; hr = InitBSTRVariantFromGUID(&varClsid, CLSID_FileSearchBand); if (SUCCEEDED(hr)) { VARIANT varEmpty = {0}; // show a search bar hr = pwb2->ShowBrowserBar(&varClsid, &varEmpty, &varEmpty); if (SUCCEEDED(hr)) { // Grab the band's IUnknown from browser property. VARIANT varFsb; hr = pwb2->GetProperty(varClsid.bstrVal, &varFsb); if (SUCCEEDED(hr)) { // QI for IFileSearchBand, which we'll use to program the search band's // search type (files or folders), inititial scope, and/or saved query file. IFileSearchBand* pfsb; if (SUCCEEDED(QueryInterfaceVariant(varFsb, IID_PPV_ARG(IFileSearchBand, &pfsb)))) { BSTR bstrSearch; hr = BSTRFromCLSID(SRCID_SFileSearch, &bstrSearch); if (SUCCEEDED(hr)) { VARIANT varQueryFile = {0}, varScope = {0}; // assign initial scope if (pidlFolder) InitVariantFromIDList(&varScope, pidlFolder); // assign query file from which to restore search else if (pidlSaveFile) InitVariantFromIDList(&varQueryFile, pidlSaveFile); pfsb->SetSearchParameters(&bstrSearch, VARIANT_TRUE, &varScope, &varQueryFile); VariantClear(&varScope); VariantClear(&varQueryFile); SysFreeString(bstrSearch); } pfsb->Release(); } VariantClear(&varFsb); } if (SUCCEEDED(hr)) hr = pwb2->put_Visible(TRUE); } VariantClear(&varClsid); // frees bstrFileSearchBand too } pwb2->Release(); } return hr; } HRESULT CFindFolder::OpenContainingFolder(IUnknown *punkSite) { IFolderView *pfv; HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv)); if (SUCCEEDED(hr)) { IEnumIDList *penum; hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IEnumIDList, &penum)); if (S_OK == hr) { LPITEMIDLIST pidl; ULONG c; while (S_OK == penum->Next(1, &pidl, &c)) { // Now get the parent of it. LPITEMIDLIST pidlParent; if (SUCCEEDED(GetParentsPIDL(pidl, &pidlParent))) { SHOpenFolderAndSelectItems(pidlParent, 1, (LPCITEMIDLIST *)&pidl, 0); ILFree(pidlParent); } ILFree(pidl); } penum->Release(); } pfv->Release(); } return hr; } // Save away the current search to a file on the desktop. // For now the name will be automatically generated. // void CFindFolder::Save(IFindFilter* pfilter, HWND hwnd, DFBSAVEINFO * pSaveInfo, IShellView* psv, IUnknown *pObject) { TCHAR szFilePath[MAX_PATH]; IStream * pstm; DFHEADER dfh; TCHAR szTemp[MAX_PATH]; SHORT cb; LARGE_INTEGER dlibMove = {0, 0}; ULARGE_INTEGER libCurPos; FOLDERSETTINGS fs; HRESULT hr; // // See if the search already has a file name associated with it. If so // we will save it in it, else we will create a new file on the desktop if (pfilter->FFilterChanged() == S_FALSE) { // Lets blow away the save file ILFree(pSaveInfo->pidlSaveFile); pSaveInfo->pidlSaveFile = NULL; } // If it still looks like we want to continue to use a save file then // continue. if (pSaveInfo->pidlSaveFile) { SHGetPathFromIDList(pSaveInfo->pidlSaveFile, szFilePath); } else { // First get the path name to the Desktop. SHGetSpecialFolderPath(NULL, szFilePath, CSIDL_PERSONAL, TRUE); // and update the title // we now do this before getting a filename because we generate // the file name from the title LPTSTR pszTitle; BOOL fNameOk = FALSE; pfilter->GenerateTitle(&pszTitle, TRUE); if (pszTitle) { // Now add on the extension. UINT cchRemaining = MAX_PATH - lstrlen(szFilePath); cchRemaining -= 1; // "\" between path and filename cchRemaining -= 4; // ".fnd" = 4 characters cchRemaining -= 3; // "###" for unique-ify-ing the name cchRemaining -= 1; // (no idea) hr = StringCchCopy(szTemp, cchRemaining, pszTitle); if (SUCCEEDED(hr)) { hr = StringCchCat(szTemp, ARRAYSIZE(szTemp), TEXT(".fnd")); if (SUCCEEDED(hr)) { fNameOk = TRUE; } } LocalFree(pszTitle); // And free the title string. } if (!fNameOk) { szTemp[0] = 0; } // Now loop through and replace all of the invalid characters with _'s // we special case a few of the characters... for (LPTSTR lpsz = szTemp; *lpsz; lpsz = CharNext(lpsz)) { if (PathGetCharType(*lpsz) & (GCT_INVALID|GCT_WILD|GCT_SEPARATOR)) { switch (*lpsz) { case TEXT(':'): *lpsz = TEXT('-'); break; case TEXT('*'): *lpsz = TEXT('@'); break; case TEXT('?'): *lpsz = TEXT('!'); break; default: *lpsz = TEXT('_'); } } } TCHAR szShortName[12]; LoadString(HINST_THISDLL, IDS_FIND_SHORT_NAME, szShortName, ARRAYSIZE(szShortName)); if (!PathYetAnotherMakeUniqueName(szFilePath, szFilePath, szShortName, szTemp)) return; } // Now lets bring up the save as dialog... TCHAR szFilter[MAX_PATH]; TCHAR szTitle[MAX_PATH]; TCHAR szFilename[MAX_PATH]; OPENFILENAME ofn = { 0 }; LoadString(g_hinst, IDS_FINDFILESFILTER, szFilter, ARRAYSIZE(szFilter)); LoadString(g_hinst, IDS_FINDSAVERESULTSTITLE, szTitle, ARRAYSIZE(szTitle)); //Strip out the # and make them Nulls for SaveAs Dialog LPTSTR psz = szFilter; while (*psz) { if (*psz == TEXT('#')) *psz = 0; psz++; } StringCchCopy(szFilename, ARRAYSIZE(szFilename), PathFindFileName(szFilePath)); PathRemoveFileSpec(szFilePath); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwnd; ofn.hInstance = g_hinst; ofn.lpstrFilter = szFilter; ofn.lpstrFile = szFilename; ofn.nMaxFile = MAX_PATH; ofn.lpstrInitialDir = szFilePath; ofn.lpstrTitle = szTitle; ofn.lpstrDefExt = TEXT("fnd"); ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; ofn.lpTemplateName = NULL; ofn.lpfnHook= NULL; ofn.lCustData = NULL; if (!GetSaveFileName(&ofn)) return; if (FAILED(SHCreateStreamOnFile(szFilename, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, &pstm))) return; // remember the file that we saved away to... ILFree(pSaveInfo->pidlSaveFile); SHParseDisplayName(szFilename, NULL, &pSaveInfo->pidlSaveFile, 0, NULL); // Now setup and write out header information ZeroMemory(&dfh, sizeof(dfh)); dfh.wSig = DOCFIND_SIG; dfh.wVer = DF_CURFILEVER; dfh.dwFlags = pSaveInfo->dwFlags; dfh.wSortOrder = (WORD)pSaveInfo->SortMode; dfh.wcbItem = sizeof(DFITEM); dfh.oCriteria = sizeof(dfh); // dfh.cCriteria = sizeof(s_aIndexes) / sizeof(SHORT); // dfh.oResults =; // Not used anymore... dfh.cResults = -1; // Note: Later we may convert this to DOCFILE where the // criteria is stored as properties. // Get the current Folder Settings if (SUCCEEDED(psv->GetCurrentInfo(&fs))) dfh.ViewMode = fs.ViewMode; else dfh.ViewMode = FVM_DETAILS; // Now call the filter object to save out his own set of criterias dlibMove.LowPart = dfh.oCriteria; pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL); hr = pfilter->SaveCriteria(pstm, DFC_FMT_ANSI); if (SUCCEEDED(hr)) dfh.cCriteria = GetScode(hr); // Now setup to output the results dlibMove.LowPart = 0; pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos); // Get current pos dfh.oResults = libCurPos.LowPart; // // Now Let our file folder serialize his results out here also... // But only if the option is set to do so... // cb = 0; // Write out a Trailing NULL for Folder list pstm->Write(&cb, sizeof(cb), NULL); // And item list. pstm->Write(&cb, sizeof(cb), NULL); // END of DFHEADER_WIN95 information // BEGIN of NT5 information: // Now setup to output the history stream if (pObject) { dlibMove.LowPart = 0; pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos); // Get current pos dfh.oHistory = libCurPos.LowPart; if (FAILED(SavePersistHistory(pObject, pstm))) { // On failure we might as well just pretend we didn't save this bit of data. // Do we need an error message -- the ui won't be right when relaunched... // dfh.oHistory = 0; dlibMove.LowPart = libCurPos.LowPart; pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL); } } // In NT the below was done AT THE END OF THE STREAM instead of // revving the DFHEADER struct. (Okay, DFHEADEREX, since Win95 // already broke DFHEADER back compat by in improper version check) // This could have been done by putting a propery signatured // DFHEADEREX that had proper versioning so we could add information // to. Unfortunately another hardcoded struct was tacked on to // the end of the stream... Next time, please fix the problem // instead of work around it. // // What this boils down to is we cannot put any information // after the DFC_UNICODE_DESC section, so might as well // always do this SaveCriteria section last... // // See comment at top of file for DFC_UNICODE_DESC. // DFC_UNICODE_DESC desc; // // Get the current location in stream. This is the offset where // we'll write the unicode find criteria. Save this // value (along with NT-specific signature) in the descriptor // dlibMove.LowPart = 0; pstm->Seek(dlibMove, STREAM_SEEK_CUR, &libCurPos); desc.oUnicodeCriteria.QuadPart = libCurPos.QuadPart; desc.NTsignature = c_NTsignature; // Append the Unicode version of the find criteria. hr = pfilter->SaveCriteria(pstm, DFC_FMT_UNICODE); // Append the unicode criteria descriptor to the end of the file. pstm->Write(&desc, sizeof(desc), NULL); // // don't put any code between the above DFC_UNICDE_DESC section // and this back-patch of the dfh header... // // Finally output the header information at the start of the file // and close the file // pstm->Seek(g_li0, STREAM_SEEK_SET, NULL); pstm->Write(&dfh, sizeof(dfh), NULL); pstm->Release(); SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL); SHChangeNotify(SHCNE_FREESPACE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL); } // Broke out from class to share with old and new code BOOL CFindFolder::HandleUpdateDir(LPCITEMIDLIST pidl, BOOL fCheckSubDirs) { // 1. Start walk through list of dirs. Find list of directories effected // and mark them // 2. Walk the list of items that we have and mark each of the items that // that are in our list of directories and then do a search... BOOL fCurrentItemsMayBeImpacted = FALSE; FIND_FOLDER_ITEM *pffli; INT cPidf; // First see which directories are effected... GetFolderListItemCount(&cPidf); for (int iPidf = 0; iPidf < cPidf; iPidf++) { if (SUCCEEDED(GetFolderListItem(iPidf, &pffli)) && !pffli->fUpdateDir) // We may have already impacted these... { pffli->fUpdateDir = ILIsParent(pidl, &pffli->idl, FALSE); fCurrentItemsMayBeImpacted |= pffli->fUpdateDir; } } if (fCurrentItemsMayBeImpacted) { // Now we need to walk through the whole list and remove any entries // that are no longer there... // int iItem; if (SUCCEEDED(GetItemCount(&iItem))) { for (--iItem; iItem >= 0; iItem--) { FIND_ITEM *pesfi; if (SUCCEEDED(GetItem(iItem, &pesfi)) && pesfi) { UINT iFolder = GetFolderIndex(&pesfi->idl); // See if item may be impacted... if (SUCCEEDED(GetFolderListItem(iFolder, &pffli)) && pffli->fUpdateDir) pesfi->dwState |= CDFITEM_STATE_MAYBEDELETE; } } } } return fCurrentItemsMayBeImpacted; } void CFindFolder::UpdateOrMaybeAddPidl(IShellFolderView *psfv, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlOld) { HRESULT hr; // First see if we should try to do an update... if (pidlOld) { LPITEMIDLIST pidlT; if (S_OK == MapToSearchIDList(pidl, TRUE, &pidlT)) { SetItemsChangedSinceSort(); UINT iItem; // cast needed for bad interface def hr = psfv->UpdateObject((LPITEMIDLIST)pidlOld, (LPITEMIDLIST)pidlT, &iItem); ILFree(pidlT); // In either case simply blow away our generated pidl... if (SUCCEEDED(hr)) return; } } IShellFolder *psf; LPCITEMIDLIST pidlChild; if (SUCCEEDED(SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild))) { BOOL fMatch = FALSE; // See if this item matches the filter... IFindFilter *pfilter; if (SUCCEEDED(GetFindFilter(&pfilter))) { fMatch = pfilter->MatchFilter(psf, pidlChild) != 0; pfilter->Release(); } psf->Release(); if (fMatch) { LPITEMIDLIST pidlT; if (S_OK != MapToSearchIDList(pidl, TRUE, &pidlT)) { fMatch = FALSE; // The folder has not been added before now... TCHAR szPath[MAX_PATH]; SHGetPathFromIDList(pidl, szPath); if (!IsFileInBitBucket(szPath)) { PathRemoveFileSpec(szPath); LPITEMIDLIST pidlFolder; if (SUCCEEDED(SHParseDisplayName(szPath, NULL, &pidlFolder, 0, NULL))) { int iFolder; hr = AddFolder(pidlFolder, TRUE, &iFolder); if (SUCCEEDED(hr)) { fMatch = (S_OK == MapToSearchIDList(pidl, TRUE, &pidlT)); } ILFree(pidlFolder); } } } if (fMatch) { // There are times we get notified twice. To handle this // see if the item is already in our list. If so punt... SetItemsChangedSinceSort(); UINT iItem; if (FAILED(psfv->UpdateObject(pidlT, pidlT, &iItem))) { // item not in the view yet... so we need to add it if (SUCCEEDED(GetItemCount((INT *)&iItem))) { // Normal case would be here to add the object // We need to add this to our dpa and dsa... FIND_ITEM *pesfi; AddPidl(iItem, pidlT, (UINT)-1, &pesfi); if (pesfi) psfv->SetObjectCount(++iItem, SFVSOC_NOSCROLL); } } ILFree(pidlT); } else { ASSERT(NULL == pidlT); } } } } void CFindFolder::HandleRMDir(IShellFolderView *psfv, LPCITEMIDLIST pidl) { BOOL fCurrentItemsMayBeImpacted = FALSE; FIND_FOLDER_ITEM *pffli; INT cItems; FIND_ITEM *pesfi; // First see which directories are effected... GetFolderListItemCount(&cItems); for (int iItem = 0; iItem < cItems; iItem++) { if (SUCCEEDED(GetFolderListItem(iItem, &pffli))) { pffli->fDeleteDir = ILIsParent(pidl, &pffli->idl, FALSE); fCurrentItemsMayBeImpacted |= pffli->fDeleteDir; } else { #ifdef DEBUG INT cItem; GetFolderListItemCount(&cItem); TraceMsg(TF_WARNING, "NULL pffli in _handleRMDir (iItem == %d, ItemCount()==%d)!!!", iItem, cItems); #endif } } if (fCurrentItemsMayBeImpacted) { // Now we need to walk through the whole list and remove any entries // that are no longer there... if (SUCCEEDED(GetItemCount(&iItem))) { for (--iItem; iItem >= 0; iItem--) { if (FAILED(GetItem(iItem, &pesfi)) || pesfi == NULL) continue; // See if item may be impacted... UINT iFolder = GetFolderIndex(&pesfi->idl); if (SUCCEEDED(GetFolderListItem(iFolder, &pffli)) && pffli->fDeleteDir) { psfv->RemoveObject(&pesfi->idl, (UINT*)&cItems); } } } } } // export used for Start.Search-> cascade menu STDAPI_(IContextMenu *) SHFind_InitMenuPopup(HMENU hmenu, HWND hwnd, UINT idCmdFirst, UINT idCmdLast) { IContextMenu * pcm = NULL; HKEY hkFind = SHGetShellKey(SHELLKEY_HKLM_EXPLORER, TEXT("FindExtensions"), FALSE); if (hkFind) { if (SUCCEEDED(CDefFolderMenu_CreateHKeyMenu(hwnd, hkFind, &pcm))) { int iItems = GetMenuItemCount(hmenu); // nuke all old entries while (iItems--) { DeleteMenu(hmenu, iItems, MF_BYPOSITION); } pcm->QueryContextMenu(hmenu, 0, idCmdFirst, idCmdLast, CMF_NODEFAULT|CMF_INCLUDESTATIC|CMF_FINDHACK); iItems = GetMenuItemCount(hmenu); if (!iItems) { TraceMsg(TF_DOCFIND, "no menus in find extension, blowing away context menu"); pcm->Release(); pcm = NULL; } } RegCloseKey(hkFind); } return pcm; } void _SetObjectCount(IShellView *psv, int cItems, DWORD dwFlags) { IShellFolderView *psfv; if (SUCCEEDED(psv->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv)))) { psfv->SetObjectCount(cItems, dwFlags); psfv->Release(); } } typedef struct { PFNLVCOMPARE pfnCompare; LPARAM lParamSort; } FIND_SORT_INFO; int CALLBACK _FindCompareItems(void *p1, void *p2, LPARAM lParam) { FIND_SORT_INFO *pfsi = (FIND_SORT_INFO*)lParam; return pfsi->pfnCompare(PtrToInt(p1), PtrToInt(p2), pfsi->lParamSort); } HRESULT CFindFolderViewCB::OnSortListData(DWORD pv, PFNLVCOMPARE pfnCompare, LPARAM lParamSort) { EnterCriticalSection(&_pff->_csSearch); // First mark the focused item in the list so we can find it later... FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, _iFocused); // indirect if (pesfi) pesfi->dwState |= LVIS_FOCUSED; int cItems = DPA_GetPtrCount(_pff->_hdpaItems); HDPA hdpaForSorting = NULL; if (cItems) { hdpaForSorting = DPA_Create(cItems); } if (hdpaForSorting) { for (int i = 0; i< cItems; i++) { DPA_SetPtr(hdpaForSorting, i, IntToPtr(i)); } // sort out items FIND_SORT_INFO fsi; fsi.pfnCompare = pfnCompare; fsi.lParamSort = lParamSort; DPA_Sort(hdpaForSorting, _FindCompareItems, (LPARAM)&fsi); for (i = 0; i < cItems; i++) { int iIndex = PtrToInt(DPA_FastGetPtr(hdpaForSorting, i)); // Move the items from _hdpaItems to hdpaForSorting in sorted order DPA_SetPtr(hdpaForSorting, i, DPA_FastGetPtr(_pff->_hdpaItems, iIndex)); } // Now switch the two HDPA to get the sorted list in the member variable DPA_Destroy(_pff->_hdpaItems); _pff->_hdpaItems = hdpaForSorting; } // Now find the focused item and scroll it into place... IShellView *psv; if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellView, &psv)))) { int iFocused = -1; // Tell the view we need to reshuffle.... // Gross, this one defaults to invalidate all which for this one is fine... _SetObjectCount(psv, cItems, SFVSOC_INVALIDATE_ALL); // Invalidate all for (int iEnd = cItems - 1; iEnd >= 0; iEnd--) { pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iEnd); // indirect if (pesfi && pesfi->dwState & LVIS_FOCUSED) iFocused = iEnd; } // Now handle the focused item... if (iFocused != -1) { _pff->_iGetIDList = iFocused; // remember the last one we retrieved... pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iFocused); // indirect if (pesfi) { // flags depend on first one and also if selected? psv->SelectItem(&pesfi->idl, SVSI_FOCUSED | SVSI_ENSUREVISIBLE | SVSI_SELECT); pesfi->dwState &= ~LVIS_FOCUSED; // don't keep it around to get lost later... } } _iFocused = iFocused; _fIgnoreSelChange = FALSE; psv->Release(); } LeaveCriticalSection(&_pff->_csSearch); return S_OK; } HRESULT CFindFolderViewCB::OnMergeMenu(DWORD pv, QCMINFO*lP) { DebugMsg(DM_TRACE, TEXT("sh TR - DF_FSNCallBack DVN_MERGEMENU")); UINT idCmdFirst = lP->idCmdFirst; UINT idBGMain = 0, idBGPopup = 0; _pff->_pfilter->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup); CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, idBGPopup, lP); // Lets remove some menu items that are not useful to us. HMENU hmenu = lP->hmenu; DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTE, MF_BYCOMMAND); DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND); // DeleteMenu(hmenu, idCmdFirst + SFVIDM_EDIT_PASTESPECIAL, MF_BYCOMMAND); // This is sortof bogus but if after the merge one of the // menus has no items in it, remove the menu. for (int i = GetMenuItemCount(hmenu) - 1; i >= 0; i--) { HMENU hmenuSub = GetSubMenu(hmenu, i); if (hmenuSub && (GetMenuItemCount(hmenuSub) == 0)) { DeleteMenu(hmenu, i, MF_BYPOSITION); } } return S_OK; } HRESULT CFindFolderViewCB::OnGETWORKINGDIR(DWORD pv, UINT wP, LPTSTR lP) { HRESULT hr = E_FAIL; IShellFolderView *psfv; if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv)))) { LPCITEMIDLIST *ppidls; // pointer to a list of pidls. UINT cpidls = 0; // Count of pidls that were returned. psfv->GetSelectedObjects(&ppidls, &cpidls); if (cpidls > 0) { LPITEMIDLIST pidl; if (SUCCEEDED(_pff->GetParentsPIDL(ppidls[0], &pidl))) { SHGetPathFromIDList(pidl, lP); ILFree(pidl); } LocalFree((void *)ppidls); // const -> non const hr = S_OK; } else { hr = E_FAIL; } psfv->Release(); } return hr; } HRESULT CFindFolderViewCB::OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream **ppstm) { return _pff->_pfilter->GetColSaveStream(wP, ppstm); } HRESULT CFindFolderViewCB::OnGETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST *ppidl) { FIND_ITEM *pesfi; if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi) { *ppidl = &pesfi->idl; // return alias! return S_OK; } *ppidl = NULL; return E_FAIL; } // in defviewx.c STDAPI SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage); HRESULT CFindFolderViewCB::OnGetItemIconIndex(DWORD pv, WPARAM iItem, int *piIcon) { FIND_ITEM *pesfi; *piIcon = -1; if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi) { if (pesfi->iIcon == -1) { IShellFolder* psf = (IShellFolder*)_pff; SHGetIconFromPIDL(psf, NULL, &pesfi->idl, 0, &pesfi->iIcon); } *piIcon = pesfi->iIcon; return S_OK; } return E_FAIL; } HRESULT CFindFolderViewCB::OnSetItemIconOverlay(DWORD pv, WPARAM iItem, int iOverlayIndex) { HRESULT hr = E_FAIL; FIND_ITEM *pesfi; if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi) { pesfi->dwMask |= ESFITEM_ICONOVERLAYSET; pesfi->dwState |= INDEXTOOVERLAYMASK(iOverlayIndex) & LVIS_OVERLAYMASK; hr = S_OK; } return hr; } HRESULT CFindFolderViewCB::OnGetItemIconOverlay(DWORD pv, WPARAM iItem, int * piOverlayIndex) { HRESULT hr = E_FAIL; *piOverlayIndex = SFV_ICONOVERLAY_DEFAULT; FIND_ITEM *pesfi; if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi) { if (pesfi->dwMask & ESFITEM_ICONOVERLAYSET) { *piOverlayIndex = OVERLAYMASKTO1BASEDINDEX(pesfi->dwState & LVIS_OVERLAYMASK); } else *piOverlayIndex = SFV_ICONOVERLAY_UNSET; hr = S_OK; } return hr; } HRESULT CFindFolderViewCB::OnSETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST pidl) { FIND_ITEM *pesfi; _pff->_iGetIDList = (int) iItem; // remember the last one we retrieved... if (SUCCEEDED(_pff->GetItem((int) iItem, &pesfi)) && pesfi) { FIND_ITEM *pesfiNew; if (SUCCEEDED(_pff->AddPidl((int) iItem, pidl, 0, &pesfiNew) && pesfiNew)) { pesfiNew->dwState = pesfi->dwState; LocalFree((HLOCAL)pesfi); // Free the old one... } return S_OK; } return E_FAIL; } BOOL DF_ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { BOOL bRet = (pidl1 == pidl2); if (!bRet) { PCHIDDENDOCFINDDATA phdfd1 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl1, IDLHID_DOCFINDDATA); PCHIDDENDOCFINDDATA phdfd2 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl2, IDLHID_DOCFINDDATA); if (phdfd1 && phdfd2) bRet = (phdfd1->iFolder == phdfd2->iFolder) && ILIsEqual(pidl1, pidl2); } return bRet; } HRESULT CFindFolderViewCB::OnGetIndexForItemIDList(DWORD pv, int * piItem, LPITEMIDLIST pidl) { int cItems; // Try to short circuit searching for pidls... if (SUCCEEDED(_pff->GetItemCount(&cItems)) && _pff->_iGetIDList < cItems) { FIND_ITEM *pesfi; if (SUCCEEDED(_pff->GetItem(_pff->_iGetIDList, &pesfi)) && pesfi) { if (DF_ILIsEqual(&pesfi->idl, pidl)) { // Yep it was ours so return the index quickly.. *piItem = _pff->_iGetIDList; return S_OK; } } } // Otherwise let it search the old fashion way... return E_FAIL; } HRESULT CFindFolderViewCB::OnDeleteItem(DWORD pv, LPCITEMIDLIST pidl) { // We simply need to remove this item from our list. The // underlying listview will decrement the count on their end... FIND_ITEM *pesfi; int iItem; int cItems; BOOL bFound; if (!pidl) { _pff->SetAsyncEnum(NULL); return S_OK; // special case telling us all items deleted... } bFound = FALSE; if (SUCCEEDED(_pff->GetItem(_pff->_iGetIDList, &pesfi)) && pesfi && (DF_ILIsEqual(&pesfi->idl, pidl))) { iItem = _pff->_iGetIDList; bFound = TRUE; } else { if (SUCCEEDED(_pff->GetItemCount(&cItems))) { for (iItem = 0; iItem < cItems; iItem++) { if (SUCCEEDED(_pff->GetItem(iItem, &pesfi)) && pesfi && (DF_ILIsEqual(&pesfi->idl, pidl))) { bFound = TRUE; break; } } } } if (bFound) { _pff->DeleteItem(iItem); } return S_OK; } HRESULT CFindFolderViewCB::OnODFindItem(DWORD pv, int * piItem, NM_FINDITEM* pnmfi) { // We have to do the subsearch ourself to find the correct item... // As the listview has no information saved in it... int iItem = pnmfi->iStart; int cItem; UINT flags = pnmfi->lvfi.flags; if (FAILED(_pff->GetItemCount(&cItem))) return E_FAIL; if ((flags & LVFI_STRING) == 0) return E_FAIL; // Not sure what type of search this is... int cbString = lstrlen(pnmfi->lvfi.psz); for (int j = cItem; j-- != 0;) { if (iItem >= cItem) { if (flags & LVFI_WRAP) iItem = 0; else break; } // Now we need to get the Display name for this item... FIND_ITEM *pesfi; TCHAR szPath[MAX_PATH]; IShellFolder* psf = (IShellFolder*)_pff; if (SUCCEEDED(_pff->GetItem(iItem, &pesfi)) && pesfi && SUCCEEDED(DisplayNameOf(psf, &pesfi->idl, NULL, szPath, ARRAYSIZE(szPath)))) { if (flags & (LVFI_PARTIAL|LVFI_SUBSTRING)) { if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pnmfi->lvfi.psz, cbString, szPath, cbString) == 2) { *piItem = iItem; return S_OK; } } else if (lstrcmpi(pnmfi->lvfi.psz, szPath) == 0) { *piItem = iItem; return S_OK; } } ++iItem; } return E_FAIL; } HRESULT CFindFolderViewCB::OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA *lP) { // Try to remember which item is focused... if (lP->uNewState & LVIS_FOCUSED) _iFocused = wPh; return S_OK; } HRESULT CFindFolderViewCB::OnSetEmptyText(DWORD pv, UINT res, LPCTSTR pszText) { if (pszText && 0 == lstrcmp(_szEmptyText, pszText)) return S_OK; StringCchCopy(_szEmptyText, ARRAYSIZE(_szEmptyText), pszText ? pszText : TEXT("")); // ok to truncate HWND hwndLV = ListviewFromViewUnk(_punkSite); if (hwndLV) SendMessage(hwndLV, LVM_RESETEMPTYTEXT, 0, 0); return S_OK; } HRESULT CFindFolderViewCB::OnGetEmptyText(DWORD pv, UINT cchTextMax, LPTSTR pszText) { HRESULT hr = S_OK; if (_szEmptyText[0]) { hr = StringCchCopy(pszText, cchTextMax, _szEmptyText); } else { LoadString(HINST_THISDLL, IDS_FINDVIEWEMPTYINIT, pszText, cchTextMax); } return hr; } HRESULT CFindFolderViewCB::OnReArrange(DWORD pv, LPARAM lparam) { UINT nCol = (UINT)lparam; // See if there is any controller object registered that may want to take over this... // if we are in a mixed query and we have already fetched the async items, simply sort // the dpa's... IFindEnum *pidfenum; if (S_OK == _pff->GetAsyncEnum(&pidfenum)) { if (!((pidfenum->FQueryIsAsync() == DF_QUERYISMIXED) && _pff->_fAllAsyncItemsCached)) { if (_pff->_pfcn) { // if they return S_FALSE it implies that they handled it and they do not // want the default processing to happen... if (_pff->_pfcn->DoSortOnColumn(nCol, _iColSort == nCol) == S_FALSE) { _iColSort = nCol; return S_OK; } } else { // If we are running in the ROWSET way, we may want to have the ROWSET do the work... // pass one we spawn off a new search with the right column sorted if (_iColSort != nCol) { _iColSort = nCol; } // Warning the call above may release our AsyncEnum and generate a new one so // Don't rely on it's existence here... return S_OK; } } // we must pull in all the results from ci if (pidfenum->FQueryIsAsync() && !_pff->_fAllAsyncItemsCached) _pff->CacheAllAsyncItems(); #ifdef DEBUG #define MAX_LISTVIEWITEMS (100000000 & ~0xFFFF) #define SANE_ITEMCOUNT(c) ((int)min(c, MAX_LISTVIEWITEMS)) if (pidfenum->FQueryIsAsync()) { ASSERT(DPA_GetPtrCount(_pff->_hdpaItems) >= SANE_ITEMCOUNT(_pff->_cAsyncItems)); for (int i = 0; i < SANE_ITEMCOUNT(_pff->_cAsyncItems); i++) { FIND_ITEM *pesfi = (FIND_ITEM *)DPA_GetPtr(_pff->_hdpaItems, i); ASSERT(pesfi); if (!pesfi) { ASSERT(SUCCEEDED(_pff->GetItem(i, &pesfi))); } } } #endif } // Use the common sort. return E_FAIL; } HRESULT CFindFolderViewCB::OnWindowCreated(DWORD pv, HWND hwnd) { _ProfferService(TRUE); // register our service w/ top level container return S_OK; } HRESULT CFindFolderViewCB::_ProfferService(BOOL bProffer) { HRESULT hr = E_FAIL; if (bProffer) { // shouldn't be redundantly registering our service ASSERT(NULL == _pps); ASSERT(-1 == _dwServiceCookie); IProfferService* pps; hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IProfferService, &pps)); if (SUCCEEDED(hr)) { hr = pps->ProfferService(SID_DocFindFolder, this, &_dwServiceCookie); if (SUCCEEDED(hr)) { pps->AddRef(); _pps = pps; } pps->Release(); } } else { if (NULL == _pps) { hr = S_OK; } else { hr = _pps->RevokeService(_dwServiceCookie); if (SUCCEEDED(hr)) { ATOMICRELEASE(_pps); _dwServiceCookie = -1; } } } return hr; } HRESULT CFindFolderViewCB::OnWindowDestroy(DWORD pv, HWND wP) { _ProfferService(FALSE); // unregister our service w/ top level container if (_pff->_pfcn) _pff->_pfcn->StopSearch(); // The search may have a circular set of pointers. So call the // delete items and folders here to remove these back references... _pff->ClearItemList(); _pff->ClearFolderList(); IFindControllerNotify *pfcn; if (_pff->GetControllerNotifyObject(&pfcn) == S_OK) { pfcn->ViewDestroyed(); pfcn->Release(); } return S_OK; } HRESULT CFindFolderViewCB::OnIsOwnerData(DWORD pv, DWORD *pdwFlags) { *pdwFlags |= FWF_OWNERDATA; // we want virtual defview support return S_OK; } HRESULT CFindFolderViewCB::OnGetODRangeObject(DWORD pv, WPARAM wWhich, ILVRange **plvr) { HRESULT hr = E_FAIL; switch (wWhich) { case LVSR_SELECTION: hr = _pff->_dflvrSel.QueryInterface(IID_PPV_ARG(ILVRange, plvr)); break; case LVSR_CUT: hr = _pff->_dflvrCut.QueryInterface(IID_PPV_ARG(ILVRange, plvr)); break; } return hr; } HRESULT CFindFolderViewCB::OnODCacheHint(DWORD pv, NMLVCACHEHINT* pnmlvc) { // The listview is giving us a hint of the items it is about to do something in a range // so make sure we have pidls for each of the items in the range... int iTo; _pff->GetItemCount(&iTo); if (iTo >= pnmlvc->iTo) iTo = pnmlvc->iTo; else iTo--; for (int i = pnmlvc->iFrom; i <= iTo; i++) { FIND_ITEM *pesfi; if (FAILED(_pff->GetItem(i, &pesfi))) break; } return S_OK; } HRESULT CFindFolderViewCB::OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP) { *lP = FVM_DETAILS; // match the advanced mode of SC (+ Win2K parity) return S_OK; } HRESULT CFindFolderViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData) { ZeroMemory(pData, sizeof(*pData)); pData->dwLayout = SFVMWVL_DETAILS | SFVMWVL_FILES; return S_OK; } HRESULT CFindFolderViewCB::_OnOpenContainingFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc) { CFindFolderViewCB* pThis = (CFindFolderViewCB*)(void*)pv; return pThis->_pff->OpenContainingFolder(pThis->_punkSite); } const WVTASKITEM c_FindTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_SEARCH, IDS_HEADER_FIND_TT); const WVTASKITEM c_FindTaskList[] = { 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), }; HRESULT CFindFolderViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData) { ZeroMemory(pData, sizeof(*pData)); Create_IUIElement(&c_FindTaskHeader, &(pData->pSpecialTaskHeader)); LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_NETWORK) }; CreateIEnumIDListOnCSIDLs(_pidl, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &pData->penumOtherPlaces); return S_OK; } HRESULT CFindFolderViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks) { ZeroMemory(pTasks, sizeof(*pTasks)); Create_IEnumUICommand((IUnknown*)(void*)this, c_FindTaskList, ARRAYSIZE(c_FindTaskList), &pTasks->penumSpecialTasks); return S_OK; } HRESULT CFindFolderViewCB::OnGetWebViewTheme(DWORD pv, SFVM_WEBVIEW_THEME_DATA* pTheme) { ZeroMemory(pTheme, sizeof(*pTheme)); pTheme->pszThemeID = L"search"; return S_OK; } HRESULT CFindFolderViewCB::OnGetIPersistHistory(DWORD pv, IPersistHistory **ppph) { // If they call us with ppph == NULL they simply want to know if we support // the history so return S_OK; if (ppph == NULL) return S_OK; // get the persist history from us and we hold folder and view objects *ppph = NULL; CFindPersistHistory *pdfph = new CFindPersistHistory(); if (!pdfph) return E_OUTOFMEMORY; HRESULT hr = pdfph->QueryInterface(IID_PPV_ARG(IPersistHistory, ppph)); pdfph->Release(); return hr; } HRESULT CFindFolderViewCB::OnRefresh(DWORD pv, BOOL fPreRefresh) { EnterCriticalSection(&_pff->_csSearch); _pff->_fInRefresh = BOOLIFY(fPreRefresh); // If we have old results tell defview the new count now... if (!fPreRefresh && _pff->_hdpaItems) { IShellFolderView *psfv; UINT cItems = DPA_GetPtrCount(_pff->_hdpaItems); if (cItems && _punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv)))) { psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL); psfv->Release(); } } LeaveCriticalSection(&_pff->_csSearch); return S_OK; } HRESULT CFindFolderViewCB::OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *phtd) { if (IsOS(OS_ANYSERVER)) { StringCchCopy(phtd->wszHelpFile, ARRAYSIZE(phtd->wszHelpFile), L"find.chm"); // ok to truncate } else { StringCchCopy(phtd->wszHelpTopic, ARRAYSIZE(phtd->wszHelpTopic), L"hcp://services/subsite?node=Unmapped/Search"); // ok to truncate } return S_OK; } STDMETHODIMP CFindFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu); HANDLE_MSG(0, SFVM_GETWORKINGDIR, OnGETWORKINGDIR); HANDLE_MSG(0, SFVM_GETCOLSAVESTREAM, OnGETCOLSAVESTREAM); HANDLE_MSG(0, SFVM_GETITEMIDLIST, OnGETITEMIDLIST); HANDLE_MSG(0, SFVM_SETITEMIDLIST, OnSETITEMIDLIST); HANDLE_MSG(0, SFVM_SELCHANGE, OnSelChange); HANDLE_MSG(0, SFVM_INDEXOFITEMIDLIST, OnGetIndexForItemIDList); HANDLE_MSG(0, SFVM_DELETEITEM, OnDeleteItem); HANDLE_MSG(0, SFVM_ODFINDITEM, OnODFindItem); HANDLE_MSG(0, SFVM_ARRANGE, OnReArrange); HANDLE_MSG(0, SFVM_GETEMPTYTEXT, OnGetEmptyText); HANDLE_MSG(0, SFVM_SETEMPTYTEXT, OnSetEmptyText); HANDLE_MSG(0, SFVM_GETITEMICONINDEX, OnGetItemIconIndex); HANDLE_MSG(0, SFVM_SETICONOVERLAY, OnSetItemIconOverlay); HANDLE_MSG(0, SFVM_GETICONOVERLAY, OnGetItemIconOverlay); HANDLE_MSG(0, SFVM_FOLDERSETTINGSFLAGS, OnIsOwnerData); HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated); HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWindowDestroy); HANDLE_MSG(0, SFVM_GETODRANGEOBJECT, OnGetODRangeObject); HANDLE_MSG(0, SFVM_ODCACHEHINT, OnODCacheHint); HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE); HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout); HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent); HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks); HANDLE_MSG(0, SFVM_GETWEBVIEWTHEME, OnGetWebViewTheme); HANDLE_MSG(0, SFVM_GETIPERSISTHISTORY, OnGetIPersistHistory); HANDLE_MSG(0, SFVM_REFRESH, OnRefresh); HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic); HANDLE_MSG(0, SFVM_SORTLISTDATA, OnSortListData); default: return E_FAIL; } return S_OK; } CFindFolderViewCB::CFindFolderViewCB(CFindFolder* pff) : CBaseShellFolderViewCB(pff->_pidl, 0), _pff(pff), _fIgnoreSelChange(FALSE), _iColSort((UINT)-1), _iFocused((UINT)-1), _cSelected(0), _pps(NULL), _dwServiceCookie(-1) { _pff->AddRef(); } CFindFolderViewCB::~CFindFolderViewCB() { _pff->Release(); ASSERT(NULL == _pps); ASSERT(_dwServiceCookie == -1); } // give the find command code access to defview via this QS that we proffered HRESULT CFindFolderViewCB::QueryService(REFGUID guidService, REFIID riid, void **ppv) { HRESULT hr = E_NOTIMPL; *ppv = NULL; if (guidService == SID_DocFindFolder) { hr = IUnknown_QueryService(_punkSite, SID_DefView, riid, ppv); } return hr; } CFindPersistHistory::CFindPersistHistory() { } STDAPI CFindPersistHistory_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; CFindPersistHistory *pdfph = new CFindPersistHistory(); if (pdfph) { hr = pdfph->QueryInterface(riid, ppv); pdfph->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; } return hr; } // Functions to support persisting the document into the history stream... STDMETHODIMP CFindPersistHistory::GetClassID(CLSID *pClassID) { *pClassID = CLSID_DocFindPersistHistory; return S_OK; } IFindFolder *CFindPersistHistory::_GetDocFindFolder() { IFindFolder *pdff = NULL; // the _punksite is to the defview so we can simply QI for frame... IFolderView *pfv; if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IFolderView, &pfv)))) { pfv->GetFolder(IID_PPV_ARG(IFindFolder, &pdff)); pfv->Release(); } return pdff; } STDMETHODIMP CFindPersistHistory::LoadHistory(IStream *pstm, IBindCtx *pbc) { int cItems = 0; IFindFolder *pdff = _GetDocFindFolder(); if (pdff) { pdff->RestoreFolderList(pstm); pdff->RestoreItemList(pstm, &cItems); pdff->Release(); } IShellFolderView *psfv; if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv)))) { psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL); psfv->Release(); } // call our base class to allow it to restore it's stuff as well. return CDefViewPersistHistory::LoadHistory(pstm, pbc); } STDMETHODIMP CFindPersistHistory::SaveHistory(IStream *pstm) { IFindFolder *pdff = _GetDocFindFolder(); if (pdff) { pdff->SaveFolderList(pstm); pdff->SaveItemList(pstm); pdff->Release(); } // Let base class save out as well return CDefViewPersistHistory::SaveHistory(pstm); } // use to manage the selection states for an owner data listview... STDMETHODIMP_(ULONG) CFindLVRange::AddRef() { return _pff->AddRef(); } STDMETHODIMP_(ULONG) CFindLVRange::Release() { return _pff->Release(); } STDMETHODIMP CFindLVRange::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFindLVRange, ILVRange), // IID_ILVRange { 0 }, }; return QISearch(this, qit, riid, ppv); } // ILVRange methods STDMETHODIMP CFindLVRange::IncludeRange(LONG iBegin, LONG iEnd) { // Including the range must load the elements as we need the object ptr... FIND_ITEM *pesfi; int iTotal; _pff->GetItemCount(&iTotal); if (iEnd > iTotal) iEnd = iTotal-1; for (long i = iBegin; i <= iEnd;i++) { if (SUCCEEDED(_pff->GetItem(i, &pesfi)) && pesfi) { if ((pesfi->dwState & _dwMask) == 0) { _cIncluded++; pesfi->dwState |= _dwMask; } } } return S_OK; } STDMETHODIMP CFindLVRange::ExcludeRange(LONG iBegin, LONG iEnd) { // Excluding the range is OK to not load the elements as this would be to deslect all... EnterCriticalSection(&_pff->_csSearch); if (iEnd >= DPA_GetPtrCount(_pff->_hdpaItems)) iEnd = DPA_GetPtrCount(_pff->_hdpaItems) - 1; for (long i = iBegin; i <= iEnd; i++) { FIND_ITEM *pesfi = (FIND_ITEM*)DPA_FastGetPtr(_pff->_hdpaItems, i); if (pesfi) { if (pesfi->dwState & _dwMask) { _cIncluded--; pesfi->dwState &= ~_dwMask; } } } LeaveCriticalSection(&_pff->_csSearch); return S_OK; } STDMETHODIMP CFindLVRange::InvertRange(LONG iBegin, LONG iEnd) { // Including the range must load the elements as we need the object ptr... int iTotal; _pff->GetItemCount(&iTotal); if (iEnd > iTotal) iEnd = iTotal-1; for (long i = iBegin; i <= iEnd;i++) { FIND_ITEM *pesfi; if (SUCCEEDED(_pff->GetItem(i, &pesfi)) && pesfi) { if ((pesfi->dwState & _dwMask) == 0) { _cIncluded++; pesfi->dwState |= _dwMask; } else { _cIncluded--; pesfi->dwState &= ~_dwMask; } } } return S_OK; } STDMETHODIMP CFindLVRange::InsertItem(LONG iItem) { // We already maintain the list anyway... return S_OK; } STDMETHODIMP CFindLVRange::RemoveItem(LONG iItem) { // We maintain the list so don't do anything... return S_OK; } STDMETHODIMP CFindLVRange::Clear() { // If there are things selected, need to unselect them now... if (_cIncluded) ExcludeRange(0, LONG_MAX); _cIncluded = 0; _pff->ClearSaveStateList(); return S_OK; } STDMETHODIMP CFindLVRange::IsSelected(LONG iItem) { // Don't force the items to be generated if they were not before... HRESULT hr = S_FALSE; EnterCriticalSection(&_pff->_csSearch); FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem); if (pesfi) hr = pesfi->dwState & _dwMask ? S_OK : S_FALSE; LeaveCriticalSection(&_pff->_csSearch); // Assume not selected if we don't have the item yet... return hr; } STDMETHODIMP CFindLVRange::IsEmpty() { return _cIncluded ? S_FALSE : S_OK; } STDMETHODIMP CFindLVRange::NextSelected(LONG iItem, LONG *piItem) { EnterCriticalSection(&_pff->_csSearch); LONG cItems = DPA_GetPtrCount(_pff->_hdpaItems); while (iItem < cItems) { FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem); if (pesfi && (pesfi->dwState & _dwMask)) { *piItem = iItem; LeaveCriticalSection(&_pff->_csSearch); return S_OK; } iItem++; } LeaveCriticalSection(&_pff->_csSearch); *piItem = -1; return S_FALSE; } STDMETHODIMP CFindLVRange::NextUnSelected(LONG iItem, LONG *piItem) { EnterCriticalSection(&_pff->_csSearch); LONG cItems = DPA_GetPtrCount(_pff->_hdpaItems); while (iItem < cItems) { FIND_ITEM *pesfi = (FIND_ITEM*)DPA_GetPtr(_pff->_hdpaItems, iItem); if (!pesfi || ((pesfi->dwState & _dwMask) == 0)) { *piItem = iItem; LeaveCriticalSection(&_pff->_csSearch); return S_OK; } iItem++; } LeaveCriticalSection(&_pff->_csSearch); *piItem = -1; return S_FALSE; } STDMETHODIMP CFindLVRange::CountIncluded(LONG *pcIncluded) { *pcIncluded = _cIncluded; // Sortof Gross, but if looking at selection then also include the list of items // that are selected in our save list... if (_dwMask & LVIS_SELECTED) *pcIncluded += _pff->_cSaveStateSelected; return S_OK; } // Define OleDBEnum translation structure... typedef struct _dfodbet // DFET for short { struct _dfodbet *pdfetNext; LPWSTR pwszFrom; int cbFrom; LPWSTR pwszTo; } DFODBET; class CContentIndexEnum : public IFindEnum, public IShellService { public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IFindEnum STDMETHODIMP Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState); STDMETHODIMP Skip(int celt); STDMETHODIMP Reset(); STDMETHODIMP StopSearch(); STDMETHODIMP_(BOOL) FQueryIsAsync(); STDMETHODIMP GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone); STDMETHODIMP GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl); STDMETHODIMP GetItemID(UINT iItem, DWORD *puWorkID); STDMETHODIMP SortOnColumn(UINT iCOl, BOOL fAscending); // IShellService STDMETHODIMP SetOwner(IUnknown* punkOwner); CContentIndexEnum(IFindFilter *pfilter, IFindFolder *pff, DWORD grfFlags, int iColSort, LPTSTR pszProgressText, IRowsetWatchNotify *prwn); HRESULT DoQuery(LPWSTR *apwszPaths, UINT *pcPaths); private: ~CContentIndexEnum(); HRESULT _BuildAndSetCommandTree(int iCol, BOOL fReverse); HRESULT _SetCmdProp(ICommand *pCommand); HRESULT _MapColumns(IUnknown *punk, DBORDINAL cCols, DBBINDING *pBindings, const DBID * pDbCols, HACCESSOR &hAccessor); void _ReleaseAccessor(); HRESULT _CacheRowSet(UINT iItem); HRESULT _TranslateFolder(LPCWSTR pszParent, LPWSTR pszResult, UINT cchSize, BOOL *pfTranslated); void _ClearFolderState(); LONG _cRef; IFindFilter *_pfilter; IRowsetWatchNotify *_prwn; IFindFolder *_pff; int _iColSort; DWORD _grfFlags; DWORD _grfWarnings; LPTSTR _pszProgressText; TCHAR _szCurrentDir[MAX_PATH]; IShellFolder *_psfCurrentDir; LPITEMIDLIST _pidlFolder; int _iFolder; HRESULT _hrCurrent; ICommand *_pCommand; IRowsetLocate *_pRowset; IRowsetAsynch *_pRowsetAsync; HACCESSOR _hAccessor; HACCESSOR _hAccessorWorkID; HROW _ahrow[100]; // Cache 100 hrows out for now UINT _ihrowFirst; // The index of which row is cached out first DBCOUNTITEM _cRows; // number of hrows in _ahrow DFODBET *_pdfetFirst; // Name translation list. }; STDAPI CreateOleDBEnum(IFindFilter *pfilter, IShellFolder *psf, LPWSTR *apwszPaths, UINT *pcPaths, DWORD grfFlags, int iColSort, LPTSTR pszProgressText, IRowsetWatchNotify *prwn, IFindEnum **ppdfenum) { *ppdfenum = NULL; HRESULT hr = E_OUTOFMEMORY; IFindFolder *pff; psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff)); CContentIndexEnum* pdfenum = new CContentIndexEnum(pfilter, pff, grfFlags, iColSort, pszProgressText, prwn); if (pdfenum) { hr = pdfenum->DoQuery(apwszPaths, pcPaths); if (hr == S_OK) // We only continue to use this if query returne S_OK... *ppdfenum = (IFindEnum*)pdfenum; else { pdfenum->Release(); // release the memory we allocated } } if (pff) pff->Release(); return hr; } const DBID c_aDbCols[] = { {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_NAME}}, {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_PATH}}, {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_ATTRIBUTES}}, {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_SIZE}}, {{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_WRITETIME}}, {{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR) PROPID_QUERY_RANK}}, }; const DBID c_aDbWorkIDCols[] = { {{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR)PROPID_QUERY_WORKID}} }; const LPCWSTR c_awszColSortNames[] = { L"FileName[a],Path[a]", L"Path[a],FileName[a]", L"Size[a]", NULL, L"Write[a]", L"Rank[d]" }; const ULONG c_cDbCols = ARRAYSIZE(c_aDbCols); const DBID c_dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0}; const GUID c_guidQueryExt = DBPROPSET_QUERYEXT; const GUID c_guidRowsetProps = {0xc8b522be,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}; CContentIndexEnum::CContentIndexEnum(IFindFilter *pfilter, IFindFolder *pff, DWORD grfFlags, int iColSort, LPTSTR pszProgressText, IRowsetWatchNotify *prwn) : _cRef(1), _ihrowFirst((UINT)-1), _pfilter(pfilter), _pff(pff), _prwn(prwn), _grfFlags(grfFlags), _grfWarnings(DFW_DEFAULT), _iColSort(iColSort), _pszProgressText(pszProgressText) { _szCurrentDir[0] = 0; ASSERT(_pRowset == 0); ASSERT(_pRowsetAsync == 0); ASSERT(_pCommand == 0); ASSERT(_hAccessor == 0); ASSERT(_hAccessorWorkID ==0); ASSERT(_cRows == 0); if (_pfilter) { _pfilter->AddRef(); _pfilter->GetWarningFlags(&_grfWarnings); } if (_pff) _pff->AddRef(); if (_prwn) _prwn->AddRef(); } void CContentIndexEnum::_ClearFolderState() { ATOMICRELEASE(_psfCurrentDir); ILFree(_pidlFolder); _pidlFolder = NULL; _iFolder = -1; _szCurrentDir[0] = 0; } CContentIndexEnum::~CContentIndexEnum() { ATOMICRELEASE(_pfilter); ATOMICRELEASE(_pff); ATOMICRELEASE(_prwn); _ClearFolderState(); if (_pRowset) { ATOMICRELEASE(_pRowsetAsync); // Release any cached rows. _CacheRowSet((UINT)-1); if (_hAccessor || _hAccessorWorkID) _ReleaseAccessor(); _pRowset->Release(); } ATOMICRELEASE(_pCommand); // Release any name translations we may have allocated. DFODBET *pdfet = _pdfetFirst; while (pdfet) { DFODBET *pdfetT = pdfet; pdfet = pdfet->pdfetNext; // First setup to look at the next item before we free stuff... LocalFree((HLOCAL)pdfetT->pwszFrom); LocalFree((HLOCAL)pdfetT->pwszTo); LocalFree((HLOCAL)pdfetT); } } HRESULT CContentIndexEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENTMULTI(CContentIndexEnum, IUnknown, IFindEnum), // IID_IUNKNOWN QITABENT(CContentIndexEnum, IShellService), // IID_IShellService { 0 }, }; return QISearch(this, qit, riid, ppv); } ULONG CContentIndexEnum::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CContentIndexEnum::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CContentIndexEnum::Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState) { return E_PENDING; // as good a return as any to say that we are async... } HRESULT CContentIndexEnum::Skip(int celt) { return E_NOTIMPL; } HRESULT CContentIndexEnum::Reset() { // overload Reset to mean dump the rowset cache!!! _CacheRowSet(-1); // still return failiure return E_NOTIMPL; } HRESULT CContentIndexEnum::StopSearch() { // Lets see if we can find one that works... HRESULT hr = _pCommand->Cancel(); if (FAILED(hr)) hr = _pRowsetAsync->Stop(); if (FAILED(hr)) { IDBAsynchStatus *pdbas; if (SUCCEEDED(_pRowset->QueryInterface(IID_PPV_ARG(IDBAsynchStatus, &pdbas)))) { hr = pdbas->Abort(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN); pdbas->Release(); } } return hr; } BOOL CContentIndexEnum::FQueryIsAsync() { return TRUE; } HRESULT CContentIndexEnum::GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone) { if (!_pRowsetAsync) return E_FAIL; BOOL fMore; DBCOUNTITEM dwDen, dwNum; HRESULT hr = _pRowsetAsync->RatioFinished(&dwDen, &dwNum, pdwTotalAsync, &fMore); if (SUCCEEDED(hr)) { *pfQueryDone = dwDen == dwNum; *pnPercentComplete = dwDen ? (int)((dwNum * 100) / dwDen) : 100; } else *pfQueryDone = TRUE; // in case that is all they are looking at... return hr; } // modify pszPath until you can parse it, return result in *ppidl HRESULT _StripToParseableName(LPTSTR pszPath, LPITEMIDLIST *ppidl) { *ppidl = NULL; HRESULT hr = E_FAIL; PathRemoveBackslash(pszPath); while (PathRemoveFileSpec(pszPath) && FAILED(hr)) { hr = SHParseDisplayName(pszPath, NULL, ppidl, 0, NULL); } return hr; } // we could not get pidl for this item for some reason. we have to put // it in the list of bad items so that we can tell ci not to give it to // us the next time we do search void _ExcludeFromFutureSearch(LPCTSTR pszParent) { HKEY hkey; HRESULT hr; TCHAR szParent[MAX_PATH]; hr = StringCchCopy(szParent, ARRAYSIZE(szParent), pszParent); if (FAILED(hr)) { return; } if (ERROR_SUCCESS == RegCreateKeyExW(HKEY_CURRENT_USER, CI_SPECIAL_FOLDERS, 0, L"", 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hkey, NULL)) { LPITEMIDLIST pidlT; if (SUCCEEDED(_StripToParseableName(szParent, &pidlT))) { ILFree(pidlT); DWORD dwInsert = 0; // init to zero in case query info bellow fails int iEnd; TCHAR sz[MAX_PATH], szName[12]; RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwInsert, NULL, NULL, NULL, NULL); // start from the end as there is a high chance we added this at the end for (int i = dwInsert - 1; i >= 0; i--) { StringCchPrintf(szName, ARRAYSIZE(szName), L"%d", i); // ok to truncate if (ERROR_SUCCESS == SHRegGetString(hkey, NULL, szName, sz, ARRAYSIZE(sz))) { LPTSTR pszTemp = StrStrI(sz + 1, szParent); // +1 to pass " that's at the beginning of the string if (pszTemp && pszTemp == sz + 1) { dwInsert = i; // overwrite this value break; } else { iEnd = lstrlen(sz); if (EVAL(iEnd > 1)) { int iBackslash = iEnd - 3; ASSERT(sz[iBackslash] == L'\\'); sz[iBackslash] = L'\0'; pszTemp = StrStrI(szParent, sz + 1); sz[iBackslash] = L'\\'; if (pszTemp && pszTemp == szParent) { dwInsert = -1; break; } } } } } if (dwInsert != -1) { hr = StringCchPrintf(szName, ARRAYSIZE(szName), L"%d", dwInsert); if (SUCCEEDED(hr)) { if (PathAppend(szParent, TEXT("*"))) { PathQuoteSpaces(szParent); RegSetValueEx(hkey, szName, 0, REG_SZ, (BYTE *)szParent, (lstrlen(szParent) + 1) * sizeof(szParent[0])); } } } } RegCloseKey(hkey); } } // If it is a UNC it might be one we need to translate, to handle the case that // content index does not support redirected drives. HRESULT CContentIndexEnum::_TranslateFolder(LPCTSTR pszParent, LPTSTR pszResult, UINT cchSize, BOOL *pfTranslated) { BOOL fTranslated = FALSE; HRESULT hr = S_OK; if (PathIsUNC(pszParent)) { for (DFODBET *pdfet = _pdfetFirst; pdfet; pdfet = pdfet->pdfetNext) { if ((StrCmpNIW(pszParent, pdfet->pwszFrom, pdfet->cbFrom) == 0) && (pszParent[pdfet->cbFrom] == L'\\')) { // Ok we have a translation to use. hr = StringCchCopy(pszResult, cchSize, pdfet->pwszTo); if (SUCCEEDED(hr)) { // need + 1 here or we'll get something like "w:\\winnt" bogus path, that is. hr = StringCchCat(pszResult, cchSize, &pszParent[pdfet->cbFrom + 1]); if (SUCCEEDED(hr)) { fTranslated = TRUE; } } } } } if (!fTranslated) { // default to the same hr = StringCchCopy(pszResult, cchSize, pszParent); } *pfTranslated = fTranslated; return hr; } HRESULT CContentIndexEnum::GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl) { *ppidl = NULL; HRESULT hr = _CacheRowSet(iItem); if (S_OK != hr) { return E_FAIL; // we could not get the item someone asked for, so error... } PROPVARIANT* data[c_cDbCols]; hr = _pRowset->GetData(_ahrow[iItem - _ihrowFirst], _hAccessor, &data); if (hr != S_OK) { return hr; } // data[0].pwszVal is the file name // data[1].pwszVal is the full path (including file name) // data[2].ulVal is the attribute // data[3].ulVal is the size in byte // data[4].filetime is the last write time in UTC // data[5].ulVal is the rank of the item... WIN32_FIND_DATA fd = {0}; fd.dwFileAttributes = data[2]->ulVal; fd.nFileSizeLow = data[3]->ulVal; fd.ftLastWriteTime = data[4]->filetime; ASSERT(ShowSuperHidden() || !IsSuperHidden(fd.dwFileAttributes)); // query should exclude these hr = StringCchCopy(fd.cFileName, ARRAYSIZE(fd.cFileName), data[0]->pwszVal); if (FAILED(hr)) { return hr; } WCHAR szParent[MAX_PATH]; hr = StringCchCopy(szParent, ARRAYSIZE(szParent), data[1]->pwszVal); // full path if (FAILED(hr)) { return hr; } PathRemoveFileSpec(szParent); // strip to parent folder path WCHAR szTranslatedParent[MAX_PATH]; BOOL fTranslated; hr = _TranslateFolder(szParent, szTranslatedParent, ARRAYSIZE(szTranslatedParent), &fTranslated); if (FAILED(hr)) { return hr; } // // Working with a new folder? // if (lstrcmp(szParent, _szCurrentDir) != 0) { _ClearFolderState(); // our previous "current folder" state is now invalid hr = SHParseDisplayName(szTranslatedParent, NULL, &_pidlFolder, 0, NULL); if (SUCCEEDED(hr)) { hr = _pff->AddFolder(_pidlFolder, TRUE, &_iFolder); if (SUCCEEDED(hr)) { hr = _pff->GetFolder(_iFolder, IID_PPV_ARG(IShellFolder, &_psfCurrentDir)); if (SUCCEEDED(hr)) { // on succesful init of this folder save the cache key hr = StringCchCopy(_szCurrentDir, ARRAYSIZE(_szCurrentDir), szParent); } } } else if (hr != E_OUTOFMEMORY && !fTranslated) { _ExcludeFromFutureSearch(szParent); } _hrCurrent = hr; // save error state for next time around if (FAILED(hr)) _ClearFolderState(); } else { hr = _hrCurrent; } // // Now that we have changed to the current folder, lets get the item out // of the folder. // if (SUCCEEDED(hr)) { // success implies the state of these variables ASSERT((NULL != _psfCurrentDir) && (NULL != _pidlFolder) && (_iFolder > 0)); DWORD dwItemID; GetItemID(iItem, &dwItemID); LPITEMIDLIST pidl; hr = SHSimpleIDListFromFindData2(_psfCurrentDir, &fd, &pidl); if (SUCCEEDED(hr)) { hr = _pff->AddDataToIDList(pidl, _iFolder, _pidlFolder, DFDF_EXTRADATA, iItem, dwItemID, data[5]->ulVal, ppidl); ILFree(pidl); } } else { // failure implies these should be clear ASSERT((NULL == _psfCurrentDir) && (NULL == _pidlFolder)); LPITEMIDLIST pidlFull; if (SUCCEEDED(_StripToParseableName(szTranslatedParent, &pidlFull))) { LPCITEMIDLIST pidlChild; if (SUCCEEDED(SplitIDList(pidlFull, &_pidlFolder, &pidlChild))) { hr = _pff->AddFolder(_pidlFolder, TRUE, &_iFolder); if (SUCCEEDED(hr)) { hr = _pff->GetFolder(_iFolder, IID_PPV_ARG(IShellFolder, &_psfCurrentDir)); if (SUCCEEDED(hr)) { hr = _pff->AddDataToIDList(pidlChild, _iFolder, _pidlFolder, DFDF_NONE, 0, 0, 0, ppidl); if (SUCCEEDED(hr)) { // on succesful init of this folder save the cache key hr = StringCchCopy(_szCurrentDir, ARRAYSIZE(_szCurrentDir), szTranslatedParent); if (SUCCEEDED(hr)) { PathRemoveFileSpec(_szCurrentDir); } } } } } ILFree(pidlFull); if (FAILED(hr)) _ClearFolderState(); } } return hr; } HRESULT CContentIndexEnum::GetItemID(UINT iItem, DWORD *puItemID) { *puItemID = (UINT)-1; HRESULT hr = _CacheRowSet(iItem); if (S_OK == hr) { PROPVARIANT* data[1]; hr = _pRowset->GetData(_ahrow[iItem - _ihrowFirst], _hAccessorWorkID, &data); if (S_OK == hr) { // Only one data column so this is easy... // The ULVal is the thing we are after... *puItemID = data[0]->ulVal; } } return hr; } HRESULT CContentIndexEnum::SortOnColumn(UINT iCol, BOOL fAscending) { // Ok We need to generate the Sort String... return _BuildAndSetCommandTree(iCol, fAscending); } HRESULT CContentIndexEnum::SetOwner(IUnknown* punkOwner) { // Used to set the docfind folder and from that the filter. ATOMICRELEASE(_pfilter); ATOMICRELEASE(_pff); if (punkOwner) { punkOwner->QueryInterface(IID_PPV_ARG(IFindFolder, &_pff)); if (_pff) _pff->GetFindFilter(&_pfilter); } return S_OK; } HRESULT CContentIndexEnum::_MapColumns(IUnknown *punk, DBORDINAL cCols, DBBINDING *pBindings, const DBID *pDbCols, HACCESSOR &hAccessor) { DBORDINAL aMappedColumnIDs[c_cDbCols]; IColumnsInfo *pColumnsInfo; HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IColumnsInfo, &pColumnsInfo)); if (SUCCEEDED(hr)) { hr = pColumnsInfo->MapColumnIDs(cCols, pDbCols, aMappedColumnIDs); if (SUCCEEDED(hr)) { for (ULONG i = 0; i < cCols; i++) pBindings[i].iOrdinal = aMappedColumnIDs[i]; IAccessor *pIAccessor; hr = punk->QueryInterface(IID_PPV_ARG(IAccessor, &pIAccessor)); if (SUCCEEDED(hr)) { hAccessor = 0; hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cCols, pBindings, 0, &hAccessor, 0); pIAccessor->Release(); } } pColumnsInfo->Release(); } return hr; } void CContentIndexEnum::_ReleaseAccessor() { IAccessor *pIAccessor; HRESULT hr = _pRowset->QueryInterface(IID_PPV_ARG(IAccessor, &pIAccessor)); if (SUCCEEDED(hr)) { if (_hAccessor) pIAccessor->ReleaseAccessor(_hAccessor, 0); if (_hAccessorWorkID) pIAccessor->ReleaseAccessor(_hAccessorWorkID, 0); pIAccessor->Release(); } } HRESULT CContentIndexEnum::_CacheRowSet(UINT iItem) { HRESULT hr = S_OK; if (!_pRowset) return E_FAIL; if (!_cRows || !InRange(iItem, _ihrowFirst, _ihrowFirst+(UINT)_cRows-1) || (iItem == (UINT)-1)) { // Release the last cached element we had. if (_cRows != 0) _pRowset->ReleaseRows(ARRAYSIZE(_ahrow), _ahrow, 0, 0, 0); // See if we are simply releasing our cached data... _cRows = 0; _ihrowFirst = (UINT)-1; if (iItem == (UINT)-1) return S_OK; // Ok try to read in the next on... BYTE bBookMark = (BYTE) DBBMK_FIRST; HROW *rghRows = (HROW *)_ahrow; // change this to fetch 100 or so rows at the time -- huge perf improvment hr = _pRowset->GetRowsAt(0, 0, sizeof(bBookMark), &bBookMark, iItem, ARRAYSIZE(_ahrow), &_cRows, &rghRows); if (FAILED(hr)) return hr; _ihrowFirst = iItem; if ((DB_S_ENDOFROWSET == hr) || (_cRows == 0)) { if (_cRows == 0) _ihrowFirst = -1; else hr = S_OK; // we got some items and caller expects S_OK so change DB_S_ENDOFROWSET to noerror } } return hr; } HRESULT CContentIndexEnum::_SetCmdProp(ICommand *pCommand) { #define MAX_PROPS 8 DBPROPSET aPropSet[MAX_PROPS]; DBPROP aProp[MAX_PROPS]; ULONG cProps = 0; HRESULT hr; // asynchronous query aProp[cProps].dwPropertyID = DBPROP_IRowsetAsynch; aProp[cProps].dwOptions = 0; aProp[cProps].dwStatus = 0; aProp[cProps].colid = c_dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = VARIANT_TRUE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = c_guidRowsetProps; cProps++; // don't timeout queries aProp[cProps].dwPropertyID = DBPROP_COMMANDTIMEOUT; aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP; aProp[cProps].dwStatus = 0; aProp[cProps].colid = c_dbcolNull; aProp[cProps].vValue.vt = VT_I4; aProp[cProps].vValue.lVal = 0; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = c_guidRowsetProps; cProps++; // We can handle PROPVARIANTs aProp[cProps].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES; aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP; aProp[cProps].dwStatus = 0; aProp[cProps].colid = c_dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = VARIANT_TRUE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = c_guidQueryExt; cProps++; ICommandProperties * pCmdProp = 0; hr = pCommand->QueryInterface(IID_PPV_ARG(ICommandProperties, &pCmdProp)); if (SUCCEEDED(hr)) { hr = pCmdProp->SetProperties(cProps, aPropSet); pCmdProp->Release(); } return hr; } // create the query command string HRESULT CContentIndexEnum::_BuildAndSetCommandTree(int iCol, BOOL fReverse) { LPWSTR pwszRestrictions = NULL; DWORD dwGQRFlags; HRESULT hr = _pfilter->GenerateQueryRestrictions(&pwszRestrictions, &dwGQRFlags); if (SUCCEEDED(hr)) { ULONG ulDialect; hr = _pfilter->GetQueryLanguageDialect(&ulDialect); if (SUCCEEDED(hr)) { // NOTE: hard coded to our current list of columns WCHAR wszSort[80]; // use this to sort by different columns... wszSort[0] = 0; if ((iCol >= 0) && (iCol < ARRAYSIZE(c_awszColSortNames)) && c_awszColSortNames[iCol]) { // Sort order is hardcoded for ascending. hr = StringCchCopy(wszSort, ARRAYSIZE(wszSort), c_awszColSortNames[iCol]); if (SUCCEEDED(hr)) { hr = StringCchCat(wszSort, ARRAYSIZE(wszSort), L",Path[a],FileName[a]"); } if (FAILED(hr)) { // don't sort on anything which doesn't let us // do proper sort string construction wszSort[0] = 0; } } if (SUCCEEDED(hr)) { DBCOMMANDTREE *pTree = NULL; hr = CITextToFullTreeEx(pwszRestrictions, ulDialect, L"FileName,Path,Attrib,Size,Write,Rank,WorkID", wszSort[0] ? wszSort : NULL, 0, &pTree, 0, 0, LOCALE_USER_DEFAULT); if (FAILED(hr)) { // Map this to one that I know about // Note: We will only do this if we require CI else we will try to fallback to old search... // Note we are running into problems where CI says we are contained in a Catalog even if // CI process is not running... So try to avoid this if possible if (dwGQRFlags & GQR_REQUIRES_CI) hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_CONSTRAINT); } if (SUCCEEDED(hr)) { ICommandTree *pCmdTree; hr = _pCommand->QueryInterface(IID_PPV_ARG(ICommandTree, &pCmdTree)); if (SUCCEEDED(hr)) { hr = pCmdTree->SetCommandTree(&pTree, DBCOMMANDREUSE_NONE, FALSE); pCmdTree->Release(); } } } } } LocalFree((HLOCAL)pwszRestrictions); return hr; } #define cbP (sizeof (PROPVARIANT *)) // [in, out] apwszPaths this is modified // [in, out] pcPaths HRESULT CContentIndexEnum::DoQuery(LPWSTR *apwszPaths, UINT *pcPaths) { UINT nPaths = *pcPaths; WCHAR** aScopes = NULL; WCHAR** aScopesOrig = NULL; ULONG* aDepths = NULL; WCHAR** aCatalogs = NULL; WCHAR** aMachines = NULL; WCHAR wszPath[MAX_PATH]; LPWSTR pwszPath = wszPath; LPWSTR pszMachineAlloc = NULL, pszCatalogAlloc = NULL; LPWSTR pwszMachine, pwszCatalog; UINT i, iPath = 0; DWORD dwQueryRestrictions; // Initiailize all of our query values back to unused _hAccessor = NULL; _hAccessorWorkID = NULL; _pRowset = NULL; _pRowsetAsync = NULL; _pCommand = NULL; // Get array of search paths... #define MAX_MACHINE_NAME_LEN 32 BOOL fIsCIRunning, fCiIndexed, fCiPermission; GetCIStatus(&fIsCIRunning, &fCiIndexed, &fCiPermission); // First pass see if we have anything that make use at all of CI if not lets simply bail and let // old code walk the list... HRESULT hr = _pfilter->GenerateQueryRestrictions(NULL, &dwQueryRestrictions); if (FAILED(hr)) goto Abort; if ((dwQueryRestrictions & GQR_MAKES_USE_OF_CI) == 0) { hr = S_FALSE; goto Abort; } // allocate the arrays that we need to pass to CIMakeICommand and // the buffers needed for the machine name and catalog name aDepths = (ULONG*)LocalAlloc(LPTR, nPaths * sizeof(ULONG)); aScopes = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*)); aScopesOrig = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*)); aCatalogs = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*)); aMachines = (WCHAR**)LocalAlloc(LPTR, nPaths * sizeof(WCHAR*)); pszMachineAlloc = pwszMachine = (LPWSTR)LocalAlloc(LPTR, nPaths * MAX_MACHINE_NAME_LEN * sizeof(WCHAR)); pszCatalogAlloc = pwszCatalog = (LPWSTR)LocalAlloc(LPTR, nPaths * MAX_PATH * sizeof(WCHAR)); if (!aDepths || !aScopes || !aScopesOrig || !aCatalogs || !aMachines || !pszMachineAlloc || !pszCatalogAlloc) { hr = E_OUTOFMEMORY; goto Abort; } // This following loop does two things, // 1. Check if all the scopes are indexed, if any one scope is not, // fail the call and we'll do the win32 find. // 2. Prepare the arrays of parameters that we need to pass to // CIMakeICommand(). // // NOTE: Reinerf says this code looks busted for nPaths > 1. See bug 199254 for comments. for (i = 0; i < nPaths; i++) { ULONG cchMachine = MAX_MACHINE_NAME_LEN; ULONG cchCatalog = MAX_PATH; WCHAR wszUNCPath[MAX_PATH]; BOOL fRemapped = FALSE; // if CI is not running we can still do ci queries on a remote drive (if it is indexed) // so we cannot just bail if ci is not running on user's machine if (!fIsCIRunning && !PathIsRemote(apwszPaths[i])) continue; // do grep on this one hr = LocateCatalogsW(apwszPaths[i], 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog); if (hr != S_OK) { // see if by chance this is a network redirected drive. If so we CI does not handle // these. See if we can remap to UNC path to ask again... if (!PathIsUNC(apwszPaths[i])) { DWORD nLength = ARRAYSIZE(wszUNCPath); // this api takes TCHAR, but we only compile this part for WINNT... DWORD dwType = SHWNetGetConnection(apwszPaths[i], wszUNCPath, &nLength); if ((dwType == NO_ERROR) || (dwType == ERROR_CONNECTION_UNAVAIL)) { fRemapped = TRUE; LPWSTR pwsz = PathSkipRootW(apwszPaths[i]); BOOL fOk = TRUE; if (pwsz) { if (!PathAppendW(wszUNCPath, pwsz)) { fOk = FALSE; } } if (fOk) { cchMachine = MAX_MACHINE_NAME_LEN; // reset in params cchCatalog = MAX_PATH; hr = LocateCatalogsW(wszUNCPath, 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog); } } } } if (hr != S_OK) { continue; // this one is not indexed. } if (S_FALSE == CatalogUptodate(pwszCatalog, pwszMachine)) { // not up todate if (dwQueryRestrictions & GQR_REQUIRES_CI) { // ci not up to date and we must use it.. // inform the user that results may not be complete if (!(_grfWarnings & DFW_IGNORE_INDEXNOTCOMPLETE)) { hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXNOTCOMPLETE); goto Abort; } //else use ci although index is not complete } else { // ci is not upto date so just use grep for this drive so user can get // complete results pwszCatalog[0] = 0; pwszMachine[0] = 0; continue; } } aDepths[iPath] = (_grfFlags & DFOO_INCLUDESUBDIRS) ? QUERY_DEEP : QUERY_SHALLOW; aScopesOrig[iPath] = apwszPaths[i]; if (fRemapped) { aScopes[iPath] = StrDupW(wszUNCPath); if (aScopes[iPath] == NULL) { hr = E_OUTOFMEMORY; goto Abort; } } else { aScopes[iPath] = apwszPaths[i]; } aCatalogs[iPath] = pwszCatalog; aMachines[iPath] = pwszMachine; pwszCatalog += MAX_PATH; // advance the catalog and machine name buffer pwszMachine += MAX_MACHINE_NAME_LEN; iPath++; // next item in this list } if (iPath == 0) { // no catalogs found; - We should check to see if by chance the user specified a query that // is CI based if so error apapropriately... hr = (dwQueryRestrictions & GQR_REQUIRES_CI) ? MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXSEARCH) : S_FALSE; goto Abort; } // Get ICommand. hr = CIMakeICommand(&_pCommand, iPath, aDepths, aScopes, aCatalogs, aMachines); if (SUCCEEDED(hr)) { // create the query command string - Assume default sort... hr = _BuildAndSetCommandTree(_iColSort, FALSE); if (SUCCEEDED(hr)) { if ((dwQueryRestrictions & GQR_REQUIRES_CI) && (nPaths != iPath)) { // check warning flags to see if we should ignore and continue if (0 == (_grfWarnings & DFW_IGNORE_CISCOPEMISMATCH)) { hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_SCOPEMISMATCH); } } if (SUCCEEDED(hr)) { // Get IRowset. _SetCmdProp(_pCommand); hr = _pCommand->Execute(0, IID_IRowsetLocate, 0, 0, (IUnknown **)&_pRowset); if (SUCCEEDED(hr)) { // we have the IRowset. // Real work to get the Accessor DBBINDING aPropMainCols[c_cDbCols] = { { 0,cbP*0,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,cbP*1,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,cbP*2,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,cbP*3,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,cbP*4,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,cbP*5,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 } }; hr = _MapColumns(_pRowset, c_cDbCols, aPropMainCols, c_aDbCols, _hAccessor); if (SUCCEEDED(hr)) { // OK lets also get the accessor for the WorkID... hr = _MapColumns(_pRowset, ARRAYSIZE(c_aDbWorkIDCols), aPropMainCols, c_aDbWorkIDCols, _hAccessorWorkID); if (SUCCEEDED(hr)) { hr = _pRowset->QueryInterface(IID_PPV_ARG(IRowsetAsynch, &_pRowsetAsync)); } } } } } } if (FAILED(hr)) goto Abort; // If we got here than at least some of our paths are indexed // we may need to compress the list down of the ones we did not handle... *pcPaths = (nPaths - iPath); // Let caller know how many we did not process // need to move all the ones we did not process to the start of the list... // we always process this list here as we may need to allocate translation lists to be used to // translate the some UNCS back to the mapped drive the user passed in. UINT j = 0, iInsert = 0; iPath--; // make it easy to detect for (i = 0; i < nPaths; i++) { if (aScopesOrig[j] == apwszPaths[i]) { if (aScopesOrig[j] != aScopes[j]) { // There is a translation in place. DFODBET *pdfet = (DFODBET*)LocalAlloc(LPTR, sizeof(*pdfet)); if (pdfet) { pdfet->pdfetNext = _pdfetFirst; _pdfetFirst = pdfet; pdfet->pwszFrom = aScopes[j]; pdfet->cbFrom = lstrlenW(pdfet->pwszFrom); pdfet->pwszTo = aScopesOrig[j]; aScopes[j] = aScopesOrig[j]; // Make sure loop below does not delete pwszFrom apwszPaths[i] = NULL; // Likewise for pswsTo... } } if (apwszPaths[i]) { LocalFree((HLOCAL)apwszPaths[i]); apwszPaths[i] = NULL; } if (j < iPath) j++; } else { apwszPaths[iInsert++] = apwszPaths[i]; // move to right place } } iPath++; // setup to go through cleanupcode... // Fall through to cleanup code... Abort: // Warning... Since a failure return from this function will // release this class, most all of the allocated items up till the failure should // be released... Also cleanup any paths we may have allocated... for (i = 0; i < iPath; i++) { if (aScopesOrig[i] != aScopes[i]) LocalFree(aScopes[i]); } if (aDepths) LocalFree(aDepths); if (aScopes) LocalFree(aScopes); if (aScopesOrig) LocalFree(aScopesOrig); if (aCatalogs) LocalFree(aCatalogs); if (aMachines) LocalFree(aMachines); if (pszMachineAlloc) LocalFree(pszMachineAlloc); if (pszCatalogAlloc) LocalFree(pszCatalogAlloc); return hr; } // This is the main external entry point to start a search. This will // create a new thread to process the STDAPI_(BOOL) SHFindComputer(LPCITEMIDLIST, LPCITEMIDLIST) { IContextMenu *pcm; HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IContextMenu, &pcm)); if (SUCCEEDED(hr)) { CMINVOKECOMMANDINFO ici = {0}; ici.cbSize = sizeof(ici); ici.lpParameters = "{996E1EB1-B524-11d1-9120-00A0C98BA67D}"; // Search Guid of Find Computers ici.nShow = SW_NORMAL; hr = pcm->InvokeCommand(&ici); pcm->Release(); } return SUCCEEDED(hr); } BOOL _IsComputerPidl(LPCITEMIDLIST pidl) { CLSID clsid; if (SUCCEEDED(GetCLSIDFromIDList(pidl, &clsid))) { return (IsEqualCLSID(clsid, CLSID_NetworkPlaces) || IsEqualCLSID(clsid, CLSID_NetworkRoot) || IsEqualCLSID(clsid, CLSID_NetworkDomain)); } return FALSE; } // This is the main external entry point to start a search. This will // create a new thread to process the // STDAPI_(BOOL) SHFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile) { // are we allowed? if (SHRestricted(REST_NOFIND)) return FALSE; // We Need a hack to allow Find to work for cases like // Rest of network and workgroups to map to find computer instead // This is rather gross, but what the heck. It is also assumed that // the pidl is of the type that we know about (either File or network) if (pidlFolder && _IsComputerPidl(pidlFolder)) { return SHFindComputer(pidlFolder, pidlSaveFile); } return RealFindFiles(pidlFolder, pidlSaveFile); }