Windows2000/private/shell/shell32/dfcmd.cpp
2020-09-30 17:12:32 +02:00

2172 lines
66 KiB
C++

#include "shellprv.h"
#pragma hdrstop
#include "dspsprt.h"
#include "dfcmd.h"
#include "docfindx.h"
#include "docfind.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"
extern "C"
{
#include "views.h"
}
// CDFCommand class
#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)
#define WM_DF_ASYNCTOSYNC (WM_USER + 47)
STDAPI CDocFindCommand_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvObj);
STDAPI CDocFindFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
typedef struct
{
BSTR szName;
VARIANT vValue;
} DFCommandConstraint;
class CDFCommand : public ISearchCommandExt,
public CImpIDispatch,
public CObjectWithSite,
public CObjectSafety,
public IConnectionPointContainer,
public IProvideClassInfo2,
public CShellFolderData,
public IRowsetWatchNotify,
public IDocFindControllerNotify
{
friend HRESULT CDocFindCommand_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvObj);
public:
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (REFIID riid, void **ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// *** IDispatch methods ***
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 methods ***
STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints **ppEnum);
STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint **ppCP);
// *** IProvideClassInfo methods ***
STDMETHOD(GetClassInfo)(ITypeInfo **ppTI);
// *** IProvideClassInfo2 methods ***
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);
// *** IDocFindControllerNotify ***
STDMETHOD(DoSortOnColumn)(UINT iCol, BOOL fSameCol);
STDMETHOD(StopSearch)(void);
STDMETHOD(GetItemCount)(UINT *pcItems);
STDMETHOD(SetItemCount)(UINT cItems);
STDMETHOD(ViewDestroyed)();
private:
CDFCommand();
~CDFCommand();
HRESULT Init(void);
HRESULT _GetSearchIDList(LPITEMIDLIST *ppidl);
HRESULT _SetEmptyText(UINT nID);
void _SelectResults();
STDMETHODIMP _Clear();
struct ExecThreadParams {
CDFCommand *pThis;
IDFEnum *pdfenum;
};
struct DeferUpdateDir {
struct DeferUpdateDir *pdudNext;
LPITEMIDLIST pidl;
BOOL fRecurse;
};
// Internal class to handle notifications from top level browser
class CWBEvents2 : public DWebBrowserEvents
{
public:
STDMETHOD(QueryInterface) (REFIID riid, void **ppvObject);
STDMETHOD_(ULONG, AddRef)(void)
{
return _pcdfc->AddRef();
}
STDMETHOD_(ULONG, Release)(void)
{
return _pcdfc->Release();
}
// *** (DwebBrowserEvents)IDispatch methods ***
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(CDFCommand *pcdfc)
{
_pcdfc = pcdfc;
} // Don't addref as part of larger object... }
void SetWaiting(BOOL fWait)
{
_fWaitingForNavigate = fWait;
}
protected:
// Internal variables...
CDFCommand *_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;
unsigned long _dwCookie; // Cookie returned by Advise
// Internal functions...
STDMETHODIMP _UpdateFilter(IDocFindFileFilter *pdfff);
void _ClearConstraints();
static DWORD CALLBACK _Execute_ThreadProc(LPVOID lpThreadParams);
STDMETHODIMP _Execute_Start(BOOL fNavigateIfFail, int iCol, LPITEMIDLIST pidlUpdate);
STDMETHODIMP _Execute_Cancel();
STDMETHODIMP _Execute_Init(ExecThreadParams **ppParams, int iCol, LPITEMIDLIST pidlUpdate);
static HRESULT _ExecParams_Free(ExecThreadParams *pExecThreadParams);
STDMETHODIMP _ExecData_Init();
HRESULT _ExecData_ValidateRightSearchResults(IShellFolder *psf);
STDMETHODIMP _ExecData_Release();
BOOL _Execute_SetupBrowserCP();
void cdecl _NotifyProgressText(UINT ids, ...);
static LRESULT CALLBACK s_ThreadNotifyWndProc(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 _handleFSChange(LONG code, LPITEMIDLIST *ppidl);
void _DeferHandleUpdateDir(LPITEMIDLIST pidl, BOOL bRecurse);
void _ClearDeferUpdateDirList();
HRESULT _SetLastError(HRESULT hr);
inline BOOL _SearchForComputer() { return (_iSearchFor == 1); };
IUnknown* _GetObjectToPersist();
HRESULT _ForcedUnadvise(void);
void _PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
// These are the things that the second thread will use during it's processing...
struct UpdateParams {
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;
IDocFindFolder *pdfFolder;
TCHAR szProgressText[MAX_PATH];
} _execData;
public:
private:
LONG _cRef;
HDSA _hdsaConstraints;
DWORD _cExecInProgress;
BITBOOL _fAsyncNotifyReceived : 1;
BITBOOL _fDeferRestore : 1;
BITBOOL _fDeferRestoreTried : 1;
BOOL _fContinue;
BOOL _fNew;
CConnectionPoint _cpEvents;
IDefViewFrame *_pdvResultsTargetFrame;
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 DeferUpdateDir *_pdudFirst; // Do we have any defered update dirs?
HRESULT _hrLastError; // the last error reported.
int _iSearchFor; // Searching for file or printers?
UINT _uStatusMsgIndex; // Files or computers found...
CRITICAL_SECTION _csThread;
DFBSAVEINFO _dfbsi;
};
class CDFConstraint : public DFConstraint,
public CImpIDispatch
{
friend HRESULT CDFConstraint_CreateInstance(BSTR bName, VARIANT vValue);
public:
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (REFIID riid, void **ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// *** IDispatch methods ***
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);
CDFConstraint(BSTR bstr, VARIANT var);
private:
~CDFConstraint();
LONG _cRef;
BSTR _bstr;
VARIANT _var;
};
// CDFCommand ctor/dtor implementation
CDFCommand::CDFCommand()
: CImpIDispatch(&LIBID_Shell32, 1, 0, &IID_ISearchCommandExt)
{
_cRef = 1;
_fAsyncNotifyReceived = 0;
_fContinue = TRUE;
ASSERT(NULL == _pidlRestore);
ASSERT(_cExecInProgress == 0);
InitializeCriticalSection(&_updateParams.csSearch);
InitializeCriticalSection(&_csThread);
_cpEvents.SetOwner((ISearchCommandExt *)this, &DIID_DSearchCommandEvents);
}
HRESULT CDFCommand::Init(void)
{
_hdsaConstraints = DSA_Create(sizeof(DFCommandConstraint), 4);
if (!_hdsaConstraints)
return E_OUTOFMEMORY;
return S_OK;
}
CDFCommand::~CDFCommand()
{
_ClearConstraints();
DSA_Destroy(_hdsaConstraints);
ATOMICRELEASE(_pdvResultsTargetFrame);
_ExecData_Release();
DeleteCriticalSection(&_updateParams.csSearch);
DeleteCriticalSection(&_csThread);
// Make sure we have removed all outstanding update dirs...
_ClearDeferUpdateDirList();
if (_updateParams.hwndThreadNotify) {
// make sure no outstanding fsnotifies registered.
SHChangeNotifyDeregisterWindow(_updateParams.hwndThreadNotify);
DestroyWindow(_updateParams.hwndThreadNotify);
}
if (_hdpaItemsToAdd1)
DPA_Destroy(_hdpaItemsToAdd1);
if (_hdpaItemsToAdd2)
DPA_Destroy(_hdpaItemsToAdd2);
ILFree(_pidlRestore);
}
// CDFParameter IUnknown implementation
STDMETHODIMP CDFCommand::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
static const QITAB qit[] = {
QITABENT(CDFCommand, ISearchCommandExt), // ISearchCommandExt
QITABENTMULTI(CDFCommand, IDispatch, ISearchCommandExt), //IID_IDispatch
QITABENT(CDFCommand, IProvideClassInfo2), //IID_IProvideClassInfo2
QITABENTMULTI(CDFCommand, IProvideClassInfo,IProvideClassInfo2), //IID_IProvideClassInfo
QITABENT(CDFCommand, IObjectWithSite), //IID_IOBjectWithSite
QITABENT(CDFCommand, IObjectSafety), // IID_IObjectSafety
QITABENT(CDFCommand, IConnectionPointContainer), // IID_IConnectionPointContainer
QITABENT(CDFCommand, ISearchCommandExt), // IID_ISearchCommandExt
QITABENT(CDFCommand, OLEDBSimpleProvider), // IID_IOLEDBSimpleProvider
#ifdef WINNT
QITABENT(CDFCommand, IRowsetWatchNotify), // IID_IRowsetWatchNotify
#endif
QITABENT(CDFCommand, IDocFindControllerNotify), // IID_IDocFindControllerNotify
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CDFCommand::AddRef()
{
InterlockedIncrement(&_cRef);
TraceMsg(TF_DOCFIND, "CDFCommand.AddRef %d", _cRef);
return _cRef;
}
STDMETHODIMP_(ULONG) CDFCommand::Release()
{
TraceMsg(TF_DOCFIND, "CDFCommand.Release %d", _cRef - 1);
if (InterlockedDecrement(&_cRef)) {
return _cRef;
}
delete this;
return 0;
}
// CDFCommand IDispatch implementation
STDMETHODIMP CDFCommand::GetTypeInfoCount(UINT * pctinfo)
{
return CImpIDispatch::GetTypeInfoCount(pctinfo);
}
STDMETHODIMP CDFCommand::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo * * pptinfo)
{
return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo);
}
STDMETHODIMP CDFCommand::GetIDsOfNames(REFIID riid, OLECHAR * * rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
{
return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
}
STDMETHODIMP CDFCommand::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);
}
// CDFCommand ADOCommand implementation
STDMETHODIMP CDFCommand::AddConstraint(BSTR szName, VARIANT vValue)
{
DFCommandConstraint dfcc;
HRESULT hr;
dfcc.szName = SysAllocString(szName);
if (dfcc.szName == NULL)
return E_OUTOFMEMORY;
VariantInit(&dfcc.vValue);
if (FAILED(hr = VariantCopy(&dfcc.vValue, &vValue)))
{
SysFreeString(dfcc.szName);
return hr;
}
if (DSA_InsertItem(_hdsaConstraints, DSA_APPEND, &dfcc) == DSA_ERR)
{
SysFreeString(dfcc.szName);
VariantClear(&dfcc.vValue);
return E_OUTOFMEMORY;
}
return S_OK;
}
STDMETHODIMP CDFCommand::GetNextConstraint(VARIANT_BOOL fReset, DFConstraint **ppdfc)
{
// Let them think we are at the end...
HRESULT hr;
IDocFindFileFilter *pdfff;
*ppdfc = NULL;
if (SUCCEEDED(hr = _execData.pdfFolder->GetDocFindFilter(&pdfff)))
{
BSTR bName;
VARIANT var;
VariantInit(&var);
VARIANT_BOOL fFound;
hr = pdfff->GetNextConstraint(fReset, &bName, &var, &fFound);
if (SUCCEEDED(hr))
{
if (!fFound)
{
// need a simple way to signal end list, how about an empty name string?
// BUGBUG:: maybe should find way to detect a NULL object?
bName = SysAllocString(L"");
}
CDFConstraint *pdfc = new CDFConstraint(bName, var);
if (pdfc)
{
hr = pdfc->QueryInterface(IID_DFConstraint, (void**)ppdfc);
pdfc->Release();
}
else
{
// error release stuff we allocated.
hr = E_OUTOFMEMORY;
SysFreeString(bName);
VariantClear(&var);
}
}
pdfff->Release();
}
return hr;
}
HRESULT CDFCommand::_UpdateFilter(IDocFindFileFilter *pdfff)
{
HRESULT hr;
DFCommandConstraint *pdfcc;
LONG cNumParams;
int iItem;
if (!pdfff)
return E_INVALIDARG;
hr = S_OK;
UINT uMode = SetErrorMode(SEM_FAILCRITICALERRORS);
pdfff->ResetFieldsToDefaults();
cNumParams = DSA_GetItemCount(_hdsaConstraints);
for (iItem = 0; iItem < cNumParams; iItem++)
{
pdfcc = (DFCommandConstraint *)DSA_GetItemPtr(_hdsaConstraints, iItem);
if (pdfcc)
{
hr = pdfff->UpdateField(pdfcc->szName, pdfcc->vValue);
}
}
// And clear out the constraint list...
_ClearConstraints();
SetErrorMode(uMode);
return hr;
}
void CDFCommand::_ClearConstraints()
{
DFCommandConstraint *pdfcc;
LONG cNumParams;
int iItem;
cNumParams = DSA_GetItemCount(_hdsaConstraints);
for (iItem = 0; iItem < cNumParams; iItem++)
{
pdfcc = (DFCommandConstraint *)DSA_GetItemPtr(_hdsaConstraints, iItem);
if (pdfcc)
{
SysFreeString(pdfcc->szName);
VariantClear(&pdfcc->vValue);
}
}
DSA_DeleteAllItems(_hdsaConstraints);
}
void cdecl CDFCommand::_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;
// a-msadek; needed only for BiDi Win95 loc
// Mirroring will take care of that over NT5 & BiDi Win98
if (g_bBiDiW95Loc)
{
_szProgressText[0] = _szProgressText[1] = TEXT('\t');
pszDst = &_szProgressText[2];
}
else
pszDst = &_szProgressText[0];
StrCpyN(pszDst, psz, ARRAYSIZE(_szProgressText) - 2);
LocalFree(psz);
}
else
{
_szProgressText[0] = TEXT('\0');
}
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_PROGRESSTEXT);
}
STDAPI CDocFindCommand_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvObj)
{
HRESULT hr;
*ppvObj = NULL;
CDFCommand *pdfCmd = new CDFCommand();
if (pdfCmd)
{
hr = pdfCmd->Init();
if (SUCCEEDED(hr))
hr = pdfCmd->QueryInterface(riid, ppvObj);
pdfCmd->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
void CDFCommand::_PTN_SearchProgress(void)
{
HRESULT hr = S_OK;
int iItem;
HDPA hdpa;
BOOL fDirChanged;
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;
fDirChanged = _updateParams.fDirChanged;
_updateParams.fDirChanged = FALSE;
LeaveCriticalSection(&_updateParams.csSearch);
int cItemsToAdd = DPA_GetPtrCount(hdpa);
int i;
int iItemStart;
LPITEMIDLIST pidl;
if (!_execData.pdfFolder)
return;
_execData.pdfFolder->GetItemCount(&iItem);
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)
{
// Now the harder part see if items in list, already if so we unmark the item
// for delete else if not there maybe add it...
// Note, we can merge this with the code in DocFindX, which once we are done
// will be removed...
int iLastMatch = -1;
int j;
LPSHELLFOLDER psfItem;
ESFItem *pesfi;
int cItems = iItem;
int iItemCheck;
for (i = 0; i < cItemsToAdd; i++)
{
pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, i);
psfItem = DocFind_GetObjectsIFolder(_execData.pdfFolder, NULL, pidl);
// Now see if the item is in the DPA
if (psfItem)
{
for (j = cItems - 1, iItemCheck = iLastMatch + 1; j > 0; j--)
{
if (iItemCheck >= cItems)
iItemCheck = 0;
_execData.pdfFolder->GetItem(j, &pesfi);
if (pesfi && DF_IFOLDER(pidl) == DF_IFOLDER(&pesfi->idl))
{
if (psfItem->CompareIDs(0, pidl, &pesfi->idl) == (HRESULT)0)
break;
}
}
if (j == 0)
{
// Not already in the list so add it...
if (SUCCEEDED(hr = _execData.pdfFolder->AddPidl(iItem, pidl, -1, NULL)))
iItem++;
}
else
{
// Item still there - remove possible delete flag...
pesfi->dwState &= ~CDFITEM_STATE_MAYBEDELETE;
iLastMatch = iItemCheck;
}
}
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 (i = 0; i < cItemsToAdd; i++)
{
if (SUCCEEDED(hr = _execData.pdfFolder->AddPidl(iItem,
pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, i), -1, NULL)))
iItem++;
ILFree(pidl); // The AddPidl does a clone of the pidl...
}
if (iItem >= iItemStart)
{
if (_execData.psfv)
{
hr = _execData.psfv->SetObjectCount(iItem, SFVSOC_NOSCROLL);
}
_execData.pdfFolder->SetItemsChangedSinceSort();
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_UPDATE);
}
if (_pListener)
{
_pListener->insertedRows(iItemStart, cItemsToAdd);
_pListener->rowsAvailable(iItemStart, cItemsToAdd);
}
}
}
else // _fContinue
{
for (i = 0; i < cItemsToAdd; i++)
{
pidl = (LPITEMIDLIST)DPA_FastGetPtr(hdpa, i);
ASSERT(pidl);
ILFree(pidl);
}
}
DPA_DeleteAllPtrs(hdpa);
}
if (fDirChanged)
{
_NotifyProgressText(IDS_SEARCHING, _execData.szProgressText);
}
}
_updateParams.dwTimeLastNotify = GetTickCount();
_updateParams.fUpdatePosted = FALSE;
}
void CDFCommand::_PTN_AsyncProgress(int nPercentComplete, DWORD cAsync)
{
if (!_execData.pdfFolder)
return;
// Async case try just setting the count...
_execData.pdfFolder->SetAsyncCount(cAsync);
if (_execData.psfv)
{
#ifdef CI_ROWSETNOTIFY
// ci no longer supports async notifications so we have to validate items
// all the time..
if (_fAsyncNotifyReceived)
#endif
{
// -1 for the first item means verify visible items only
_execData.pdfFolder->ValidateItems(-1, -1, FALSE);
#ifdef CI_ROWSETNOTIFY
_fAsyncNotifyReceived = FALSE;
#endif
}
_execData.psfv->SetObjectCount(cAsync, SFVSOC_NOSCROLL);
}
_execData.pdfFolder->SetItemsChangedSinceSort();
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_UPDATE);
_NotifyProgressText(IDS_SEARCHINGASYNC, cAsync, nPercentComplete);
}
void CDFCommand::_PTN_AsyncToSync()
{
if (_execData.pdfFolder)
_execData.pdfFolder->CacheAllAsyncItems();
}
void CDFCommand::_PTN_SearchComplete(HRESULT hr, BOOL fAbort)
{
int iItem;
// 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);
// 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.pdfFolder is NULL when Searh is complete by navigating away from the search page
if (!_execData.pdfFolder)
{
// do clean up of hdpaToItemsToadd1 and 2
// make sure all items in buffer 1 and 2 are empty
HDPA hdpa = _hdpaItemsToAdd1;
int cItems;
int i;
if (hdpa)
{
EnterCriticalSection(&_updateParams.csSearch);
cItems = DPA_GetPtrCount(hdpa);
for (i = 0; i < cItems; i++)
{
ILFree((LPITEMIDLIST)DPA_GetPtr(hdpa, i));
}
DPA_DeleteAllPtrs(hdpa);
LeaveCriticalSection(&_updateParams.csSearch);
}
hdpa = _hdpaItemsToAdd2;
if (hdpa)
{
EnterCriticalSection(&_updateParams.csSearch);
cItems = DPA_GetPtrCount(hdpa);
for (i = 0; i < cItems; i++)
{
ILFree((LPITEMIDLIST)DPA_GetPtr(hdpa, i));
}
DPA_DeleteAllPtrs(hdpa);
LeaveCriticalSection(&_updateParams.csSearch);
}
return;
}
// if we have a _pidlUpdate are completing an update
if (_pidlUpdate)
{
ESFItem *pesfi;
int i, cPidf;
UINT uItem;
_execData.pdfFolder->GetItemCount(&i);
for (; i-- > 0; )
{
// Pidl at start of structure...
_execData.pdfFolder->GetItem(i, &pesfi);
if (pesfi && pesfi->dwState & CDFITEM_STATE_MAYBEDELETE)
{
_execData.psfv->RemoveObject(&pesfi->idl, &uItem);
}
}
ILFree(_pidlUpdate);
_pidlUpdate = NULL;
// clear the update dir flags
_execData.pdfFolder->GetFolderListItemCount(&cPidf);
for (i = 0; i < cPidf; i++)
{
DFFolderListItem *pdffli;
if (SUCCEEDED(_execData.pdfFolder->GetFolderListItem(i, &pdffli)))
pdffli->fUpdateDir = FALSE;
}
}
// 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 DeferUpdateDir *pdud = _pdudFirst;
_pdudFirst = pdud->pdudNext;
if (DFB_handleUpdateDir(_execData.pdfFolder, pdud->pidl, pdud->fRecurse))
{
// Need to spawn sub-search on this...
_Execute_Start(FALSE, -1, pdud->pidl);
}
ILFree(pdud->pidl);
LocalFree((HLOCAL)pdud);
}
else
{
if (_cExecInProgress)
_cExecInProgress--;
if (_execData.psfv)
{
#ifdef CI_ROWSETNOTIFY
// ci no longer supports async notifications so we have to validate items
// all the time..
if (_fAsyncNotifyReceived)
#endif
{
// validate all the items we pulled in already
_execData.pdfFolder->ValidateItems(0, -1, TRUE);
#ifdef CI_ROWSETNOTIFY
_fAsyncNotifyReceived = FALSE;
#endif
}
}
_execData.pdfFolder->GetItemCount(&iItem);
_NotifyProgressText(_uStatusMsgIndex, iItem);
if (!fAbort)
_SelectResults();
}
}
void CDFCommand::_handleFSChange(LONG code, LPITEMIDLIST *ppidl)
{
LPITEMIDLIST pidlT;
UINT idsMsg;
UINT cItems;
IDFEnum *pdfEnumAsync;
if (!_execData.pdfFolder)
_ExecData_Init();
// If we are running async then for now ignore notifications...
// Unless we have cached all of the items...
if (!_execData.pdfFolder)
return; // we do not have anything to listen...
_execData.pdfFolder->GetAsyncEnum(&pdfEnumAsync);
// 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
DFB_UpdateOrMaybeAddPidl(_execData.pdfFolder, _execData.psfv, code, *ppidl, NULL);
break;
case SHCNE_UPDATEDIR:
{
// BUGBUG:: How to handle updatedir when there is an async enum involved...
// for now punt
TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_UPDATEDIR, pidl=0x%X", *ppidl);
BOOL bRecurse = (ppidl[1] != NULL);
if (!pdfEnumAsync)
{
if (_cExecInProgress)
{
_DeferHandleUpdateDir(*ppidl, bRecurse);
}
else
{
if (DFB_handleUpdateDir(_execData.pdfFolder, *ppidl, bRecurse))
{
// Need to spawn sub-search on this...
_Execute_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.pdfFolder->MapFSPidlToDFPidl(*ppidl, FALSE, &pidlT);
switch (code)
{
case SHCNE_RMDIR:
TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_RMDIR, pidl=0x%X", *ppidl);
DFB_handleRMDir(_execData.pdfFolder, _execData.psfv, *ppidl);
// Fall through to see if we should delete the item itself...
goto RMObj;
case SHCNE_DELETE:
TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_DELETE, pidl=0x%X", *ppidl);
RMObj:
if (pidlT != NULL)
{
_execData.psfv->RemoveObject(pidlT, &idsMsg);
}
break;
case SHCNE_RENAMEFOLDER:
// BUGBUG:: On rename directory we should see if the old one is in our
// range and the new one is not and then call the HandleRMDir function
case SHCNE_RENAMEITEM:
{
if (pidlT)
{
LPITEMIDLIST pidl1;
LPITEMIDLIST pidl2;
// If the two items dont have the same parent, we will go ahead
// and remove it...
_execData.pdfFolder->GetParentsPIDL(pidlT, &pidl1);
pidl2 = ILClone(ppidl[1]);
if (pidl1 && pidl2)
{
ILRemoveLastID(pidl2);
if (!ILIsEqual(pidl1, pidl2))
{
_execData.psfv->RemoveObject(pidlT, &idsMsg);
// And maybe add it back to the end... of the list
DFB_UpdateOrMaybeAddPidl(_execData.pdfFolder, _execData.psfv, code, 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
DFB_UpdateOrMaybeAddPidl(_execData.pdfFolder, _execData.psfv, code, ppidl[1], pidlT);
}
}
ILFree(pidl2);
}
else
DFB_UpdateOrMaybeAddPidl(_execData.pdfFolder, _execData.psfv, code, ppidl[1], NULL);
}
break;
case SHCNE_UPDATEITEM:
{
TraceMsg(TF_DOCFIND, "DocFind got notify SHCNE_UPDATEITEM, pidl=0x%X", *ppidl);
// We need to do a find first and convert to pidl...
if (pidlT)
DFB_UpdateOrMaybeAddPidl(_execData.pdfFolder, _execData.psfv, code, pidlT, pidlT);
}
break;
}
// Update the count...
_execData.psfv->GetObjectCount(&cItems);
_NotifyProgressText(_uStatusMsgIndex, cItems);
ILFree(pidlT);
}
void CDFCommand::_DeferHandleUpdateDir(LPITEMIDLIST pidl, BOOL bRecurse)
{
// Ok we need to add a defer
struct DeferUpdateDir *pdud = _pdudFirst;
struct DeferUpdateDir *pdudPrev = NULL;
// 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...
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 DeferUpdateDir*)LocalAlloc(LPTR, sizeof(struct DeferUpdateDir));
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 CDFCommand::_ClearDeferUpdateDirList()
{
// Cancel any Pending updatedirs also.
while (_pdudFirst) {
struct DeferUpdateDir *pdud = _pdudFirst;
_pdudFirst = pdud->pdudNext;
ILFree(pdud->pidl);
LocalFree((HLOCAL)pdud);
}
}
LRESULT CALLBACK CDFCommand::s_ThreadNotifyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CDFCommand* pThis = (CDFCommand*)GetWindowLongPtr(hwnd, 0);
LRESULT lRes = 0L;
if (uMsg < WM_USER)
return(::DefWindowProc(hwnd, uMsg, wParam, lParam));
else
{
switch (uMsg) {
case WM_DF_FSNOTIFY:
{
LPSHChangeNotificationLock pshcnl;
LPITEMIDLIST *ppidl;
LONG lEvent;
pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
if (pshcnl)
{
pThis->_handleFSChange(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;
case WM_DF_ASYNCTOSYNC:
pThis->_PTN_AsyncToSync();
pThis->Release();
break;
}
}
return lRes;
}
DWORD CALLBACK CDFCommand::_Execute_ThreadProc(LPVOID lpThreadParams)
{
HRESULT hr;
LPITEMIDLIST pidl;
INT state;
INT cFoldersSearched = 0;
INT cFoldersSearchedPrev;
INT cItemsSearched;
ExecThreadParams *pParams = ((ExecThreadParams *)lpThreadParams);
CDFCommand *&pThis = pParams->pThis;
BOOL fAbort = FALSE;
BOOL fQueryIsAsync;
EnterCriticalSection(&pThis->_csThread);
// previous thread might have exited but we're still processing search complete message
if (pThis->_cExecInProgress > 1)
Sleep(1000); // give it a chance to finish
// Don't have the enum bring up error dialog on trying to hit a: drive...
SetErrorMode(SEM_FAILCRITICALERRORS);
if (EVAL(SUCCEEDED(hr = CoInitialize(NULL))))
{
TraceMsg(TF_DOCFIND, "CDFCommand: starting exec.\n");
pThis->_updateParams.hdpa = NULL;
pThis->_updateParams.fFilesAdded = FALSE;
pThis->_updateParams.fDirChanged = FALSE;
pThis->_updateParams.fUpdatePosted = FALSE;
pThis->_PostMessage(WM_DF_SEARCHSTART, 0, 0);
// Now see if this is an Sync or an Async version of the search...
if (fQueryIsAsync = pParams->pdfenum->FQueryIsAsync())
{
DBCOUNTITEM dwTotalAsync;
BOOL fDone;
int nPercentComplete;
while ((hr = pParams->pdfenum->GetAsyncCount(&dwTotalAsync, &nPercentComplete, &fDone) == S_OK))
{
if (!pThis->_fContinue)
{
TraceMsg(TF_DOCFIND, "CDFCommand: cancel exec.\n");
fAbort = TRUE;
break;
}
pThis->_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)
{
TraceMsg(TF_DOCFIND, "CDFCommand: done exec.\n");
break;
}
DWORD dwSleep = 300;
if (pThis->_execData.pdfFolder)
{
if (pThis->_execData.pdfFolder->IsSlow())
dwSleep = 3000;
}
Sleep(dwSleep); // wait between looking again...
}
}
// 42 is special value to say mixed query...
if (!fQueryIsAsync || (fQueryIsAsync == DF_QUERYISMIXED))
{
// this is not necessary -- just slows down the transition
// from ci to grep search
#if 0
if (fQueryIsAsync == DF_QUERYISMIXED)
{
pThis->_PostMessage(WM_DF_ASYNCTOSYNC, 0, 0);
}
#endif
cFoldersSearchedPrev = 0;
pThis->_updateParams.hdpa = pThis->_hdpaItemsToAdd1; // Assume first one now...
pThis->_updateParams.dwTimeLastNotify = GetTickCount();
while (((hr = pParams->pdfenum->Next(&pidl, &cItemsSearched, &cFoldersSearched, &pThis->_fContinue, &state, NULL)) == S_OK)) {
if (state == GNF_DONE)
{ // we ran out of people to search
TraceMsg(TF_DOCFIND, "CDFCommand: done exec.\n");
break;
}
if (!pThis->_fContinue)
{
fAbort = TRUE;
TraceMsg(TF_DOCFIND, "CDFCommand: cancel exec.\n");
break;
}
// See if we should abort
if (state == GNF_MATCH)
{
// BUGBUG:: need to handle maximum number case...
EnterCriticalSection(&pThis->_updateParams.csSearch);
DPA_AppendPtr(pThis->_updateParams.hdpa, pidl);
pThis->_updateParams.fFilesAdded = TRUE;
LeaveCriticalSection(&pThis->_updateParams.csSearch);
}
if (cFoldersSearchedPrev != cFoldersSearched) {
pThis->_updateParams.fDirChanged = TRUE;
cFoldersSearchedPrev = cFoldersSearched;
}
if (!pThis->_updateParams.fUpdatePosted
&& (pThis->_updateParams.fDirChanged || pThis->_updateParams.fFilesAdded))
{
if (GetTickCount() > (pThis->_updateParams.dwTimeLastNotify + 200))
{
pThis->_updateParams.fUpdatePosted = TRUE;
pThis->_PostMessage(WM_DF_SEARCHPROGRESS, 0, 0);
}
}
}
if (!pThis->_updateParams.fUpdatePosted && pThis->_updateParams.fFilesAdded)
{
pThis->_updateParams.fUpdatePosted = TRUE;
pThis->_PostMessage(WM_DF_SEARCHPROGRESS, 0, 0);
}
}
if (hr != S_OK)
{
TraceMsg(TF_DOCFIND, "CDFCommand: cancel exec.\n");
fAbort = TRUE;
}
CoUninitialize();
}
pThis->_PostMessage(WM_DF_SEARCHCOMPLETE, (WPARAM)hr, (LPARAM)fAbort);
pThis->_fContinue = TRUE;
LeaveCriticalSection(&pThis->_csThread);
_ExecParams_Free(pParams);
return (DWORD)hr;
}
STDMETHODIMP CDFCommand::_Execute_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;
}
STDMETHODIMP CDFCommand::_Execute_Init(ExecThreadParams **ppParams, int iCol, LPITEMIDLIST pidlUpdate)
{
HRESULT hr;
IDocFindFileFilter *pdfff;
DWORD dwFlags = 0;
*ppParams = new ExecThreadParams;
if (!(*ppParams))
return E_OUTOFMEMORY;
ExecThreadParams *&pParams = *ppParams;
// Clear any previous registrations...
SHChangeNotifyDeregisterWindow(_updateParams.hwndThreadNotify);
// Prepare to execute the query
hr = _execData.pdfFolder->GetDocFindFilter(&pdfff);
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(pdfff)))
{
_execData.szProgressText[0] = 0;
pdfff->DeclareFSNotifyInterest(_updateParams.hwndThreadNotify, WM_DF_FSNOTIFY);
pdfff->GetStatusMessageIndex(0, &_uStatusMsgIndex);
hr = pdfff->PrepareToEnumObjects(&dwFlags);
if (SUCCEEDED(hr))
{
hr = pdfff->EnumObjects(_execData.psf, pidlUpdate, dwFlags, iCol,
_execData.szProgressText, SAFECAST_IROWSETTWATCHNOTIFY(this), &(pParams->pdfenum));
}
}
pdfff->Release();
}
// Fill in the exec params
pParams->pThis = this;
AddRef(); // ExecParams_Free will release this interface addref...
if (FAILED(hr))
{
_ExecParams_Free(pParams);
*ppParams = NULL;
}
return hr;
}
STDMETHODIMP CDFCommand::_ExecParams_Free(ExecThreadParams *pParams)
{
if (!pParams)
return S_OK;
// Don't use atomic release as this a pointer to a class not an interface, which can screw up...
CDFCommand *pThis = pParams->pThis;
pParams->pThis = NULL;
pThis->Release();
ATOMICRELEASE(pParams->pdfenum);
delete pParams;
return S_OK;
}
STDMETHODIMP CDFCommand::_ExecData_Release()
{
ATOMICRELEASE(_execData.psf);
ATOMICRELEASE(_execData.psfv);
if (_execData.pdfFolder)
_execData.pdfFolder->SetControllerNotifyObject(NULL); // release back pointer to us...
ATOMICRELEASE(_execData.pdfFolder);
_cExecInProgress = 0; // we must be in process of shutting down at least...
return S_OK;
}
HRESULT CDFCommand::_ExecData_ValidateRightSearchResults(IShellFolder *psf)
{
HRESULT hr = E_FAIL;
LPITEMIDLIST pidlFolder;
if (S_OK == SHGetIDListFromUnk((IUnknown *)psf, &pidlFolder))
{
LPITEMIDLIST pidl;
if (SUCCEEDED(_GetSearchIDList(&pidl)))
{
if (ILIsEqual(pidlFolder, pidl))
hr = S_OK;
ILFree(pidl);
}
ILFree(pidlFolder);
}
return hr;
}
STDMETHODIMP CDFCommand::_ExecData_Init()
{
// OK, Make sure we are navigated to the search results...
// NavigateToSearchResults();
_ExecData_Release();
IShellBrowser *psb;
HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IShellBrowser, (void**)&psb);
if (SUCCEEDED(hr))
{
IShellView *psv;
hr = psb->QueryActiveShellView(&psv);
if (SUCCEEDED(hr))
{
ATOMICRELEASE(_pdvResultsTargetFrame);
hr = psv->QueryInterface(IID_IDefViewFrame, (void **)&_pdvResultsTargetFrame);
if (SUCCEEDED(hr))
{
IShellFolder *psf;
hr = _pdvResultsTargetFrame->GetShellFolder(&psf);
if (SUCCEEDED(hr))
{
IDocFindFolder *pdfFolder;
hr = psf->QueryInterface(IID_IDocFindFolder, (void **)&pdfFolder);
if (SUCCEEDED(hr))
{
hr = _ExecData_ValidateRightSearchResults(psf);
if (SUCCEEDED(hr))
{
IShellFolderView *psfv;
hr = _pdvResultsTargetFrame->QueryInterface(IID_IShellFolderView, (void **)&psfv);
if (SUCCEEDED(hr))
{
IUnknown_Set((IUnknown **)&_execData.pdfFolder, pdfFolder);
IUnknown_Set((IUnknown **)&_execData.psf, psf);
IUnknown_Set((IUnknown **)&(_execData.psfv), psfv);
_execData.pdfFolder->SetControllerNotifyObject(SAFECAST(this, IDocFindControllerNotify*));
psfv->Release();
}
}
pdfFolder->Release();
}
psf->Release();
}
}
psv->Release();
}
psb->Release();
}
if (FAILED(hr))
_ExecData_Release();
else
SetShellFolder(_execData.psf);
return hr;
}
BOOL CDFCommand::_Execute_SetupBrowserCP()
{
HRESULT hr;
if (!_dwCookie) {
_cwbe.SetOwner(this); // make sure our owner is set...
IServiceProvider *pspTLB;
IConnectionPointContainer *pcpc;
// OK now lets register ourself with the Defview to get any events that they may generate...
if (SUCCEEDED(hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser,
IID_IServiceProvider, (void**)&pspTLB))) {
if (SUCCEEDED(hr = pspTLB->QueryService(IID_IExpDispSupport, IID_IConnectionPointContainer, (void **)&pcpc))) {
hr = ConnectToConnectionPoint(SAFECAST(&_cwbe, IDispatch*), DIID_DWebBrowserEvents2,
TRUE, pcpc, &_dwCookie, &_pcpBrowser);
pcpc->Release();
}
pspTLB->Release();
}
}
if (_dwCookie) {
_cwbe.SetWaiting(TRUE);
return TRUE;
}
return FALSE;
}
STDMETHODIMP CDFCommand::_Execute_Start(BOOL fNavigateIfFail, int iCol, LPITEMIDLIST pidlUpdate)
{
HRESULT hr;
ExecThreadParams *pExecThreadParams;
HANDLE hThread;
ULONG lThreadId;
if (!_hdpaItemsToAdd1)
{ // First time through...
_hdpaItemsToAdd1 = DPA_CreateEx(64, GetProcessHeap());
if (!_hdpaItemsToAdd1)
return E_OUTOFMEMORY;
}
if (!_hdpaItemsToAdd2)
{ // First time through...
_hdpaItemsToAdd2 = DPA_CreateEx(64, GetProcessHeap());
if (!_hdpaItemsToAdd2)
return E_OUTOFMEMORY;
}
if (!_updateParams.hwndThreadNotify)
{
_updateParams.hwndThreadNotify = SHCreateWorkerWindow(s_ThreadNotifyWndProc, NULL, 0, 0, (HMENU)0, this);
if (!_updateParams.hwndThreadNotify)
return E_OUTOFMEMORY;
}
if (FAILED(hr = _ExecData_Init()))
{
if (fNavigateIfFail)
{
if (_Execute_SetupBrowserCP())
NavigateToSearchResults();
}
return hr;
}
if (SUCCEEDED(hr = _Execute_Init(&pExecThreadParams, iCol, pidlUpdate)))
{
// See if we should be saving away the selection...
if (iCol >= 0)
_execData.pdfFolder->RememberSelectedItems();
// If this is an update then we need to remember our IDList else clear list...
if (pidlUpdate)
{
_pidlUpdate = ILClone(pidlUpdate);
}
else
{
// Tell defview to delete everything. - Use our Clear function to save code
_Clear();
}
_execData.pdfFolder->SetAsyncEnum(pExecThreadParams->pdfenum);
// Start the query
_cExecInProgress++;
// _fContinue = TRUE;went exiting threads should set this to true
_fNew = FALSE;
if (hThread = CreateThread(NULL, 0, _Execute_ThreadProc, pExecThreadParams, 0, &lThreadId))
{
hr = S_OK;
CloseHandle(hThread);
}
else
{
_cExecInProgress--;
_ExecParams_Free(pExecThreadParams);
_SetEmptyText(IDS_FINDVIEWEMPTY);
}
}
else
hr = _SetLastError(hr);
return hr;
}
HRESULT CDFCommand::_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 CDFCommand::Execute(VARIANT *RecordsAffected, VARIANT *Parameters, long Options)
{
if (Options == 0)
return _Execute_Cancel();
return _Execute_Start(TRUE, -1, NULL);
}
// IConnectionPointContainer implementation
STDMETHODIMP CDFCommand::EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum)
{
return CreateInstance_IEnumConnectionPoints(ppEnum, 1, _cpEvents.CastToIConnectionPoint());
}
STDMETHODIMP CDFCommand::FindConnectionPoint(REFIID iid, LPCONNECTIONPOINT *ppCP)
{
ASSERT(ppCP);
if (!ppCP)
return E_POINTER;
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 CDFCommand::GetClassInfo(ITypeInfo **ppTI)
{
return GetTypeInfoFromLibId(0, LIBID_Shell32, 1, 0, CLSID_DocFindCommand, ppTI);
}
STDMETHODIMP CDFCommand::GetGUID(DWORD dwGuidKind, GUID *pGUID)
{
ASSERT(pGUID);
if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID) {
*pGUID = DIID_DSearchCommandEvents;
return S_OK;
}
*pGUID = GUID_NULL;
return E_FAIL;
}
STDMETHODIMP CDFCommand::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 CDFCommand::_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_IShellView, (void**)&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 CDFCommand::ClearResults(void)
{
HRESULT hr = _Clear();
if (SUCCEEDED(hr))
{
_fNew = TRUE;
_SetEmptyText(IDS_FINDVIEWEMPTYINIT);
}
return hr;
}
STDMETHODIMP CDFCommand::_Clear()
{
// Tell defview to delete everything.
UINT u;
if (_execData.psfv)
_execData.psfv->RemoveObject(NULL, &u);
// And cleanup our folderList
if (_execData.pdfFolder)
{
_execData.pdfFolder->ClearItemList();
_execData.pdfFolder->ClearFolderList();
}
return S_OK;
}
HRESULT CDFCommand::_SetEmptyText(UINT nIDEmptyText)
{
if (_execData.pdfFolder)
{
TCHAR szEmptyText[128];
EVAL(LoadString(HINST_THISDLL, nIDEmptyText,
szEmptyText, ARRAYSIZE(szEmptyText)));
return _execData.pdfFolder->SetEmptyText(szEmptyText);
}
return E_FAIL;
}
HRESULT CDFCommand::_GetSearchIDList(LPITEMIDLIST *ppidl)
{
return SHILCreateFromPath(_SearchForComputer() ?
TEXT("::{1f4de370-d627-11d1-ba4f-00a0c91eedba}") : // CLSID_ComputerFindFolder
TEXT("::{e17d4fc0-5564-11d1-83f2-00a0c90dc849}"), // CLSID_DocFindFolder
ppidl, NULL);
}
STDMETHODIMP CDFCommand::NavigateToSearchResults(void)
{
IShellBrowser *psb;
HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IShellBrowser, (void**)&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* CDFCommand::_GetObjectToPersist()
{
IUnknown *punk = NULL;
// We could grovel immediately above our CDFCommand for the object to persist,
// or go to the top and walk down one level. That is a tad more robust just
// in case our cdfcommand object is within a frame
IShellBrowser * psbFrame;
if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopFrameBrowser, IID_IShellBrowser, (void**)&psbFrame)))
{
IShellView* psv;
if (SUCCEEDED(psbFrame->QueryActiveShellView(&psv)))
{
psv->GetItemObject(SVGIO_BACKGROUND, IID_IOleObject, (void**)&punk);
psv->Release();
}
psbFrame->Release();
}
return punk;
}
void CDFCommand::_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();
}
}
STDMETHODIMP CDFCommand::SaveSearch(void)
{
IDocFindFileFilter * pdfff;
HRESULT hr = _execData.pdfFolder->GetDocFindFilter(&pdfff);
if (SUCCEEDED(hr))
{
IShellBrowser* psb;
hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IShellBrowser, (void**)&psb);
if (SUCCEEDED(hr))
{
IShellView* psv;
hr = psb->QueryActiveShellView(&psv);
if (SUCCEEDED(hr))
{
HWND hwnd;
IUnknown* punk;
IUnknown_GetWindow(_punkSite, &hwnd);
punk = _GetObjectToPersist();
DFB_Save(pdfff, hwnd, &_dfbsi, psv, _execData.pdfFolder, punk);
ATOMICRELEASE(punk);
psv->Release();
}
psb->Release();
}
pdfff->Release();
}
return hr;
}
STDMETHODIMP CDFCommand::RestoreSearch(void)
{
// let script know that a restore happened...
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_RESTORE);
return S_OK;
}
STDMETHODIMP CDFCommand::StopSearch(void)
{
if (_cExecInProgress)
return _Execute_Cancel();
return S_OK;
}
STDMETHODIMP CDFCommand::GetItemCount(UINT *pcItems)
{
ASSERT(pcItems);
if (_execData.psfv)
{
return _execData.psfv->GetObjectCount(pcItems);
}
return E_FAIL;
}
STDMETHODIMP CDFCommand::SetItemCount(UINT cItems)
{
if (_execData.psfv)
{
return _execData.psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
}
return E_FAIL;
}
STDMETHODIMP CDFCommand::ViewDestroyed()
{
_ExecData_Release();
return S_OK;
}
STDMETHODIMP CDFCommand::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 CDFCommand::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 CDFCommand::SearchFor(int iFor)
{
_iSearchFor = iFor;
return S_OK;
}
STDMETHODIMP CDFCommand::GetScopeInfo(BSTR bsScope, int *pdwScopeInfo)
{
*pdwScopeInfo = 0;
return E_NOTIMPL;
}
STDMETHODIMP CDFCommand::RestoreSavedSearch(IN VARIANT *pvarFile)
{
if (pvarFile != NULL)
{
if (pvarFile->vt != VT_EMPTY)
{
LPITEMIDLIST pidl = VariantToIDList(pvarFile);
if (pidl)
{
ILFree(_pidlRestore);
_pidlRestore = pidl;
}
}
VariantClear(pvarFile);
}
if (_pidlRestore)
{
IShellBrowser *psb;
if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_IShellBrowser, (void**)&psb)))
{
// Warning:: We check for shell view simply to see how the search pane was
// loaded. If this fails it is because we were loaded on the CoCreateInstance
// of the browser window and as such it is a race condition to know if the
// properties were set or not. So in this case wait until we get a
// navigate complete. This lets us know for sure if a save file was passed
// in or not.
IShellView *psv;
if (SUCCEEDED(psb->QueryActiveShellView(&psv)))
{
psv->Release();
IWebBrowser2* pwb;
if (EVAL(SUCCEEDED(IUnknown_QueryService(psb, SID_SWebBrowserApp, IID_IWebBrowser2, (void **)&pwb))))
{
if (SUCCEEDED(_ExecData_Init()))
{
_execData.pdfFolder->RestoreSearchFromSaveFile(_pidlRestore, _execData.psfv);
_cpEvents.InvokeDispid(DISPID_SEARCHCOMMAND_RESTORE);
ILFree(_pidlRestore);
_pidlRestore = NULL;
}
pwb->Release();
}
}
else
{
// appears to be race condition to load
if (!_fDeferRestoreTried)
{
TraceMsg(TF_WARNING, "CDFCommand::MaybeRestoreSearch - QueryActiveShellView failed...");
_fDeferRestore = TRUE;
if (!_Execute_SetupBrowserCP())
_fDeferRestore = FALSE;
}
}
psb->Release();
}
}
return S_OK;
}
STDMETHODIMP CDFCommand::OnChange(IRowset *prowset, DBWATCHNOTIFY eChangeReason)
{
_fAsyncNotifyReceived = TRUE;
return S_OK;
}
STDMETHODIMP CDFCommand::DoSortOnColumn(UINT iCol, BOOL fSameCol)
{
IDFEnum *pdfEnumAsync;
if (SUCCEEDED(_execData.pdfFolder->GetAsyncEnum(&pdfEnumAsync)) && 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...
_Execute_Start(FALSE, iCol, NULL);
return S_FALSE; // tell system to not do default processing.
}
_execData.pdfFolder->CacheAllAsyncItems();
}
return S_OK; // let it do default processing.
}
// Implemention of our IDispatch to hookup to the top level browsers connnection point...
STDMETHODIMP CDFCommand::CWBEvents2::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
if (riid == IID_IUnknown || riid == IID_IDispatch || riid == DIID_DWebBrowserEvents2
|| riid == DIID_DWebBrowserEvents) {
*ppvObj = (LPVOID)this;
AddRef();
}
else
{
return E_NOINTERFACE;
}
return NOERROR;
}
STDMETHODIMP CDFCommand::CWBEvents2::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
{
if (_fWaitingForNavigate) {
TraceMsg(TF_WARNING, "CDFCommand::CWBEvents2::Invoke dispid=%d.", dispidMember);
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->_Execute_Start(FALSE, -1, NULL);
}
}
return S_OK;
}
// Now implement our simple constraint object
// CDFConstraint ctor/dtor implementation
CDFConstraint::CDFConstraint(BSTR bstr, VARIANT var)
: CImpIDispatch(&LIBID_Shell32, 1, 0, &IID_DFConstraint)
{
_cRef = 1;
_bstr = bstr;
_var = var;
}
CDFConstraint::~CDFConstraint()
{
SysFreeString(_bstr);
VariantClear(&_var);
}
// CDFParameter IUnknown implementation
STDMETHODIMP CDFConstraint::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
static const QITAB qit[] = {
QITABENT(CDFConstraint, DFConstraint), // IID_DFConstraint
QITABENTMULTI(CDFConstraint, IDispatch, DFConstraint), //IID_IDispatch
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CDFConstraint::AddRef()
{
InterlockedIncrement(&_cRef);
TraceMsg(TF_DOCFIND, "CDFConstraint.AddRef %d", _cRef);
return _cRef;
}
STDMETHODIMP_(ULONG) CDFConstraint::Release()
{
TraceMsg(TF_DOCFIND, "CDFConstraint.Release %d", _cRef - 1);
if (InterlockedDecrement(&_cRef)) {
return _cRef;
}
delete this;
return 0;
}
STDMETHODIMP CDFConstraint::GetTypeInfoCount(UINT * pctinfo)
{
return CImpIDispatch::GetTypeInfoCount(pctinfo);
}
STDMETHODIMP CDFConstraint::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo * * pptinfo)
{
return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo);
}
STDMETHODIMP CDFConstraint::GetIDsOfNames(REFIID riid, OLECHAR * * rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
{
return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
}
STDMETHODIMP CDFConstraint::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 CDFConstraint::get_Name(BSTR *pbs)
{
*pbs = SysAllocString(_bstr);
return *pbs ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP CDFConstraint::get_Value(VARIANT *pvar)
{
VariantInit(pvar);
return VariantCopy(pvar, &_var);
}