windows-nt/Source/XPSP1/NT/shell/shell32/netfind.cpp
2020-09-26 16:20:57 +08:00

630 lines
19 KiB
C++

#include "shellprv.h"
#pragma hdrstop
#include "shellids.h" // new help ids are stored here
#include "findfilter.h"
#include "netview.h"
#include "prop.h"
#include "ids.h"
STDAPI CNetwork_EnumSearches(IShellFolder2 *psf2, IEnumExtraSearch **ppenum);
class CNetFindEnum;
class CNetFindFilter : public IFindFilter
{
friend CNetFindEnum;
public:
CNetFindFilter();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IFindFilter
STDMETHODIMP GetStatusMessageIndex(UINT uContext, UINT *puMsgIndex);
STDMETHODIMP GetFolderMergeMenuIndex(UINT *puBGMainMergeMenu, UINT *puBGPopupMergeMenu);
STDMETHODIMP FFilterChanged();
STDMETHODIMP GenerateTitle(LPTSTR *ppszTile, BOOL fFileName);
STDMETHODIMP PrepareToEnumObjects(HWND hwnd, DWORD * pdwFlags);
STDMETHODIMP ClearSearchCriteria();
STDMETHODIMP EnumObjects(IShellFolder *psf, LPCITEMIDLIST pidlStart, DWORD grfFlags, int iColSort,
LPTSTR pszProgressText, IRowsetWatchNotify *prwn, IFindEnum **ppfindenum);
STDMETHODIMP GetColumnsFolder(IShellFolder2 **ppsf);
STDMETHODIMP_(BOOL) MatchFilter(IShellFolder *psf, LPCITEMIDLIST pidl);
STDMETHODIMP SaveCriteria(IStream * pstm, WORD fCharType);
STDMETHODIMP RestoreCriteria(IStream * pstm, int cCriteria, WORD fCharType);
STDMETHODIMP DeclareFSNotifyInterest(HWND hwndDlg, UINT uMsg);
STDMETHODIMP GetColSaveStream(WPARAM wParam, IStream **ppstm);
STDMETHODIMP GenerateQueryRestrictions(LPWSTR *ppwszQuery, DWORD *pdwGQRFlags);
STDMETHODIMP ReleaseQuery();
STDMETHODIMP UpdateField(LPCWSTR pszField, VARIANT vValue);
STDMETHODIMP ResetFieldsToDefaults();
STDMETHODIMP GetItemContextMenu(HWND hwndOwner, IFindFolder* pff, IContextMenu** ppcm);
STDMETHODIMP GetDefaultSearchGUID(IShellFolder2 *psf2, LPGUID lpGuid);
STDMETHODIMP EnumSearches(IShellFolder2 *psf2, LPENUMEXTRASEARCH *ppenum);
STDMETHODIMP GetSearchFolderClassId(LPGUID lpGuid);
STDMETHODIMP GetNextConstraint(VARIANT_BOOL fReset, BSTR *pName, VARIANT *pValue, VARIANT_BOOL *pfFound);
STDMETHODIMP GetQueryLanguageDialect(ULONG * pulDialect);
STDMETHODIMP GetWarningFlags(DWORD *pdwWarningFlags) { return E_NOTIMPL; }
protected:
LPTSTR _pszCompName; // the one we do compares with
TCHAR _szUserInputCompName[MAX_PATH]; // User input
private:
~CNetFindFilter();
LONG _cRef;
LPITEMIDLIST _pidlStart; // Where to start the search from.
// Data associated with the file name.
};
CNetFindFilter::CNetFindFilter() : _cRef(1)
{
}
CNetFindFilter::~CNetFindFilter()
{
ILFree(_pidlStart);
Str_SetPtr(&_pszCompName, NULL);
}
STDMETHODIMP CNetFindFilter::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CNetFindFilter, IFindFilter),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CNetFindFilter::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CNetFindFilter::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CNetFindFilter::GetStatusMessageIndex(UINT uContext, UINT *puMsgIndex)
{
// Currently context is not used
*puMsgIndex = IDS_COMPUTERSFOUND;
return S_OK;
}
STDMETHODIMP CNetFindFilter::GetFolderMergeMenuIndex(UINT *puBGMainMergeMenu, UINT *puBGPopupMergeMenu)
{
*puBGPopupMergeMenu = 0;
return S_OK;
}
STDMETHODIMP CNetFindFilter::GetItemContextMenu(HWND hwndOwner, IFindFolder* pff, IContextMenu **ppcm)
{
return CFindItem_Create(hwndOwner, pff, ppcm);
}
STDMETHODIMP CNetFindFilter::GetDefaultSearchGUID(IShellFolder2 *psf2, GUID *pGuid)
{
*pGuid = SRCID_SFindComputer;
return S_OK;
}
STDMETHODIMP CNetFindFilter::EnumSearches(IShellFolder2 *psf2, IEnumExtraSearch **ppenum)
{
return CNetwork_EnumSearches(psf2, ppenum);
}
STDMETHODIMP CNetFindFilter::GetSearchFolderClassId(LPGUID lpGuid)
{
*lpGuid = CLSID_ComputerFindFolder;
return S_OK;
}
STDMETHODIMP CNetFindFilter::GetNextConstraint(VARIANT_BOOL fReset, BSTR *pName, VARIANT *pValue, VARIANT_BOOL *pfFound)
{
*pName = NULL;
*pfFound = FALSE;
VariantClear(pValue);
return E_NOTIMPL;
}
STDMETHODIMP CNetFindFilter::GetQueryLanguageDialect(ULONG * pulDialect)
{
if (pulDialect)
*pulDialect = 0;
return E_NOTIMPL;
}
STDMETHODIMP CNetFindFilter::FFilterChanged()
{
// Currently not saving so who cares?
return S_FALSE;
}
STDMETHODIMP CNetFindFilter::GenerateTitle(LPTSTR *ppszTitle, BOOL fFileName)
{
// Now lets construct the message from the resource
*ppszTitle = ShellConstructMessageString(HINST_THISDLL,
MAKEINTRESOURCE(IDS_FIND_TITLE_COMPUTER), fFileName ? TEXT(" #") : TEXT(":"));
return *ppszTitle ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP CNetFindFilter::ClearSearchCriteria()
{
return S_OK;
}
STDAPI CreateDefaultComputerFindFilter(IFindFilter **ppff)
{
*ppff = new CNetFindFilter;
return *ppff ? S_OK : E_OUTOFMEMORY;
}
class CNetFindEnum : public IFindEnum
{
public:
CNetFindEnum(CNetFindFilter *pnff, IShellFolder *psf, LPTSTR pszDisplayText, DWORD grfFlags, LPITEMIDLIST pidlStart);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IFindEnum
STDMETHODIMP Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState);
STDMETHODIMP Skip(int celt) { return E_NOTIMPL; }
STDMETHODIMP Reset() { return E_NOTIMPL; }
STDMETHODIMP StopSearch() { return E_NOTIMPL; }
STDMETHODIMP_(BOOL) FQueryIsAsync();
STDMETHODIMP GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone);
STDMETHODIMP GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl);
STDMETHODIMP GetItemID(UINT iItem, DWORD *puWorkID);
STDMETHODIMP SortOnColumn(UINT iCol, BOOL fAscending);
private:
~CNetFindEnum();
HRESULT _FindCompByUNCName(LPITEMIDLIST *ppidl, int *piState);
LONG _cRef;
IFindFolder *_pff; // find folder
// Stuff to use in the search
DWORD _grfFlags; // Flags that control things like recursion
// filter info...
LPTSTR _pszDisplayText; // Place to write feadback text into
CNetFindFilter *_pnetf; // Pointer to the net filter...
// enumeration state
IShellFolder *_psfEnum; // Pointer to shell folder for the object.
IEnumIDList *_penum; // Enumerator in use.
LPITEMIDLIST _pidlFolder; // The idlist of the currently processing
LPITEMIDLIST _pidlStart; // Pointer to the starting point.
int _iFolder; // Which folder are we adding items for?
BOOL _fFindUNC; // Find UNC special case
int _iPassCnt; // Used to control when to reiterat...
};
CNetFindEnum::CNetFindEnum(CNetFindFilter *pnff, IShellFolder *psf, LPTSTR pszDisplayText, DWORD grfFlags, LPITEMIDLIST pidlStart) :
_cRef(1), _pnetf(pnff), _pszDisplayText(pszDisplayText), _grfFlags(grfFlags), _iFolder(-1)
{
ASSERT(0 == _iPassCnt);
_pnetf->AddRef();
psf->QueryInterface(IID_PPV_ARG(IFindFolder, &_pff));
ASSERT(_pff);
if (pidlStart)
SHILClone(pidlStart, &_pidlStart);
else
SHGetDomainWorkgroupIDList(&_pidlStart);
// special case to force us to search for specific UNC
_fFindUNC = _pnetf->_pszCompName && (_pnetf->_pszCompName[0] == TEXT('\\'));
}
CNetFindEnum::~CNetFindEnum()
{
// Release any open enumerator and open IShell folder we may have.
if (_psfEnum)
_psfEnum->Release();
if (_penum)
_penum->Release();
_pff->Release();
_pnetf->Release();
ILFree(_pidlStart);
ILFree(_pidlFolder);
}
STDMETHODIMP CNetFindEnum::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
// QITABENT(CNetFindEnum, IFindEnum),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CNetFindEnum::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CNetFindEnum::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CNetFindFilter::EnumObjects(IShellFolder *psf, LPCITEMIDLIST pidlStart,
DWORD grfFlags, int iColSort, LPTSTR pszDisplayText,
IRowsetWatchNotify *prsn, IFindEnum **ppfindenum)
{
// We need to construct the iterator
*ppfindenum = new CNetFindEnum(this, psf, pszDisplayText, grfFlags, _pidlStart);
return *ppfindenum ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP CNetFindFilter::PrepareToEnumObjects(HWND hwnd, DWORD *pdwFlags)
{
*pdwFlags = 0;
// Also lets convert the Computer name pattern into the strings
// will do the compares against.
if ((_szUserInputCompName[0] == TEXT('\\')) &&
(_szUserInputCompName[1] == TEXT('\\')))
{
Str_SetPtr(&_pszCompName, _szUserInputCompName);
}
else
{
SetupWildCardingOnFileSpec(_szUserInputCompName, &_pszCompName);
}
return S_OK;
}
STDMETHODIMP CNetFindFilter::GetColumnsFolder(IShellFolder2 **ppsf)
{
LPITEMIDLIST pidl;
HRESULT hr = SHGetDomainWorkgroupIDList(&pidl);
if (SUCCEEDED(hr))
{
hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidl, ppsf));
ILFree(pidl);
}
else
*ppsf = NULL;
return hr;
}
STDMETHODIMP_(BOOL) CNetFindFilter::MatchFilter(IShellFolder *psf, LPCITEMIDLIST pidl)
{
if (this->_pszCompName && this->_pszCompName[0])
{
// Although for now not much...
TCHAR szPath[MAX_PATH];
return SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_NORMAL, szPath, ARRAYSIZE(szPath))) &&
PathMatchSpec(szPath, _pszCompName);
}
return TRUE; // emtpy search, return TRUE (yes)
}
STDMETHODIMP CNetFindFilter::SaveCriteria(IStream *pstm, WORD fCharType)
{
return S_OK;
}
STDMETHODIMP CNetFindFilter::RestoreCriteria(IStream *pstm, int cCriteria, WORD fCharType)
{
return S_OK;
}
STDMETHODIMP CNetFindFilter::GetColSaveStream(WPARAM wparam, IStream **ppstm)
{
*ppstm = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CNetFindFilter::GenerateQueryRestrictions(LPWSTR *ppszQuery, DWORD *pdwFlags)
{
if (ppszQuery)
*ppszQuery = NULL;
*pdwFlags = 0;
return E_NOTIMPL;
}
STDMETHODIMP CNetFindFilter::ReleaseQuery()
{
return S_OK;
}
HRESULT CNetFindFilter::UpdateField(LPCWSTR pszField, VARIANT vValue)
{
HRESULT hr = E_FAIL;
if (0 == StrCmpIW(pszField, L"LookIn"))
{
hr = S_OK; // ignored
}
else if (0 == StrCmpIW(pszField, L"SearchFor"))
{
// Careful! VariantToStr returns a pointer, not an HRESULT
if (VariantToStr(&vValue, _szUserInputCompName, ARRAYSIZE(_szUserInputCompName)))
{
hr = S_OK;
}
}
return hr;
}
HRESULT CNetFindFilter::ResetFieldsToDefaults()
{
_szUserInputCompName[0] = 0;
return S_OK;
}
STDMETHODIMP CNetFindFilter::DeclareFSNotifyInterest(HWND hwndDlg, UINT uMsg)
{
SHChangeNotifyEntry fsne;
fsne.fRecursive = TRUE;
fsne.pidl = _pidlStart;
if (fsne.pidl)
{
SHChangeNotifyRegister(hwndDlg, SHCNRF_NewDelivery | SHCNRF_ShellLevel, SHCNE_DISKEVENTS, uMsg, 1, &fsne);
}
return S_OK;
}
// chop off all of an UNC path except the \\server portion
void _StripToServer(LPTSTR pszUNC)
{
for (pszUNC += 2; *pszUNC; pszUNC = CharNext(pszUNC))
{
if (*pszUNC == TEXT('\\'))
{
// found something after server name, so get rid of it
*pszUNC = 0;
break;
}
}
}
// Helper function to the next function to help process find computer
// on returning computers by UNC names...
HRESULT CNetFindEnum::_FindCompByUNCName(LPITEMIDLIST *ppidl, int *piState)
{
*piState = GNF_DONE; // assume we are done
// Two cases, There is a UNC name entered. If so we need to process
// this by extracting everythign off after the server name...
if (_pnetf->_pszCompName && _pnetf->_pszCompName[0])
{
if (PathIsUNC(_pnetf->_pszCompName))
{
_StripToServer(_pnetf->_pszCompName);
}
else
{
// no unc name, but lets try to convert to unc name
TCHAR szTemp[MAX_PATH];
szTemp[0] = TEXT('\\');
szTemp[1] = TEXT('\\');
szTemp[2] = 0;
StrCatBuff(szTemp, _pnetf->_szUserInputCompName, ARRAYSIZE(szTemp));
_StripToServer(szTemp);
Str_SetPtr(&_pnetf->_pszCompName, szTemp);
}
}
if (_pnetf->_pszCompName && _pnetf->_pszCompName[0])
{
// see if we can parse this guy... if so we have a match
LPITEMIDLIST pidl;
if (SUCCEEDED(SHParseDisplayName(_pnetf->_pszCompName, NULL, &pidl, 0, NULL)))
{
LPITEMIDLIST pidlFolder;
LPCITEMIDLIST pidlChild;
if (SUCCEEDED(SplitIDList(pidl, &pidlFolder, &pidlChild)))
{
if (SUCCEEDED(_pff->AddFolder(pidlFolder, FALSE, &_iFolder)))
{
if (SUCCEEDED(_pff->AddDataToIDList(pidlChild, _iFolder, pidlFolder, DFDF_NONE, 0, 0, 0, ppidl)))
*piState = GNF_MATCH;
}
ILFree(pidlFolder);
}
ILFree(pidl);
}
}
return S_OK;
}
STDMETHODIMP CNetFindEnum::Next(LPITEMIDLIST *ppidl, int *pcObjectSearched,
int *pcFoldersSearched, BOOL *pfContinue, int *piState)
{
HRESULT hr;
// Special case to find UNC Names quickly
if (_fFindUNC)
{
// If not the first time through return that we are done!
if (_iPassCnt)
{
*piState = GNF_DONE;
return S_OK;
}
_iPassCnt = 1;
hr = _FindCompByUNCName(ppidl, piState);
}
else
{
BOOL fContinue = TRUE;
do
{
if (_penum)
{
LPITEMIDLIST pidl;
if (S_OK == _penum->Next(1, &pidl, NULL))
{
// Now see if this is someone we might want to return.
// Our Match function take esither find data or idlist...
// for networks we work off of the idlist,
fContinue = FALSE; // We can exit the loop;
(*pcObjectSearched)++;
if (_pnetf->MatchFilter(_psfEnum, pidl))
{
*piState = GNF_MATCH;
// see if we have to add this folder to our list.
if (-1 == _iFolder)
_pff->AddFolder(_pidlFolder, FALSE, &_iFolder);
if (SUCCEEDED(_pff->AddDataToIDList(pidl, _iFolder, _pidlFolder, DFDF_NONE, 0, 0, 0, ppidl)))
{
if ((_iPassCnt == 1) && _pnetf->_pszCompName && _pnetf->_pszCompName[0])
{
// See if this is an exact match of the name
// we are looking for. If it is we set pass=2
// as to not add the item twice.
TCHAR szName[MAX_PATH];
if (SUCCEEDED(DisplayNameOf(_psfEnum, pidl, SHGDN_NORMAL, szName, ARRAYSIZE(szName))) &&
(0 == lstrcmpi(szName, _pnetf->_szUserInputCompName)))
{
_iPassCnt = 2;
}
}
ILFree(pidl);
pidl = NULL;
break;
}
}
else
{
*piState = GNF_NOMATCH;
}
ILFree(pidl);
}
else
{
ATOMICRELEASE(_penum); // release and zero
ATOMICRELEASE(_psfEnum);
}
}
if (!_penum)
{
switch (_iPassCnt)
{
case 1:
// We went through all of the items see if there is
// an exact match...
_iPassCnt = 2;
return _FindCompByUNCName(ppidl, piState);
case 2:
// We looped through everything so return done!
*piState = GNF_DONE;
return S_OK;
case 0:
// This is the main pass through here...
// Need to clone the idlist
hr = SHILClone(_pidlStart, &_pidlFolder);
if (SUCCEEDED(hr))
{
_iPassCnt = 1;
// We will do the first on in our own thread.
if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, _pidlFolder, &_psfEnum))))
{
if (S_OK != _psfEnum->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &_penum))
{
// Failed to get iterator so release folder.
ATOMICRELEASE(_psfEnum);
ASSERT(NULL == _penum);
}
}
break;
}
else
{
*piState = GNF_ERROR;
return hr;
}
}
(*pcFoldersSearched)++;
// update progress text
SHGetNameAndFlags(_pidlFolder, SHGDN_NORMAL, _pszDisplayText, MAX_PATH, NULL);
}
} while (fContinue && *pfContinue);
hr = S_OK;
}
return hr;
}
STDMETHODIMP_(BOOL) CNetFindEnum::FQueryIsAsync()
{
return FALSE;
}
STDMETHODIMP CNetFindEnum::GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone)
{
return E_NOTIMPL;
}
STDMETHODIMP CNetFindEnum::GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl)
{
return E_NOTIMPL;
}
STDMETHODIMP CNetFindEnum::GetItemID(UINT iItem, DWORD *puWorkID)
{
*puWorkID = (UINT)-1;
return E_NOTIMPL;
}
STDMETHODIMP CNetFindEnum::SortOnColumn(UINT iCOl, BOOL fAscending)
{
return E_NOTIMPL;
}