|
|
#include "shellprv.h"
#pragma hdrstop
#include "dspsprt.h"
#include "findfilter.h"
#include "cowsite.h"
#include "cobjsafe.h"
#include "cnctnpt.h"
#include "stdenum.h"
#include "exdisp.h"
#include "exdispid.h"
#include "shldisp.h"
#include "shdispid.h"
#include "dataprv.h"
#include "ids.h"
#include "views.h"
#include "findband.h"
#define WM_DF_SEARCHPROGRESS (WM_USER + 42)
#define WM_DF_ASYNCPROGRESS (WM_USER + 43)
#define WM_DF_SEARCHSTART (WM_USER + 44)
#define WM_DF_SEARCHCOMPLETE (WM_USER + 45)
#define WM_DF_FSNOTIFY (WM_USER + 46)
STDAPI CDocFindCommand_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
typedef struct { BSTR bstrName; VARIANT vValue; } CMD_CONSTRAINT;
typedef struct { LPTSTR pszDotType; LPTSTR pszDefaultValueMatch; LPTSTR pszGuid; // If NULL, patch either pszDefaultValueMatch, or pszDotType whichever you find
} TYPE_FIX_ENTRY;
class CFindCmd : public ISearchCommandExt, public CImpIDispatch, public CObjectWithSite, public CObjectSafety, public IConnectionPointContainer, public IProvideClassInfo2, public CSimpleData, public IRowsetWatchNotify, public IFindControllerNotify { public: // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IDispatch
STDMETHOD(GetTypeInfoCount)(UINT * pctinfo); STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfo); STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid); STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
// IConnectionPointContainer
STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints **ppEnum); STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint **ppCP);
// IProvideClassInfo
STDMETHOD(GetClassInfo)(ITypeInfo **ppTI);
// IProvideClassInfo2
STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID *pGUID);
// IObjectWithSite
STDMETHOD(SetSite)(IUnknown *punkSite);
// ISearchCommandExt
STDMETHOD(ClearResults)(void); STDMETHOD(NavigateToSearchResults)(void); STDMETHOD(get_ProgressText)(BSTR *pbs); STDMETHOD(SaveSearch)(void); STDMETHOD(RestoreSearch)(void); STDMETHOD(GetErrorInfo)(BSTR *pbs, int *phr); STDMETHOD(SearchFor)(int iFor); STDMETHOD(GetScopeInfo)(BSTR bsScope, int *pdwScopeInfo); STDMETHOD(RestoreSavedSearch)(VARIANT *pvarFile); STDMETHOD(Execute)(VARIANT *RecordsAffected, VARIANT *Parameters, long Options); STDMETHOD(AddConstraint)(BSTR Name, VARIANT Value); STDMETHOD(GetNextConstraint)(VARIANT_BOOL fReset, DFConstraint **ppdfc);
// IRowsetWatchNotify
STDMETHODIMP OnChange(IRowset *prowset, DBWATCHNOTIFY eChangeReason);
// IFindControllerNotify
STDMETHODIMP DoSortOnColumn(UINT iCol, BOOL fSameCol); STDMETHODIMP StopSearch(void); STDMETHODIMP GetItemCount(UINT *pcItems); STDMETHODIMP SetItemCount(UINT cItems); STDMETHODIMP ViewDestroyed();
CFindCmd(); HRESULT Init(void);
private: ~CFindCmd(); HRESULT _GetSearchIDList(LPITEMIDLIST *ppidl); HRESULT _SetEmptyText(UINT nID); HRESULT _Clear(); void _SelectResults(); HWND _GetWindow();
struct THREAD_PARAMS { CFindCmd *pThis; IFindEnum *penum; };
struct DEFER_UPDATE_DIR { struct DEFER_UPDATE_DIR *pdudNext; LPITEMIDLIST pidl; BOOL fRecurse; };
// Internal class to handle notifications from top level browser
class CWBEvents2: public DWebBrowserEvents, DWebBrowserEvents2 { public: STDMETHOD(QueryInterface) (REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(void) { return _pcdfc->AddRef();} STDMETHOD_(ULONG, Release)(void) { return _pcdfc->Release();} // (DwebBrowserEvents)IDispatch
STDMETHOD(GetTypeInfoCount)(UINT * pctinfo) { return E_NOTIMPL;} STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) { return E_NOTIMPL;} STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return E_NOTIMPL;} STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
// Some helper functions...
void SetOwner(CFindCmd *pcdfc) { _pcdfc = pcdfc; } // Don't addref as part of larger object... }
void SetWaiting(BOOL fWait) {_fWaitingForNavigate = fWait;}
protected: // Internal variables...
CFindCmd *_pcdfc; // pointer to top object... could cast, but...
BOOL _fWaitingForNavigate; // Are we waiting for the navigate to search resluts?
};
friend class CWBEvents2; CWBEvents2 _cwbe; IConnectionPoint *_pcpBrowser; // hold onto browsers connection point;
ULONG _dwCookie; // Cookie returned by Advise
HRESULT _UpdateFilter(IFindFilter *pfilter); void _ClearConstraints(); static DWORD CALLBACK _ThreadProc(void *pv); void _DoSearch(IFindEnum *penum); HRESULT _Start(BOOL fNavigateIfFail, int iCol, LPCITEMIDLIST pidlUpdate); HRESULT _Cancel(); HRESULT _Init(THREAD_PARAMS **ppParams, int iCol, LPCITEMIDLIST pidlUpdate); static HRESULT _FreeThreadParams(THREAD_PARAMS *ptp); HRESULT _ExecData_Init(); HRESULT _EnsureResultsViewIsCurrent(IUnknown *punk); HRESULT _ExecData_Release(); BOOL _SetupBrowserCP(); void cdecl _NotifyProgressText(UINT ids,...); static LRESULT CALLBACK _WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void _PTN_SearchProgress(void); void _PTN_AsyncProgress(int nPercentComplete, DWORD cAsync); void _PTN_AsyncToSync(void); void _PTN_SearchComplete(HRESULT hr, BOOL fAbort); void _OnChangeNotify(LONG code, LPITEMIDLIST *ppidl); void _DeferHandleUpdateDir(LPCITEMIDLIST pidl, BOOL bRecurse); void _ClearDeferUpdateDirList(); void _ClearItemDPA(HDPA hdpa); HRESULT _SetLastError(HRESULT hr); void _SearchResultsCLSID(CLSID *pclsid) { *pclsid = _clsidResults; }; IUnknown* _GetObjectToPersist(); HRESULT _ForcedUnadvise(void); void _PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); HRESULT _GetShellView(REFIID riid, void **ppv); BOOL _FixPersistHandler(LPCTSTR pszBase, LPCTSTR pszDefaultHandler); void _ProcessTypes(const TYPE_FIX_ENTRY *ptfeTypes, UINT cTypes, TCHAR *pszClass); void _FixBrokenTypes(void);
// These are the things that the second thread will use during it's processing...
struct { CRITICAL_SECTION csSearch; HWND hwndThreadNotify; HDPA hdpa; DWORD dwTimeLastNotify; BOOL fFilesAdded : 1; BOOL fDirChanged : 1; BOOL fUpdatePosted : 1; } _updateParams; // Pass callback params through this object to avoid alloc/free cycle
struct { IShellFolder *psf; IShellFolderView *psfv; IFindFolder *pff; TCHAR szProgressText[MAX_PATH]; } _execData;
private: LONG _cRef; HDSA _hdsaConstraints; DWORD _cExecInProgress; BOOL _fAsyncNotifyReceived; BOOL _fDeferRestore; BOOL _fDeferRestoreTried; BOOL _fContinue; BOOL _fNew; CConnectionPoint _cpEvents; OLEDBSimpleProviderListener *_pListener; HDPA _hdpaItemsToAdd1; HDPA _hdpaItemsToAdd2; TCHAR _szProgressText[MAX_PATH+40]; // progress text leave room for chars...
LPITEMIDLIST _pidlUpdate; // Are we processing an updatedir?
LPITEMIDLIST _pidlRestore; // pidl to do restore from...
struct DEFER_UPDATE_DIR *_pdudFirst; // Do we have any defered update dirs?
HRESULT _hrLastError; // the last error reported.
UINT _uStatusMsgIndex; // Files or computers found...
CRITICAL_SECTION _csThread; DFBSAVEINFO _dfbsi; CLSID _clsidResults; };
class CFindConstraint: public DFConstraint, public CImpIDispatch { public: // IUnknown
STDMETHOD(QueryInterface) (REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(void); STDMETHOD_(ULONG, Release)(void);
// IDispatch
STDMETHOD(GetTypeInfoCount)(UINT * pctinfo); STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfo); STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid); STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
// DFConstraint
STDMETHOD(get_Name)(BSTR *pbs); STDMETHOD(get_Value)(VARIANT *pvar);
CFindConstraint(BSTR bstr, VARIANT var); private: ~CFindConstraint(); LONG _cRef; BSTR _bstr; VARIANT _var; };
CFindCmd::CFindCmd() : CImpIDispatch(LIBID_Shell32, 1, 0, IID_ISearchCommandExt), CSimpleData(&_pListener) { _cRef = 1; _fAsyncNotifyReceived = 0; _fContinue = TRUE; ASSERT(NULL == _pidlRestore);
ASSERT(_cExecInProgress == 0);
InitializeCriticalSection(&_updateParams.csSearch); InitializeCriticalSection(&_csThread);
_clsidResults = CLSID_DocFindFolder; // default
_cpEvents.SetOwner(SAFECAST(this, ISearchCommandExt *), &DIID_DSearchCommandEvents); }
HRESULT CFindCmd::Init(void) { _hdsaConstraints = DSA_Create(sizeof(CMD_CONSTRAINT), 4); if (!_hdsaConstraints) return E_OUTOFMEMORY; return S_OK; }
CFindCmd::~CFindCmd() { if (_updateParams.hwndThreadNotify) { // make sure no outstanding fsnotifies registered.
SHChangeNotifyDeregisterWindow(_updateParams.hwndThreadNotify); DestroyWindow(_updateParams.hwndThreadNotify); }
_ClearConstraints(); DSA_Destroy(_hdsaConstraints); _ExecData_Release();
DeleteCriticalSection(&_updateParams.csSearch); DeleteCriticalSection(&_csThread);
// Make sure we have removed all outstanding update dirs...
_ClearDeferUpdateDirList();
if (_hdpaItemsToAdd1) DPA_Destroy(_hdpaItemsToAdd1);
if (_hdpaItemsToAdd2) DPA_Destroy(_hdpaItemsToAdd2);
ILFree(_pidlRestore); }
STDMETHODIMP CFindCmd::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFindCmd, ISearchCommandExt), QITABENTMULTI(CFindCmd, IDispatch, ISearchCommandExt), QITABENT(CFindCmd, IProvideClassInfo2), QITABENTMULTI(CFindCmd, IProvideClassInfo,IProvideClassInfo2), QITABENT(CFindCmd, IObjectWithSite), QITABENT(CFindCmd, IObjectSafety), QITABENT(CFindCmd, IConnectionPointContainer), QITABENT(CFindCmd, OLEDBSimpleProvider), QITABENT(CFindCmd, IRowsetWatchNotify), QITABENT(CFindCmd, IFindControllerNotify), { 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CFindCmd::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CFindCmd::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; }
// IDispatch implementation
STDMETHODIMP CFindCmd::GetTypeInfoCount(UINT * pctinfo) { return CImpIDispatch::GetTypeInfoCount(pctinfo); }
STDMETHODIMP CFindCmd::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) { return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo); }
STDMETHODIMP CFindCmd::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); }
STDMETHODIMP CFindCmd::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); }
// ADOCommand implementation, dual interface method callable via script
STDMETHODIMP CFindCmd::AddConstraint(BSTR bstrName, VARIANT vValue) { HRESULT hr = E_OUTOFMEMORY;
CMD_CONSTRAINT dfcc = {0}; dfcc.bstrName = SysAllocString(bstrName); if (dfcc.bstrName) { hr = VariantCopy(&dfcc.vValue, &vValue); if (SUCCEEDED(hr)) { if (DSA_ERR == DSA_InsertItem(_hdsaConstraints, DSA_APPEND, &dfcc)) { SysFreeString(dfcc.bstrName); VariantClear(&dfcc.vValue); hr = E_OUTOFMEMORY; } } else { SysFreeString(dfcc.bstrName); } } return hr; }
STDMETHODIMP CFindCmd::GetNextConstraint(VARIANT_BOOL fReset, DFConstraint **ppdfc) { *ppdfc = NULL;
IFindFilter *pfilter; HRESULT hr = _execData.pff->GetFindFilter(&pfilter); if (SUCCEEDED(hr)) { BSTR bName; VARIANT var; VARIANT_BOOL fFound; hr = pfilter->GetNextConstraint(fReset, &bName, &var, &fFound); if (SUCCEEDED(hr)) { if (!fFound) { // need a simple way to signal end list, how about an empty name string?
bName = SysAllocString(L""); } CFindConstraint *pdfc = new CFindConstraint(bName, var); if (pdfc) { hr = pdfc->QueryInterface(IID_PPV_ARG(DFConstraint, ppdfc)); pdfc->Release(); } else { // error release stuff we allocated.
hr = E_OUTOFMEMORY; SysFreeString(bName); VariantClear(&var); } } pfilter->Release(); } return hr; }
HRESULT CFindCmd::_UpdateFilter(IFindFilter *pfilter) { HRESULT hr = S_OK;
pfilter->ResetFieldsToDefaults();
int cNumParams = DSA_GetItemCount(_hdsaConstraints); for (int iItem = 0; iItem < cNumParams; iItem++) { CMD_CONSTRAINT *pdfcc = (CMD_CONSTRAINT *)DSA_GetItemPtr(_hdsaConstraints, iItem); if (pdfcc) { hr = pfilter->UpdateField(pdfcc->bstrName, pdfcc->vValue); } }
// And clear out the constraint list...
_ClearConstraints(); return hr; }
void CFindCmd::_ClearConstraints() { int cNumParams = DSA_GetItemCount(_hdsaConstraints); for (int iItem = 0; iItem < cNumParams; iItem++) { CMD_CONSTRAINT *pdfcc = (CMD_CONSTRAINT *)DSA_GetItemPtr(_hdsaConstraints, iItem); if (pdfcc) { SysFreeString(pdfcc->bstrName); VariantClear(&pdfcc->vValue); } } DSA_DeleteAllItems(_hdsaConstraints); }
void cdecl CFindCmd::_NotifyProgressText(UINT ids,...) { va_list ArgList; va_start(ArgList, ids); LPTSTR psz = _ConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(ids), &ArgList); va_end(ArgList);
if (psz) { LPTSTR pszDst = &_szProgressText[0];
StrCpyN(pszDst, psz, ARRAYSIZE(_szProgressText)-2);
LocalFree(psz); } else { _szProgressText[0] = 0; }
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_PROGRESSTEXT); }
STDAPI CDocFindCommand_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { *ppv = NULL;
HRESULT hr; CFindCmd *pfc = new CFindCmd(); if (pfc) { hr = pfc->Init(); if (SUCCEEDED(hr)) hr = pfc->QueryInterface(riid, ppv); pfc->Release(); } else hr = E_OUTOFMEMORY;
return hr; }
void CFindCmd::_PTN_SearchProgress(void) { HRESULT hr = S_OK; HDPA hdpa = _updateParams.hdpa; if (hdpa) { // Ok lets swap things out from under other thread so that we can process it and still
// let the other thread run...
EnterCriticalSection(&_updateParams.csSearch);
if (_updateParams.hdpa == _hdpaItemsToAdd2) _updateParams.hdpa = _hdpaItemsToAdd1; else _updateParams.hdpa = _hdpaItemsToAdd2;
// say that we don't have any thing here such that other thread will reset up...
_updateParams.fFilesAdded = FALSE; BOOL fDirChanged = _updateParams.fDirChanged; _updateParams.fDirChanged = FALSE;
LeaveCriticalSection(&_updateParams.csSearch);
int cItemsToAdd = DPA_GetPtrCount(hdpa);
if (!_execData.pff) return; int iItem; _execData.pff->GetItemCount(&iItem); int iItemStart = iItem + 1; // needed for notifies 1 based.
if (cItemsToAdd) { if (_fContinue) { // Are we in an updatedir? If so then need to do merge, else...
if (_pidlUpdate) { // see if items in list, already if so we unmark the item
// for delete else if not there maybe add it...
int cItems = iItem; for (int i = 0; i < cItemsToAdd; i++) { LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, i); FIND_ITEM *pfi;
for (int j = cItems - 1; j >= 0; j--) { _execData.pff->GetItem(j, &pfi); if (pfi && (_execData.pff->GetFolderIndex(pidl) == _execData.pff->GetFolderIndex(&pfi->idl))) { if (_execData.psf->CompareIDs(0, pidl, &pfi->idl) == 0) break; } }
if (j == -1) { // Not already in the list so add it...
hr = _execData.pff->AddPidl(iItem, pidl, -1, NULL); if (SUCCEEDED(hr)) iItem++; } else { // Item still there - remove possible delete flag...
if (pfi) pfi->dwState &= ~CDFITEM_STATE_MAYBEDELETE; }
ILFree(pidl); // The AddPidl does a clone of the pidl...
} if (iItem && _execData.psfv) { hr = _execData.psfv->SetObjectCount(iItem, SFVSOC_NOSCROLL); } } else { if (_pListener) _pListener->aboutToInsertRows(iItemStart, cItemsToAdd); for (int i = 0; i < cItemsToAdd; i++) { LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, i); hr = _execData.pff->AddPidl(iItem, pidl, -1, NULL); if (SUCCEEDED(hr)) iItem++; ILFree(pidl); // AddPidl makes a copy
} if (iItem >= iItemStart) { if (_execData.psfv) hr = _execData.psfv->SetObjectCount(iItem, SFVSOC_NOSCROLL); _execData.pff->SetItemsChangedSinceSort(); _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_UPDATE); }
if (_pListener) { _pListener->insertedRows(iItemStart, cItemsToAdd); _pListener->rowsAvailable(iItemStart, cItemsToAdd); } } } else // _fContinue
{ for (int i = 0; i < cItemsToAdd; i++) { ILFree((LPITEMIDLIST)DPA_FastGetPtr(hdpa, i)); } } DPA_DeleteAllPtrs(hdpa); }
if (fDirChanged) { _NotifyProgressText(IDS_SEARCHING, _execData.szProgressText); } } _updateParams.dwTimeLastNotify = GetTickCount(); _updateParams.fUpdatePosted = FALSE; }
void CFindCmd::_PTN_AsyncProgress(int nPercentComplete, DWORD cAsync) { if (!_execData.pff) return; // Async case try just setting the count...
_execData.pff->SetAsyncCount(cAsync); if (_execData.psfv) { // -1 for the first item means verify visible items only
_execData.pff->ValidateItems(_execData.psfv, -1, -1, FALSE); _execData.psfv->SetObjectCount(cAsync, SFVSOC_NOSCROLL); }
_execData.pff->SetItemsChangedSinceSort(); _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_UPDATE); _NotifyProgressText(IDS_SEARCHINGASYNC, cAsync, nPercentComplete); }
void CFindCmd::_PTN_AsyncToSync() { if (_execData.pff) _execData.pff->CacheAllAsyncItems(); }
void CFindCmd::_ClearItemDPA(HDPA hdpa) { if (hdpa) { EnterCriticalSection(&_updateParams.csSearch); int cItems = DPA_GetPtrCount(hdpa); for (int i = 0; i < cItems; i++) { ILFree((LPITEMIDLIST)DPA_GetPtr(hdpa, i)); } DPA_DeleteAllPtrs(hdpa); LeaveCriticalSection(&_updateParams.csSearch); } }
void CFindCmd::_PTN_SearchComplete(HRESULT hr, BOOL fAbort) { int iItem;
// someone clicked on new button -- cannot set no files found text in listview
// because we'll overwrite enter search criteria to begin
if (!_fNew) _SetEmptyText(IDS_FINDVIEWEMPTY); _SetLastError(hr);
// _execData.pff is NULL when Searh is complete by navigating away from the search page
if (!_execData.pff) { // do clean up of hdpaToItemsToadd1 and 2
// make sure all items in buffer 1 and 2 are empty
_ClearItemDPA(_hdpaItemsToAdd1); _ClearItemDPA(_hdpaItemsToAdd2); } else { // if we have a _pidlUpdate are completing an update
if (_pidlUpdate) { int i, cPidf; UINT uItem;
_execData.pff->GetItemCount(&i); for (; i-- > 0;) { // Pidl at start of structure...
FIND_ITEM *pfi; _execData.pff->GetItem(i, &pfi); if (pfi->dwState & CDFITEM_STATE_MAYBEDELETE) { _execData.psfv->RemoveObject(&pfi->idl, &uItem); } }
ILFree(_pidlUpdate); _pidlUpdate = NULL;
// clear the update dir flags
_execData.pff->GetFolderListItemCount(&cPidf); for (i = 0; i < cPidf; i++) { FIND_FOLDER_ITEM *pdffli; if (SUCCEEDED(_execData.pff->GetFolderListItem(i, &pdffli))) pdffli->fUpdateDir = FALSE; } }
// Release our reference count on the searching.
if (_cExecInProgress) _cExecInProgress--;
// Tell everyone the final count and that we are done...
// But first check if there are any cached up Updatedirs to be processed...
if (_pdudFirst) { // first unlink the first one...
struct DEFER_UPDATE_DIR *pdud = _pdudFirst; _pdudFirst = pdud->pdudNext;
if (_execData.pff->HandleUpdateDir(pdud->pidl, pdud->fRecurse)) { // Need to spawn sub-search on this...
_Start(FALSE, -1, pdud->pidl); } ILFree(pdud->pidl); LocalFree((HLOCAL)pdud); } else { if (_execData.psfv) { // validate all the items we pulled in already
_execData.pff->ValidateItems(_execData.psfv, 0, -1, TRUE); } _execData.pff->GetItemCount(&iItem); _NotifyProgressText(_uStatusMsgIndex, iItem); if (!fAbort) _SelectResults(); } }
// weird connection point corruption can happen here. somehow the number of sinks is 0 but
// some of the array entries are non null thus causing fault. this problem does not want to
// repro w/ manual testing or debug binaries, only sometimes after an automation run. when
// it happens it is too late to figure out what happened so just patch it here.
if (_cpEvents._HasSinks()) _cpEvents.InvokeDispid(fAbort ? DISPID_SEARCHCOMMAND_ABORT : DISPID_SEARCHCOMMAND_COMPLETE); }
// see if we need to restart the search based on an update dir
BOOL ShouldRestartSearch(LPCITEMIDLIST pidl) { BOOL fRestart = TRUE; // assume we should, non file system pidls
WCHAR szPath[MAX_PATH]; if (SHGetPathFromIDList(pidl, szPath)) { // Check if this is either a network drive or a remote drive:
if (PathIsRemote(szPath)) { // If we can find the CI catalogs for the drive on the other machine, then we do
// not want to search.
WCHAR wszCatalog[MAX_PATH], wszMachine[32]; ULONG cchCatalog = ARRAYSIZE(wszCatalog), cchMachine = ARRAYSIZE(wszMachine);
fRestart = (S_OK != LocateCatalogsW(szPath, 0, wszMachine, &cchMachine, wszCatalog, &cchCatalog)); } else if (-1 != PathGetDriveNumber(szPath)) { // It is a local dirve...
// Is this machine running the content indexer (CI)?
BOOL fCiRunning, fCiIndexed, fCiPermission; GetCIStatus(&fCiRunning, &fCiIndexed, &fCiPermission);
fRestart = !fCiRunning || !fCiIndexed; // restart if not running or not fully indexed
} } return fRestart; }
void CFindCmd::_OnChangeNotify(LONG code, LPITEMIDLIST *ppidl) { LPITEMIDLIST pidlT; UINT idsMsg; UINT cItems;
if (!_execData.pff) { _ExecData_Init();
// If we are running async then for now ignore notifications...
// Unless we have cached all of the items...
if (!_execData.pff) return; // we do not have anything to listen...
}
// see if we want to process the notificiation or not.
switch (code) { case SHCNE_RENAMEFOLDER: // With trashcan this is what we see...
case SHCNE_RENAMEITEM: // With trashcan this is what we see...
case SHCNE_DELETE: case SHCNE_RMDIR: case SHCNE_UPDATEITEM: break;
case SHCNE_CREATE: case SHCNE_MKDIR: // Process this one out of place
_execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, *ppidl, NULL); break;
case SHCNE_UPDATEDIR: TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_UPDATEDIR, pidl=0x%X",*ppidl); if (ShouldRestartSearch(*ppidl)) { BOOL bRecurse = (ppidl[1] != NULL); if (_cExecInProgress) { _DeferHandleUpdateDir(*ppidl, bRecurse); } else { if (_execData.pff->HandleUpdateDir(*ppidl, bRecurse)) { // Need to spawn sub-search on this...
_Start(FALSE, -1, *ppidl); } } } return;
default: return; // we are not interested in this event
}
//
// Now we need to see if the item might be in our list
// First we need to extract off the last part of the id list
// and see if the contained id entry is in our list. If so we
// need to see if can get the defview find the item and update it.
//
_execData.pff->MapToSearchIDList(*ppidl, FALSE, &pidlT);
switch (code) { case SHCNE_RMDIR: TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_RMDIR, pidl=0x%X",*ppidl); _execData.pff->HandleRMDir(_execData.psfv, *ppidl); if (pidlT) { _execData.psfv->RemoveObject(pidlT, &idsMsg); } break;
case SHCNE_DELETE: TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_DELETE, pidl=0x%X",*ppidl); if (pidlT) { _execData.psfv->RemoveObject(pidlT, &idsMsg); } break;
case SHCNE_RENAMEFOLDER: case SHCNE_RENAMEITEM: if (pidlT) { // If the two items dont have the same parent, we will go ahead
// and remove it...
LPITEMIDLIST pidl1; if (SUCCEEDED(_execData.pff->GetParentsPIDL(pidlT, &pidl1))) { LPITEMIDLIST pidl2 = ILClone(ppidl[1]); if (pidl2) { ILRemoveLastID(pidl2); if (!ILIsEqual(pidl1, pidl2)) { _execData.psfv->RemoveObject(pidlT, &idsMsg);
// And maybe add it back to the end... of the list
_execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, ppidl[1], NULL); } else { // The object is in same folder so must be rename...
// And maybe add it back to the end... of the list
_execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, ppidl[1], pidlT); } ILFree(pidl2); } ILFree(pidl1); } } else { _execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, ppidl[1], NULL); } break;
case SHCNE_UPDATEITEM: TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_UPDATEITEM, pidl=0x%X",*ppidl); if (pidlT) _execData.pff->UpdateOrMaybeAddPidl(_execData.psfv, *ppidl, pidlT); break; }
// Update the count...
_execData.psfv->GetObjectCount(&cItems); _NotifyProgressText(_uStatusMsgIndex, cItems);
ILFree(pidlT); }
// Ok we need to add a defer
void CFindCmd::_DeferHandleUpdateDir(LPCITEMIDLIST pidl, BOOL bRecurse) { // See if we already have some items in the list which are lower down in the tree if so we
// can replace it. Or is there one that is higher up, in which case we can ignore it...
struct DEFER_UPDATE_DIR *pdudPrev = NULL; struct DEFER_UPDATE_DIR *pdud = _pdudFirst; while (pdud) { if (ILIsParent(pdud->pidl, pidl, FALSE)) return; // Already one in the list that will handle this one...
if (ILIsParent(pidl, pdud->pidl, FALSE)) break; pdudPrev = pdud; pdud = pdud->pdudNext; }
// See if we found one that we can replace...
if (pdud) { LPITEMIDLIST pidlT = ILClone(pidl); if (pidlT) { ILFree(pdud->pidl); pdud->pidl = pidlT;
// See if there are others...
pdudPrev = pdud; pdud = pdud->pdudNext; while (pdud) { if (ILIsParent(pidl, pdud->pidl, FALSE)) { // Yep lets trash this one.
ILFree(pdud->pidl); pdudPrev->pdudNext = pdud->pdudNext; pdud = pdudPrev; // Let it fall through to setup to look at next...
} pdudPrev = pdud; pdud = pdud->pdudNext; } } } else { // Nope simply add us in to the start of the list.
pdud = (struct DEFER_UPDATE_DIR*)LocalAlloc(LPTR, sizeof(struct DEFER_UPDATE_DIR)); if (!pdud) return; // Ooop could not alloc...
pdud->pidl = ILClone(pidl); if (!pdud->pidl) { LocalFree((HLOCAL)pdud); return; } pdud->fRecurse = bRecurse; pdud->pdudNext = _pdudFirst; _pdudFirst = pdud; } }
void CFindCmd::_ClearDeferUpdateDirList() { // Cancel any Pending updatedirs also.
while (_pdudFirst) { struct DEFER_UPDATE_DIR *pdud = _pdudFirst; _pdudFirst = pdud->pdudNext; ILFree(pdud->pidl); LocalFree((HLOCAL)pdud); } }
LRESULT CALLBACK CFindCmd::_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CFindCmd* pThis = (CFindCmd*)GetWindowLongPtr(hwnd, 0); LRESULT lRes = 0; switch (uMsg) { case WM_DESTROY: SetWindowLong(hwnd, 0, 0); // make sure we don't deref pThis
break;
case WM_DF_FSNOTIFY: { LPITEMIDLIST *ppidl; LONG lEvent; LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent); if (pshcnl) { if (pThis) pThis->_OnChangeNotify(lEvent, ppidl); SHChangeNotification_Unlock(pshcnl); } } break; case WM_DF_SEARCHPROGRESS: pThis->_PTN_SearchProgress(); pThis->Release(); break;
case WM_DF_ASYNCPROGRESS: pThis->_PTN_AsyncProgress((int)wParam, (DWORD)lParam); pThis->Release(); break;
case WM_DF_SEARCHSTART: pThis->_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_START); pThis->_SetEmptyText(IDS_FINDVIEWEMPTYBUSY); pThis->Release(); break;
case WM_DF_SEARCHCOMPLETE: pThis->_PTN_SearchComplete((HRESULT)wParam, (BOOL)lParam); pThis->Release(); break;
default: lRes = ::DefWindowProc(hwnd, uMsg, wParam, lParam); break; } return lRes; }
// test to see if the view is in a mode where many items are displayed
BOOL LotsOfItemsInView(IUnknown *punkSite) { BOOL bLotsOfItemsInView = FALSE;
IFolderView * pfv; HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv)); if (SUCCEEDED(hr)) { UINT uViewMode; bLotsOfItemsInView = SUCCEEDED(pfv->GetCurrentViewMode(&uViewMode)) && ((FVM_ICON == uViewMode) || (FVM_SMALLICON == uViewMode)); pfv->Release(); } return bLotsOfItemsInView; }
void CFindCmd::_DoSearch(IFindEnum *penum) { BOOL fAbort = FALSE;
BOOL bLotsOfItems = LotsOfItemsInView(_execData.psfv);
EnterCriticalSection(&_csThread);
// previous thread might have exited but we're still processing search complete message
if (_cExecInProgress > 1) Sleep(1000); // give it a chance to finish
_updateParams.hdpa = NULL; _updateParams.fFilesAdded = FALSE; _updateParams.fDirChanged = FALSE; _updateParams.fUpdatePosted = FALSE;
_PostMessage(WM_DF_SEARCHSTART, 0, 0);
// Now see if this is an Sync or an Async version of the search...
HRESULT hr = S_OK;
BOOL fQueryIsAsync = penum->FQueryIsAsync(); if (fQueryIsAsync) { DBCOUNTITEM dwTotalAsync; BOOL fDone; int nPercentComplete; while (S_OK == (hr = penum->GetAsyncCount(&dwTotalAsync, &nPercentComplete, &fDone))) { if (!_fContinue) { fAbort = TRUE; break; }
_PostMessage(WM_DF_ASYNCPROGRESS, (WPARAM)nPercentComplete, (LPARAM)dwTotalAsync);
// If we are done we can simply let the ending callback tell of the new count...
if (fDone) break;
// sleep .3 or 1.5 sec
Sleep(bLotsOfItems ? 1500 : 300); // wait between looking again...
} }
if (!fQueryIsAsync || (fQueryIsAsync == DF_QUERYISMIXED)) { int state, cItemsSearched = 0, cFoldersSearched = 0, cFoldersSearchedPrev = 0;
_updateParams.hdpa = _hdpaItemsToAdd1; // Assume first one now...
_updateParams.dwTimeLastNotify = GetTickCount();
LPITEMIDLIST pidl; while (S_OK == (hr = penum->Next(&pidl, &cItemsSearched, &cFoldersSearched, &_fContinue, &state))) { if (state == GNF_DONE) break; // no more
if (!_fContinue) { fAbort = TRUE; break; }
// See if we should abort
if (state == GNF_MATCH) { EnterCriticalSection(&_updateParams.csSearch); DPA_AppendPtr(_updateParams.hdpa, pidl); _updateParams.fFilesAdded = TRUE; LeaveCriticalSection(&_updateParams.csSearch); }
if (cFoldersSearchedPrev != cFoldersSearched) { _updateParams.fDirChanged = TRUE; cFoldersSearchedPrev = cFoldersSearched; }
if (!_updateParams.fUpdatePosted && (_updateParams.fDirChanged || _updateParams.fFilesAdded)) { if ((GetTickCount() - _updateParams.dwTimeLastNotify) > 200) { _updateParams.fUpdatePosted = TRUE; _PostMessage(WM_DF_SEARCHPROGRESS, 0, 0); } } }
_PostMessage(WM_DF_SEARCHPROGRESS, 0, 0); }
if (hr != S_OK) { fAbort = TRUE; }
_PostMessage(WM_DF_SEARCHCOMPLETE, (WPARAM)hr, (LPARAM)fAbort);
LeaveCriticalSection(&_csThread); }
DWORD CALLBACK CFindCmd::_ThreadProc(void *pv) { THREAD_PARAMS *pParams = (THREAD_PARAMS *)pv; pParams->pThis->_DoSearch(pParams->penum); _FreeThreadParams(pParams); return 0; }
HRESULT CFindCmd::_Cancel() { _ClearDeferUpdateDirList();
if (DSA_GetItemCount(_hdsaConstraints) == 0) { _fContinue = FALSE; // Cancel current query if we have a null paramter collection
return S_OK; }
return E_FAIL; }
HRESULT CFindCmd::_Init(THREAD_PARAMS **ppParams, int iCol, LPCITEMIDLIST pidlUpdate) { *ppParams = new THREAD_PARAMS; if (NULL == *ppParams) return E_OUTOFMEMORY;
// Clear any previous registrations...
SHChangeNotifyDeregisterWindow(_updateParams.hwndThreadNotify);
// Prepare to execute the query
IFindFilter *pfilter; HRESULT hr = _execData.pff->GetFindFilter(&pfilter); if (SUCCEEDED(hr)) { // We do not need to update the filter if this is done as part of an FSNOTIFY or a Sort...
if ((iCol >= 0) || pidlUpdate || SUCCEEDED(hr = _UpdateFilter(pfilter))) { _execData.szProgressText[0] = 0;
pfilter->DeclareFSNotifyInterest(_updateParams.hwndThreadNotify, WM_DF_FSNOTIFY); pfilter->GetStatusMessageIndex(0, &_uStatusMsgIndex);
DWORD dwFlags; hr = pfilter->PrepareToEnumObjects(_GetWindow(), &dwFlags); if (SUCCEEDED(hr)) { hr = pfilter->EnumObjects(_execData.psf, pidlUpdate, dwFlags, iCol, _execData.szProgressText, SAFECAST(this, IRowsetWatchNotify*), &(*ppParams)->penum); } } pfilter->Release(); }
// Fill in the exec params
(*ppParams)->pThis = this; AddRef(); // ExecParams_Free will release this interface addref...
if (FAILED(hr)) { _FreeThreadParams(*ppParams); *ppParams = NULL; }
return hr; }
HRESULT CFindCmd::_FreeThreadParams(THREAD_PARAMS *pParams) { if (!pParams) return S_OK;
// Don't use atomic release as this a pointer to a class not an interface.
CFindCmd *pThis = pParams->pThis; pParams->pThis = NULL; pThis->Release();
ATOMICRELEASE(pParams->penum);
delete pParams; return S_OK; }
HRESULT CFindCmd::_ExecData_Release() { ATOMICRELEASE(_execData.psf); ATOMICRELEASE(_execData.psfv); if (_execData.pff) _execData.pff->SetControllerNotifyObject(NULL); // release back pointer to us...
ATOMICRELEASE(_execData.pff); _cExecInProgress = 0; // we must be in process of shutting down at least...
return S_OK; }
HRESULT CFindCmd::_EnsureResultsViewIsCurrent(IUnknown *punk) { HRESULT hr = E_FAIL; LPITEMIDLIST pidlFolder; if (S_OK == SHGetIDListFromUnk(punk, &pidlFolder)) { LPITEMIDLIST pidl; if (SUCCEEDED(_GetSearchIDList(&pidl))) { if (ILIsEqual(pidlFolder, pidl)) hr = S_OK; ILFree(pidl); } ILFree(pidlFolder); } return hr; }
// the search results view callback proffeerd itself and we can use that
// to get a hold of defview and can program it
HRESULT CFindCmd::_GetShellView(REFIID riid, void **ppv) { return IUnknown_QueryService(_punkSite, SID_DocFindFolder, riid, ppv); }
HRESULT CFindCmd::_ExecData_Init() { _ExecData_Release();
IFolderView *pfv; HRESULT hr = _GetShellView(IID_PPV_ARG(IFolderView, &pfv)); if (SUCCEEDED(hr)) { IShellFolder *psf; hr = pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { IFindFolder *pff; hr = psf->QueryInterface(IID_PPV_ARG(IFindFolder, &pff)); if (SUCCEEDED(hr)) { hr = _EnsureResultsViewIsCurrent(psf); if (SUCCEEDED(hr)) { IShellFolderView *psfv; hr = pfv->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv)); if (SUCCEEDED(hr)) { IUnknown_Set((IUnknown **)&_execData.pff, pff); IUnknown_Set((IUnknown **)&_execData.psf, psf); IUnknown_Set((IUnknown **)&_execData.psfv, psfv); _execData.pff->SetControllerNotifyObject(SAFECAST(this, IFindControllerNotify*)); psfv->Release(); } } pff->Release(); } psf->Release(); } pfv->Release(); }
if (FAILED(hr)) _ExecData_Release(); else SetShellFolder(_execData.psf); return hr; }
BOOL CFindCmd::_SetupBrowserCP() { if (!_dwCookie) { _cwbe.SetOwner(this); // make sure our owner is set...
// register ourself with the Defview to get any events that they may generate...
IServiceProvider *pspTLB; HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IServiceProvider, &pspTLB)); if (SUCCEEDED(hr)) { IConnectionPointContainer *pcpc; hr = pspTLB->QueryService(IID_IExpDispSupport, IID_PPV_ARG(IConnectionPointContainer, &pcpc)); if (SUCCEEDED(hr)) { hr = ConnectToConnectionPoint(SAFECAST(&_cwbe, DWebBrowserEvents*), DIID_DWebBrowserEvents2, TRUE, pcpc, &_dwCookie, &_pcpBrowser); pcpc->Release(); } pspTLB->Release(); } }
if (_dwCookie) _cwbe.SetWaiting(TRUE);
return _dwCookie ? TRUE : FALSE; }
HRESULT CFindCmd::_Start(BOOL fNavigateIfFail, int iCol, LPCITEMIDLIST pidlUpdate) { if (_cExecInProgress) return E_UNEXPECTED;
if (!_hdpaItemsToAdd1) { _hdpaItemsToAdd1 = DPA_CreateEx(64, GetProcessHeap()); if (!_hdpaItemsToAdd1) return E_OUTOFMEMORY; }
if (!_hdpaItemsToAdd2) { _hdpaItemsToAdd2 = DPA_CreateEx(64, GetProcessHeap()); if (!_hdpaItemsToAdd2) return E_OUTOFMEMORY; }
if (!_updateParams.hwndThreadNotify) { _updateParams.hwndThreadNotify = SHCreateWorkerWindow(_WndProc, NULL, 0, 0, 0, this); if (!_updateParams.hwndThreadNotify) return E_OUTOFMEMORY; }
HRESULT hr = _ExecData_Init(); if (FAILED(hr)) { if (fNavigateIfFail) { if (_SetupBrowserCP()) NavigateToSearchResults(); } // Return S_False so that when we check if this succeeded in finddlg, we wee that it
// did, and therefore let the animation run. If we return a failure code here, we
// will stop the animation. This will only hapen when we are navigating to the search
// results as well as starting the search.
return S_FALSE; }
THREAD_PARAMS *ptp; hr = _Init(&ptp, iCol, pidlUpdate); if (SUCCEEDED(hr)) { // See if we should be saving away the selection...
if (iCol >= 0) _execData.pff->RememberSelectedItems();
// If this is an update then we need to remember our IDList else clear list...
if (pidlUpdate) { _pidlUpdate = ILClone(pidlUpdate); } else { _Clear(); // tell defview to delete everything
}
_execData.pff->SetAsyncEnum(ptp->penum);
// Start the query
_cExecInProgress++; _fContinue = TRUE; _fNew = FALSE;
if (SHCreateThread(_ThreadProc, ptp, CTF_COINIT, NULL)) { hr = S_OK; } else { _cExecInProgress--; _FreeThreadParams(ptp); _SetEmptyText(IDS_FINDVIEWEMPTY); } } else hr = _SetLastError(hr);
return hr; }
HRESULT CFindCmd::_SetLastError(HRESULT hr) { if (HRESULT_FACILITY(hr) == FACILITY_SEARCHCOMMAND) { _hrLastError = hr; hr = S_FALSE; // Don't error out script...
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_ERROR); } return hr; }
STDMETHODIMP CFindCmd::Execute(VARIANT *RecordsAffected, VARIANT *Parameters, long Options) { if (Options == 0) return _Cancel();
_FixBrokenTypes();
return _Start(TRUE, -1, NULL); }
// IConnectionPointContainer
STDMETHODIMP CFindCmd::EnumConnectionPoints(IEnumConnectionPoints **ppEnum) { return CreateInstance_IEnumConnectionPoints(ppEnum, 1, _cpEvents.CastToIConnectionPoint()); }
STDMETHODIMP CFindCmd::FindConnectionPoint(REFIID iid, IConnectionPoint **ppCP) { if (IsEqualIID(iid, DIID_DSearchCommandEvents) || IsEqualIID(iid, IID_IDispatch)) { *ppCP = _cpEvents.CastToIConnectionPoint(); } else { *ppCP = NULL; return E_NOINTERFACE; }
(*ppCP)->AddRef(); return S_OK; }
// IProvideClassInfo2 methods
STDMETHODIMP CFindCmd::GetClassInfo(ITypeInfo **ppTI) { return GetTypeInfoFromLibId(0, LIBID_Shell32, 1, 0, CLSID_DocFindCommand, ppTI); }
STDMETHODIMP CFindCmd::GetGUID(DWORD dwGuidKind, GUID *pGUID) { if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID) { *pGUID = DIID_DSearchCommandEvents; return S_OK; } *pGUID = GUID_NULL; return E_FAIL; }
STDMETHODIMP CFindCmd::SetSite(IUnknown *punkSite) { if (!punkSite) { if (!_cExecInProgress) { _ExecData_Release(); } _fContinue = FALSE; // Cancel existing queries
// See if we have a connection point... If so unadvise now...
if (_dwCookie) { _pcpBrowser->Unadvise(_dwCookie); ATOMICRELEASE(_pcpBrowser); _dwCookie = 0; }
// Bug #199671
// Trident won't call UnAdvise and they except ActiveX Controls
// to use IOleControl::Close() to do their own UnAdvise, and hope
// nobody will need events after that. I don't impl IOleControl so
// we need to do the same thing during IObjectWithSite::SetSite(NULL)
// and hope someone won't want to reparent us. This is awkward but
// saves Trident some perf so we will tolerate it.
EVAL(SUCCEEDED(_cpEvents.UnadviseAll())); }
return CObjectWithSite::SetSite(punkSite); }
void CFindCmd::_SelectResults() { if (_execData.psfv) { // If there are any items...
UINT cItems = 0; if (SUCCEEDED(_execData.psfv->GetObjectCount(&cItems)) && cItems > 0) { IShellView* psv; if (SUCCEEDED(_execData.psfv->QueryInterface(IID_PPV_ARG(IShellView, &psv)))) { // If none are selected (don't want to rip the user's selection out of his hand)...
UINT cSelected = 0; if (SUCCEEDED(_execData.psfv->GetSelectedCount(&cSelected)) && cSelected == 0) { // Retrieve the pidl for the first item in the list...
LPITEMIDLIST pidlFirst = NULL; if (SUCCEEDED(_execData.psfv->GetObject(&pidlFirst, 0))) { // Give it the focus
psv->SelectItem(pidlFirst, SVSI_FOCUSED | SVSI_ENSUREVISIBLE); } }
// Activate the view.
psv->UIActivate(SVUIA_ACTIVATE_FOCUS); psv->Release(); } } } }
STDMETHODIMP CFindCmd::ClearResults(void) { HRESULT hr = _Clear();
if (SUCCEEDED(hr)) { _fNew = TRUE; _SetEmptyText(IDS_FINDVIEWEMPTYINIT); }
return hr ; }
HRESULT CFindCmd::_Clear() { // Tell defview to delete everything.
if (_execData.psfv) { UINT u; _execData.psfv->RemoveObject(NULL, &u); }
// And cleanup our folderList
if (_execData.pff) { _execData.pff->ClearItemList(); _execData.pff->ClearFolderList(); } return S_OK; }
HRESULT CFindCmd::_SetEmptyText(UINT nIDEmptyText) { IShellFolderViewCB *psfvcb; HRESULT hr = IUnknown_QueryService(_execData.psfv, SID_ShellFolderViewCB, IID_PPV_ARG(IShellFolderViewCB, &psfvcb)); if (SUCCEEDED(hr)) { TCHAR szEmptyText[128]; LoadString(HINST_THISDLL, nIDEmptyText, szEmptyText, ARRAYSIZE(szEmptyText));
hr = psfvcb->MessageSFVCB(SFVM_SETEMPTYTEXT, 0, (LPARAM)szEmptyText); psfvcb->Release(); } return hr; }
HRESULT CFindCmd::_GetSearchIDList(LPITEMIDLIST *ppidl) { CLSID clsid; _SearchResultsCLSID(&clsid); return ILCreateFromCLSID(clsid, ppidl); }
STDMETHODIMP CFindCmd::NavigateToSearchResults(void) { IShellBrowser *psb; HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = _GetSearchIDList(&pidl); if (SUCCEEDED(hr)) { hr = psb->BrowseObject(pidl, SBSP_SAMEBROWSER | SBSP_ABSOLUTE | SBSP_WRITENOHISTORY); ILFree(pidl); } psb->Release(); } return hr; }
IUnknown* CFindCmd::_GetObjectToPersist() { IOleObject *pole = NULL; IShellView *psv; HRESULT hr = _GetShellView(IID_PPV_ARG(IShellView, &psv)); if (SUCCEEDED(hr)) { psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IOleObject, &pole)); psv->Release(); }
return (IUnknown *)pole; }
void CFindCmd::_PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { AddRef(); // to be released after processing of the message bellow
if (!PostMessage(_updateParams.hwndThreadNotify, uMsg, wParam, lParam)) { Release(); } }
HWND CFindCmd::_GetWindow() { HWND hwnd; return SUCCEEDED(IUnknown_GetWindow(_punkSite, &hwnd)) ? hwnd : NULL; }
STDMETHODIMP CFindCmd::SaveSearch(void) { IFindFilter *pfilter; HRESULT hr = _execData.pff->GetFindFilter(&pfilter); if (SUCCEEDED(hr)) { IShellView *psv; hr = _GetShellView(IID_PPV_ARG(IShellView, &psv)); if (SUCCEEDED(hr)) { IUnknown* punk = _GetObjectToPersist(); // NULL is OK
_execData.pff->Save(pfilter, _GetWindow(), &_dfbsi, psv, punk);
ATOMICRELEASE(punk);
psv->Release(); } pfilter->Release(); }
return hr; }
STDMETHODIMP CFindCmd::RestoreSearch(void) { // let script know that a restore happened...
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_RESTORE); return S_OK; }
STDMETHODIMP CFindCmd::StopSearch(void) { if (_cExecInProgress) return _Cancel();
return S_OK; }
STDMETHODIMP CFindCmd::GetItemCount(UINT *pcItems) { if (_execData.psfv) { return _execData.psfv->GetObjectCount(pcItems); } return E_FAIL; }
STDMETHODIMP CFindCmd::SetItemCount(UINT cItems) { if (_execData.psfv) { return _execData.psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL); } return E_FAIL; }
STDMETHODIMP CFindCmd::ViewDestroyed() { _ExecData_Release(); return S_OK; }
STDMETHODIMP CFindCmd::get_ProgressText(BSTR *pbs) {
*pbs = SysAllocStringT(_szProgressText); return *pbs ? S_OK : E_OUTOFMEMORY; }
//------ error string mappings ------//
static const UINT error_strings[] = { SCEE_CONSTRAINT, IDS_DOCFIND_CONSTRAINT, SCEE_PATHNOTFOUND, IDS_DOCFIND_PATHNOTFOUND, SCEE_INDEXSEARCH, IDS_DOCFIND_SCOPEERROR, SCEE_CASESENINDEX, IDS_DOCFIND_CI_NOT_CASE_SEN, };
STDMETHODIMP CFindCmd::GetErrorInfo(BSTR *pbs, int *phr) { int nCode = HRESULT_CODE(_hrLastError); UINT uSeverity = HRESULT_SEVERITY(_hrLastError);
if (phr) *phr = nCode; if (pbs) { UINT nIDString = 0; *pbs = NULL;
for(int i = 0; i < ARRAYSIZE(error_strings); i += 2) { if (error_strings[i] == (UINT)nCode) { nIDString = error_strings[i+1]; break ; } }
if (nIDString) { WCHAR wszMsg[MAX_PATH]; EVAL(LoadStringW(HINST_THISDLL, nIDString, wszMsg, ARRAYSIZE(wszMsg))); *pbs = SysAllocString(wszMsg); } else *pbs = SysAllocString(L""); } return S_OK; }
STDMETHODIMP CFindCmd::SearchFor(int iFor) { if (SCE_SEARCHFORFILES == iFor) { _clsidResults = CLSID_DocFindFolder; } else if (SCE_SEARCHFORCOMPUTERS == iFor) { _clsidResults = CLSID_ComputerFindFolder; } return S_OK; }
STDMETHODIMP CFindCmd::GetScopeInfo(BSTR bsScope, int *pdwScopeInfo) { *pdwScopeInfo = 0; return E_NOTIMPL; }
STDMETHODIMP CFindCmd::RestoreSavedSearch(VARIANT *pvarFile) { if (pvarFile && pvarFile->vt != VT_EMPTY) { LPITEMIDLIST pidl = VariantToIDList(pvarFile); if (pidl) { ILFree(_pidlRestore); _pidlRestore = pidl ; } }
if (_pidlRestore) { IShellView *psv; HRESULT hr = _GetShellView(IID_PPV_ARG(IShellView, &psv)); if (SUCCEEDED(hr)) { psv->Release();
if (SUCCEEDED(_ExecData_Init())) { _execData.pff->RestoreSearchFromSaveFile(_pidlRestore, _execData.psfv); _cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_RESTORE); ILFree(_pidlRestore); _pidlRestore = NULL; } } else if (!_fDeferRestoreTried) { // appears to be race condition to load
TraceMsg(TF_WARNING, "CFindCmd::MaybeRestoreSearch - _GetShellView failed..."); _fDeferRestore = TRUE; if (!_SetupBrowserCP()) _fDeferRestore = FALSE; } } return S_OK; }
STDMETHODIMP CFindCmd::OnChange(IRowset *prowset, DBWATCHNOTIFY eChangeReason) { _fAsyncNotifyReceived = TRUE; return S_OK; }
STDMETHODIMP CFindCmd::DoSortOnColumn(UINT iCol, BOOL fSameCol) { IFindEnum *pdfEnumAsync;
if (S_OK == _execData.pff->GetAsyncEnum(&pdfEnumAsync)) { // If the search is still running we will restart with the other column else we
// will make sure all of the items have been cached and let the default processing happen
if (!fSameCol && _cExecInProgress) { // We should try to sort on the right column...
_Start(FALSE, iCol, NULL); return S_FALSE; // tell system to not do default processing.
}
_execData.pff->CacheAllAsyncItems(); } return S_OK; // let it do default processing.
}
// Implemention of our IDispatch to hookup to the top level browsers connnection point...
STDMETHODIMP CFindCmd::CWBEvents2::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENTMULTI(CFindCmd::CWBEvents2, IDispatch, DWebBrowserEvents2), QITABENTMULTI2(CFindCmd::CWBEvents2, DIID_DWebBrowserEvents2, DWebBrowserEvents2), QITABENTMULTI2(CFindCmd::CWBEvents2, DIID_DWebBrowserEvents, DWebBrowserEvents), { 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP CFindCmd::CWBEvents2::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { if (_fWaitingForNavigate) { if ((dispidMember == DISPID_NAVIGATECOMPLETE) || (dispidMember == DISPID_DOCUMENTCOMPLETE)) { // Assume this is ours... Should maybe check parameters...
_fWaitingForNavigate = FALSE;
// Now see if it is a case where we are to restore the search...
if (_pcdfc->_fDeferRestore) { _pcdfc->_fDeferRestore = FALSE; _pcdfc->_fDeferRestoreTried = TRUE; _pcdfc->RestoreSavedSearch(NULL); } else return _pcdfc->_Start(FALSE, -1, NULL); } } return S_OK; }
#define MAX_DEFAULT_VALUE 40 // Longest of all of the below pszDefaultValueMatch strings (plus slop)
#define MAX_KEY_PH_NAME 70 // "CLSID\{GUID}\PersistentHandler" (plus slop)
const TYPE_FIX_ENTRY g_tfeTextTypes[] = { { TEXT(".rtf"), NULL, NULL }, };
const TYPE_FIX_ENTRY g_tfeNullTypes[] = { { TEXT(".mdb"), TEXT("Access.Application.10"), TEXT("{73A4C9C1-D68D-11D0-98BF-00A0C90DC8D9}") }, { TEXT(".msg"), TEXT("msgfile"), NULL }, { TEXT(".sc2"), TEXT("SchedulePlus.Application.7"), TEXT("{0482E074-C5B7-101A-82E0-08002B36A333}") }, { TEXT(".wll"), TEXT("Word.Addin.8"), NULL }, };
//
// rtf is listed twice, once above for TextTypes (to fix when office
// un-installed) and once here as an OfficeType (to fix when office
// is re-installed). Uninstalled = TextFilter, Reinstalled = OfficeFilter
//
const TYPE_FIX_ENTRY g_tfeOfficeTypes[] = { { TEXT(".rtf"), TEXT("Word.RTF.8"), TEXT("{00020906-0000-0000-C000-000000000046}") }, { TEXT(".doc"), TEXT("Word.Document.8"), TEXT("{00020906-0000-0000-C000-000000000046}") }, { TEXT(".dot"), TEXT("Word.Template.8"), TEXT("{00020906-0000-0000-C000-000000000046}") }, { TEXT(".pot"), TEXT("PowerPoint.Template.8"), TEXT("{64818D11-4F9B-11CF-86EA-00AA00B929E8}") }, { TEXT(".pps"), TEXT("PowerPoint.SlideShow.8"), TEXT("{64818D10-4F9B-11CF-86EA-00AA00B929E8}") }, { TEXT(".ppt"), TEXT("PowerPoint.Show.8"), TEXT("{64818D10-4F9B-11CF-86EA-00AA00B929E8}") }, { TEXT(".rtf"), TEXT("Word.RTF.8"), TEXT("{00020906-0000-0000-C000-000000000046}") }, { TEXT(".xlb"), TEXT("Excel.Sheet.8"), TEXT("{00020820-0000-0000-C000-000000000046}") }, { TEXT(".xlc"), TEXT("Excel.Chart.8"), TEXT("{00020821-0000-0000-C000-000000000046}") }, { TEXT(".xls"), TEXT("Excel.Sheet.8"), TEXT("{00020820-0000-0000-C000-000000000046}") }, { TEXT(".xlt"), TEXT("Excel.Template"), TEXT("{00020820-0000-0000-C000-000000000046}") }, };
const TYPE_FIX_ENTRY g_tfeHtmlTypes[] = { { TEXT(".asp"), TEXT("aspfile"), NULL }, { TEXT(".htx"), TEXT("htxfile"), NULL }, };
BOOL CFindCmd::_FixPersistHandler(LPCTSTR pszBase, LPCTSTR pszDefaultHandler) { TCHAR szPHName[MAX_KEY_PH_NAME]; LONG lr; HKEY hkeyPH; HKEY hkeyBase;
wnsprintf(szPHName,ARRAYSIZE(szPHName), TEXT("%s\\PersistentHandler"), pszBase);
lr = RegOpenKey(HKEY_CLASSES_ROOT, szPHName, &hkeyPH); if (lr == ERROR_SUCCESS) { // We found an existing PersistHandler key, leave it alone
RegCloseKey(hkeyPH); return TRUE; }
lr = RegOpenKey(HKEY_CLASSES_ROOT, pszBase, &hkeyBase); if (lr != ERROR_SUCCESS) { // We didn't find the base key (normally "CLSID\\{GUID}"), get out
return FALSE; } RegCloseKey(hkeyBase);
lr = RegCreateKey(HKEY_CLASSES_ROOT, szPHName, &hkeyPH); if (lr != ERROR_SUCCESS) { // We couldn't create the ...\PersistHandler key, get out
return FALSE; }
// Able to create the ...\PersistHandler key, write out the default handler
lr = RegSetValue(hkeyPH, NULL, REG_SZ, pszDefaultHandler, lstrlen(pszDefaultHandler)); RegCloseKey(hkeyPH);
// Success if write succeeded
return (lr == ERROR_SUCCESS); }
void CFindCmd::_ProcessTypes( const TYPE_FIX_ENTRY *ptfeTypes, UINT cTypes, TCHAR *pszClass) { UINT iType; LONG lr; HKEY hkeyType;
for (iType = 0; iType < cTypes; iType++) { lr = RegOpenKey(HKEY_CLASSES_ROOT, ptfeTypes[iType].pszDotType, &hkeyType); if (lr == ERROR_SUCCESS) { //
// If it has a default value to match, repair that (if it exists).
// If there is no default value to match, just repair the .foo type
//
if (ptfeTypes[iType].pszDefaultValueMatch) { TCHAR szDefaultValue[MAX_DEFAULT_VALUE]; LONG cb = sizeof(szDefaultValue); lr = RegQueryValue(hkeyType, NULL, szDefaultValue, &cb); if (lr == ERROR_SUCCESS) { if (lstrcmp(szDefaultValue,ptfeTypes[iType].pszDefaultValueMatch) == 0) { if (ptfeTypes[iType].pszGuid == NULL) { // Fix either the progid or the type, whichever we can
if (!_FixPersistHandler(ptfeTypes[iType].pszDefaultValueMatch,pszClass)) { _FixPersistHandler(ptfeTypes[iType].pszDotType,pszClass); } } else { // Fix the persist handler for the guid, since its specified
TCHAR szPHName[MAX_KEY_PH_NAME];
wnsprintf(szPHName, ARRAYSIZE(szPHName), TEXT("CLSID\\%s"), ptfeTypes[iType].pszGuid); _FixPersistHandler(szPHName, pszClass); } } } } else { _FixPersistHandler(ptfeTypes[iType].pszDotType, pszClass); } RegCloseKey(hkeyType); } else if (lr == ERROR_FILE_NOT_FOUND) { //
// .foo doesn't exist - this can happen because of bad un-install program
// Create .foo and .foo\PersistentHandler
//
lr = RegCreateKey(HKEY_CLASSES_ROOT, ptfeTypes[iType].pszDotType, &hkeyType); if (lr == ERROR_SUCCESS) { _FixPersistHandler(ptfeTypes[iType].pszDotType, pszClass); RegCloseKey(hkeyType); } } } }
void CFindCmd::_FixBrokenTypes(void) { _ProcessTypes(g_tfeNullTypes, ARRAYSIZE(g_tfeNullTypes), TEXT("{098f2470-bae0-11cd-b579-08002b30bfeb}")); _ProcessTypes(g_tfeTextTypes, ARRAYSIZE(g_tfeTextTypes), TEXT("{5e941d80-bf96-11cd-b579-08002b30bfeb}")); _ProcessTypes(g_tfeOfficeTypes, ARRAYSIZE(g_tfeOfficeTypes), TEXT("{98de59a0-d175-11cd-a7bd-00006b827d94}")); _ProcessTypes(g_tfeHtmlTypes, ARRAYSIZE(g_tfeHtmlTypes), TEXT("{eec97550-47a9-11cf-b952-00aa0051fe20}")); }
CFindConstraint::CFindConstraint(BSTR bstr, VARIANT var) : CImpIDispatch(LIBID_Shell32, 1, 0, IID_DFConstraint) { _cRef = 1; _bstr = bstr; _var = var; }
CFindConstraint::~CFindConstraint() { SysFreeString(_bstr); VariantClear(&_var); }
STDMETHODIMP CFindConstraint::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFindConstraint, DFConstraint), // IID_DFConstraint
QITABENTMULTI(CFindConstraint, IDispatch, DFConstraint), // IID_IDispatch
{ 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CFindConstraint::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CFindConstraint::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; }
STDMETHODIMP CFindConstraint::GetTypeInfoCount(UINT * pctinfo) { return CImpIDispatch::GetTypeInfoCount(pctinfo); }
STDMETHODIMP CFindConstraint::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) { return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo); }
STDMETHODIMP CFindConstraint::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); }
STDMETHODIMP CFindConstraint::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr) { return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); }
STDMETHODIMP CFindConstraint::get_Name(BSTR *pbs) { *pbs = SysAllocString(_bstr); return *pbs? S_OK : E_OUTOFMEMORY; }
STDMETHODIMP CFindConstraint::get_Value(VARIANT *pvar) { VariantInit(pvar); return VariantCopy(pvar, &_var); }
|