2152 lines
66 KiB
C++
2152 lines
66 KiB
C++
#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);
|
|
}
|