/* This file contains the Find Files IShellFolder (CDFFolder) and IShellBrowser (CDFBrowse) implementations */ #include "shellprv.h" #pragma hdrstop extern "C" { #include #include #include "ids.h" // Why isn't this gotten from shellprv.h??? #include "findhlp.h" // " #include "pidl.h" // " #include "shitemid.h" // " #include "defview.h" // " #include "fstreex.h" // for views.h #include "views.h" } #include "docfind.h" #include "docfindx.h" #include "cowsite.h" #include "exdisp.h" #include "shguidp.h" #include "dfcmd.h" #include "dbgmem.h" #include "prop.h" // COL_DATA // HACK! 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)) #ifndef DEBUG #define DF_USE_EXCEPTION #endif // NTRAID 277332: To enable the Save Results logic, uncomment this line: [scotthan] // #define DOCFIND_SAVERESULTS_ENABLED #ifdef DEBUG // For leak detection EXTERN_C void GetAndRegisterLeakDetection(void); EXTERN_C BOOL g_fInitTable; EXTERN_C LEAKDETECTFUNCS LeakDetFunctionTable; #endif #define _FILESEARCHBAND_ // use WIN32 file/folder search band. #define ID_LISTVIEW 1 #define WM_DF_THREADNOTIFY (WM_USER + 42) #define WM_DF_FSNOTIFY (WM_USER + 43) #define WM_DF_THREADNOTIFYASYNC (WM_USER + 44) #define DF_THREADNOTIFY_COMPLETE ((LPARAM)-1) #define DF_THREADNOTIFY_NORMAL ((LPARAM)0) #define DF_THREADNOTIFY_ASYNC ((LPARAM)42) #define DF_THREADNOTIFY_REDRAW ((LPARAM)43) #define DF_MAX_MATCHFILES 10000 const UINT c_auDFMenuIDs[] = { FCIDM_MENU_FILE, FCIDM_MENU_EDIT, FCIDM_MENU_VIEW, IDM_MENU_OPTIONS, FCIDM_MENU_HELP }; const TCHAR s_szDocFind[]= TEXT("DocFind"); const TCHAR s_szFlags[] = TEXT("Flags"); const TCHAR s_szDocSpecMRU[] = REGSTR_PATH_EXPLORER TEXT("\\Doc Find Spec MRU"); #ifdef WINNT // 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 _dfc_unicode_desc { ULARGE_INTEGER oUnicodeCriteria; // Offset of unicode find criteria. DWORD NTsignature; // Signature of NT-generated find file. } DFC_UNICODE_DESC; #endif static const DWORD aFindHelpIDs[] = { IDD_PAGELIST, NO_HELP, IDD_ANIMATE, NO_HELP, IDD_STATUS, NO_HELP, IDD_START, IDH_FINDFILENAME_FINDNOW, IDD_STOP, IDH_FINDFILENAME_STOP, IDD_NEWSEARCH, IDH_FINDFILENAME_NEWSEARCH, ID_LISTVIEW, IDH_FINDFILENAME_STATUSSCREEN, 0, 0 }; // Setup the column number definitions. - This may need to be converted // into somethings that is filter related... #ifdef WINNT const UINT s_auMapDFColToFSCol[] = {0, (UINT)-1, (UINT)-1, 1, 2, 3, 4, 5, 6}; // More items than are needed but... #else // no rank column on win9x const UINT s_auMapDFColToFSCol[] = {0, (UINT)-1, 1, 2, 3, 4, 5, 6}; // More items than are needed but...c #endif BOOL DocFind_StartFind(HWND hwndDlg, LPFINDTHREAD pft); BOOL DocFind_StopFind(HWND hwndDlg); BOOL DocFind_FindNext(HWND hwndDlg); // IContextMenuCB implementation class CDFContextMenuBase : public IContextMenuCB , public CObjectWithSite { public: CDFContextMenuBase(); virtual ~CDFContextMenuBase(); // *** IUnknown methods *** STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj); STDMETHOD_(ULONG,AddRef)(); STDMETHOD_(ULONG,Release)(); // *** IContextMenuCB methods *** STDMETHOD(CallBack)(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) PURE; private: LONG _cRef; }; CDFContextMenuBase::CDFContextMenuBase() { TraceMsg(TF_DOCFIND, "CDFContextMenuBase ctor"); _cRef = 1; } CDFContextMenuBase::~CDFContextMenuBase() { TraceMsg(TF_DOCFIND, "CDFContextMenuBase dtor"); } STDMETHODIMP CDFContextMenuBase::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDFContextMenuBase, IContextMenuCB), // IID_IContextMenuCB QITABENT(CDFContextMenuBase, IObjectWithSite), // IID_IObjectWithSite { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CDFContextMenuBase::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CDFContextMenuBase::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } class CDFContextMenuCB : public CDFContextMenuBase { public: // *** IContextMenuCB methods *** STDMETHOD(CallBack)(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); }; // in fstreex.h EXTERN_C DWORD CALLBACK _CFSFolder_PropertiesThread(void* pps); // bugbug: not actually a void // static const QCMINFO_IDMAP static const struct { UINT max; struct { UINT id; UINT fFlags; } list[2]; } idMap = { 2, { {FSIDM_FOLDER_SEP, QCMINFO_PLACE_BEFORE}, {FSIDM_VIEW_SEP, QCMINFO_PLACE_AFTER}, }, }; STDMETHODIMP CDFContextMenuCB::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hres = NOERROR; switch (uMsg) { case DFM_MERGECONTEXTMENU: if (!(wParam & CMF_VERBSONLY)) { LPQCMINFO pqcm = (LPQCMINFO)lParam; if (pdtobj) { if (!(wParam & CMF_DVFILE)) { CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_FSVIEW_ITEM, 0, pqcm); } } else { UINT idStart = pqcm->idCmdFirst; UINT idBGMain = 0, idBGPopup = 0; IDocFindFolder *pdfFolder; IDocFindFileFilter *pdfff; if (SUCCEEDED(psf->QueryInterface(IID_IDocFindFolder, (void **)&pdfFolder))) { if (SUCCEEDED(pdfFolder->GetDocFindFilter(&pdfff))) { pdfff->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup); CDefFolderMenu_MergeMenu(HINST_THISDLL, idBGMain, idBGPopup, pqcm); DeleteMenu(pqcm->hmenu, SFVIDM_EDIT_PASTE, MF_BYCOMMAND); DeleteMenu(pqcm->hmenu, SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND); DeleteMenu(pqcm->hmenu, SFVIDM_MISC_REFRESH, MF_BYCOMMAND); IDocFindControllerNotify *pdcn; if (S_OK == pdfFolder->GetControllerNotifyObject(&pdcn)) { pdcn->Release(); } else { DeleteMenu(pqcm->hmenu, idStart+FSIDM_SAVESEARCH, MF_BYCOMMAND); } pdfff->Release(); } pdfFolder->Release(); } } pqcm->pIdMap = (QCMINFO_IDMAP *)&idMap; } break; case DFM_INVOKECOMMAND: { // Check if this is from item context menu if (pdtobj) { switch(wParam) { case DFM_CMD_LINK: hres = SHCreateLinks(hwndOwner, NULL, pdtobj, SHCL_USETEMPLATE | SHCL_USEDESKTOP | SHCL_CONFIRM, NULL); break; case DFM_CMD_DELETE: // TODO: Do this stuff for CShellBrowser case as well [t-ashram] { IDocFindBrowser *pdfb = NULL; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IDocFindBrowser, (void **)&pdfb))) { pdfb->DeferProcessUpdateDir(); } // convert to DFM_INVOKCOMMANDEX to get flags bits hres = DeleteFilesInDataObject(hwndOwner, 0, pdtobj); if (pdfb) { pdfb->EndDeferProcessUpdateDir(); pdfb->Release(); } } break; case DFM_CMD_PROPERTIES: // We need to pass an empty IDlist to combine with. SHLaunchPropSheet(_CFSFolder_PropertiesThread, pdtobj, (LPCTSTR)lParam, NULL, (void *)&c_idlDesktop); break; default: // BUGBUG: 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. // BUGBUG: 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 hres = (S_FALSE); break; } } else { IShellFolderView *psfv; // No. switch(wParam) { case FSIDM_SAVESEARCH: { IDocFindFolder *pdfFolder; if (SUCCEEDED(psf->QueryInterface(IID_IDocFindFolder, (void **)&pdfFolder))) { IDocFindControllerNotify *pdcn; if (S_OK == pdfFolder->GetControllerNotifyObject(&pdcn)) { pdcn->SaveSearch(); pdcn->Release(); } pdfFolder->Release(); } break; } case FSIDM_SORTBYNAME: case FSIDM_SORTBYSIZE: case FSIDM_SORTBYTYPE: case FSIDM_SORTBYDATE: case FSIDM_SORTBYLOCATION: if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_DefView, IID_IShellFolderView, (void **)&psfv))) { psfv->Rearrange(DFSortIDToICol((int) wParam)); psfv->Release(); } break; default: // This is one of view menu items, use the default code. hres = (S_FALSE); 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: hres = E_NOTIMPL; break; } return hres; } class CDFFolderContextMenuItemCB : public CDFContextMenuBase { public: // *** IContextMenuCB methods *** STDMETHOD(CallBack)(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); private: CDFFolderContextMenuItemCB(IDocFindFolder* pdfFolder); ~CDFFolderContextMenuItemCB(); friend IContextMenu* CDFFolderContextMenuItem_Create(HWND hwndOwner, IDocFindFolder* pdfFolder); IDocFindFolder *_pdfFolder; }; CDFFolderContextMenuItemCB::CDFFolderContextMenuItemCB(IDocFindFolder* pdfFolder) { ASSERT(pdfFolder); _pdfFolder = pdfFolder; _pdfFolder->AddRef(); } CDFFolderContextMenuItemCB::~CDFFolderContextMenuItemCB() { _pdfFolder->Release(); } STDMETHODIMP CDFFolderContextMenuItemCB::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hres = NOERROR; 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: { IShellFolderView *psfv; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_DefView, IID_IShellFolderView, (void **)&psfv))) { DFB_HandleOpenContainingFolder(psfv, _pdfFolder); psfv->Release(); } break; } } break; case DFM_GETHELPTEXT: case DFM_GETHELPTEXTW: // probably need to implement these... default: hres = E_NOTIMPL; break; } return hres; } IContextMenu* CDFFolderContextMenuItem_Create(HWND hwndOwner, IDocFindFolder* pdfFolder) { IContextMenu* pcm = NULL; // We want a quick IContextMenu implementation -- an empty defcm looks easiest IContextMenuCB* pcmcb = new CDFFolderContextMenuItemCB(pdfFolder); if (pcmcb) { CDefFolderMenu_CreateEx((LPCITEMIDLIST)NULL, hwndOwner, 0, NULL, NULL, pcmcb, NULL, NULL, &pcm); pcmcb->Release(); } return pcm; } // CDFFolder: class definition // in docfind.h // CDFFolder : Helper Functions void cdecl DocFind_SetStatusText(HWND hwndStatus, int iField, UINT ids,...) { TCHAR sz2[MAX_PATH+32]; // leave slop for message + max path name va_list ArgList; if (hwndStatus) { va_start(ArgList, ids); LPTSTR psz = _ConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(ids), &ArgList); if (psz) { // a-msadek; needed only for BiDi Win95 loc // Mirroring will take care of that over NT5 & BiDi Win98 if(g_bBiDiW95Loc) { StrCpyN(sz2, psz, ARRAYSIZE(sz2)); // for some reason, this has always copied to sz2 sz2[0] = sz2[1] = TEXT('\t'); // localizers must be leaving 2 spaces for these tabs... } else StrCpyN(sz2, psz, ARRAYSIZE(sz2)); LocalFree(psz); } else { sz2[0] = 0; } va_end(ArgList); if (iField < 0) { // a-msadek; needed only for BiDi Win95 loc // Mirroring will take care of that over NT5 & BiDi Win98 if(g_bBiDiW95Loc) { SendMessage(hwndStatus, SB_SETTEXT, SBT_RTLREADING | SBT_NOBORDERS | 255, (LPARAM)(LPTSTR)sz2); } else SendMessage(hwndStatus, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)(LPTSTR)sz2); } else SendMessage(hwndStatus, SB_SETTEXT, iField, (LPARAM)(LPTSTR)sz2); UpdateWindow(hwndStatus); } } HRESULT CDFFolder::AddFolderToFolderList(LPITEMIDLIST pidl, BOOL fCheckForDup, int * piFolder) { HRESULT hres = ERROR_SUCCESS; DFFolderListItem *pdffli; int i; int cbPidl; cbPidl = ILGetSize(pidl); if (fCheckForDup) { EnterCriticalSection(&_csSearch); for (i = DPA_GetPtrCount(this->hdpaPidf)-1; i >= 0; i--) { pdffli = (DFFolderListItem *)DPA_FastGetPtr( this->hdpaPidf, i); if (pdffli && (pdffli->cbPidl == cbPidl) && (ILIsEqual(&pdffli->idl, pidl))) { LeaveCriticalSection(&_csSearch); *piFolder = i; return S_OK; } } LeaveCriticalSection(&_csSearch); } // For now it is path based, but... pdffli = (DFFolderListItem *)LocalAlloc(LPTR, SIZEOF(DFFolderListItem) - sizeof(ITEMIDLIST) + cbPidl); if (pdffli == NULL) { *piFolder = -1; return (E_OUTOFMEMORY); } // lpddfli->psf = NULL; // pdffli->fUpdateDir = FALSE; memcpy(&pdffli->idl, pidl, cbPidl); pdffli->iImage = -1; // Don't know the image yet for this one... pdffli->cbPidl = cbPidl; EnterCriticalSection(&_csSearch); // Now add this item to our DPA... i = DPA_AppendPtr( this->hdpaPidf, pdffli); LeaveCriticalSection(&_csSearch); if (i == -1) { LocalFree((HLOCAL)pdffli); hres = E_OUTOFMEMORY; } *piFolder = i; /* 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, pidl); } return hres; } void CDFFolder::_AddESFItemToSaveStateList(ESFItem *pesfi) { ESFSaveStateItem essi; essi.dwState = pesfi->dwState & CDFITEM_STATE_MASK; PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA); ASSERT(phdfd && (phdfd->wFlags & DFDF_EXTRADATA)); essi.dwItemID = phdfd->dwItemID; DSA_AppendItem(this->_hdsaSaveStateForIDs, (void *)&essi); if (essi.dwState & LVIS_SELECTED) _cSaveStateSelected++; } HRESULT CDFFolder::RememberSelectedItems() { int i; ESFItem *pesfi; EnterCriticalSection(&_csSearch); // Currently has list of pidls... for (i = DPA_GetPtrCount(this->hdpaItems); i-- > 0; ) { // Pidl at start of structure... pesfi = (ESFItem*)DPA_FastGetPtr(this->hdpaItems, i); if (pesfi != NULL) { if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED)) _AddESFItemToSaveStateList(pesfi); } } LeaveCriticalSection(&_csSearch); return S_OK; } STDMETHODIMP CDFFolder::ClearItemList() { int i; ESFItem *pesfi; // Clear out any async enumerators we may have SetAsyncEnum(NULL); _cAsyncItems = 0; // clear out our count of items... pdfff->ReleaseQuery(); // Also tell the filter to release everything... EnterCriticalSection(&_csSearch); if (this->hdpaItems == NULL) { LeaveCriticalSection(&_csSearch); return S_OK; // Nothing to do } // Currently has list of pidls... for (i = DPA_GetPtrCount(this->hdpaItems); i-- > 0; ) { // Pidl at start of structure... pesfi = (ESFItem*)DPA_FastGetPtr(this->hdpaItems, i); if (pesfi != NULL) LocalFree((HLOCAL)pesfi); } _fSearchComplete = FALSE; DPA_DeleteAllPtrs(this->hdpaItems); LeaveCriticalSection(&_csSearch); return S_OK; } STDMETHODIMP CDFFolder::ClearFolderList() { int i; DFFolderListItem *pdffli; EnterCriticalSection(&_csSearch); if (this->hdpaPidf == NULL) { LeaveCriticalSection(&_csSearch); return S_OK; // Nothing to do } for (i = DPA_GetPtrCount(this->hdpaPidf); i-- > 0; ) { pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, i); if (pdffli != NULL) { // Release the IShellFolder if we have one if (pdffli->psf != NULL) pdffli->psf->Release(); // And delete the item if (LocalFree((HLOCAL)pdffli)) { ASSERT(FALSE); // Something bad happened! return E_FAIL; } } } DPA_DeleteAllPtrs(this->hdpaPidf); LeaveCriticalSection(&_csSearch); return S_OK; } // CDFFolder : Constructor CDFFolder::CDFFolder() { _cRef = 1; ASSERT(_pidl == NULL); _iGetIDList = -1; *_szEmptyText = 0; ASSERT(this->pdfff == NULL); // Create the heap for the folder lists. this->hdpaPidf = DPA_CreateEx(64, GetProcessHeap()); // Create the DPA and DSA for the item list. this->cbItem = sizeof (ESFItem); this->hdpaItems = DPA_CreateEx(64, GetProcessHeap()); this->_hdsaSaveStateForIDs = DSA_Create(sizeof(ESFSaveStateItem), 16); // initialize our LV selection objects... _dflvrSel.SetOwner(this, LVIS_SELECTED); _dflvrCut.SetOwner(this, LVIS_CUT); InitializeCriticalSection(&_csSearch); TraceMsg(TF_DOCFIND, "CDFFolder ctor"); } CDFFolder::~CDFFolder() { 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); if (pdfff) pdfff->Release(); DeleteCriticalSection(&_csSearch); TraceMsg(TF_DOCFIND, "CDFFolder dtor"); } HRESULT CDFFolder_Create(void **ppvOut, IDocFindFileFilter *pdfff) { CDFFolder *pdffNew; HRESULT hr = E_OUTOFMEMORY; *ppvOut = NULL; pdffNew = new CDFFolder(); if (pdffNew) { pdffNew->pdfff = pdfff; // Also handle case they do not pass one in... if (pdffNew->pdfff == NULL) pdffNew->pdfff = CreateDefaultDocFindFilter(); // Check for out of memory conditions... if ( (pdffNew->pdfff == NULL) || (pdffNew->hdpaPidf == NULL) || (pdffNew->hdpaItems == NULL)) { if (pdffNew->hdpaPidf) DPA_Destroy(pdffNew->hdpaPidf); if (pdffNew->hdpaItems) DPA_Destroy(pdffNew->hdpaItems); delete pdffNew->pdfff; delete pdffNew; pdffNew = NULL; } else { hr = NOERROR; } } *ppvOut = pdffNew; return hr; } STDAPI CDocFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; CDFFolder *pdff; hr = CDFFolder_Create((void **)&pdff, NULL); if (SUCCEEDED(hr)) { hr = pdff->QueryInterface(riid, ppv); pdff->Release(); } else { *ppv = NULL; } return hr; } //Should combine with above STDAPI CComputerFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; CDFFolder *pdff; IDocFindFileFilter *pdfff = CreateDefaultComputerFindFilter(); if (!pdfff) return E_FAIL; // Warning This call takes ownership of pdfff - so don't release hr = CDFFolder_Create((void **)&pdff, pdfff); if (SUCCEEDED(hr)) { hr = pdff->QueryInterface(riid, ppv); pdff->Release(); } else { *ppv = NULL; } return hr; } // CDFFolder : Now part of IDocFindFolder HRESULT CDFFolder::MapFSPidlToDFPidl(LPITEMIDLIST pidl, BOOL fMapToReal, LPITEMIDLIST *ppidl) { LPITEMIDLIST pidlParent = ILCloneParent(pidl); *ppidl = NULL; if (pidlParent) { EnterCriticalSection(&_csSearch); // Now loop through our DPA list and see if we can find a matach for (int i = 0; i hdpaPidf); i++ ) { DFFolderListItem *pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, i); if (pdffli != NULL && ILIsEqual(pidlParent, &pdffli->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 pidlReal; // If this is not a FS folder, just clone it. if (fMapToReal) { // We need to make sure we have an IShellFolder to // work with... IShellFolder *psf = DocFind_GetObjectsIFolder(this, pdffli, NULL); if (psf) { SHGetRealIDL(psf, (LPITEMIDLIST)ILFindLastID(pidl), &pidlReal); psf->Release(); } else { pidlReal = ILClone((LPITEMIDLIST)ILFindLastID(pidl)); } } else pidlReal = ILClone((LPITEMIDLIST)ILFindLastID(pidl)); if (pidlReal) // return the appended *ppidl = DocFind_AppendIFolder(pidlReal, i); // exit regardless of success. break; } } LeaveCriticalSection(&_csSearch); ILFree(pidlParent); } return *ppidl ? S_OK : S_FALSE; } // _UpdateItemList() // 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 CDFFolder::_UpdateItemList() { ESFItem *pesfi; int cItems; int i; USHORT cb = 0; if (SUCCEEDED(GetItemCount(&cItems))) { for (i=0; i < cItems; i++) { HRESULT hres = GetItem(i, &pesfi); if (hres == DB_S_ENDOFROWSET) break; } } return S_OK; } // CDFFolder : External function to Save results out to file. // Now part of IDocfindFolder HRESULT CDFFolder::SaveFolderList(IStream *pstm) { // We First pull all the items from RowSet (in Asynch case) _UpdateItemList(); // Then, We serialize all of our PIDLS for each folder in our list DFFolderListItem *pdffli; USHORT cb; int i; EnterCriticalSection(&_csSearch); // Now loop through our DPA list and see if we can find a matach for (i = 0; i hdpaPidf); i++ ) { pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, i); if (EVAL(pdffli)) ILSaveToStream(pstm, &pdffli->idl); else break; } LeaveCriticalSection(&_csSearch); // Now out a zero size item.. cb = 0; pstm->Write((TCHAR *)&cb, SIZEOF(cb), NULL); return(TRUE); } // CDFFolder : External function to Restore results out to file. // Now part of IDocfindFolder HRESULT CDFFolder::RestoreFolderList(IStream *pstm) { // loop through and all all of the folders to our list... LPITEMIDLIST pidl = NULL; HRESULT hres; for (;;) { int i; hres = ILLoadFromStream(pstm, &pidl); // frees pidl for us if (pidl == NULL) break; // end of the list AddFolderToFolderList(pidl, FALSE, &i); } ILFree(pidl); // don't forget to free last pidl return hres; } HRESULT CDFFolder::SaveItemList(IStream *pstm) { // We First serialize all of our PIDLS for each item in our list ESFItem *pesfi; int cItems; int i; USHORT cb = 0; if (SUCCEEDED(GetItemCount(&cItems))) { // And Save the items that are in the list for (i=0; i < cItems; i++) { HRESULT hres = GetItem(i, &pesfi); if (hres == DB_S_ENDOFROWSET) break; if (SUCCEEDED(hres) && pesfi) ILSaveToStream(pstm, &pesfi->idl); } } // Write out a Trailing NULL size to say end of pidl list... pstm->Write(&cb, SIZEOF(cb), NULL); return S_OK; } HRESULT CDFFolder::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 ESFItem *pesfi; for (; ;) { if (FAILED(ILLoadFromStream(pstm, &pidl)) || (pidl == NULL)) break; if (FAILED(AddPidl(cItems, pidl, (UINT)-1, &pesfi)) || !pesfi) break; cItems++; } if (pidl) ILFree(pidl); // Free the last one read in *pcItems = cItems; return S_OK; } // CDFFolder : External function to Restore results out to file. // Now part of IDocfindFolder HRESULT CDFFolder::GetParentsPIDL(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlParent) { PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl, IDLHID_DOCFINDDATA); *ppidlParent = NULL; if (phdfd) { EnterCriticalSection(&_csSearch); // Now get to the item associated with that folder out of our dpa. ASSERT(hdpaPidf && DPA_GetPtrCount(hdpaPidf) > phdfd->iFolder); DFFolderListItem *pdffli = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, phdfd->iFolder); if (pdffli) { // BUGBUG: we are returning pidl from a structure that may be freed on a different thread. should clone *ppidlParent = &pdffli->idl; LeaveCriticalSection(&_csSearch); return S_OK; } LeaveCriticalSection(&_csSearch); } else AssertMsg(FALSE, TEXT("CDF::GetParentsPIDL() passed invalid DocFind pidl")); return S_FALSE; } HRESULT CDFFolder::SetControllerNotifyObject(IDocFindControllerNotify *pdfcn) { ATOMICRELEASE(_pdfcn); _pdfcn = pdfcn; if (_pdfcn) { _pdfcn->AddRef(); } return S_OK; } HRESULT CDFFolder::GetControllerNotifyObject(IDocFindControllerNotify **ppdfcn) { *ppdfcn = _pdfcn; if (_pdfcn) _pdfcn->AddRef(); return _pdfcn ? S_OK : S_FALSE; } // CDFFolder : Members // AddRef STDMETHODIMP_(ULONG) CDFFolder::AddRef() { TraceMsg(TF_DOCFIND, "cdff.AddRef %d",_cRef+1); return (++_cRef); } // Release STDMETHODIMP_(ULONG) CDFFolder::Release() { ASSERT(_cRef >= 1); // Don't release more than we can! _cRef--; TraceMsg(TF_DOCFIND,"cdff.Release %d",_cRef); if (_cRef > 0) { return _cRef; } delete this; return 0; } STDMETHODIMP CDFFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR pwzDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG * pdwAttributes) { return E_NOTIMPL; } STDMETHODIMP CDFFolder::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) } IShellFolder *DocFind_GetObjectsIFolder(IDocFindFolder *pdff, DFFolderListItem *pdffli, LPCITEMIDLIST pidl) { // First Validate the pidl that was passed in. // is what we expect - cb, Folder number, followed by a mkid if (pdffli == NULL) { PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl, IDLHID_DOCFINDDATA); if (phdfd) pdff->GetFolderListItem(phdfd->iFolder, &pdffli); else AssertMsg(FALSE, TEXT("DocFind_GetObjectsIFolder() passed invalid DocFind pidl")); } // Now see if we need to build an IFolder for this object. if (pdffli && pdffli->psf == NULL) SHBindToObject(NULL, IID_IShellFolder, &pdffli->idl, (void **)&pdffli->psf); if (pdffli && pdffli->psf) pdffli->psf->AddRef(); return pdffli ? pdffli->psf : NULL; } STDMETHODIMP CDFFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut) { HRESULT hr = E_INVALIDARG; // We need to parse the ID and see which sub-folder it belongs to // then we will see if we have an IShellFolder for the object. If not // we will construct it and then extract our part off of the IDLIST and // then forward it... IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl); if (psfItem) { hr = psfItem->BindToObject(pidl, pbc, riid, ppvOut); psfItem->Release(); } return hr; } // Little helper function for bellow HRESULT CDFFolder::_CompareFolderIndexes(int iFolder1, int iFolder2) { TCHAR szPath1[MAX_PATH]; TCHAR szPath2[MAX_PATH]; EnterCriticalSection(&_csSearch); DFFolderListItem *pdffli1 = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, iFolder1); DFFolderListItem *pdffli2 = (DFFolderListItem *) DPA_FastGetPtr(this->hdpaPidf, iFolder2); if ((pdffli1 != NULL) && (pdffli2 != NULL)) { SHGetPathFromIDList(&pdffli1->idl, szPath1); SHGetPathFromIDList(&pdffli2->idl, szPath2); LeaveCriticalSection(&_csSearch); return(ResultFromShort(lstrcmpi(szPath1, szPath2))); } LeaveCriticalSection(&_csSearch); // If we got here there is an error! return (E_INVALIDARG); } STDMETHODIMP CDFFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hres = (E_INVALIDARG); IShellFolder *psf1; LPITEMIDLIST pidl1Last; int iFolder1; int iFolder2; // Note there are times in the defview that may call us back looking for // a fully qualified pidl in pidl1, so we need to detect this case and // only process the last part. pidl1Last = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)pidl1); iFolder1 = DF_IFOLDER(pidl1Last); iFolder2 = DF_IFOLDER(pidl2); // We need to handle the case our self if the sort is by // the containing folder. But otherwise we simply forward the informat // to the FSFolders compare function. This will only work if both ids // are file system objects, but if this is not the case, then what??? if (lParam == IDFCOL_PATH) { if (iFolder1 != iFolder2) return _CompareFolderIndexes(iFolder1, iFolder2); // If we get here the paths are the same so set the sort order // to name... lParam = IDFCOL_NAME; } else if (lParam == IDFCOL_RANK) { PCHIDDENDOCFINDDATA phdfd1 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl1, IDLHID_DOCFINDDATA); PCHIDDENDOCFINDDATA phdfd2 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl2, IDLHID_DOCFINDDATA); // Could be mixed if so put ones without rank at the end... if (phdfd1 && (phdfd1->wFlags & DFDF_EXTRADATA)) { if (phdfd2 && (phdfd2->wFlags & DFDF_EXTRADATA)) { // Have both... if (phdfd1->ulRank < phdfd2->ulRank) return ResultFromShort(-1); if (phdfd1->ulRank > phdfd2->ulRank) return ResultFromShort(1); } else return ResultFromShort(1); } else if (phdfd2) return ResultFromShort(-1); // Otherwise next sort on name... lParam = IDFCOL_NAME; } // For now we just forward this to the function the that CFSFolder // uses. This is not clean but... psf1 = DocFind_GetObjectsIFolder(this, NULL, pidl1Last); if (psf1) { hres = psf1->CompareIDs(s_auMapDFColToFSCol[lParam], pidl1Last, pidl2); psf1->Release(); if ((hres == 0) && (lParam == IDFCOL_NAME) && (iFolder1 != iFolder2)) { // They compared the same and by name so make sure truelly the // same path as this is used in processing notifications. return _CompareFolderIndexes(iFolder1, iFolder2); } return hres; } else return E_INVALIDARG; } STDMETHODIMP CDFFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppvOut) { HRESULT hr; // We need to parse the ID and see which sub-folder it belongs to // then we will see if we have an IShellFolder for the object. If not // we will construct it and then extract our part off of the IDLIST and // then forward it... if (IsEqualIID(riid, IID_IShellView)) { SFV_CREATE sSFV; sSFV.cbSize = sizeof(sSFV); sSFV.pshf = this; sSFV.psvOuter = NULL; sSFV.psfvcb = DocFind_CreateSFVCB(SAFECAST(this, IShellFolder*), this); if (sSFV.psfvcb) { hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppvOut); sSFV.psfvcb->Release(); } else hr = E_OUTOFMEMORY; } else if (IsEqualIID(riid, IID_IContextMenu)) { IContextMenuCB *pcmcb = new CDFContextMenuCB(); if (pcmcb) { hr = CDefFolderMenu_CreateEx(NULL, hwnd, 0, NULL, this, pcmcb, NULL, NULL, (IContextMenu * *)ppvOut); pcmcb->Release(); } else hr = E_OUTOFMEMORY; } else { *ppvOut = NULL; hr = E_NOINTERFACE; } return hr; } STDMETHODIMP CDFFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG * prgfInOut) { HRESULT hr = E_FAIL; // We need to simply forward this to the the IShellfolder of the // first one. We will pass him all of them as I know he does not // process the others... // call off to the containing folder if (cidl == 0) { // They are asking for capabilities, so tell them that we support Rename... // Review: See what other ones we need to return. *prgfInOut = SFGAO_CANRENAME; hr = S_OK; } else { IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, ILFindLastID(*apidl)); if (psfItem) { hr = psfItem->GetAttributesOf(cidl, apidl, prgfInOut); psfItem->Release(); } } return hr; } // Helper function to Map the Sort Id to IColumn int DFSortIDToICol(int uCmd) { switch (uCmd) { case FSIDM_SORTBYNAME: return IDFCOL_NAME; case FSIDM_SORTBYLOCATION: return IDFCOL_PATH; default: #ifdef WINNT return uCmd - FSIDM_SORT_FIRST - IDFCOL_NAME + 2; // make room for location and rank #else // win9x doesn't have rank return uCmd - FSIDM_SORT_FIRST - IDFCOL_NAME + 1; // make room for location #endif } } // To be called back from within CDefFolderMenuE - Currently only used // Some helper functions STDMETHODIMP CDFFolder::SetItemsChangedSinceSort() { this->fItemsChangedSinceSort = TRUE; return S_OK; } STDMETHODIMP CDFFolder::GetItemCount(INT *pcItems) { ASSERT(pcItems); DBCOUNTITEM cItems = 0; EnterCriticalSection(&_csSearch); if (this->hdpaItems) cItems = DPA_GetPtrCount(this->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 CDFFolder::GetItem(int iItem, ESFItem **ppItem) { HRESULT hres = E_FAIL; // just to init, use anything ESFItem *pesfi; IDFEnum *pidfenum; GetAsyncEnum(&pidfenum); DWORD dwItemID = (UINT)-1; EnterCriticalSection(&_csSearch); int i = DPA_GetPtrCount(this->hdpaItems); pesfi = (ESFItem *) DPA_GetPtr(this->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 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA); // 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) { // Overlload, pass NULL to ADDPIDL to tell system to free that item if (pesfi->dwState & (LVIS_SELECTED|LVIS_FOCUSED)) _AddESFItemToSaveStateList(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; hres = pidfenum->GetItemIDList(SANE_ITEMCOUNT(iItem), &pidlT); if (SUCCEEDED(hres) && hres != 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 (hres != DB_S_ENDOFROWSET) hres = pesfi? S_OK : E_FAIL; return hres; } STDMETHODIMP CDFFolder::DeleteItem(int iItem) { HRESULT hres = E_FAIL; if (!_fInRefresh) { ESFItem *pesfi; hres = 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 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA); 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); hres = S_OK; } } return hres; } STDMETHODIMP CDFFolder::ValidateItems(int iItem, int cItems, BOOL bSearchComplete) { ESFItem *pesfi; IDFEnum *pidfenum; int cItemsInList; int iLVFirst; int cLVItems; GetAsyncEnum(&pidfenum); if (!pidfenum || _fAllAsyncItemsCached) return S_OK; // nothing to validate. DWORD dwItemID = (UINT)-1; GetItemCount(&cItemsInList); // force reload of rows pidfenum->Reset(); iLVFirst = ListView_GetTopIndex(_hwndLV); 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); pesfi = (ESFItem *) DPA_GetPtr(this->hdpaItems, iItem); LeaveCriticalSection(&_csSearch); if (!pesfi) // Assume that if we have not gotten this one we are in the clear... break; PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(&pesfi->idl, IDLHID_DOCFINDDATA); if (phdfd && (phdfd->wFlags & DFDF_EXTRADATA)) { pidfenum->GetItemID(iItem, &dwItemID); if (dwItemID != phdfd->dwItemID) { ESFItem *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; DebugMsg(TF_DOCFIND, TEXT("sh TR - CDFFolder::ValidateItems - OK")); return S_OK; } STDMETHODIMP CDFFolder::AddPidl(int i, LPITEMIDLIST pidl, DWORD dwItemID, ESFItem **ppcdfi) { HRESULT hr = S_OK; ESFItem* pesfi; if (!pidl) { EnterCriticalSection(&_csSearch); pesfi = (ESFItem*)DPA_GetPtr(this->hdpaItems, i); if (pesfi) { LocalFree((HLOCAL)pesfi); DPA_SetPtr(this->hdpaItems, i, NULL); } LeaveCriticalSection(&_csSearch); if (ppcdfi) *ppcdfi = NULL; return S_OK; } int cb = ILGetSize(pidl); pesfi = (ESFItem*)LocalAlloc(LMEM_FIXED, sizeof(ESFItem) - sizeof(ITEMIDLIST) + cb); if (pesfi) { pesfi->dwMask = 0; pesfi->dwState = 0; // pesfi->dwItemID = dwItemID; pesfi->iIcon = -1; memcpy(&pesfi->idl, pidl, cb); EnterCriticalSection(&_csSearch); BOOL bRet = DPA_SetPtr(this->hdpaItems, i, (void *)pesfi); LeaveCriticalSection(&_csSearch); if (!bRet) { LocalFree((HLOCAL)pesfi); pesfi = NULL; hr = E_FAIL; } } if (ppcdfi) *ppcdfi = pesfi; return hr; } STDMETHODIMP CDFFolder::SetAsyncEnum(IDFEnum *pdfEnumAsync) { if (_pDFEnumAsync) _pDFEnumAsync->Release(); _pDFEnumAsync = pdfEnumAsync; if (pdfEnumAsync) pdfEnumAsync->AddRef(); return S_OK; } STDMETHODIMP CDFFolder::CacheAllAsyncItems() { ESFItem *pesfi; IDFEnum *pidfenum; int i, maxItems; if (_fAllAsyncItemsCached) return S_OK; // Allready done it... GetAsyncEnum(&pidfenum); if (!pidfenum) return S_FALSE; // nothing to do... // Probably the easiest thing to do is to simply walk through all of the items... maxItems = SANE_ITEMCOUNT(_cAsyncItems); for (i=0;i < maxItems; i++) { GetItem(i, &pesfi); } _fAllAsyncItemsCached = TRUE; return S_OK; } BOOL CDFFolder::AllAsyncItemsCached() { return _fAllAsyncItemsCached; } STDMETHODIMP CDFFolder::GetAsyncEnum(IDFEnum **ppdfEnumAsync) { *ppdfEnumAsync = _pDFEnumAsync; return S_OK; } STDMETHODIMP CDFFolder::SetAsyncCount(DBCOUNTITEM cCount) { _cAsyncItems = cCount; _fAllAsyncItemsCached = FALSE; return S_OK; } STDMETHODIMP CDFFolder::ClearSaveStateList() { DSA_DeleteAllItems(this->_hdsaSaveStateForIDs); _cSaveStateSelected = 0; return S_OK; } STDMETHODIMP CDFFolder::GetStateFromSaveStateList(DWORD dwItemID, DWORD *pdwState) { ESFSaveStateItem *pessi; int i; for (i = DSA_GetItemCount(this->_hdsaSaveStateForIDs); i-- > 0; ) { // Pidl at start of structure... pessi = (ESFSaveStateItem*)DSA_GetItemPtr(this->_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(this->_hdsaSaveStateForIDs, i); return S_OK; } } return S_FALSE; } STDMETHODIMP CDFFolder::GetFolderListItemCount(INT *pcItemCount) { *pcItemCount = 0; EnterCriticalSection(&_csSearch); if (this->hdpaPidf) *pcItemCount = DPA_GetPtrCount(this->hdpaPidf); LeaveCriticalSection(&_csSearch); return S_OK; } STDMETHODIMP CDFFolder::GetFolderListItem(int iItem, DFFolderListItem **ppdffi) { EnterCriticalSection(&_csSearch); *ppdffi = (DFFolderListItem *) DPA_GetPtr(this->hdpaPidf, iItem); LeaveCriticalSection(&_csSearch); return *ppdffi ? S_OK : E_FAIL; } // Now define a simple wrapper IContext menu which allows us to catch // special things like Create Link... // Also this is a DELEGATING icontextmenu implementation. We could rip // the delegating stuff into a base class which might be useful somewhere... // CDefFolderMenu class #define MAX_CM_WRAP 2 class CDFMenuWrap : public IContextMenu3, public CObjectWithSite { public: // *** IUnknown methods *** virtual STDMETHODIMP QueryInterface(REFIID,void **); virtual STDMETHODIMP_(ULONG) AddRef(void); virtual STDMETHODIMP_(ULONG) Release(void); // *** IContextMenu methods *** virtual STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags); virtual STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici); virtual STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax); // *** IContextMenu2 methods *** virtual STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam); // *** IContextMenu3 methods *** virtual STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult); //*** IObjectWithSite *** STDMETHOD(SetSite)(IUnknown *punkSite); // override private: // This would be a completely generic IContextMenu delegating object except: // 1) There's this silly _pdtobj hack which is the reason it was first written and // 2) The constructor function only takes 2 pcms -- easily modifiable however friend IContextMenu* DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2); CDFMenuWrap(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2); ~CDFMenuWrap(); void _InsertMenu(IContextMenu* pcm); UINT _cRef; // Reference count IDataObject * _pdtobj; // Data object UINT _count; UINT _idFirst; UINT _idOffsets[MAX_CM_WRAP]; // The END of each range (BEGINing of next range is +1) IContextMenu *_pcmItem[MAX_CM_WRAP]; // The contextmenu for the item IContextMenu2 *_pcm2Item[MAX_CM_WRAP]; // The contextmenu for the item IContextMenu3 *_pcm3Item[MAX_CM_WRAP]; // The contextmenu for the item }; CDFMenuWrap::CDFMenuWrap(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2) { _cRef = 1; ASSERT(pdo); pdo->AddRef(); _pdtobj = pdo; _InsertMenu(pcm1); _InsertMenu(pcm2); ASSERT(_count>0); // we better have something or we're worthless } void CDFMenuWrap::_InsertMenu(IContextMenu* pcm) { if (pcm) { if (_count >= MAX_CM_WRAP) { TraceMsg(TF_ERROR, "Someone's inserting more contextmenus than CDFMenuWrap can handle..."); } else { pcm->AddRef(); _pcmItem[_count] = pcm; pcm->QueryInterface(IID_IContextMenu2, (void**)&_pcm2Item[_count]); pcm->QueryInterface(IID_IContextMenu3, (void**)&_pcm3Item[_count]); _count++; } } } CDFMenuWrap::~CDFMenuWrap() { ASSERT(_cRef == 0); for (UINT i = 0 ; i < _count ; i++) { ATOMICRELEASE(_pcmItem[i]); ATOMICRELEASE(_pcm2Item[i]); ATOMICRELEASE(_pcm3Item[i]); } if (_pdtobj) _pdtobj->Release(); } STDMETHODIMP CDFMenuWrap::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENTMULTI(CDFMenuWrap, IContextMenu, IContextMenu3), // IID_IContextMenu QITABENTMULTI(CDFMenuWrap, IContextMenu2, IContextMenu3), // IID_IContextMenu2 QITABENT(CDFMenuWrap, IContextMenu3), // IID_IContextMenu3 QITABENT(CDFMenuWrap, IObjectWithSite), // IID_IObjectWithSite { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CDFMenuWrap::AddRef() { _cRef++; TraceMsg(TF_DOCFIND, "dfmw.AddRef %d",_cRef); return _cRef; } STDMETHODIMP_(ULONG) CDFMenuWrap::Release() { ASSERT(_cRef >= 1); // Don't release more than we can! _cRef--; TraceMsg(TF_DOCFIND, "dfmw.Release %d",_cRef); if (_cRef > 0) { return _cRef; } delete this; return 0; } STDMETHODIMP CDFMenuWrap::SetSite(IUnknown *punkSite) { // let all the kids know for (UINT i = 0 ; i < _count ; i++) { IUnknown_SetSite(_pcmItem[i], punkSite); } return CObjectWithSite::SetSite(punkSite); } STDMETHODIMP CDFMenuWrap::QueryContextMenu( HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags) { HRESULT hres; // simply foward this to the one we are wrapping... TraceMsg(TF_DOCFIND,"dfmw.queryContextMenu, iMenu=%d, iFirst=%d, iLast=%d", indexMenu, idCmdFirst, idCmdLast); _idFirst = idCmdFirst; for (UINT i = 0 ; i < _count && idCmdFirst < idCmdLast; i++) { hres = _pcmItem[i]->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags); if (SUCCEEDED(hres)) { _idOffsets[i] = idCmdFirst - _idFirst + (UINT)ShortFromResult(hres); idCmdFirst = idCmdFirst + (UINT)ShortFromResult(hres) + 1; } else { if (0 == i) _idOffsets[i] = 0; else _idOffsets[i] = _idOffsets[i-1]; } } return hres; } STDMETHODIMP CDFMenuWrap::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) { HRESULT hres; TraceMsg(TF_DOCFIND, "dfmw.InvokeCommand, verbstr=%lx", lpici->lpVerb); // This is sortof Gross, but we will attempt to pickoff the Link command // which looks like the pcmitem will be SHARED_FILE_LINK.... // NOTE: This assumes the filesys context menu is FIRST in our array if ((UINT_PTR)lpici->lpVerb == SHARED_FILE_LINK || (!IS_INTRESOURCE(lpici->lpVerb) && (lstrcmpiA(lpici->lpVerb, "link") == 0))) { // 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); } for (UINT i = 0 ; i < _count ; i++) { if (IS_INTRESOURCE(lpici->lpVerb)) { UINT idCmd = (UINT)LOWORD((DWORD_PTR)lpici->lpVerb); if (idCmd <= _idOffsets[i]) { // adjust id to be in proper range for this pcm if (i>0) { lpici->lpVerb = MAKEINTRESOURCEA(idCmd - _idOffsets[i-1] - 1); } hres = _pcmItem[i]->InvokeCommand(lpici); return hres; } } else { // I guess we try until it works hres = _pcmItem[i]->InvokeCommand(lpici); if (SUCCEEDED(hres)) return hres; } } TraceMsg(TF_ERROR, "Someone's passing CDFMenuWrap::InvokeCommand an id we didn't insert..."); return E_FAIL; } STDMETHODIMP CDFMenuWrap::GetCommandString( UINT_PTR idCmd, UINT wFlags, UINT * pmf, LPSTR pszName, UINT cchMax) { for (UINT i = 0 ; i < _count ; i++) { if (idCmd <= _idOffsets[i]) { // adjust id to be in proper range for this pcm if (i>0) { idCmd = idCmd - _idOffsets[i-1] - 1; } return _pcmItem[i]->GetCommandString(idCmd, wFlags, pmf, pszName, cchMax); } } TraceMsg(TF_ERROR, "Someone's passing CDFMenuWrap::GetCommandString an id we didn't insert..."); return E_FAIL; } STDMETHODIMP CDFMenuWrap::HandleMenuMsg( UINT uMsg, WPARAM wParam, LPARAM lParam) { return HandleMenuMsg2(uMsg, wParam, lParam, NULL); } STDMETHODIMP CDFMenuWrap::HandleMenuMsg2( UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) { HRESULT hres = E_FAIL; UINT idCmd; // Find the menu command id -- it's packed differently depending on the message switch (uMsg) { case WM_MEASUREITEM: idCmd = ((MEASUREITEMSTRUCT *)lParam)->itemID; break; case WM_DRAWITEM: idCmd = ((LPDRAWITEMSTRUCT)lParam)->itemID; break; case WM_INITMENUPOPUP: idCmd = GetMenuItemID((HMENU)wParam, 0); break; case WM_MENUSELECT: idCmd = (UINT) LOWORD(wParam); break; case WM_MENUCHAR: case WM_NEXTMENU: idCmd = GetMenuItemID((HMENU)lParam, 0); break; default: return E_FAIL; } // make sure it's in our range if (idCmd >= _idFirst) { idCmd -= _idFirst; for (UINT i = 0 ; i < _count ; i++) { if (idCmd <= _idOffsets[i]) { if (_pcm3Item[i]) hres = _pcm3Item[i]->HandleMenuMsg2(uMsg, wParam, lParam, plResult); else if (_pcm2Item[i] && NULL == plResult) hres = _pcm2Item[i]->HandleMenuMsg(uMsg, wParam, lParam); break; } } } return hres; } HRESULT DFWrapIContextMenu(HWND hwndOwner, IShellFolder *psfItem, LPCITEMIDLIST pidl, IContextMenu* pcmExtra, void **ppvInOut) { HRESULT hres; IContextMenu *pcmWrap = NULL; IContextMenu *pcmFree = (IContextMenu*)*ppvInOut; IDataObject* pdo; hres = psfItem->GetUIObjectOf(hwndOwner, 1, &pidl, IID_IDataObject, NULL, (void **) &pdo); if (SUCCEEDED(hres)) { pcmWrap = DFWrapIContextMenus(pdo, pcmFree, pcmExtra); if (!pcmWrap) hres = E_OUTOFMEMORY; pdo->Release(); } pcmFree->Release(); *ppvInOut = pcmWrap; return hres; } IContextMenu* DFWrapIContextMenus(IDataObject* pdo, IContextMenu* pcm1, IContextMenu* pcm2) { CDFMenuWrap * pcm = new CDFMenuWrap(pdo, pcm1, pcm2); return SAFECAST(pcm, IContextMenu*); } STDMETHODIMP CDFFolder::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDFFolder, IShellFolder2), //IID_ISHELLFolder2 QITABENTMULTI(CDFFolder, IShellFolder, IShellFolder2), // IID_IShellFolder QITABENT(CDFFolder, IDocFindFolder), //IID_IDocFindFolder QITABENT(CDFFolder, IShellIcon), //IID_IShellIcon QITABENT(CDFFolder, IPersistFolder2), //IID_IPersistFolder2 QITABENTMULTI(CDFFolder, IPersistFolder, IPersistFolder2), //IID_IPersistFolder QITABENTMULTI(CDFFolder, IPersist, IPersistFolder2), //IID_IPersist QITABENT(CDFFolder, IShellIconOverlay), //IID_IShellIconOverlay { 0 }, }; return QISearch(this, qit, riid, ppvObj); } // IPersistFolder2 implementation STDMETHODIMP CDFFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_DocFindFolder; return S_OK; } STDMETHODIMP CDFFolder::Initialize(LPCITEMIDLIST pidl) { if (_pidl) ILFree(_pidl); return SHILClone(pidl, &_pidl); } STDMETHODIMP CDFFolder::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 CDFFolder_SortForFileOp(void *lp1, void *lp2, 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)lp1); LPITEMIDLIST pidl2 = (LPITEMIDLIST)ILFindLastID((LPITEMIDLIST)lp2); return DF_IFOLDER(pidl2) - DF_IFOLDER(pidl1); } LPITEMIDLIST CDFFolder::_GetFullPidlForItem(LPCITEMIDLIST pidl) { LPITEMIDLIST pidlParent; if (S_OK == GetParentsPIDL(pidl, &pidlParent)) { return ILCombine(pidlParent, pidl); } return NULL; } STDMETHODIMP CDFFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppvOut) { HRESULT hres = E_INVALIDARG; *ppvOut = NULL; // If Count of items passed in is == 1 simply pass to the appropriate // folder if (cidl == 1) { // Note we may have been passed in a complex item so find the last // id. ASSERT(ILIsEmpty(_ILNext(*apidl))); // should be a single level PIDL! LPCITEMIDLIST pidl = ILFindLastID(*apidl); // BUGBUG, this is bogus IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl); if (psfItem) { hres = psfItem->GetUIObjectOf(hwndOwner, 1, &pidl, riid, prgfInOut, ppvOut); // 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(hres) && IsEqualIID(riid, IID_IContextMenu)) { // we also let the net/file guy add in a context menu if they want to IContextMenu* pcmExtra = NULL; pdfff->GetItemContextMenu(hwndOwner, SAFECAST(this, IDocFindFolder*), &pcmExtra); hres = DFWrapIContextMenu(hwndOwner, psfItem, pidl, pcmExtra, ppvOut); ATOMICRELEASE(pcmExtra); } psfItem->Release(); } return hres; // error... } if (IsEqualIID(riid, IID_IContextMenu)) { // Is there any selection? if (cidl == 0) { hres = E_INVALIDARG; } else { IContextMenuCB *pcmcb; // So we have at least two items in the list. // Try to create a menu object that we process ourself // Yes, do context menu. HKEY ahkeys[3] = {0, 0, 0}; LPITEMIDLIST pidlFull = _GetFullPidlForItem(apidl[0]); if (pidlFull) { // Get the hkeyProgID and hkeyBaseProgID from the first item. SHGetClassKey(pidlFull, &ahkeys[0], &ahkeys[1]); ILFree(pidlFull); } RegOpenKey(HKEY_CLASSES_ROOT, TEXT("AllFilesystemObjects"), &ahkeys[2]); pcmcb = new CDFContextMenuCB(); if (pcmcb) { hres = CDefFolderMenu_Create2Ex(NULL, hwndOwner, cidl, apidl, this, pcmcb, ARRAYSIZE(ahkeys), ahkeys, (IContextMenu **)ppvOut); pcmcb->Release(); } SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys)); } } else if (cidl && IsEqualIID(riid, IID_IDataObject)) { // We need to generate a data object that each item as being // fully qualified. This is a pain, but... // This is a really gross use of memory! UINT i; HDPA hdpa = DPA_Create(0); if (!hdpa) return hres; if (!DPA_Grow(hdpa, cidl)) { DPA_Destroy(hdpa); return hres; } for (i = 0; i < cidl; i++) { LPITEMIDLIST pidl = _GetFullPidlForItem(apidl[i]); if (pidl) DPA_InsertPtr(hdpa, 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(hdpa, CDFFolder_SortForFileOp, 0); hres = CFSFolder_CreateDataObject(&c_idlDesktop, cidl, (LPCITEMIDLIST*)DPA_GetPtrPtr(hdpa), (IDataObject **)ppvOut); // now to cleanup what we created. for (i = 0; i < cidl; i++) { ILFree((LPITEMIDLIST)DPA_FastGetPtr(hdpa, i)); } DPA_Destroy(hdpa); } return hres; } STDMETHODIMP CDFFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwRes, LPSTRRET pStrRet) { HRESULT hr = E_INVALIDARG; IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl); if (psfItem) { hr = psfItem->GetDisplayNameOf(pidl, dwRes, pStrRet); psfItem->Release(); } return hr; } STDMETHODIMP CDFFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwRes, LPITEMIDLIST *ppidlOut) { HRESULT hr = E_INVALIDARG; IShellFolder *psfItem = DocFind_GetObjectsIFolder(this, NULL, pidl); if (psfItem) { hr = psfItem->SetNameOf(hwnd, pidl, lpszName, dwRes, ppidlOut); psfItem->Release(); } return hr; } STDMETHODIMP CDFFolder::GetDefaultSearchGUID(LPGUID lpGuid) { return pdfff->GetDefaultSearchGUID(SAFECAST(this, IShellFolder2*), lpGuid); } STDMETHODIMP CDFFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum) { return pdfff->EnumSearches(SAFECAST(this, IShellFolder2*), ppenum); } HRESULT CDFFolder::_QueryItemInterface(LPCITEMIDLIST pidl, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; IShellFolder *psf = DocFind_GetObjectsIFolder(this, NULL, pidl); if (psf) { hr = psf->QueryInterface(riid, ppv); psf->Release(); } return hr; } STDMETHODIMP CDFFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { return E_NOTIMPL; } STDMETHODIMP CDFFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState) { return E_NOTIMPL; } STDMETHODIMP CDFFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { IShellFolder2 *psf2; HRESULT hr = _QueryItemInterface(pidl, IID_IShellFolder2, (void **)&psf2); if (SUCCEEDED(hr)) { hr = psf2->GetDetailsEx(pidl, pscid, pv); psf2->Release(); } return hr; } STDMETHODIMP CDFFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi) { // first let the filters have a crack at this... IDocFindFileFilter *pdfff; HRESULT hres = GetDocFindFilter(&pdfff); if (SUCCEEDED(hres)) { EnterCriticalSection(&_csSearch); hres = pdfff->GetDetailsOf(hdpaPidf, pidl, &iColumn, pdi); LeaveCriticalSection(&_csSearch); pdfff->Release(); // special case to go to async guy... #ifdef IF_ADD_MORE_COLS if (hres == S_FALSE) { if (_pDFEnumAsync) { // OK lets ask it for the information... // First we need to get which row it is in. CDFItem *pesfi; if (SUCCEEDED(GetItem(_iGetIDList, &pesfi)) && pesfi && ((&pesfi->idl == pidl) || ILIsEqual(pidl, &pesfi->idl))) { // Yep it was ours so return the index quickly.. return _pDFEnumAsync->GetExtendedDetailsOf(pidl, iColumn, pdi); } } else hres = S_OK; // No extended data so ignore it for now... } #endif } return hres; } STDMETHODIMP CDFFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) { return E_NOTIMPL; } STDMETHODIMP CDFFolder::SetDocFindFilter(IDocFindFileFilter *pdfffNew) { // TODO: add type checked version of IUnknown_Set to avoid code like this [t-ashram] if (pdfff) pdfff->Release(); pdfff = pdfffNew; if (pdfff) pdfff->AddRef(); return S_OK; } STDMETHODIMP CDFFolder::GetDocFindFilter(IDocFindFileFilter **ppdfff) { *ppdfff = pdfff; if (pdfff) pdfff->AddRef(); return pdfff ? S_OK : E_FAIL; } // CDFFolder::IShellIcon : Members // GetIconOf STDMETHODIMP CDFFolder::GetIconOf(LPCITEMIDLIST pidl, UINT flags, int *piIndex) { IShellIcon * psiItem; HRESULT hres = _QueryItemInterface(pidl, IID_IShellIcon, (void **)&psiItem); if (SUCCEEDED(hres)) { hres = psiItem->GetIconOf(pidl, flags, piIndex); psiItem->Release(); } return hres; } // CDFFolder::IShellIconOverlay : Members STDMETHODIMP CDFFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int * pIndex) { IShellIconOverlay * psioItem; HRESULT hres = _QueryItemInterface(pidl, IID_IShellIconOverlay, (void **)&psioItem); if (SUCCEEDED(hres)) { hres = psioItem->GetOverlayIndex(pidl, pIndex); psioItem->Release(); } return hres; } STDMETHODIMP CDFFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int * pIndex) { return E_NOTIMPL; } STDMETHODIMP CDFFolder::RestoreSearchFromSaveFile(LPITEMIDLIST pidlSaveFile, IShellFolderView *psfv) { // See if we can restore most of the search from here... IStream *pstm; ULONG cbRead; TCHAR szPathName[MAX_PATH]; LARGE_INTEGER dlibMove = {0, 0}; DFHEADER dfh; int cItems = 0; HRESULT hr; // First try to open the file SHGetPathFromIDList(pidlSaveFile, szPathName); if (FAILED(hr = SHCreateStreamOnFile(szPathName, STGM_READ, &pstm))) return hr; // Now Read in the header // 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 (FAILED(pstm->Read(&dfh, SIZEOF(DFHEADER), &cbRead)) || (cbRead != SIZEOF(DFHEADER)) || (dfh.wSig != DOCFIND_SIG)) { // Not the correct format... pstm->Release(); return E_FAIL; } #if 0 //BUGBUG:: need to add support for remembering which view we are in and then restoring it. if (dfh.wVer < 3) { // The ViewMode was added in version 3. dfh.ViewMode = FVM_DETAILS; } #endif // Now try to read in the criteria... #ifdef WINNT DFC_UNICODE_DESC desc; WORD fCharType = 0; // Check the stream's signature to see if it was generated by Win95 or NT. dlibMove.QuadPart = 0 - 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); pdfff->RestoreCriteria(pstm, dfh.cCriteria, fCharType); #else // Now Read in the criteria dlibMove.LowPart = dfh.oCriteria; pstm->Seek(dlibMove, STREAM_SEEK_SET, NULL); pdfff->RestoreCriteria(pstm, dfh.cCriteria, DFC_FMT_ANSI); #endif // 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); RestoreItemList(pstm, &cItems); if (cItems > 0) psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL); } // and close the file pstm->Release(); return S_OK; } STDMETHODIMP CDFFolder::SetEmptyText(LPCTSTR pszText) { if (pszText && 0 == lstrcmp(_szEmptyText, pszText)) return S_OK ; if (pszText) lstrcpyn(_szEmptyText, pszText, ARRAYSIZE(_szEmptyText)); else *_szEmptyText = 0 ; if (IsWindow(_hwndLV)) SendMessage(_hwndLV, LVM_RESETEMPTYTEXT, 0, 0L); return S_OK ; } BOOL CDFFolder::IsSlow() { BOOL bRet = FALSE; if (IsWindow(_hwndLV)) { LONG lStyle = GetWindowLong(_hwndLV, GWL_STYLE) & LVS_TYPEMASK; bRet = lStyle == LVS_ICON || lStyle == LVS_SMALLICON; } return bRet; } // this is the same as shfindfiles except without the gross hack BOOL RealFindFiles(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile) { HRESULT hres; IWebBrowser2* pwb2 = NULL; // First create the top level browser... hres = CoCreateInstance(CLSID_ShellBrowserWindow, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void **)&pwb2); if (SUCCEEDED( hres ) && pwb2) { SA_BSTR bstrClsid; VARIANT varClsid; VARIANT varEmpty = {0}; SHStringFromGUIDW(CLSID_FileSearchBand, bstrClsid.wsz, ARRAYSIZE(bstrClsid.wsz)); bstrClsid.cb = lstrlenW(bstrClsid.wsz) * sizeof(WCHAR); varClsid.vt = VT_BSTR; varClsid.bstrVal = bstrClsid.wsz; // show a search bar hres = pwb2->ShowBrowserBar(&varClsid, &varEmpty, &varEmpty); if (SUCCEEDED(hres)) { // Grab the band's IUnknown from browser property. VARIANT varFsb ; BSTR bstrProperty = NULL ; WCHAR wszProperty[GUIDSTR_MAX+1] ; EVAL( SUCCEEDED( SHStringFromGUIDW( CLSID_FileSearchBand, wszProperty, ARRAYSIZE(wszProperty) ) ) ) ; VariantInit( &varFsb ) ; if( (bstrProperty = SysAllocString( wszProperty )) != NULL && SUCCEEDED( pwb2->GetProperty( bstrProperty, &varFsb ) ) ) { if( VT_UNKNOWN == varFsb.vt && varFsb.punkVal ) { // 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( varFsb.punkVal->QueryInterface( IID_IFileSearchBand, (PVOID*)&pfsb ) ) ) { WCHAR wszSearch[GUIDSTR_MAX+1] ; BSTR bstrSearch ; // Get the File/Folders search guid in string form EVAL( SUCCEEDED( SHStringFromGUIDW( SRCID_SFileSearch, wszSearch, ARRAYSIZE(wszSearch) ) ) ) ; if( (bstrSearch = SysAllocString( wszSearch )) != NULL ) { VARIANT varQueryFile ; VARIANT varScope ; VariantInit( &varQueryFile ) ; VariantInit( &varScope ) ; // 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 ) ; } pfsb->Release() ; } } VariantClear( &varFsb ) ; } SysFreeString( bstrProperty ) ; if (SUCCEEDED(hres)) hres = pwb2->put_Visible(TRUE); } pwb2->Release(); } else hres = E_FAIL; return hres; } // 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) { BYTE bType; bType = SIL_GetType(ILFindLastID(pidlFolder)); if ((bType == SHID_NET_NETWORK) || (bType == SHID_NET_RESTOFNET) || (bType == SHID_NET_DOMAIN)) return SHFindComputer(pidlFolder, pidlSaveFile); } return RealFindFiles(pidlFolder, pidlSaveFile); } // This is the main external entry point to start a search. This will // create a new thread to process the STDAPI_(BOOL) SHFindComputer(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlSaveFile) { IContextMenu *pcm; HRESULT hres = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu, (void **)&pcm); if (SUCCEEDED(hres)) { CMINVOKECOMMANDINFO ici = {0}; ici.cbSize = SIZEOF(ici); ici.lpParameters = "{996E1EB1-B524-11d1-9120-00A0C98BA67D}"; // Search Guid of Find Computers ici.nShow = SW_NORMAL; hres = pcm->InvokeCommand(&ici); pcm->Release(); } return SUCCEEDED(hres); } HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts) { VARIANT var; HRESULT hres = InitVariantFromIDList(&var, pidl); if (SUCCEEDED(hres)) { hres = psfv->SelectItem(&var, dwOpts); VariantClear(&var); } return hres; } HRESULT OpenContainingFolderAndGetShellFolderView(LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv) { *ppsfv = NULL; IWebBrowserApp *pauto; HRESULT hres = SHGetIDispatchForFolder(pidlFolder, &pauto); if (SUCCEEDED(hres)) { // WARNING! This needs to change to LONG_PTR once the MIDL // compiler supports it. HWND hwnd; if (SUCCEEDED(pauto->get_HWND((LONG*)&hwnd))) { // Make sure we make this the active window SetForegroundWindow(hwnd); ShowWindow(hwnd, SW_SHOWNORMAL); } // We have IDispatch for window, now try to get one for // the folder object... IDispatch *pautoDoc; hres = pauto->get_Document(&pautoDoc); if (SUCCEEDED(hres)) { hres = pautoDoc->QueryInterface(IID_IShellFolderViewDual, (void **)ppsfv); pautoDoc->Release(); } pauto->Release(); } return hres; } void DFB_HandleOpenContainingFolder(IShellFolderView* psfv, IDocFindFolder* pdff) { LPCITEMIDLIST *ppidls; // pointer to a list of pidls. UINT cpidls; // Count of pidls that were returned. // Ok lets ask the view for the list of selected objects. psfv->GetSelectedObjects(&ppidls, &cpidls); if (cpidls > 0) { UINT i; for (i = 0; i < cpidls; i++) { LPITEMIDLIST pidl; LPITEMIDLIST pidlT; IShellFolderViewDual *psfvDual; // See if we have already processed this one. if (ppidls[i] == NULL) continue; // Now get the parent of it. pdff->GetParentsPIDL(ppidls[i], &pidl); if (SUCCEEDED(OpenContainingFolderAndGetShellFolderView(pidl, &psfvDual))) { UINT j; SelectPidlInSFV(psfvDual, ppidls[i], SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE); // Now see if there are any other items selected in // this same cabinet. If so we might as well process // them here and save the work of trying to open it // again and the like! for (j=i+1; j < cpidls; j++) { if (ppidls[j] == NULL) continue; // Now see if it has the same parent as we // are processing... pdff->GetParentsPIDL(ppidls[j], &pidlT); if (pidlT == pidl) { SelectPidlInSFV(psfvDual, ppidls[j], SVSI_SELECT); ppidls[j] = NULL; // dont process again. } } psfvDual->Release(); } } } // Free the memory associated with the item list. if (ppidls != NULL) LocalFree((HLOCAL)ppidls); } #ifdef DOCFIND_SAVERESULTS_ENABLED // Hook procedure to allow us to our Save Results checkbox to the Save file dialog UINT_PTR APIENTRY DocFindSaveDialogHookProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { DFBSAVEINFO * pdfb; switch ( msg ) { case WM_INITDIALOG: pdfb = (DFBSAVEINFO*)((LPOPENFILENAME)lParam)->lCustData; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pdfb); CheckDlgButton( hWnd, IDD_SAVERESULTS, (pdfb->dwFlags & DFOO_SAVERESULTS) ? BST_CHECKED : BST_UNCHECKED); break; case WM_NOTIFY: switch (((LPOFNOTIFY)lParam)->hdr.code) { case CDN_FILEOK: { // See if the Save results option changed. BOOL fChecked; pdfb = (DFBSAVEINFO*)GetWindowLongPtr(hWnd, GWLP_USERDATA); fChecked = (BOOL) SendDlgItemMessage( hWnd, IDD_SAVERESULTS, BM_GETCHECK, 0, 0 ); if ((fChecked && ((pdfb->dwFlags & DFOO_SAVERESULTS) == 0)) || (!fChecked && (pdfb->dwFlags & DFOO_SAVERESULTS))) { pdfb->dwFlags ^= DFOO_SAVERESULTS; HKEY hkeyExp = SHGetExplorerHkey(HKEY_LOCAL_MACHINE, TRUE); if (hkeyExp) SHSetValue(hkeyExp, s_szDocFind, s_szFlags, REG_BINARY, &pdfb->dwFlags, SIZEOF(pdfb->dwFlags)); } } // Returning zero signifies that it is ok to exit the common dialog... return FALSE; } break; } return ( FALSE ); } #endif //DOCFIND_SAVERESULTS_ENABLED // DFBSave // Save away the current search to a file on the desktop. // For now the name will be automatically generated. void DFB_Save(IDocFindFileFilter* pdfff, HWND hwndOwner, DFBSAVEINFO * pSaveInfo, IShellView* psv, IDocFindFolder* pDocfindFolder, IUnknown *pObject) { TCHAR szFilePath[MAX_PATH]; IStream * pstm; DFHEADER dfh; TCHAR szTemp[MAX_PATH]; SHORT cb; HRESULT hres; LARGE_INTEGER dlibMove = {0, 0}; ULARGE_INTEGER libCurPos; FOLDERSETTINGS fs; LPCTSTR pszLastPath = c_szNULL; // Null string to begin with. // 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 (pdfff->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 { LPTSTR lpsz; LPTSTR pszTitle; TCHAR szShortName[12]; // 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 pdfff->GenerateTitle(&pszTitle, TRUE); if (pszTitle) { // Now add on the extension. lstrcpyn(szTemp, pszTitle, MAX_PATH - (lstrlen(szFilePath) + 1 + 4 + 1+3)); lstrcat(szTemp, TEXT(".fnd")); LocalFree(pszTitle); // And free the title string. } // Now loop through and replace all of the invalid characters with _'s // we special case a few of the characters... for (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('_'); } } } 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]; LPTSTR psz; OPENFILENAME ofn = { 0 }; LoadString(g_hinst, IDS_FINDFILESFILTER, szFilter, MAX_PATH); LoadString(g_hinst, IDS_FINDSAVERESULTSTITLE, szTitle, MAX_PATH); //Strip out the # and make them Nulls for SaveAs Dialog psz = szFilter; while (*psz) { if (*psz == TEXT('#')) *psz = TEXT('\0'); psz++; } lstrcpy(szFilename, PathFindFileName(szFilePath)); PathRemoveFileSpec(szFilePath); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hwndOwner; 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; #ifdef DOCFIND_SAVERESULTS_ENABLED ofn.lpTemplateName= MAKEINTRESOURCE(DLG_DOCFIND_SAVE); ofn.lpfnHook= DocFindSaveDialogHookProc; ofn.lCustData = (LPARAM)pSaveInfo; ofn.Flags |= (OFN_ENABLETEMPLATE | OFN_ENABLEHOOK) ; #else //DOCFIND_SAVERESULTS_ENABLED ofn.lpTemplateName = NULL; ofn.lpfnHook= NULL; ofn.lCustData = NULL; #endif //DOCFIND_SAVERESULTS_ENABLED 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); pSaveInfo->pidlSaveFile = ILCreateFromPath(szFilename); // 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); hres = pdfff->SaveCriteria(pstm, DFC_FMT_ANSI); if (SUCCEEDED(hres)) dfh.cCriteria = GetScode(hres); // 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; #ifdef DOCFIND_SAVERESULTS_ENABLED if (pSaveInfo->dwFlags & DFOO_SAVERESULTS) { pDocfindFolder->SaveFolderList(pstm); pDocfindFolder->SaveItemList(pstm); } else #endif DOCFIND_SAVERESULTS_ENABLED { // 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... #ifdef WINNT { // 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. hres = pdfff->SaveCriteria(pstm, DFC_FMT_UNICODE); // Append the unicode criteria descriptor to the end of the file. pstm->Write(&desc, SIZEOF(desc), NULL); } #endif // 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((LPTSTR)&dfh, SIZEOF(dfh), NULL); pstm->Release(); SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL); SHChangeNotify(SHCNE_FREESPACE, SHCNF_IDLIST, pSaveInfo->pidlSaveFile, NULL); } void DocFind_SetObjectCount(IShellView *psv, int cItems, DWORD dwFlags) { IShellFolderView *psfv; if (SUCCEEDED(psv->QueryInterface(IID_IShellFolderView, (void **)&psfv))) { psfv->SetObjectCount(cItems, dwFlags); psfv->Release(); } } // Broke out from class to share with old and new code BOOL DFB_handleUpdateDir(IDocFindFolder *pdfFolder, LPITEMIDLIST 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... int iPidf; BOOL fCurrentItemsMayBeImpacted = FALSE; DFFolderListItem *pdffli; int iItem; INT cPidf; ESFItem *pesfi; IShellFolder *psfDesktop; SHGetDesktopFolder(&psfDesktop); // First see which directories are effected... pdfFolder->GetFolderListItemCount(&cPidf); for (iPidf = 0; iPidf < cPidf; iPidf++ ) { if (SUCCEEDED(pdfFolder->GetFolderListItem(iPidf, &pdffli)) && !pdffli->fUpdateDir) // We may have already impacted these... { pdffli->fUpdateDir = ILIsParent(pidl, &pdffli->idl, FALSE); fCurrentItemsMayBeImpacted |= pdffli->fUpdateDir; } } if (fCurrentItemsMayBeImpacted) { // Now we need to walk through the whole list and remove any entries // that are no longer there... if (SUCCEEDED(pdfFolder->GetItemCount(&iItem))) { for (--iItem; iItem >= 0; iItem--) { WORD iFolder; if (FAILED(pdfFolder->GetItem(iItem, &pesfi)) || pesfi == NULL) continue; iFolder = DF_IFOLDER(&pesfi->idl); // See if item may be impacted... if (SUCCEEDED(pdfFolder->GetFolderListItem(iFolder, &pdffli)) && pdffli->fUpdateDir) pesfi->dwState |= CDFITEM_STATE_MAYBEDELETE; } } } return fCurrentItemsMayBeImpacted; } void DFB_UpdateOrMaybeAddPidl(IDocFindFolder *pdfFolder, IShellFolderView *psfv, int code, LPITEMIDLIST pidl, LPITEMIDLIST pidlOld) { IShellFolder *psf; LPITEMIDLIST pidlT; BOOL fMatch = FALSE; UINT iItem; ESFItem *pesfi; HRESULT hres; // First see if we should try to do an update... if (pidlOld) { if (pdfFolder->MapFSPidlToDFPidl(pidl, TRUE, &pidlT) == S_OK) { // SFV owns apidl[1] after this pdfFolder->SetItemsChangedSinceSort(); hres = psfv->UpdateObject(pidlOld, pidlT, &iItem); ILFree(pidlT); // In either case simply blow away our generated pidl... if (SUCCEEDED(hres)) return; } } if (SUCCEEDED(SHBindToIDListParent(pidl, IID_IShellFolder, (void **)&psf, (LPCITEMIDLIST *) &pidlT))) { // See if this item matches the filter... IDocFindFileFilter *pdfff; if (SUCCEEDED(pdfFolder->GetDocFindFilter(&pdfff))) { fMatch = pdfff->FDoesItemMatchFilter(NULL, NULL, psf, pidl) != 0; pdfff->Release(); } psf->Release(); if (fMatch) { LPITEMIDLIST pidlT; if (pdfFolder->MapFSPidlToDFPidl(pidl, TRUE, &pidlT) != S_OK) { // The folder has not been added before now... int iFolder; LPITEMIDLIST pidlFolder; TCHAR szPath[MAX_PATH]; // Don't add stuff in trashcan... SHGetPathFromIDList(pidl, szPath); if (IsFileInBitBucket(szPath)) return; // yes, don't add it to the list... PathRemoveFileSpec(szPath); pidlFolder = ILCreateFromPath(szPath); if (!pidlFolder) return; hres = pdfFolder->AddFolderToFolderList(pidlFolder, TRUE, &iFolder); ILFree(pidlFolder); if (FAILED(hres)) return; if (pdfFolder->MapFSPidlToDFPidl(pidl, TRUE, &pidlT) != S_OK) return; } // There are times we get notified twice. To handle this // see if the item is already in our list. If so punt... // SFV owns apidl[1] after this pdfFolder->SetItemsChangedSinceSort(); if (SUCCEEDED(psfv->UpdateObject(pidlT, pidlT, &iItem))) { ILFree(pidlT); return; // We were able to add the item without problem... } // Normal case would be here to add the object // We need to add this to our dpa and dsa... if (SUCCEEDED(pdfFolder->GetItemCount((INT *)&iItem))) { pdfFolder->AddPidl(iItem, pidlT, (UINT)-1, &pesfi); ILFree(pidlT); if (pesfi) psfv->SetObjectCount(++iItem, SFVSOC_NOSCROLL); } } } } void DFB_handleRMDir(IDocFindFolder *pdfFolder, IShellFolderView *psfv, LPITEMIDLIST pidl) { BOOL fCurrentItemsMayBeImpacted = FALSE; DFFolderListItem *pdffli; int iItem; INT cItems; ESFItem *pesfi; // First see which directories are effected... pdfFolder->GetFolderListItemCount(&cItems); for (iItem = 0; iItem < cItems; iItem++ ) { if (SUCCEEDED(pdfFolder->GetFolderListItem(iItem, &pdffli))) { pdffli->fDeleteDir = ILIsParent(pidl, &pdffli->idl, FALSE); fCurrentItemsMayBeImpacted |= pdffli->fDeleteDir; } else { #ifdef DEBUG INT cItem; pdfFolder->GetFolderListItemCount(&cItem); TraceMsg(TF_WARNING, "NULL pdffli 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(pdfFolder->GetItemCount(&iItem))) { for (--iItem; iItem >= 0; iItem--) { WORD iFolder; if (FAILED(pdfFolder->GetItem(iItem, &pesfi)) || pesfi == NULL) continue; iFolder = DF_IFOLDER(&pesfi->idl); // See if item may be impacted... if (SUCCEEDED(pdfFolder->GetFolderListItem(iFolder, &pdffli)) && pdffli->fDeleteDir) { psfv->RemoveObject(&pesfi->idl, (UINT*)&cItems); } } } } } UINT GetControlCharWidth(HWND hwnd) { SIZE siz; HDC hdc = GetDC(HWND_DESKTOP); HFONT hfOld = SelectFont(hdc, FORWARD_WM_GETFONT(hwnd, SendMessage)); GetTextExtentPoint(hdc, TEXT("0"), 1, &siz); SelectFont(hdc, hfOld); ReleaseDC(HWND_DESKTOP, hdc); return siz.cx; } // Context menu stuff TCHAR const c_szFindExtensions[] = TEXT("FindExtensions"); IContextMenu * WINAPI SHFind_InitMenuPopup(HMENU hmenu, HWND hwndOwner, UINT idCmdFirst, UINT idCmdLast) { IContextMenu * pcm = NULL; HKEY hkFind = SHGetExplorerSubHkey(HKEY_LOCAL_MACHINE, c_szFindExtensions, FALSE); if (hkFind) { if (SUCCEEDED(CDefFolderMenu_CreateHKeyMenu(hwndOwner, 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; } // This whole class is so "Find Computer" can redirect from a static item to this one. // The FindFile stuff below is DEAD CODE. BUT, it may be resurrected (see HACKHACK // comment in defcm's _Static_Add function. class CShellFindExt : public IShellExtInit, public IContextMenu { public: // *** IUnknown methods *** virtual STDMETHODIMP QueryInterface(THIS_ REFIID riid, void **ppvObj); virtual STDMETHODIMP_(ULONG) AddRef(); virtual STDMETHODIMP_(ULONG) Release(); // *** IShellExtInit methods *** virtual STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID); // *** IContextMenu methods *** virtual STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); virtual STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici); virtual STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT wFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax); // constructor CShellFindExt(); private: UINT cRef; LPITEMIDLIST pidl; }; typedef CShellFindExt* LPSHELLFINDEXT; STDMETHODIMP CShellFindExt::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CShellFindExt, IShellExtInit), //IID_IDocFindFolder QITABENT(CShellFindExt, IContextMenu), //IID_IContextMenu { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CShellFindExt::Release() { ASSERT(this->cRef >= 1); // Don't release more than we can! this->cRef--; if (this->cRef > 0) { return this->cRef; } ILFree(this->pidl); delete this; return 0; } STDMETHODIMP_(ULONG) CShellFindExt::AddRef() { this->cRef++; return this->cRef; } CShellFindExt::CShellFindExt() { this->cRef = 1; } STDMETHODIMP CShellFindExt::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) { return NOERROR; } STDMETHODIMP CShellFindExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { int iCommands = 0; int idMax = idCmdFirst; HMENU hmMerge = SHLoadPopupMenu(HINST_THISDLL, POPUP_FINDEXT_POPUPMERGE); if (hmMerge) { MENUITEMINFO mii; mii.cbSize = SIZEOF(MENUITEMINFO); mii.fMask = MIIM_DATA; mii.dwItemData = Shell_GetCachedImageIndex(c_szShell32Dll, EIRESID(IDI_DOCFIND), 0); // setting the icon index TraceMsg(TF_DOCFIND, "CSFE::QueryContextMenu: %d is the icon index being set", mii.dwItemData); if (mii.dwItemData != -1) SetMenuItemInfo(hmMerge, FSIDM_FINDFILES, FALSE, &mii); if (!(GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS) || !SHRestricted(REST_HASFINDCOMPUTERS) ) { // Need to remove the Find Computer as we dont have a network DeleteMenu(hmMerge, FSIDM_FINDCOMPUTER, MF_BYCOMMAND); } else { mii.dwItemData = Shell_GetCachedImageIndex(c_szShell32Dll, EIRESID(IDI_COMPFIND),0); if (mii.dwItemData != -1) SetMenuItemInfo(hmMerge, FSIDM_FINDCOMPUTER, FALSE, &mii); } idMax = Shell_MergeMenus(hmenu, hmMerge, indexMenu, idCmdFirst, idCmdLast, 0); DestroyMenu(hmMerge); } return ResultFromShort(idMax - idCmdFirst); } STDMETHODIMP CShellFindExt::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { LPITEMIDLIST pidl; UINT_PTR id; TraceMsg(TF_DOCFIND, "CSFE::InvokeCommand %d", pici->lpVerb); id = (UINT_PTR)pici->lpVerb; if (pici->lpParameters) { CLSID guid; if (SUCCEEDED(GUIDFromStringA(pici->lpParameters, &guid)) && IsEqualGUID(guid, SRCID_SFindComputer)) id = FSIDM_FINDCOMPUTER; } switch (id) { case FSIDM_FINDFILES: if (pici->lpDirectory) { #ifdef UNICODE WCHAR szDirectory[MAX_PATH]; LPCWSTR lpDirectory; if (pici->cbSize < CMICEXSIZE_NT4 || (pici->fMask & CMIC_MASK_UNICODE) != CMIC_MASK_UNICODE) { MultiByteToWideChar(CP_ACP, 0, pici->lpDirectory, -1, szDirectory, ARRAYSIZE(szDirectory)); lpDirectory = szDirectory; } else { lpDirectory = ((LPCMINVOKECOMMANDINFOEX)pici)->lpDirectoryW; } pidl = ILCreateFromPath(lpDirectory); #else pidl = ILCreateFromPath(pici->lpDirectory); #endif } else { pidl = NULL; } RealFindFiles(pidl, NULL); ILFree(pidl); break; case FSIDM_FINDCOMPUTER: SHFindComputer(NULL, NULL); break; } return NOERROR; } STDMETHODIMP CShellFindExt::GetCommandString( UINT_PTR idCmd, UINT wFlags, UINT * pwReserved, LPSTR pszName, UINT cchMax) { TraceMsg(TF_DOCFIND, "CSFE::GetCommandString idCmd = %d", idCmd); if (wFlags & GCS_HELPTEXTA) { UINT cch; if ((wFlags & GCS_HELPTEXTW) == GCS_HELPTEXTW) cch = LoadStringW(HINST_THISDLL, (UINT)(idCmd + IDS_MH_FSIDM_FIRST), (LPWSTR)pszName, cchMax); else cch = LoadStringA(HINST_THISDLL, (UINT)(idCmd + IDS_MH_FSIDM_FIRST), pszName, cchMax); if (cch) return NOERROR; else return E_OUTOFMEMORY; } else return E_NOTIMPL; } STDAPI CShellFindExt_CreateInstance(LPUNKNOWN, REFIID , void **); HRESULT CALLBACK CShellFindExt_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { LPSHELLFINDEXT psfe; HRESULT hres; psfe = new CShellFindExt; if (psfe) { hres = psfe->QueryInterface(riid, ppv); psfe->Release(); } else { *ppv = NULL; hres = E_OUTOFMEMORY; } return hres; }