2663 lines
78 KiB
C++
2663 lines
78 KiB
C++
#include "shellprv.h"
|
|
|
|
extern "C" {
|
|
#include <shellp.h>
|
|
#include "idlcomm.h"
|
|
#include "pidl.h"
|
|
#include "fstreex.h"
|
|
#include "views.h"
|
|
#include "ids.h"
|
|
#include "shitemid.h"
|
|
#include <limits.h>
|
|
};
|
|
|
|
|
|
#include "docfind.h"
|
|
#include "docfindx.h"
|
|
|
|
#include "sfviewp.h"
|
|
#include "shguidp.h"
|
|
#include "netview.h"
|
|
#ifdef WINNT
|
|
#include "mtpt.h"
|
|
#endif
|
|
|
|
// in defviewx.c
|
|
STDAPI SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage);
|
|
|
|
typedef struct
|
|
{
|
|
IShellFolder *pshf; // The shell folder
|
|
UINT iCol; // Which Column
|
|
} DFSINFO;
|
|
|
|
int CALLBACK DFSCompareItems(void *p1, void *p2, LPARAM lParam);
|
|
|
|
#define FILE_ATTRIBUTE_SUPERHIDDEN (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)
|
|
extern "C" BOOL IsSuperHidden(DWORD dwAttribs)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
if (!ShowSuperHidden())
|
|
{
|
|
bRet = (dwAttribs & FILE_ATTRIBUTE_SUPERHIDDEN) == FILE_ATTRIBUTE_SUPERHIDDEN;
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
#ifdef WINNT
|
|
|
|
extern "C" HRESULT _IsNTFSDrive(int iDrive)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CMountPoint* pMtPt = CMountPoint::GetMountPoint(iDrive);
|
|
|
|
if (pMtPt)
|
|
{
|
|
hr = pMtPt->IsNTFS()? S_OK : S_FALSE;
|
|
pMtPt->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
#endif
|
|
|
|
HRESULT CDocFindSFVCB::OnMERGEMENU(DWORD pv, QCMINFO*lP)
|
|
{
|
|
int i;
|
|
|
|
if (m_pDFFolder->pdfff == NULL)
|
|
return S_FALSE;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - DF_FSNCallBack DVN_MERGEMENU"));
|
|
|
|
UINT idBGMain = 0, idBGPopup = 0;
|
|
m_pDFFolder->pdfff->GetFolderMergeMenuIndex(&idBGMain, &idBGPopup);
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, idBGPopup, lP);
|
|
|
|
// Lets remove some menu items that are not useful to us.
|
|
HMENU hmenu = lP->hmenu;
|
|
DeleteMenu(hmenu, SFVIDM_EDIT_PASTE, MF_BYCOMMAND);
|
|
DeleteMenu(hmenu, SFVIDM_EDIT_PASTELINK, MF_BYCOMMAND);
|
|
// DeleteMenu(hmenu, SFVIDM_EDIT_PASTESPECIAL, MF_BYCOMMAND);
|
|
|
|
// This is sortof bogus but if after the merge one of the
|
|
// menus has no items in it, remove the menu.
|
|
|
|
for (i = GetMenuItemCount(hmenu)-1; i >= 0; i--)
|
|
{
|
|
HMENU hmenuSub;
|
|
|
|
if ((hmenuSub = GetSubMenu(hmenu, i)) &&
|
|
(GetMenuItemCount(hmenuSub) == 0))
|
|
{
|
|
DeleteMenu(hmenu, i, MF_BYPOSITION);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnReArrange(DWORD pv, LPARAM lp)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - DF_FSNCallBack ONREARRANGE iCOl=%d,"), lp);
|
|
return SortOnColumn((UINT)lp);
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGETWORKINGDIR(DWORD pv, UINT wP, LPTSTR lP)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
IShellFolderView *psfv;
|
|
if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_IShellFolderView, (void**)&psfv)))
|
|
{
|
|
LPCITEMIDLIST *ppidls; // pointer to a list of pidls.
|
|
UINT cpidls = 0; // Count of pidls that were returned.
|
|
|
|
psfv->GetSelectedObjects( &ppidls, &cpidls);
|
|
|
|
if (cpidls > 0)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
m_pDFFolder->GetParentsPIDL(ppidls[0], &pidl);
|
|
SHGetPathFromIDList(pidl, lP);
|
|
LocalFree( (void *) ppidls );
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hres = E_FAIL;
|
|
}
|
|
psfv->Release();
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnINVOKECOMMAND(DWORD pv, UINT wP)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - DF_FSNCallBack DVN_INVOKECOMMAND (id=%x)"), wP);
|
|
INSTRUMENT_INVOKECOMMAND(SHCNFI_DEFFOLDER_FNV_INVOKE, m_hwndMain, wP);
|
|
switch(wP)
|
|
{
|
|
case FSIDM_SORTBYLOCATION:
|
|
case FSIDM_SORTBYNAME:
|
|
case FSIDM_SORTBYSIZE:
|
|
case FSIDM_SORTBYTYPE:
|
|
case FSIDM_SORTBYDATE:
|
|
return SortOnColumn(DFSortIDToICol(wP));
|
|
break;
|
|
}
|
|
return(S_OK);
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream**lP)
|
|
{
|
|
if (m_pDFFolder->pdfff == NULL) {
|
|
*lP = NULL;
|
|
return E_FAIL;
|
|
}
|
|
|
|
return(m_pDFFolder->pdfff->GetColSaveStream(wP, lP));
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST *ppidl)
|
|
{
|
|
ESFItem *pesfi;
|
|
|
|
|
|
if (SUCCEEDED(m_pDFFolder->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
*ppidl = &pesfi->idl;
|
|
return S_OK;
|
|
}
|
|
|
|
*ppidl = NULL;
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetItemIconIndex(DWORD pv, WPARAM iItem, int *piIcon)
|
|
{
|
|
ESFItem *pesfi;
|
|
|
|
*piIcon = -1;
|
|
|
|
if (SUCCEEDED(m_pDFFolder->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
if (pesfi->iIcon == -1)
|
|
{
|
|
IShellFolder* psf = (IShellFolder*)m_pDFFolder;
|
|
SHGetIconFromPIDL(psf, NULL, &pesfi->idl, 0, &pesfi->iIcon);
|
|
}
|
|
|
|
*piIcon = pesfi->iIcon;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
HRESULT CDocFindSFVCB::OnSetItemIconOverlay(DWORD pv, WPARAM iItem, int iOverlayIndex)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
ESFItem *pesfi;
|
|
if (SUCCEEDED(m_pDFFolder->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
pesfi->dwMask |= ESFITEM_ICONOVERLAYSET;
|
|
pesfi->dwState |= INDEXTOOVERLAYMASK(iOverlayIndex) & LVIS_OVERLAYMASK;
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetItemIconOverlay(DWORD pv, WPARAM iItem, int * piOverlayIndex)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
*piOverlayIndex = SFV_ICONOVERLAY_DEFAULT;
|
|
ESFItem *pesfi;
|
|
if (SUCCEEDED(m_pDFFolder->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
if (pesfi->dwMask & ESFITEM_ICONOVERLAYSET)
|
|
{
|
|
*piOverlayIndex = OVERLAYMASKTO1BASEDINDEX(pesfi->dwState & LVIS_OVERLAYMASK);
|
|
}
|
|
else
|
|
*piOverlayIndex = SFV_ICONOVERLAY_UNSET;
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
HRESULT CDocFindSFVCB::OnSETITEMIDLIST(DWORD pv, WPARAM iItem, LPITEMIDLIST pidl)
|
|
{
|
|
ESFItem *pesfi;
|
|
|
|
m_pDFFolder->_iGetIDList = (int) iItem; // remember the last one we retrieved...
|
|
|
|
if (SUCCEEDED(m_pDFFolder->GetItem((int) iItem, &pesfi)) && pesfi)
|
|
{
|
|
ESFItem *pesfiNew;
|
|
|
|
if (SUCCEEDED(m_pDFFolder->AddPidl((int) iItem, pidl, 0, &pesfiNew) && pesfiNew))
|
|
{
|
|
pesfiNew->dwState = pesfi->dwState;
|
|
LocalFree((HLOCAL)pesfi); // Free the old one...
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
BOOL DF_ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
BOOL bRet = (pidl1 == pidl2);
|
|
|
|
if (!bRet)
|
|
{
|
|
PCHIDDENDOCFINDDATA phdfd1 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl1, IDLHID_DOCFINDDATA);
|
|
PCHIDDENDOCFINDDATA phdfd2 = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl2, IDLHID_DOCFINDDATA);
|
|
|
|
if (phdfd1 && phdfd2)
|
|
bRet = (phdfd1->iFolder == phdfd2->iFolder) && ILIsEqual(pidl1, pidl2);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetIndexForItemIDList(DWORD pv, int * piItem, LPITEMIDLIST pidl)
|
|
{
|
|
INT cItems;
|
|
|
|
// Try to short circuit searching for pidls...
|
|
if (SUCCEEDED(m_pDFFolder->GetItemCount(&cItems)) && m_pDFFolder->_iGetIDList < cItems)
|
|
{
|
|
ESFItem *pesfi;
|
|
|
|
if (SUCCEEDED(m_pDFFolder->GetItem(m_pDFFolder->_iGetIDList, &pesfi)) && pesfi)
|
|
{
|
|
if (DF_ILIsEqual(&pesfi->idl, pidl))
|
|
{
|
|
// Yep it was ours so return the index quickly..
|
|
*piItem = m_pDFFolder->_iGetIDList;
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise let it search the old fashion way...
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnDeleteItem(DWORD pv, LPCITEMIDLIST pidl)
|
|
{
|
|
// We simply need to remove this item from our list. The
|
|
// underlying listview will decrement the count on their end...
|
|
ESFItem *pesfi;
|
|
int iItem;
|
|
int cItems;
|
|
BOOL bFound;
|
|
|
|
if (!pidl)
|
|
{
|
|
m_pDFFolder->SetAsyncEnum(NULL);
|
|
return NOERROR; // special case telling us all items deleted...
|
|
}
|
|
|
|
bFound = FALSE;
|
|
|
|
if (SUCCEEDED(m_pDFFolder->GetItem(m_pDFFolder->_iGetIDList, &pesfi))
|
|
&& pesfi
|
|
&& (DF_ILIsEqual(&pesfi->idl, pidl)))
|
|
{
|
|
iItem = m_pDFFolder->_iGetIDList;
|
|
bFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (SUCCEEDED(m_pDFFolder->GetItemCount(&cItems))) {
|
|
for (iItem = 0; iItem < cItems; iItem++)
|
|
{
|
|
if (SUCCEEDED(m_pDFFolder->GetItem(iItem, &pesfi)) && pesfi && (DF_ILIsEqual(&pesfi->idl, pidl)))
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
m_pDFFolder->DeleteItem(iItem);
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
HRESULT CDocFindSFVCB::OnODFindItem(DWORD pv, int * piItem, NM_FINDITEM* pnmfi)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - DF_FSNCallBack SFVM_ODFINDITEM"));
|
|
// We have to do the subsearch ourself to find the correct item...
|
|
// As the listview has no information saved in it...
|
|
|
|
int iItem = pnmfi->iStart;
|
|
int cItem;
|
|
UINT flags = pnmfi->lvfi.flags;
|
|
|
|
if (FAILED(m_pDFFolder->GetItemCount(&cItem)))
|
|
return E_FAIL;
|
|
|
|
if ((flags & LVFI_STRING) == 0)
|
|
return E_FAIL; // Not sure what type of search this is...
|
|
|
|
int cbString = lstrlen(pnmfi->lvfi.psz);
|
|
|
|
for (int j = cItem; j-- != 0; )
|
|
{
|
|
if (iItem >= cItem)
|
|
{
|
|
if (flags & LVFI_WRAP)
|
|
iItem = 0;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Now we need to get the Display name for this item...
|
|
ESFItem *pesfi;
|
|
STRRET strret;
|
|
TCHAR szPath[MAX_PATH];
|
|
IShellFolder* psf = (IShellFolder*)m_pDFFolder;
|
|
|
|
if (SUCCEEDED(m_pDFFolder->GetItem(iItem, &pesfi)) && pesfi && SUCCEEDED(psf->GetDisplayNameOf(&pesfi->idl, NULL, &strret)) &&
|
|
SUCCEEDED(StrRetToBuf(&strret, &pesfi->idl, szPath, ARRAYSIZE(szPath))))
|
|
{
|
|
if (flags & (LVFI_PARTIAL|LVFI_SUBSTRING))
|
|
{
|
|
if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
|
|
pnmfi->lvfi.psz, cbString, szPath, cbString) == 2)
|
|
{
|
|
*piItem = iItem;
|
|
return S_OK;
|
|
}
|
|
|
|
}
|
|
else if (lstrcmpi(pnmfi->lvfi.psz, szPath) == 0)
|
|
{
|
|
*piItem = iItem;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
++iItem;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP)
|
|
{
|
|
// Try to remember which item is focused...
|
|
if (lP->uNewState & LVIS_FOCUSED)
|
|
m_iFocused = wPh;
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetEmptyText(DWORD pv, UINT cchTextMax, LPTSTR pszText)
|
|
{
|
|
if (cchTextMax && pszText)
|
|
{
|
|
if( *m_pDFFolder->_szEmptyText )
|
|
{
|
|
lstrcpyn( pszText, m_pDFFolder->_szEmptyText, cchTextMax ) ;
|
|
return S_OK ;
|
|
}
|
|
|
|
if (!LoadString(HINST_THISDLL, IDS_FINDVIEWEMPTYINIT,
|
|
pszText, cchTextMax))
|
|
{
|
|
ASSERT( FALSE ) ; // bad resource string identifier.
|
|
return E_UNEXPECTED ;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::SortOnColumn(UINT wP)
|
|
{
|
|
int iStart;
|
|
int iEnd;
|
|
|
|
// See if their is any controller object registered that may want to take over this...
|
|
// if we are in a mixed query and we have already sucked up the async items, simply sort
|
|
// the dpa's...
|
|
IDFEnum *pidfenum;
|
|
m_pDFFolder->GetAsyncEnum(&pidfenum);
|
|
|
|
if (pidfenum && !((pidfenum->FQueryIsAsync() == DF_QUERYISMIXED) && m_pDFFolder->_fAllAsyncItemsCached))
|
|
{
|
|
if (m_pDFFolder->_pdfcn)
|
|
{
|
|
// if they return S_FALSE it implies that they handled it and they do not
|
|
// want the default processing to happen...
|
|
if (m_pDFFolder->_pdfcn->DoSortOnColumn(wP, m_iColSort == wP) == S_FALSE)
|
|
{
|
|
m_iColSort = wP;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// If we are running in the ROWSET way, we may want to have the ROWSET do the work...
|
|
else
|
|
{
|
|
HRESULT hres = S_OK;
|
|
// pass one we spawn off a new search with the right column sorted
|
|
if (m_iColSort != wP)
|
|
{
|
|
m_iColSort = wP;
|
|
}
|
|
|
|
// Warning the call above may release our AsyncEnum and generate a new one so
|
|
// Don't rely on it's existence here...
|
|
return hres;
|
|
}
|
|
}
|
|
|
|
// we must pull in all the results from ci
|
|
if (pidfenum && pidfenum->FQueryIsAsync() && !m_pDFFolder->_fAllAsyncItemsCached)
|
|
m_pDFFolder->CacheAllAsyncItems();
|
|
|
|
#ifdef DEBUG
|
|
#define MAX_LISTVIEWITEMS (100000000 & ~0xFFFF)
|
|
#define SANE_ITEMCOUNT(c) ((int)min(c, MAX_LISTVIEWITEMS))
|
|
|
|
if (pidfenum && pidfenum->FQueryIsAsync())
|
|
{
|
|
ASSERT(DPA_GetPtrCount(m_pDFFolder->hdpaItems) >= SANE_ITEMCOUNT(m_pDFFolder->_cAsyncItems));
|
|
for (int i = 0; i < SANE_ITEMCOUNT(m_pDFFolder->_cAsyncItems); i++)
|
|
{
|
|
ESFItem *pesfi = (ESFItem *)DPA_GetPtr(m_pDFFolder->hdpaItems, i);
|
|
|
|
ASSERT(pesfi);
|
|
if (!pesfi)
|
|
{
|
|
ASSERT(SUCCEEDED(m_pDFFolder->GetItem(i, &pesfi)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
EnterCriticalSection(&m_pDFFolder->_csSearch);
|
|
// First mark the focused item in the list so we can find it later...
|
|
ESFItem *pesfi;
|
|
pesfi = (ESFItem*)DPA_GetPtr(m_pDFFolder->hdpaItems, m_iFocused); // indirect
|
|
if (pesfi)
|
|
pesfi->dwState |= LVIS_FOCUSED;
|
|
|
|
// Being that we are in virtual mode now, we need to do the sort
|
|
// internal...
|
|
// First remember which item is focused(BUGBUG)
|
|
HDPA hdpa = m_pDFFolder->hdpaItems;
|
|
UINT cItems = DPA_GetPtrCount(hdpa);
|
|
|
|
if (wP != m_iColSort || (m_pDFFolder->fItemsChangedSinceSort))
|
|
{
|
|
DFSINFO dfsinfo = {m_pshf, wP};
|
|
DPA_Sort(hdpa, DFSCompareItems, (LPARAM)&dfsinfo);
|
|
m_pDFFolder->fItemsChangedSinceSort = FALSE;
|
|
m_iColSort = wP;
|
|
}
|
|
else
|
|
{
|
|
// We simply need to reverse the items in the dpa
|
|
iStart = 0;
|
|
iEnd = (int)cItems - 1;
|
|
while (iStart < iEnd)
|
|
{
|
|
void *pv = DPA_FastGetPtr(hdpa, iStart);
|
|
DPA_SetPtr(hdpa, iStart, DPA_FastGetPtr(hdpa, iEnd));
|
|
DPA_SetPtr(hdpa, iEnd, pv);
|
|
iStart++;
|
|
iEnd--;
|
|
}
|
|
}
|
|
|
|
// Now find the focused item and scroll it into place...
|
|
|
|
|
|
IShellView *psv;
|
|
if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_IShellView, (void **)&psv)))
|
|
{
|
|
int iFocused = -1;
|
|
|
|
// Tell the view we need to reshuffle....
|
|
// Gross, this one defaults to invalidate all which for this one is fine...
|
|
DocFind_SetObjectCount(psv, DPA_GetPtrCount(hdpa), SFVSOC_INVALIDATE_ALL); // Invalidate all
|
|
|
|
for (iEnd = DPA_GetPtrCount(hdpa)-1; iEnd >= 0; iEnd--)
|
|
{
|
|
pesfi = (ESFItem*)DPA_GetPtr(m_pDFFolder->hdpaItems, iEnd); // indirect
|
|
if (pesfi && pesfi->dwState & LVIS_FOCUSED)
|
|
iFocused = iEnd;
|
|
}
|
|
// Now handle the focused item...
|
|
if (iFocused != -1)
|
|
{
|
|
m_pDFFolder->_iGetIDList = iFocused; // remember the last one we retrieved...
|
|
pesfi = (ESFItem*)DPA_GetPtr(m_pDFFolder->hdpaItems, iFocused); // indirect
|
|
if (pesfi)
|
|
{
|
|
// flags depend on first one and also if selected?
|
|
psv->SelectItem(&pesfi->idl, SVSI_FOCUSED | SVSI_ENSUREVISIBLE | SVSI_SELECT);
|
|
pesfi->dwState &= ~LVIS_FOCUSED; // don't keep it around to get lost later...
|
|
}
|
|
}
|
|
|
|
m_iFocused = iFocused;
|
|
|
|
// Release our use of the view...
|
|
m_fIgnoreSelChange = FALSE;
|
|
psv->Release();
|
|
}
|
|
LeaveCriticalSection(&m_pDFFolder->_csSearch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// BUGBUG:: Need a cleaner way to handle getting listview first item and number of items on page...
|
|
HRESULT CDocFindSFVCB::OnWindowCreated(DWORD pv, HWND hwnd)
|
|
{
|
|
HWND hwndT;
|
|
LONG id;
|
|
#define ID_LISTVIEW 1
|
|
|
|
hwndT = GetWindow(hwnd, GW_CHILD);
|
|
while(hwndT)
|
|
{
|
|
id = GetWindowLong(hwndT, GWL_ID);
|
|
if (id == ID_LISTVIEW)
|
|
{
|
|
m_pDFFolder->_hwndLV = hwndT; // remember the listview for later...
|
|
break;
|
|
}
|
|
hwndT = GetWindow(hwndT, GW_HWNDNEXT);
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnWindowDestroy(DWORD pv, HWND wP)
|
|
{
|
|
if (m_pDFFolder->_pdfcn)
|
|
m_pDFFolder->_pdfcn->StopSearch();
|
|
// The search may have a circular set of pointers. So call the
|
|
// delete items and folders here to remove these back references...
|
|
m_pDFFolder->ClearItemList();
|
|
m_pDFFolder->ClearFolderList();
|
|
|
|
IDocFindControllerNotify *pdfcn;
|
|
if (m_pDFFolder->GetControllerNotifyObject(&pdfcn) == S_OK)
|
|
{
|
|
ASSERT(pdfcn);
|
|
pdfcn->ViewDestroyed();
|
|
pdfcn->Release();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnIsOwnerData(DWORD pv, BOOL *pfIsOwnerData)
|
|
{
|
|
*pfIsOwnerData = TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetODRangeObject(DWORD pv, WPARAM wWhich, ILVRange **plvr)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
switch (wWhich)
|
|
{
|
|
case LVSR_SELECTION:
|
|
hres = m_pDFFolder->_dflvrSel.QueryInterface(IID_ILVRange, (void **)plvr);
|
|
break;
|
|
case LVSR_CUT:
|
|
hres = m_pDFFolder->_dflvrCut.QueryInterface(IID_ILVRange, (void **)plvr);
|
|
break;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnODCacheHint(DWORD pv, NMLVCACHEHINT* pnmlvc)
|
|
{
|
|
// The listview is giving us a hint of the items it is about to do something in a range
|
|
// so make sure we have pidls for each of the items in the range...
|
|
int i;
|
|
int iTo;
|
|
|
|
m_pDFFolder->GetItemCount(&iTo);
|
|
if (iTo >= pnmlvc->iTo)
|
|
iTo = pnmlvc->iTo;
|
|
else
|
|
iTo--;
|
|
|
|
for (i=pnmlvc->iFrom; i <= iTo; i++)
|
|
{
|
|
ESFItem *pesfi;
|
|
if (FAILED(m_pDFFolder->GetItem(i, &pesfi)))
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP)
|
|
{
|
|
*lP = FVM_DETAILS;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetViews(DWORD pv, SHELLVIEWID *pvid, IEnumSFVViews **ppObj)
|
|
{
|
|
// BUGBUG:: may want to allow some template to specified, but for now not...
|
|
CViewsList cViews;
|
|
|
|
if (m_pDFFolder->pdfff != NULL)
|
|
{
|
|
CLSID clsid;
|
|
if (SUCCEEDED(m_pDFFolder->pdfff->GetSearchFolderClassId(&clsid)))
|
|
cViews.AddCLSID(&clsid);
|
|
}
|
|
|
|
*pvid = VID_Details; // We want to default to report view...
|
|
|
|
// Add this class stuff
|
|
// cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory"));
|
|
|
|
return CreateEnumCViewList(&cViews, ppObj);
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnOverrideItemCount(DWORD pv, UINT*lP)
|
|
{
|
|
// make sure we have room for a lot of items, since normally
|
|
// we have 0 and the window gets sized too small...
|
|
*lP = 4000;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetIPersistHistory(DWORD pv, IPersistHistory **ppph)
|
|
{
|
|
// If they call us with ppph == NULL they simply want to know if we support
|
|
// the history so return S_OK;
|
|
if (ppph == NULL)
|
|
return S_OK;
|
|
|
|
// get the persist history from us and we hold folder and view objects
|
|
*ppph = NULL;
|
|
|
|
CDocFindPersistHistory *pdfph = new CDocFindPersistHistory();
|
|
if (!pdfph)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hres = pdfph->QueryInterface(IID_IPersistHistory, (void**)ppph);
|
|
pdfph->Release();
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnRefresh(DWORD pv, BOOL fPreRefresh)
|
|
{
|
|
EnterCriticalSection(&m_pDFFolder->_csSearch);
|
|
|
|
m_pDFFolder->_fInRefresh = BOOLIFY(fPreRefresh);
|
|
// If we have old results tell defview the new count now...
|
|
if (!fPreRefresh && m_pDFFolder->hdpaItems)
|
|
{
|
|
IShellFolderView *psfv;
|
|
UINT cItems = DPA_GetPtrCount(m_pDFFolder->hdpaItems);
|
|
if (cItems && _punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_IShellFolderView, (void**)&psfv)))
|
|
{
|
|
psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
|
|
psfv->Release();
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_pDFFolder->_csSearch);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocFindSFVCB::OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *phtd)
|
|
{
|
|
StrCpyW( phtd->wszHelpFile, L"find.chm" );
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindSFVCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(0, SFVM_MERGEMENU, OnMERGEMENU);
|
|
HANDLE_MSG(0, SFVM_GETWORKINGDIR, OnGETWORKINGDIR);
|
|
HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND);
|
|
HANDLE_MSG(0, SFVM_GETCOLSAVESTREAM, OnGETCOLSAVESTREAM);
|
|
HANDLE_MSG(0, SFVM_GETITEMIDLIST, OnGETITEMIDLIST);
|
|
HANDLE_MSG(0, SFVM_SETITEMIDLIST, OnSETITEMIDLIST);
|
|
HANDLE_MSG(0, SFVM_SELCHANGE, OnSelChange);
|
|
HANDLE_MSG(0, SFVM_INDEXOFITEMIDLIST, OnGetIndexForItemIDList);
|
|
HANDLE_MSG(0, SFVM_DELETEITEM, OnDeleteItem);
|
|
HANDLE_MSG(0, SFVM_ODFINDITEM, OnODFindItem);
|
|
HANDLE_MSG(0, SFVM_ARRANGE, OnReArrange);
|
|
HANDLE_MSG(0, SFVM_GETEMPTYTEXT, OnGetEmptyText);
|
|
HANDLE_MSG(0, SFVM_GETITEMICONINDEX, OnGetItemIconIndex);
|
|
HANDLE_MSG(0, SFVM_SETICONOVERLAY, OnSetItemIconOverlay);
|
|
HANDLE_MSG(0, SFVM_GETICONOVERLAY, OnGetItemIconOverlay);
|
|
HANDLE_MSG(0, SFVM_ISOWNERDATA, OnIsOwnerData);
|
|
HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
|
|
HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWindowDestroy);
|
|
HANDLE_MSG(0, SFVM_GETODRANGEOBJECT, OnGetODRangeObject);
|
|
HANDLE_MSG(0, SFVM_ODCACHEHINT, OnODCacheHint);
|
|
HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE);
|
|
HANDLE_MSG(0, SFVM_GETVIEWS, OnGetViews);
|
|
HANDLE_MSG(0, SFVM_OVERRIDEITEMCOUNT, OnOverrideItemCount);
|
|
HANDLE_MSG(0, SFVM_GETIPERSISTHISTORY, OnGetIPersistHistory);
|
|
HANDLE_MSG(0, SFVM_REFRESH, OnRefresh);
|
|
HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
|
|
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
CDocFindSFVCB::~CDocFindSFVCB()
|
|
{
|
|
ATOMICRELEASE(m_pDFFolder);
|
|
}
|
|
|
|
CDocFindSFVCB* DocFind_CreateSFVCB(IShellFolder* psf, CDFFolder* pDFFolder)
|
|
{
|
|
return(new CDocFindSFVCB(psf, pDFFolder));
|
|
}
|
|
|
|
|
|
|
|
|
|
// Docfind Persistent history implemention.
|
|
|
|
CDocFindPersistHistory::CDocFindPersistHistory()
|
|
{
|
|
}
|
|
|
|
|
|
STDAPI CDocFindPersistHistory_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
CDocFindPersistHistory *pdfph = new CDocFindPersistHistory();
|
|
if (!pdfph)
|
|
{
|
|
*ppv = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hr = pdfph->QueryInterface(riid, ppv);
|
|
pdfph->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Functions to support persisting the document into the history stream...
|
|
STDMETHODIMP CDocFindPersistHistory::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_DocFindPersistHistory;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
IDocFindFolder *CDocFindPersistHistory::_GetDocFindFolder()
|
|
{
|
|
IDocFindFolder *pdff = NULL;
|
|
IShellFolder *psf;
|
|
IDefViewFrame *pdvf;
|
|
|
|
// the _punksite is to the defview so we can simply QI for frame...
|
|
if (SUCCEEDED(_punkSite->QueryInterface(IID_IDefViewFrame, (void **)&pdvf)))
|
|
{
|
|
if (SUCCEEDED(pdvf->GetShellFolder(&psf)))
|
|
{
|
|
psf->QueryInterface(IID_IDocFindFolder, (void **)&pdff);
|
|
psf->Release();
|
|
}
|
|
pdvf->Release();
|
|
}
|
|
|
|
return pdff;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindPersistHistory::LoadHistory(IStream *pstm, IBindCtx *pbc)
|
|
{
|
|
int cItems = 0;
|
|
IDocFindFolder *pdff = _GetDocFindFolder();
|
|
if (pdff)
|
|
{
|
|
pdff->RestoreFolderList(pstm);
|
|
pdff->RestoreItemList(pstm, &cItems);
|
|
pdff->Release();
|
|
}
|
|
|
|
IShellFolderView *psfv;
|
|
if (_punkSite && SUCCEEDED(_punkSite->QueryInterface(IID_IShellFolderView, (void**)&psfv)))
|
|
{
|
|
psfv->SetObjectCount(cItems, SFVSOC_NOSCROLL);
|
|
psfv->Release();
|
|
}
|
|
|
|
// call our base class to allow it to restore it's stuff as well.
|
|
return CDefViewPersistHistory::LoadHistory(pstm, pbc);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDocFindPersistHistory::SaveHistory(IStream *pstm)
|
|
{
|
|
IShellFolderView *psfv = NULL;
|
|
IDocFindFolder *pdff = _GetDocFindFolder();
|
|
|
|
if (pdff)
|
|
{
|
|
pdff->SaveFolderList(pstm);
|
|
pdff->SaveItemList(pstm);
|
|
pdff->Release();
|
|
}
|
|
|
|
// Let base class save out as well
|
|
return CDefViewPersistHistory::SaveHistory(pstm);
|
|
}
|
|
|
|
|
|
|
|
|
|
int CALLBACK DFSCompareItems(void *p1, void *p2, LPARAM lParam)
|
|
{
|
|
DFSINFO *pdfsinfo = (DFSINFO*)lParam;
|
|
HRESULT hres = E_FAIL;
|
|
|
|
// we should not have NULL items in DPA!!!!
|
|
if (EVAL(p1 && p2))
|
|
{
|
|
hres = pdfsinfo->pshf->CompareIDs(pdfsinfo->iCol,
|
|
&(((ESFItem*)p1)->idl), &(((ESFItem*)p2)->idl));
|
|
}
|
|
return (short)SCODE_CODE(GetScode(hres));
|
|
}
|
|
|
|
|
|
// CDocFindLVRange object - Object to manage the selection states for an
|
|
// owner data listview...
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CDocFindLVRange::AddRef()
|
|
{
|
|
return m_pdff->AddRef();
|
|
}
|
|
STDMETHODIMP_(ULONG) CDocFindLVRange::Release()
|
|
{
|
|
return m_pdff->Release();
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CDocFindLVRange, ILVRange), // IID_ILVRange
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
|
|
// ILVRange methods
|
|
STDMETHODIMP CDocFindLVRange::IncludeRange(LONG iBegin, LONG iEnd)
|
|
{
|
|
// Including the range must load the elements as we need the object ptr...
|
|
ESFItem *pesfi;
|
|
long i;
|
|
int iTotal;
|
|
|
|
m_pdff->GetItemCount(&iTotal);
|
|
if (iEnd > iTotal)
|
|
iEnd = iTotal-1;
|
|
|
|
for (i = iBegin; i <= iEnd;i++)
|
|
{
|
|
if (SUCCEEDED(m_pdff->GetItem(i, &pesfi)) && pesfi)
|
|
{
|
|
if ((pesfi->dwState & m_dwMask) == 0)
|
|
{
|
|
m_cIncluded++;
|
|
pesfi->dwState |= m_dwMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::ExcludeRange(LONG iBegin, LONG iEnd)
|
|
{
|
|
// Excluding the range is OK to not load the elements as this would be to deslect all...
|
|
long i;
|
|
|
|
ESFItem *pesfi;
|
|
|
|
EnterCriticalSection(&m_pdff->_csSearch);
|
|
if (iEnd >= DPA_GetPtrCount(m_pdff->hdpaItems))
|
|
iEnd = DPA_GetPtrCount(m_pdff->hdpaItems)-1;
|
|
|
|
for (i = iBegin; i <= iEnd; i++)
|
|
{
|
|
pesfi = (ESFItem*)DPA_FastGetPtr(m_pdff->hdpaItems, i);
|
|
if (pesfi)
|
|
{
|
|
if (pesfi->dwState & m_dwMask)
|
|
{
|
|
m_cIncluded--;
|
|
pesfi->dwState &= ~m_dwMask;
|
|
}
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_pdff->_csSearch);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::InvertRange(LONG iBegin, LONG iEnd)
|
|
{
|
|
// Including the range must load the elements as we need the object ptr...
|
|
|
|
ESFItem *pesfi;
|
|
long i;
|
|
int iTotal;
|
|
|
|
m_pdff->GetItemCount(&iTotal);
|
|
if (iEnd > iTotal)
|
|
iEnd = iTotal-1;
|
|
|
|
for (i = iBegin; i <= iEnd;i++)
|
|
{
|
|
if (SUCCEEDED(m_pdff->GetItem(i, &pesfi)) && pesfi)
|
|
{
|
|
if ((pesfi->dwState & m_dwMask) == 0)
|
|
{
|
|
m_cIncluded++;
|
|
pesfi->dwState |= m_dwMask;
|
|
}
|
|
else
|
|
{
|
|
m_cIncluded--;
|
|
pesfi->dwState &= ~m_dwMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::InsertItem(LONG iItem)
|
|
{
|
|
// We already maintain the list anyway...
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::RemoveItem(LONG iItem)
|
|
{
|
|
// We maintain the list so don't do anything...
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::Clear()
|
|
{
|
|
// If there are things selected, need to unselect them now...
|
|
if (m_cIncluded)
|
|
ExcludeRange(0, LONG_MAX);
|
|
|
|
m_cIncluded = 0;
|
|
m_pdff->ClearSaveStateList();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::IsSelected(LONG iItem)
|
|
{
|
|
// Don't force the items to be generated if they were not before...
|
|
ESFItem *pesfi;
|
|
|
|
EnterCriticalSection(&m_pdff->_csSearch);
|
|
pesfi = (ESFItem*)DPA_GetPtr(m_pdff->hdpaItems, iItem);
|
|
LeaveCriticalSection(&m_pdff->_csSearch);
|
|
if (pesfi)
|
|
return (pesfi->dwState & m_dwMask)? S_OK : S_FALSE;
|
|
|
|
// Assume not selected if we don't have the item yet...
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::IsEmpty()
|
|
{
|
|
return m_cIncluded ? S_FALSE : S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::NextSelected(LONG iItem, LONG *piItem)
|
|
{
|
|
EnterCriticalSection(&m_pdff->_csSearch);
|
|
LONG cItems = DPA_GetPtrCount(m_pdff->hdpaItems);
|
|
|
|
while (iItem < cItems)
|
|
{
|
|
ESFItem *pesfi;
|
|
pesfi = (ESFItem*)DPA_GetPtr(m_pdff->hdpaItems, iItem);
|
|
if (pesfi && (pesfi->dwState & m_dwMask))
|
|
{
|
|
*piItem = iItem;
|
|
LeaveCriticalSection(&m_pdff->_csSearch);
|
|
return S_OK;
|
|
}
|
|
iItem++;
|
|
}
|
|
LeaveCriticalSection(&m_pdff->_csSearch);
|
|
*piItem = -1;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::NextUnSelected(LONG iItem, LONG *piItem)
|
|
{
|
|
EnterCriticalSection(&m_pdff->_csSearch);
|
|
LONG cItems = DPA_GetPtrCount(m_pdff->hdpaItems);
|
|
|
|
while (iItem < cItems)
|
|
{
|
|
ESFItem *pesfi;
|
|
pesfi = (ESFItem*)DPA_GetPtr(m_pdff->hdpaItems, iItem);
|
|
if (!pesfi || ((pesfi->dwState & m_dwMask) == 0))
|
|
{
|
|
*piItem = iItem;
|
|
LeaveCriticalSection(&m_pdff->_csSearch);
|
|
return S_OK;
|
|
}
|
|
iItem++;
|
|
}
|
|
LeaveCriticalSection(&m_pdff->_csSearch);
|
|
*piItem = -1;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CDocFindLVRange::CountIncluded(LONG *pcIncluded)
|
|
{
|
|
*pcIncluded = m_cIncluded;
|
|
|
|
// Sortof Gross, but if looking at selection then also include the list of items
|
|
// that are selected in our save list...
|
|
if (m_dwMask & LVIS_SELECTED)
|
|
*pcIncluded += m_pdff->_cSaveStateSelected;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
// OLEDB search
|
|
|
|
|
|
#ifdef WINNT
|
|
|
|
#ifdef CI_ROWSETNOTIFY
|
|
class CDFOleDBRowsetWatchNotify;
|
|
#endif //#ifdef CI_ROWSETNOTIFY
|
|
|
|
// Define OleDBEnum translation structure...
|
|
typedef struct _dfodbet // DFET for short
|
|
{
|
|
struct _dfodbet *pdfetNext;
|
|
LPWSTR pwszFrom;
|
|
int cbFrom;
|
|
LPWSTR pwszTo;
|
|
} DFODBET;
|
|
|
|
|
|
class CDFOleDBEnum : public IDFEnum, public IShellService
|
|
{
|
|
public:
|
|
// *** IUnknown methods ***
|
|
STDMETHOD(QueryInterface) (REFIID riid, void **ppvObj);
|
|
STDMETHOD_(ULONG,AddRef) () ;
|
|
STDMETHOD_(ULONG,Release) ();
|
|
|
|
// *** IDFEnum methods (sortof stander iterator methods ***
|
|
STDMETHOD(Next)(LPITEMIDLIST *ppidl,
|
|
int *pcObjectSearched,
|
|
int *pcFoldersSearched,
|
|
BOOL *pfContinue,
|
|
int *pState,
|
|
HWND hwnd);
|
|
STDMETHOD(Skip)(int celt);
|
|
STDMETHOD(Reset)();
|
|
STDMETHOD(StopSearch)();
|
|
STDMETHOD_(BOOL,FQueryIsAsync)();
|
|
STDMETHOD(GetAsyncCount)(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone);
|
|
STDMETHOD(GetItemIDList)(UINT iItem, LPITEMIDLIST *ppidl);
|
|
STDMETHOD(GetExtendedDetailsOf)(LPCITEMIDLIST pidl, UINT iCol, LPSHELLDETAILS pdi);
|
|
STDMETHOD(GetExtendedDetailsULong)(LPCITEMIDLIST pidl, UINT iCol, ULONG *pul);
|
|
STDMETHOD(GetItemID)(UINT iItem, DWORD *puWorkID);
|
|
STDMETHOD(SortOnColumn)(UINT iCOl, BOOL fAscending);
|
|
|
|
// *** IShellService methods (sortof stander iterator methods ***
|
|
STDMETHOD(SetOwner)(struct IUnknown* punkOwner);
|
|
|
|
CDFOleDBEnum(
|
|
IDocFindFileFilter * pdfff,
|
|
IDocFindFolder *pdfFolder,
|
|
DWORD grfFlags,
|
|
int iColSort,
|
|
LPTSTR pszProgressText,
|
|
IRowsetWatchNotify *prwn);
|
|
~CDFOleDBEnum();
|
|
|
|
protected:
|
|
LONG _cRef;
|
|
IDocFindFileFilter* _pdfff;
|
|
IRowsetWatchNotify* _prwn;
|
|
IDocFindFolder* _pdfFolder;
|
|
int _iColSort;
|
|
DWORD _grfFlags;
|
|
DWORD _grfWarnings;
|
|
LPTSTR _pszProgressText;
|
|
TCHAR _szCurrentDir[MAX_PATH];
|
|
IShellFolder *_psfCurrentDir;
|
|
int _iFolder;
|
|
ICommand* _pCommand;
|
|
IRowsetLocate* _pRowset;
|
|
IRowsetAsynch* _pRowsetAsync;
|
|
HACCESSOR _hAccessor;
|
|
HACCESSOR _hAccessorWorkID;
|
|
HROW _ahrow[100]; // Cache 100 hrows out for now
|
|
UINT _ihrowFirst; // The index of which row is cached out first
|
|
DBCOUNTITEM _cRows; // number of hrows in _ahrow
|
|
#ifdef CI_ROWSETNOTIFY
|
|
CDFOleDBRowsetWatchNotify *_pdbrWatchNotify; // We hold onto the notify...
|
|
DWORD _dwAdviseID; // Remember our notify ID...
|
|
IConnectionPoint *_pcpointWatchNotify;
|
|
#endif
|
|
DFODBET *_pdfetFirst; // Name translation list.
|
|
|
|
DWORD _fDone:1;
|
|
|
|
// called from our WatchNotifyClass...
|
|
void HandleWatchNotify(DBWATCHNOTIFY eChangeReason);
|
|
|
|
HRESULT _BuildAndSetCommandTree(int iCol, BOOL fReverse);
|
|
|
|
HRESULT _SetCmdProp(ICommand *pCommand);
|
|
HRESULT _MapColumns(
|
|
IUnknown * pUnknown,
|
|
DBORDINAL cCols,
|
|
DBBINDING * pBindings,
|
|
const DBID * pDbCols,
|
|
HACCESSOR & hAccessor );
|
|
void _ReleaseAccessor();
|
|
HRESULT _CacheRowSet(UINT iItem);
|
|
|
|
HRESULT DoQuery(LPWSTR *apwszPaths, UINT *pcPaths);
|
|
|
|
friend class CDFOleDBRowsetWatchNotify;
|
|
friend HRESULT DocFind_CreateOleDBEnum(
|
|
IDocFindFileFilter * pdfff,
|
|
IShellFolder *psf,
|
|
LPWSTR *apwszPaths,
|
|
UINT *pcPaths,
|
|
DWORD grfFlags,
|
|
int iColSort,
|
|
LPTSTR pszProgressText,
|
|
IRowsetWatchNotify *prwn,
|
|
IDFEnum **ppdfenum);
|
|
};
|
|
|
|
#ifdef CI_ROWSETNOTIFY
|
|
// Define RowsetWatchNotify class...
|
|
class CDFOleDBRowsetWatchNotify : public IRowsetNotify
|
|
{
|
|
public:
|
|
// *** IUnknown methods ***
|
|
STDMETHOD(QueryInterface) (REFIID riid, void **ppvObj);
|
|
STDMETHOD_(ULONG,AddRef) () ;
|
|
STDMETHOD_(ULONG,Release) ();
|
|
|
|
// *** IRowsetWatchNotify methods ***
|
|
STDMETHOD(OnFieldChange)(IRowset *pRowset, HROW hrow, ULONG cColumns,
|
|
ULONG rgColumns[], DBREASON eReason,
|
|
DBEVENTPHASE ePhase, BOOL fCantDeny)
|
|
{
|
|
return DB_S_UNWANTEDREASON;
|
|
}
|
|
STDMETHOD(OnRowChange)(IRowset *pRowset, ULONG cColumns, const HROW rgColumns[],
|
|
DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny);
|
|
STDMETHOD(OnRowsetChange)(IRowset *pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny)
|
|
{
|
|
return DB_S_UNWANTEDREASON;
|
|
}
|
|
|
|
// *** Constructor and destructor ***
|
|
CDFOleDBRowsetWatchNotify(CDFOleDBEnum *pdfoe);
|
|
~CDFOleDBRowsetWatchNotify();
|
|
|
|
protected:
|
|
LONG _cRef;
|
|
CDFOleDBEnum *_pdfoe;
|
|
|
|
};
|
|
#endif //#ifdef CI_ROWSETNOTIFY
|
|
|
|
EXTERN_C HMODULE g_hmodQuery;
|
|
|
|
|
|
// Create an instance of docfind OleDB enum (CDFOleDBEnum) object.
|
|
|
|
HRESULT DocFind_CreateOleDBEnum(
|
|
IDocFindFileFilter * pdfff,
|
|
IShellFolder *psf,
|
|
LPWSTR *apwszPaths,
|
|
UINT *pcPaths,
|
|
DWORD grfFlags,
|
|
int iColSort,
|
|
LPTSTR pszProgressText,
|
|
IRowsetWatchNotify *prwn,
|
|
IDFEnum **ppdfenum)
|
|
{
|
|
HRESULT hres;
|
|
|
|
// BUGBUG !!! this is a temp hack.
|
|
// detect if query client is installed.
|
|
|
|
// currently just check if query.dll exist.
|
|
// and we do it here instead of dllload.c so
|
|
// that we don't assert if query.dll does not exist.
|
|
// will switch to a registry look up when it's available.
|
|
if (!g_hmodQuery)
|
|
{
|
|
g_hmodQuery = LoadLibrary(TEXT("query.dll"));
|
|
if (!g_hmodQuery)
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
IDocFindFolder *pdfFolder;
|
|
psf->QueryInterface(IID_IDocFindFolder, (void **)&pdfFolder);
|
|
CDFOleDBEnum* pdfenum = new CDFOleDBEnum(pdfff, pdfFolder, grfFlags, iColSort, pszProgressText, prwn);
|
|
if (pdfFolder)
|
|
pdfFolder->Release();
|
|
|
|
if (pdfenum)
|
|
{
|
|
hres = pdfenum->DoQuery(apwszPaths, pcPaths);
|
|
if (hres == S_OK) // We only continue to use this if query returne S_OK...
|
|
*ppdfenum = (IDFEnum*)pdfenum;
|
|
else
|
|
{
|
|
pdfenum->Release(); // release the memory we allocated
|
|
*ppdfenum = NULL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
else
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
|
|
|
|
const DBID c_aDbCols[] =
|
|
{
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_NAME}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_PATH}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_ATTRIBUTES}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_SIZE}},
|
|
{{PSGUID_STORAGE}, DBKIND_GUID_PROPID, {(LPOLESTR)(ULONG_PTR)(ULONG)PID_STG_WRITETIME}},
|
|
{{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR) PROPID_QUERY_RANK}},
|
|
};
|
|
|
|
const DBID c_aDbWorkIDCols[] =
|
|
{
|
|
{{PSGUID_QUERY_D}, DBKIND_GUID_PROPID, {(LPOLESTR)PROPID_QUERY_WORKID}}
|
|
};
|
|
|
|
const WCHAR c_wszColNames[] = L"FileName,Path,Attrib,Size,Write,Rank,WorkID";
|
|
#define ICOLNAME_RANK 5
|
|
const LPWSTR c_awszColSortNames[] = {L"FileName[a],Path[a]", L"Path[a],FileName[a]", L"Size[a]", NULL, L"Write[a]", L"Rank[d]"};
|
|
|
|
const ULONG c_cDbCols = ARRAYSIZE(c_aDbCols);
|
|
const DBID c_dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0};
|
|
const GUID c_guidQueryExt = DBPROPSET_QUERYEXT;
|
|
const GUID c_guidRowsetProps = {0xc8b522be,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}};
|
|
|
|
|
|
|
|
|
|
|
|
CDFOleDBEnum::CDFOleDBEnum(
|
|
IDocFindFileFilter * pdfff,
|
|
IDocFindFolder * pdfFolder,
|
|
DWORD grfFlags,
|
|
int iColSort,
|
|
LPTSTR pszProgressText,
|
|
IRowsetWatchNotify *prwn
|
|
) :
|
|
_cRef(1),
|
|
_ihrowFirst((UINT)-1),
|
|
_pdfff(pdfff),
|
|
_pdfFolder(pdfFolder),
|
|
_prwn(prwn),
|
|
_grfFlags(grfFlags),
|
|
_grfWarnings(DFW_DEFAULT),
|
|
_iColSort(iColSort),
|
|
_pszProgressText(pszProgressText)
|
|
{
|
|
*_szCurrentDir = TEXT('\0');
|
|
|
|
ASSERT(_pRowset == 0);
|
|
ASSERT(_pRowsetAsync == 0);
|
|
ASSERT(_pCommand == 0);
|
|
ASSERT(_hAccessor == 0);
|
|
ASSERT(_hAccessorWorkID ==0);
|
|
ASSERT(_cRows == 0);
|
|
ASSERT(_fDone == 0);
|
|
#ifdef CI_ROWSETNOTIFY
|
|
ASSERT(_pdbrWatchNotify == NULL);
|
|
ASSERT(_dwAdviseID == 0);
|
|
ASSERT(_pcpointWatchNotify == NULL);
|
|
#endif
|
|
|
|
if (_pdfff)
|
|
{
|
|
_pdfff->AddRef();
|
|
_pdfff->GetWarningFlags( &_grfWarnings );
|
|
}
|
|
|
|
if (_pdfFolder)
|
|
_pdfFolder->AddRef();
|
|
|
|
if (_prwn)
|
|
_prwn->AddRef();
|
|
}
|
|
|
|
CDFOleDBEnum::~CDFOleDBEnum()
|
|
{
|
|
ATOMICRELEASE(_pdfff);
|
|
ATOMICRELEASE(_pdfFolder);
|
|
ATOMICRELEASE(_prwn);
|
|
ATOMICRELEASE(_psfCurrentDir);
|
|
|
|
if (_pRowset)
|
|
{
|
|
#ifdef CI_ROWSETNOTIFY
|
|
// If we have an advise going, remove it now
|
|
if (_pdbrWatchNotify)
|
|
{
|
|
if (_pcpointWatchNotify)
|
|
{
|
|
_pcpointWatchNotify->Unadvise(_dwAdviseID);
|
|
_pcpointWatchNotify->Release();
|
|
}
|
|
_pdbrWatchNotify->Release();
|
|
}
|
|
#endif
|
|
|
|
|
|
ATOMICRELEASE(_pRowsetAsync);
|
|
|
|
// Release any cached rows.
|
|
_CacheRowSet((UINT)-1);
|
|
|
|
if (_hAccessor || _hAccessorWorkID)
|
|
_ReleaseAccessor();
|
|
|
|
_pRowset->Release();
|
|
}
|
|
|
|
ATOMICRELEASE(_pCommand);
|
|
|
|
// Release any name translations we may have allocated.
|
|
DFODBET *pdfet = _pdfetFirst;
|
|
while (pdfet)
|
|
{
|
|
DFODBET *pdfetT = pdfet;
|
|
pdfet = pdfet->pdfetNext; // First setup to look at the next item before we free stuff...
|
|
LocalFree((HLOCAL)pdfetT->pwszFrom);
|
|
LocalFree((HLOCAL)pdfetT->pwszTo);
|
|
LocalFree((HLOCAL)pdfetT);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CDFOleDBEnum::QueryInterface(REFIID riid, void ** ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENTMULTI(CDFOleDBEnum, IUnknown, IDFEnum), // IID_IUNKNOWN
|
|
QITABENT(CDFOleDBEnum, IShellService), // IID_IShellService
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
ULONG CDFOleDBEnum::AddRef()
|
|
{
|
|
TraceMsg(TF_DOCFIND, "CDFOleDBEnum.AddRefe %d",_cRef+1);
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CDFOleDBEnum::Release()
|
|
{
|
|
TraceMsg(TF_DOCFIND, "CDFOleDBEnum.Release %d",_cRef-1);
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::Next(
|
|
LPITEMIDLIST *ppidl,
|
|
int *pcObjectSearched,
|
|
int *pcFoldersSearched,
|
|
BOOL *pfContinue,
|
|
int *pState,
|
|
HWND hwnd
|
|
)
|
|
{
|
|
// we are async from the main docind search loop...
|
|
return E_PENDING; // as good a return as any to say that we are async...
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::Skip(int celt)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::Reset()
|
|
{
|
|
// overload Reset to mean dump the rowset cache!!!
|
|
_CacheRowSet(-1);
|
|
// still return failiure
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::StopSearch()
|
|
{
|
|
// Lets see if we can find one that works...
|
|
// BUGBUG:: Should we try everything in case different rowsets use different ways to abort?
|
|
HRESULT hres = _pCommand->Cancel();
|
|
if (FAILED(hres))
|
|
hres = _pRowsetAsync->Stop();
|
|
if (FAILED(hres))
|
|
{
|
|
IDBAsynchStatus *pdbas;
|
|
if (SUCCEEDED(_pRowset->QueryInterface(IID_IDBAsynchStatus, (void **)&pdbas)))
|
|
{
|
|
hres = pdbas->Abort(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN);
|
|
pdbas->Release();
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
BOOL CDFOleDBEnum::FQueryIsAsync()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone)
|
|
{
|
|
DBCOUNTITEM dwDen;
|
|
DBCOUNTITEM dwNum;
|
|
BOOL fMore;
|
|
|
|
if (!_pRowsetAsync)
|
|
return E_FAIL;
|
|
|
|
HRESULT hres = _pRowsetAsync->RatioFinished( &dwDen, &dwNum, pdwTotalAsync, &fMore );
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
*pfQueryDone = dwDen == dwNum;
|
|
*pnPercentComplete = dwDen? (int)((dwNum * 100)/dwDen) : 100;
|
|
}
|
|
else
|
|
*pfQueryDone = TRUE; // in case that is all they are looking at...
|
|
|
|
#ifdef WANT_CI_QUERY_STATUS
|
|
if (*pfQueryDone)
|
|
{
|
|
IRowsetInfo *pinfo;
|
|
HRESULT hr = _pRowsetAsync->QueryInterface(IID_IRowsetInfo, (void **)&pinfo);
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
// This rowset property is Indexing-Service specific
|
|
|
|
DBPROPID propId = MSIDXSPROP_ROWSETQUERYSTATUS;
|
|
DBPROPIDSET propSet;
|
|
propSet.rgPropertyIDs = &propId;
|
|
propSet.cPropertyIDs = 1;
|
|
const GUID guidRowsetExt = DBPROPSET_MSIDXS_ROWSETEXT;
|
|
propSet.guidPropertySet = guidRowsetExt;
|
|
|
|
ULONG cPropertySets = 0;
|
|
DBPROPSET * pPropertySets;
|
|
|
|
hr = pinfo->GetProperties(1,
|
|
&propSet,
|
|
&cPropertySets,
|
|
&pPropertySets );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
DWORD dwStatus = pPropertySets->rgProperties->vValue.ulVal;
|
|
|
|
CoTaskMemFree( pPropertySets->rgProperties );
|
|
CoTaskMemFree( pPropertySets );
|
|
|
|
DWORD dwFill = QUERY_FILL_STATUS( dwStatus );
|
|
DWORD dwReliability = QUERY_RELIABILITY_STATUS( dwStatus );
|
|
}
|
|
}
|
|
} //DisplayRowsetStatus
|
|
|
|
#endif
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl)
|
|
{
|
|
HRESULT hres;
|
|
|
|
*ppidl = NULL;
|
|
|
|
hres = _CacheRowSet(iItem);
|
|
if (hres != S_OK)
|
|
{
|
|
return E_FAIL; // we could not get the item someone asked for, so error...
|
|
}
|
|
|
|
PROPVARIANT* data[c_cDbCols];
|
|
|
|
hres = _pRowset->GetData(_ahrow[iItem-_ihrowFirst], _hAccessor, &data);
|
|
if (S_OK == hres)
|
|
{
|
|
// data[0].pwszVal is the file name
|
|
// data[1].pwszVal is the full path (including file name)
|
|
// data[2].ulVal is the attribute
|
|
// data[3].ulVal is the size in byte
|
|
// data[4].filetime is the last write time in UTC
|
|
// data[5].ulVal is the rank of the item...
|
|
|
|
WIN32_FIND_DATA fd = {0};
|
|
WCHAR wszParent[MAX_PATH];
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
fd.dwFileAttributes = data[2]->ulVal;
|
|
fd.nFileSizeLow = data[3]->ulVal;
|
|
fd.ftLastWriteTime = data[4]->filetime;
|
|
|
|
StrCpyW(fd.cFileName, data[0]->pwszVal);
|
|
StrCpyW(wszParent, data[1]->pwszVal);
|
|
// get the parent folder name.
|
|
|
|
PathRemoveFileSpec(wszParent);
|
|
|
|
// If it is a UNC it might be one we need to translate, to handle the case that
|
|
// content index does not support redirected drives.
|
|
WCHAR wszTranslatedParent[MAX_PATH];
|
|
BOOL fTranslated = FALSE;
|
|
BOOL fFolderChanged = TRUE;
|
|
|
|
if (PathIsUNC(wszParent))
|
|
{
|
|
DFODBET *pdfet;
|
|
for (pdfet = _pdfetFirst; pdfet; pdfet = pdfet->pdfetNext)
|
|
{
|
|
if ((StrCmpNIW(wszParent, pdfet->pwszFrom, pdfet->cbFrom) == 0)
|
|
&& (wszParent[pdfet->cbFrom] == L'\\'))
|
|
{
|
|
// Ok we have a translation to use.
|
|
fTranslated = TRUE;
|
|
StrCpyW(wszTranslatedParent, pdfet->pwszTo);
|
|
// need + 1 here or we'll get something like w:\\winnt! bogus path, that is.
|
|
StrCatW(wszTranslatedParent, &wszParent[pdfet->cbFrom+1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fTranslated)
|
|
hres = SHILCreateFromPath(wszTranslatedParent, &pidl, NULL);
|
|
else
|
|
hres = SHILCreateFromPath(wszParent, &pidl, NULL);
|
|
|
|
// we've moved into another folder.
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (lstrcmp(wszParent, _szCurrentDir) != 0)
|
|
{
|
|
// folders are different, must clear the cached folder
|
|
ATOMICRELEASE(_psfCurrentDir);
|
|
|
|
hres = _pdfFolder->AddFolderToFolderList(pidl, TRUE, &_iFolder);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
DFFolderListItem *pdffli;
|
|
// save the folder name away so we don't add it
|
|
// to the list again.
|
|
lstrcpy(_szCurrentDir, wszParent);
|
|
|
|
hres = _pdfFolder->GetFolderListItem(_iFolder, &pdffli);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
ASSERT(pdffli);
|
|
|
|
_psfCurrentDir = DocFind_GetObjectsIFolder(_pdfFolder, pdffli, NULL);// no addref here!!!
|
|
if (!_psfCurrentDir)
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// failed to add folder to the list.
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fFolderChanged = FALSE;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
DWORD dwItemID;
|
|
|
|
GetItemID(iItem, &dwItemID);
|
|
|
|
hres = E_FAIL;
|
|
if (EVAL(_psfCurrentDir))
|
|
{
|
|
hres = DocFind_CreateIDList(_psfCurrentDir, &fd, _iFolder, DFDF_EXTRADATA, iItem, dwItemID, data[5]->ulVal, ppidl);
|
|
}
|
|
}
|
|
|
|
ASSERT(ShowSuperHidden() || !IsSuperHidden(fd.dwFileAttributes));
|
|
if (FAILED(hres) && hres != E_OUTOFMEMORY)
|
|
{
|
|
if (!fTranslated && fFolderChanged) // don't add net paths to the list of special folders
|
|
{
|
|
HKEY hkey;
|
|
// we could not get pidl for this item for some reason. we have to put
|
|
// it in the list of bad items so that we can tell ci not to give it to
|
|
// us the next time we do search
|
|
if (RegCreateKeyExW(HKEY_CURRENT_USER, CI_SPECIAL_FOLDERS, 0, L"", 0, KEY_WRITE | KEY_QUERY_VALUE, NULL, &hkey, NULL) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwInsert = 0; // init to zero in case query info bellow fails
|
|
WCHAR wszName[10];
|
|
WCHAR wsz[MAX_PATH];
|
|
int iEnd;
|
|
|
|
// no pidl for parent folder. it is probably a subfolder of a special folder
|
|
// so keep going up until we can find the first non special folder
|
|
if (!pidl)
|
|
{
|
|
StrCpyNW(wsz, wszParent, ARRAYSIZE(wsz));
|
|
PathRemoveBackslashW(wsz);
|
|
do
|
|
{
|
|
if (!PathRemoveFileSpecW(wsz))
|
|
break;
|
|
hres = SHILCreateFromPath(wsz, &pidl, NULL);
|
|
}
|
|
while (FAILED(hres) && hres != E_OUTOFMEMORY);
|
|
|
|
if (pidl)
|
|
{
|
|
StrCpyNW(wszParent, wsz, ARRAYSIZE(wszParent));
|
|
}
|
|
}
|
|
|
|
|
|
RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwInsert, NULL, NULL, NULL, NULL);
|
|
// start from the end as there is a high chance we added this at the end
|
|
for (int i=dwInsert-1; i>=0; i--)
|
|
{
|
|
DWORD cb = ARRAYSIZE(wsz)*SIZEOF(WCHAR);
|
|
|
|
wsprintfW(wszName, L"%d", i);
|
|
if (RegQueryValueExW(hkey, wszName, NULL, NULL, (BYTE *)wsz, &cb) == ERROR_SUCCESS)
|
|
{
|
|
LPWSTR wszTemp = StrStrIW(wsz+1, wszParent); // +1 to pass " that's at the beginning of the string
|
|
if (wszTemp && wszTemp == wsz+1)
|
|
{
|
|
dwInsert = i; // overwrite this value
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
iEnd = lstrlen(wsz);
|
|
if (EVAL(iEnd > 1))
|
|
{
|
|
int iBackslash = iEnd-3;
|
|
ASSERT(wsz[iBackslash] == L'\\');
|
|
wsz[iBackslash] = L'\0';
|
|
wszTemp = StrStrIW(wszParent, wsz+1);
|
|
wsz[iBackslash] = L'\\';
|
|
if (wszTemp && wszTemp == wszParent)
|
|
{
|
|
dwInsert = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwInsert != -1)
|
|
{
|
|
wsprintfW(wszName, L"%d", dwInsert);
|
|
WCHAR wszPath[MAX_PATH+2]; // cannot party on wszParent since we may need it bellow
|
|
|
|
PathCombine(wszPath+1, wszParent, L"*");
|
|
wszPath[0] = L'"';
|
|
iEnd = lstrlenW(wszPath);
|
|
wszPath[iEnd] = L'"';
|
|
wszPath[iEnd+1] = L'\0';
|
|
RegSetValueExW(hkey, wszName, 0, REG_SZ, (BYTE *)wszPath, (lstrlenW(wszPath)+1)*SIZEOF(WCHAR));
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
// now that we took care of this folder for the future, let's produce some kind of pidl so that defview
|
|
// doesn't have blank items...
|
|
if (!pidl)
|
|
{
|
|
LPWSTR pwszFolder;
|
|
|
|
if (fTranslated)
|
|
pwszFolder = wszTranslatedParent;
|
|
else
|
|
pwszFolder = wszParent;
|
|
PathRemoveBackslashW(pwszFolder);
|
|
do
|
|
{
|
|
if (!PathRemoveFileSpecW(pwszFolder))
|
|
break;
|
|
hres = SHILCreateFromPath(pwszFolder, &pidl, NULL);
|
|
}
|
|
while (FAILED(hres) && hres != E_OUTOFMEMORY);
|
|
}
|
|
|
|
if (pidl)
|
|
{
|
|
LPITEMIDLIST pidlChild = ILClone(ILFindLastID(pidl));
|
|
|
|
ASSERT(!*ppidl);
|
|
if (pidlChild)
|
|
{
|
|
if (ILRemoveLastID(pidl))
|
|
{
|
|
hres = _pdfFolder->AddFolderToFolderList(pidl, TRUE, &_iFolder);
|
|
// clear the cache
|
|
ATOMICRELEASE(_psfCurrentDir);
|
|
_szCurrentDir[0] = TEXT('\0');
|
|
// now we have to patch the pidl to be docfind pidl. for that we use the special items finddata
|
|
// we could hit the disk but is there any point in doing so? we have to use the item's rank value
|
|
// just so that sorting can appear to work (on the fly -- done by ci).
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
DWORD dwItemID;
|
|
|
|
GetItemID(iItem, &dwItemID);
|
|
*ppidl = DocFind_AppendDocFindData(pidlChild, _iFolder, DFDF_EXTRADATA, iItem, dwItemID, data[5]->ulVal);
|
|
// pidlChild is destroyed by DocFind_AppendDocFindData so cannot free it here..
|
|
pidlChild = NULL;
|
|
}
|
|
}
|
|
ILFree(pidlChild);
|
|
}
|
|
}
|
|
hres = *ppidl? S_OK : E_FAIL;
|
|
}
|
|
ILFree(pidl);
|
|
#ifdef DEBUG
|
|
if (FAILED(hres))
|
|
TraceMsg(TF_DOCFIND, "CI: skipping %ls\\%ls \n", wszParent, fd.cFileName);
|
|
#endif
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDFOleDBEnum::GetExtendedDetailsOf(LPCITEMIDLIST pidl, UINT iCol, LPSHELLDETAILS pdi)
|
|
{
|
|
#ifdef IF_ADD_MORE_COLS
|
|
|
|
|
|
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
|
|
if (!phdfd || !(phdfd->wFlags & DFDF_EXTRADATA))
|
|
return E_FAIL;
|
|
|
|
HRESULT hres = _CacheRowSet(phdfd->uRow);
|
|
if (hres != S_OK)
|
|
return hres; // either error or end of list...
|
|
|
|
PROPVARIANT* data[c_cDbCols];
|
|
|
|
hres = _pRowset->GetData(_ahrow[phdfd->uRow-_ihrowFirst], _hAccessor, &data);
|
|
if (S_OK == hres)
|
|
{
|
|
// The first members are associated with PIDL and not returned directly
|
|
// data[0].pwszVal is the file name
|
|
// data[1].pwszVal is the full path (including file name)
|
|
// data[2].ulVal is the attribute
|
|
// data[3].ulVal is the size in byte
|
|
// data[4].filetime is the last write time in UTC
|
|
// The first column to remove
|
|
// data[5].ulVal is the rank of the item... - ICol == 0
|
|
// BUGBUG:: currently hard wacked to only support COL = 0 (rank...)
|
|
ASSERT (iCol == 0);
|
|
TCHAR szNum[MAX_INT64_SIZE];
|
|
AddCommas(data[5]->ulVal, szNum);
|
|
#ifdef UNICODE
|
|
pdi->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szNum)+1)*SIZEOF(TCHAR));
|
|
if ( pdi->str.pOleStr != NULL ) {
|
|
pdi->str.uType = STRRET_WSTR;
|
|
lstrcpy(pdi->str.pOleStr, szNum);
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
#else
|
|
lstrcpy(pdi->str.cStr, szNum);
|
|
#endif
|
|
}
|
|
return hres;
|
|
#else
|
|
return E_FAIL;
|
|
#endif
|
|
}
|
|
|
|
|
|
HRESULT CDFOleDBEnum::GetExtendedDetailsULong(LPCITEMIDLIST pidl, UINT iCol, ULONG *pul)
|
|
{
|
|
PCHIDDENDOCFINDDATA phdfd = (PCHIDDENDOCFINDDATA) ILFindHiddenID(pidl, IDLHID_DOCFINDDATA);
|
|
|
|
*pul = 0;
|
|
if (!phdfd || !(phdfd->wFlags & DFDF_EXTRADATA))
|
|
return E_FAIL;
|
|
|
|
HRESULT hres = _CacheRowSet(phdfd->uRow);
|
|
if (hres != S_OK)
|
|
return hres; // either error or end of list...
|
|
|
|
PROPVARIANT* data[c_cDbCols];
|
|
|
|
hres = _pRowset->GetData(_ahrow[phdfd->uRow-_ihrowFirst], _hAccessor, &data);
|
|
if (S_OK == hres)
|
|
{
|
|
// The first members are associated with PIDL and not returned directly
|
|
// data[0].pwszVal is the file name
|
|
// data[1].pwszVal is the full path (including file name)
|
|
// data[2].ulVal is the attribute
|
|
// data[3].ulVal is the size in byte
|
|
// data[4].filetime is the last write time in UTC
|
|
// The first column to remove
|
|
// data[5].ulVal is the rank of the item... - ICol == 0
|
|
// BUGBUG:: currently hard wacked to only support COL = 0 (rank...)
|
|
ASSERT (iCol == 0);
|
|
*pul = data[5]->ulVal;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDFOleDBEnum::GetItemID(UINT iItem, DWORD *puItemID)
|
|
{
|
|
*puItemID = (UINT)-1;
|
|
HRESULT hres = _CacheRowSet(iItem);
|
|
if (hres != S_OK)
|
|
return hres; // either error or end of list...
|
|
|
|
PROPVARIANT* data[1];
|
|
|
|
hres = _pRowset->GetData(_ahrow[iItem-_ihrowFirst], _hAccessorWorkID, &data);
|
|
if (S_OK == hres)
|
|
{
|
|
// Only one data column so this is easy...
|
|
// The ULVal is the thing we are after...
|
|
*puItemID = data[0]->ulVal;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::SortOnColumn(UINT iCol, BOOL fAscending)
|
|
{
|
|
// Ok We need to generate the Sort String...
|
|
return _BuildAndSetCommandTree(iCol, fAscending);
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::SetOwner(struct IUnknown* punkOwner)
|
|
{
|
|
// Used to set the docfind folder and from that the filter.
|
|
ATOMICRELEASE(_pdfff);
|
|
ATOMICRELEASE(_pdfFolder);
|
|
|
|
if (punkOwner)
|
|
{
|
|
punkOwner->QueryInterface(IID_IDocFindFolder, (void **)&_pdfFolder);
|
|
if (_pdfFolder)
|
|
_pdfFolder->GetDocFindFilter(&_pdfff);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT CDFOleDBEnum::_MapColumns(
|
|
IUnknown * pUnknown,
|
|
DBORDINAL cCols,
|
|
DBBINDING * pBindings,
|
|
const DBID * pDbCols,
|
|
HACCESSOR & hAccessor )
|
|
{
|
|
IColumnsInfo * pColumnsInfo = 0;
|
|
DBORDINAL aMappedColumnIDs[c_cDbCols];
|
|
|
|
HRESULT sc = pUnknown->QueryInterface( IID_IColumnsInfo,
|
|
(void **)&pColumnsInfo);
|
|
if ( FAILED( sc ) )
|
|
return sc;
|
|
|
|
sc = pColumnsInfo->MapColumnIDs(cCols, pDbCols, aMappedColumnIDs);
|
|
|
|
pColumnsInfo->Release();
|
|
|
|
if ( FAILED( sc ) )
|
|
return sc;
|
|
|
|
for (ULONG i = 0; i < cCols; i++)
|
|
pBindings[i].iOrdinal = aMappedColumnIDs[i];
|
|
|
|
IAccessor * pIAccessor = 0;
|
|
|
|
sc = pUnknown->QueryInterface( IID_IAccessor, (void **)&pIAccessor);
|
|
if ( FAILED( sc ) )
|
|
return sc;
|
|
|
|
hAccessor = 0;
|
|
sc = pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA,
|
|
cCols, pBindings, 0, &hAccessor, 0 );
|
|
|
|
pIAccessor->Release();
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CDFOleDBEnum::_ReleaseAccessor()
|
|
{
|
|
IAccessor * pIAccessor = 0;
|
|
|
|
HRESULT sc = _pRowset->QueryInterface(IID_IAccessor, (void **)&pIAccessor);
|
|
if (FAILED(sc))
|
|
return;
|
|
|
|
if (_hAccessor)
|
|
pIAccessor->ReleaseAccessor(_hAccessor, 0);
|
|
if (_hAccessorWorkID)
|
|
pIAccessor->ReleaseAccessor(_hAccessorWorkID, 0);
|
|
|
|
pIAccessor->Release();
|
|
}
|
|
|
|
HRESULT CDFOleDBEnum::_CacheRowSet(UINT iItem)
|
|
{
|
|
HRESULT hres=S_OK;
|
|
|
|
if (!_pRowset)
|
|
return E_FAIL;
|
|
|
|
if (!_cRows || !InRange(iItem, _ihrowFirst, _ihrowFirst+(UINT)_cRows-1) || (iItem == (UINT)-1))
|
|
{
|
|
// Release the last cached element we had.
|
|
if (_cRows != 0)
|
|
_pRowset->ReleaseRows(ARRAYSIZE(_ahrow), _ahrow, 0, 0, 0);
|
|
|
|
// See if we are simply releasing our cached data...
|
|
_cRows = 0;
|
|
_ihrowFirst = (UINT)-1;
|
|
if (iItem == (UINT)-1)
|
|
return S_OK;
|
|
|
|
// Ok try to read in the next on...
|
|
BYTE bBookMark = (BYTE) DBBMK_FIRST;
|
|
HROW *rghRows = (HROW *)_ahrow;
|
|
|
|
// change this to fetch 100 or so rows at the time -- huge perf improvment
|
|
hres = _pRowset->GetRowsAt(0, 0, sizeof(bBookMark), &bBookMark, iItem, ARRAYSIZE(_ahrow), &_cRows, &rghRows);
|
|
if (FAILED(hres))
|
|
return hres;
|
|
|
|
_ihrowFirst = iItem;
|
|
|
|
if ((DB_S_ENDOFROWSET == hres) || (_cRows == 0))
|
|
{
|
|
if (_cRows == 0)
|
|
_ihrowFirst = -1;
|
|
else
|
|
hres = S_OK; // we got some items and caller expects S_OK so change DB_S_ENDOFROWSET to noerror
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
void CDFOleDBEnum::HandleWatchNotify(DBWATCHNOTIFY eChangeReason)
|
|
{
|
|
// For now we will simply Acknoledge the change...
|
|
if (_prwn)
|
|
_prwn->OnChange(NULL, eChangeReason);
|
|
|
|
// Lets see what all is in the list to see if it works or not...
|
|
#ifdef RICHER_NOTIFY_IS_FIXED
|
|
ULONG ulChanges;
|
|
DBROWWATCHCHANGE *pdbrwc;
|
|
|
|
DebugMsg(TF_DOCFIND, TEXT("sh TR - CDFOleDBEnum::HandleWatchNotify Reason(%d) RowsetWatch->Refresh called hres=%lx, changes %d"),
|
|
eChangeReason,hres, ulChanges);
|
|
if (hres == S_OK)
|
|
{
|
|
ULONG i;
|
|
for (i=0; i < ulChanges; i++)
|
|
{
|
|
DebugMsg(TF_DOCFIND, TEXT(" I:%d, Change:%d iRow:%d"),
|
|
i, pdbrwc->eChangeKind, pdbrwc->iRow);
|
|
if (pdbrwc->hRow)
|
|
_pRowset->ReleaseRows(1, &pdbrwc->hRow, 0, 0, 0);
|
|
pdbrwc++;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notes:
|
|
|
|
HRESULT CDFOleDBEnum::_SetCmdProp(ICommand *pCommand)
|
|
{
|
|
#define MAX_PROPS 8
|
|
|
|
DBPROPSET aPropSet[MAX_PROPS];
|
|
DBPROP aProp[MAX_PROPS];
|
|
ULONG cProps = 0;
|
|
HRESULT sc;
|
|
|
|
// asynchronous query
|
|
|
|
aProp[cProps].dwPropertyID = DBPROP_IRowsetAsynch;
|
|
aProp[cProps].dwOptions = 0;
|
|
aProp[cProps].dwStatus = 0;
|
|
aProp[cProps].colid = c_dbcolNull;
|
|
aProp[cProps].vValue.vt = VT_BOOL;
|
|
aProp[cProps].vValue.boolVal = VARIANT_TRUE;
|
|
|
|
aPropSet[cProps].rgProperties = &aProp[cProps];
|
|
aPropSet[cProps].cProperties = 1;
|
|
aPropSet[cProps].guidPropertySet = c_guidRowsetProps;
|
|
|
|
cProps++;
|
|
|
|
// don't timeout queries
|
|
|
|
aProp[cProps].dwPropertyID = DBPROP_COMMANDTIMEOUT;
|
|
aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
|
|
aProp[cProps].dwStatus = 0;
|
|
aProp[cProps].colid = c_dbcolNull;
|
|
aProp[cProps].vValue.vt = VT_I4;
|
|
aProp[cProps].vValue.lVal = 0;
|
|
|
|
aPropSet[cProps].rgProperties = &aProp[cProps];
|
|
aPropSet[cProps].cProperties = 1;
|
|
aPropSet[cProps].guidPropertySet = c_guidRowsetProps;
|
|
|
|
cProps++;
|
|
|
|
// We can handle PROPVARIANTs
|
|
|
|
aProp[cProps].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
|
|
aProp[cProps].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
|
|
aProp[cProps].dwStatus = 0;
|
|
aProp[cProps].colid = c_dbcolNull;
|
|
aProp[cProps].vValue.vt = VT_BOOL;
|
|
aProp[cProps].vValue.boolVal = VARIANT_TRUE;
|
|
|
|
aPropSet[cProps].rgProperties = &aProp[cProps];
|
|
aPropSet[cProps].cProperties = 1;
|
|
aPropSet[cProps].guidPropertySet = c_guidQueryExt;
|
|
|
|
cProps++;
|
|
|
|
#ifdef CI_ROWSETNOTIFY
|
|
aProp[cProps].dwPropertyID = DBPROP_ROWSET_ASYNCH;
|
|
aProp[cProps].dwOptions = DBPROPOPTIONS_REQUIRED;
|
|
aProp[cProps].dwStatus = 0;
|
|
aProp[cProps].colid = c_dbcolNull;
|
|
aProp[cProps].vValue.vt = VT_I4;
|
|
aProp[cProps].vValue.iVal = DBPROPVAL_ASYNCH_INITIALIZE |
|
|
DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION |
|
|
DBPROPVAL_ASYNCH_RANDOMPOPULATION;
|
|
|
|
aPropSet[cProps].rgProperties = &aProp[cProps];
|
|
aPropSet[cProps].cProperties = 1;
|
|
aPropSet[cProps].guidPropertySet = c_guidRowsetProps;
|
|
|
|
cProps++;
|
|
#endif
|
|
ICommandProperties * pCmdProp = 0;
|
|
sc = pCommand->QueryInterface( IID_ICommandProperties,
|
|
(void **)&pCmdProp );
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
sc = pCmdProp->SetProperties( cProps, aPropSet );
|
|
pCmdProp->Release();
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
// Notes:
|
|
|
|
HRESULT CDFOleDBEnum::_BuildAndSetCommandTree(int iCol, BOOL fReverse)
|
|
{
|
|
HRESULT hres;
|
|
|
|
ICommandTree *pCmdTree = NULL;
|
|
DBCOMMANDTREE * pTree = NULL;
|
|
|
|
LPWSTR pwszRestrictions = NULL;
|
|
ULONG ulDialect = ISQLANG_V2 ;
|
|
DWORD dwGQRFlags;
|
|
LPWSTR pwszSortBy = NULL;
|
|
|
|
// create the query command string
|
|
if (FAILED(hres = _pdfff->GenerateQueryRestrictions(&pwszRestrictions, &dwGQRFlags)))
|
|
goto Abort;
|
|
|
|
if( FAILED(hres = _pdfff->GetQueryLanguageDialect( &ulDialect )) )
|
|
goto Abort;
|
|
|
|
// BUGBUG: This is hard coded to our current list of columns, but what the heck...
|
|
WCHAR wszSort[80]; // use this to sort by different columns...
|
|
|
|
if ((iCol >= 0) && (c_awszColSortNames[iCol] != NULL))
|
|
{
|
|
// BUGBUG we are ignoring direction right now...
|
|
StrCpyW(wszSort, c_awszColSortNames[iCol]);
|
|
StrCatW(wszSort, (LPWSTR)L",Path[a],FileName[a]");
|
|
pwszSortBy = wszSort;
|
|
}
|
|
|
|
hres = CITextToFullTreeEx(pwszRestrictions, // the query
|
|
ulDialect,
|
|
c_wszColNames, // return these columns
|
|
pwszSortBy,
|
|
0, // no grouping
|
|
&pTree,
|
|
0, // no custom properties
|
|
0, // no custom properties
|
|
LOCALE_USER_DEFAULT);
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
// Map this to one that I know about
|
|
// Note: We will only do this if we require CI else we will try to fallback to old search...
|
|
// Note we are running into problems where CI says we are contained in a Catalog even if
|
|
// CI process is not running... So try to avoid this if possible
|
|
if (dwGQRFlags & GQR_REQUIRES_CI)
|
|
hres = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_CONSTRAINT);
|
|
goto Abort;
|
|
}
|
|
|
|
hres = _pCommand->QueryInterface(IID_ICommandTree, (void **)&pCmdTree);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = pCmdTree->SetCommandTree(&pTree, DBCOMMANDREUSE_NONE, FALSE);
|
|
pCmdTree->Release();
|
|
}
|
|
|
|
// Fall through to cleanup code...
|
|
|
|
Abort:
|
|
// Warning... Since a failure return from this function will
|
|
// release this class, all of the allocated items up till the failure should
|
|
// be released...
|
|
if (pwszRestrictions)
|
|
LocalFree((HLOCAL)pwszRestrictions);
|
|
|
|
return hres;
|
|
}
|
|
|
|
extern "C" HRESULT QueryCIStatus(LPDWORD pdwStatus, LPBOOL pbConfigAccess); //in fsearch.cpp
|
|
|
|
HRESULT CDFOleDBEnum::DoQuery(LPWSTR *apwszPaths, UINT *pcPaths)
|
|
{
|
|
UINT nPaths = *pcPaths;
|
|
WCHAR** aScopes;
|
|
WCHAR** aScopesOrig;
|
|
ULONG* aDepths = NULL;
|
|
WCHAR** aCatalogs;
|
|
WCHAR** aMachines;
|
|
|
|
// List of paths to search...
|
|
|
|
WCHAR wszPath[MAX_PATH];
|
|
LPWSTR pwszPath = wszPath;
|
|
LPWSTR pwszMachine, pwszCatalog;
|
|
UINT i;
|
|
UINT iPath = 0;
|
|
HRESULT hres;
|
|
DWORD dwGQR_Flags;
|
|
|
|
// Initiailize all of our query values back to unused
|
|
_hAccessor = NULL;
|
|
_hAccessorWorkID = NULL;
|
|
_pRowset = NULL;
|
|
_pRowsetAsync = NULL;
|
|
_pCommand = NULL;
|
|
|
|
DBBINDING aPropMainCols[c_cDbCols] =
|
|
{
|
|
{ 0,4*0,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,4*1,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,4*2,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,4*3,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,4*4,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
|
|
{ 0,4*5,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }
|
|
};
|
|
|
|
// Get array of search paths...
|
|
#define MAX_MACHINE_NAME_LEN 32
|
|
#define MAX_CATALOG_NAME_LEN MAX_PATH
|
|
|
|
|
|
DWORD dwStatus;
|
|
BOOL bPermission;
|
|
BOOL bIsCIRunning = TRUE;
|
|
if (SUCCEEDED(QueryCIStatus(&dwStatus, &bPermission)))
|
|
{
|
|
if (!(dwStatus == SERVICE_START_PENDING || dwStatus == SERVICE_RUNNING || dwStatus == SERVICE_CONTINUE_PENDING))
|
|
{
|
|
bIsCIRunning = FALSE;
|
|
}
|
|
}
|
|
|
|
// First pass see if we have anything that make use at all of CI if not lets simply bail and let
|
|
// old code walk the list...
|
|
hres = _pdfff->GenerateQueryRestrictions(NULL, &dwGQR_Flags);
|
|
if (FAILED(hres))
|
|
goto Abort;
|
|
|
|
if ((dwGQR_Flags & GQR_MAKES_USE_OF_CI) == 0)
|
|
{
|
|
hres = S_FALSE;
|
|
goto Abort;
|
|
}
|
|
|
|
// allocate the arrays that we need to pass to CIMakeICommand and
|
|
// the buffers needed for the machine name and catalog name
|
|
|
|
// i is the size we need for one search path.
|
|
// nPaths * (1 ULONG + 4 WCHAR* + machine name buffer + catalog name buffer).
|
|
i = sizeof(ULONG) + (4 * sizeof(WCHAR*)) +
|
|
((MAX_MACHINE_NAME_LEN + MAX_CATALOG_NAME_LEN) * sizeof(WCHAR));
|
|
|
|
aDepths = (ULONG*)LocalAlloc(LPTR, nPaths * i);
|
|
if (!aDepths)
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
goto Abort;
|
|
}
|
|
|
|
// This following loop does two things,
|
|
// 1. Check if all the scopes are indexed, if any one scope is not,
|
|
// fail the call and we'll do the win32 find.
|
|
// 2. Prepare the arrays of parameters that we need to pass to
|
|
// CIMakeICommand().
|
|
|
|
aScopes = (WCHAR**)&aDepths[nPaths];
|
|
aScopesOrig = (WCHAR**)&aScopes[nPaths];
|
|
aCatalogs = (WCHAR**)&aScopesOrig[nPaths];
|
|
aMachines = (WCHAR**)&aCatalogs[nPaths];
|
|
pwszCatalog = (LPWSTR)&aMachines[nPaths];
|
|
pwszMachine = (LPWSTR)&pwszCatalog[MAX_CATALOG_NAME_LEN];
|
|
|
|
for (i = 0; i < nPaths; i++)
|
|
{
|
|
// specify the depth
|
|
|
|
// assign the search to scope
|
|
|
|
ULONG cchMachine = MAX_MACHINE_NAME_LEN;
|
|
ULONG cchCatalog = MAX_CATALOG_NAME_LEN;
|
|
WCHAR wszUNCPath[MAX_PATH];
|
|
bool fRemapped = FALSE;
|
|
|
|
// if CI is not running we can still do ci queries on a remote drive (if it is indexed)
|
|
// so we cannot just bail if ci is not running on user's machine
|
|
if (!bIsCIRunning && !PathIsUNCW(apwszPaths[i]) && !IsRemoteDrive(PathGetDriveNumberW(apwszPaths[i])))
|
|
continue; // do grep on this one
|
|
hres = LocateCatalogsW(apwszPaths[i], 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog);
|
|
if (hres != S_OK)
|
|
{
|
|
// see if by chance this is a network redirected drive. If so we CI does not handle
|
|
// these. See if we can remap to UNC path to ask again...
|
|
if (!PathIsUNC(apwszPaths[i]))
|
|
{
|
|
DWORD dwType;
|
|
DWORD nLength = ARRAYSIZE(wszUNCPath);
|
|
|
|
// BUGBUG:: this api takes TCHAR, but we only compile this part for
|
|
// WINNT...
|
|
dwType = SHWNetGetConnection(apwszPaths[i], wszUNCPath, &nLength);
|
|
if ((dwType == NO_ERROR) || (dwType == ERROR_CONNECTION_UNAVAIL))
|
|
{
|
|
LPWSTR pwsz;
|
|
|
|
fRemapped = TRUE;
|
|
pwsz = PathSkipRootW(apwszPaths[i]);
|
|
if (pwsz)
|
|
PathAppendW(wszUNCPath, pwsz);
|
|
hres = LocateCatalogsW(wszUNCPath, 0, pwszMachine, &cchMachine, pwszCatalog, &cchCatalog);
|
|
}
|
|
}
|
|
}
|
|
if (hres != S_OK)
|
|
{
|
|
if (_grfFlags & DFOO_OLDFINDCODE)
|
|
{
|
|
hres = E_FAIL; // OLD UI did not know about mixing CI and NON-CI...
|
|
goto Abort;
|
|
}
|
|
else
|
|
continue; // this one is not indexed.
|
|
}
|
|
|
|
CI_STATE state={0};
|
|
|
|
state.cbStruct = SIZEOF(state);
|
|
if (SUCCEEDED(CIState(pwszCatalog, pwszMachine, &state)))
|
|
{
|
|
BOOL fUpToDate = ((0 == state.cDocuments ) &&
|
|
(0 == (state.eState & CI_STATE_SCANNING)) &&
|
|
(0 == (state.eState & CI_STATE_READING_USNS)) &&
|
|
(0 == (state.eState & CI_STATE_STARTING)) &&
|
|
(0 == (state.eState & CI_STATE_RECOVERING)));
|
|
|
|
if (!fUpToDate)
|
|
{
|
|
if (dwGQR_Flags & GQR_REQUIRES_CI)
|
|
{
|
|
// ci not up to date and we must use it..
|
|
// inform the user that results may not be complete
|
|
if (!(_grfWarnings & DFW_IGNORE_INDEXNOTCOMPLETE))
|
|
{
|
|
hres = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXNOTCOMPLETE);
|
|
goto Abort;
|
|
}
|
|
//else use ci although index is not complete
|
|
}
|
|
else
|
|
{
|
|
// ci is not upto date so just use grep for this drive so user can get
|
|
// complete results
|
|
pwszCatalog[0] = L'\0';
|
|
pwszMachine[0] = L'\0';
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
aDepths[iPath] = (_grfFlags & DFOO_INCLUDESUBDIRS) ? QUERY_DEEP : QUERY_SHALLOW;
|
|
aScopesOrig[iPath] = apwszPaths[i];
|
|
if (fRemapped)
|
|
{
|
|
aScopes[iPath] = (LPWSTR)LocalAlloc(LPTR, (lstrlenW(wszUNCPath)+1)*sizeof(WCHAR));
|
|
if (aScopes[iPath] == NULL)
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
goto Abort;
|
|
}
|
|
StrCpyW(aScopes[iPath], wszUNCPath);
|
|
}
|
|
else
|
|
aScopes[iPath] = apwszPaths[i];
|
|
|
|
aCatalogs[iPath] = pwszCatalog;
|
|
aMachines[iPath] = pwszMachine;
|
|
|
|
// advance the catalog and machine name buffer
|
|
pwszCatalog = (LPWSTR)&pwszMachine[MAX_MACHINE_NAME_LEN];
|
|
pwszMachine = (LPWSTR)&pwszCatalog[MAX_CATALOG_NAME_LEN];
|
|
iPath++; // increment our out pointer...
|
|
}
|
|
|
|
if (iPath == 0)
|
|
{
|
|
// nothing found; - We should check to see if by chance the user specified a query that
|
|
// is CI based if so error apapropriately...
|
|
hres = (dwGQR_Flags & GQR_REQUIRES_CI)? MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_INDEXSEARCH) : S_FALSE;
|
|
goto Abort;
|
|
}
|
|
|
|
// Get ICommand.
|
|
hres = CIMakeICommand(&_pCommand,
|
|
iPath,
|
|
aDepths,
|
|
(WCHAR const * const *) aScopes,
|
|
(WCHAR const * const *) aCatalogs,
|
|
(WCHAR const * const *) aMachines);
|
|
|
|
if (FAILED(hres))
|
|
goto Abort;
|
|
|
|
|
|
// create the query command string - Assume default sort...
|
|
if (FAILED(hres = _BuildAndSetCommandTree(_iColSort, FALSE)))
|
|
goto Abort;
|
|
|
|
// commenting out for now. in the future we should inform the user that not all the drives are indexed and ask
|
|
// if they want to continue doing search on the ones that are indexed.
|
|
if ((dwGQR_Flags & GQR_REQUIRES_CI) && (nPaths != iPath))
|
|
{
|
|
// check warning flags to see if we should ignore and continue
|
|
if (0 == (_grfWarnings & DFW_IGNORE_CISCOPEMISMATCH))
|
|
{
|
|
hres = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_SCOPEMISMATCH);
|
|
goto Abort;
|
|
}
|
|
}
|
|
|
|
// Get IRowset.
|
|
_SetCmdProp(_pCommand);
|
|
if (FAILED(hres = _pCommand->Execute( 0, // no aggr. IUnknown
|
|
IID_IRowsetLocate, // IID for i/f to return
|
|
0, // disp. params
|
|
0, // chapter
|
|
(IUnknown **) &_pRowset )))
|
|
goto Abort;
|
|
|
|
// we have the IRowset.
|
|
// Real work to get the Accessor
|
|
if (FAILED(hres = _MapColumns( _pRowset, c_cDbCols, aPropMainCols,
|
|
c_aDbCols, _hAccessor )))
|
|
goto Abort;
|
|
|
|
// OK lets also get the accessor for the WorkID...
|
|
if (FAILED(hres = _MapColumns( _pRowset, ARRAYSIZE(c_aDbWorkIDCols), aPropMainCols,
|
|
c_aDbWorkIDCols, _hAccessorWorkID )))
|
|
goto Abort;
|
|
|
|
if (FAILED(hres = _pRowset->QueryInterface( IID_IRowsetAsynch,
|
|
(void **) &_pRowsetAsync)))
|
|
goto Abort;
|
|
|
|
#ifdef CI_ROWSETNOTIFY
|
|
// And setup our notification object and register it with the rowset
|
|
IConnectionPointContainer *pcont;
|
|
|
|
// If we have an advise going, remove it now
|
|
// Will not totally bail out if these are not supported for the query.
|
|
_pdbrWatchNotify = new CDFOleDBRowsetWatchNotify(this);
|
|
if (_pdbrWatchNotify)
|
|
{
|
|
if (SUCCEEDED(_pRowset->QueryInterface(IID_IConnectionPointContainer, (PVOID*)&pcont)))
|
|
{
|
|
// Hold on to the connection point as if we release it, it looks like they
|
|
// release the rest...
|
|
if (SUCCEEDED(pcont->FindConnectionPoint(IID_IRowsetNotify, &_pcpointWatchNotify)))
|
|
{
|
|
if (_pcpointWatchNotify)
|
|
_pcpointWatchNotify->Advise((IUnknown*)_pdbrWatchNotify, &_dwAdviseID);
|
|
}
|
|
pcont->Release();
|
|
}
|
|
}
|
|
#endif //#ifdef CI_ROWSETNOTIFY
|
|
|
|
// If we got here than at least some of our paths are indexed
|
|
// we may need to compress the list down of the ones we did not handle...
|
|
*pcPaths = (nPaths - iPath); // Let caller know how many we did not process
|
|
|
|
// need to move all the ones we did not process to the start of the list...
|
|
// we always process this list here as we may need to allocate translation lists to be used to
|
|
// translate the some UNCS back to the mapped drive the user passed in.
|
|
|
|
UINT j;
|
|
UINT iInsert;
|
|
iPath--; // make it easy to detect
|
|
j = 0;
|
|
iInsert = 0;
|
|
for (i = 0; i < nPaths; i++)
|
|
{
|
|
if (aScopesOrig[j] == apwszPaths[i])
|
|
{
|
|
if (aScopesOrig[j] != aScopes[j])
|
|
{
|
|
// There is a translation in place.
|
|
DFODBET *pdfet = (DFODBET*)LocalAlloc(LPTR, sizeof(DFODBET));
|
|
if (pdfet)
|
|
{
|
|
pdfet->pdfetNext = _pdfetFirst;
|
|
_pdfetFirst = pdfet;
|
|
pdfet->pwszFrom = aScopes[j];
|
|
pdfet->cbFrom = lstrlenW(pdfet->pwszFrom);
|
|
pdfet->pwszTo = aScopesOrig[j];
|
|
aScopes[j] = aScopesOrig[j]; // Make sure loop below does not delete pwszFrom
|
|
apwszPaths[i] = NULL; // Likewise for pswsTo...
|
|
}
|
|
|
|
}
|
|
if (apwszPaths[i])
|
|
LocalFree((HLOCAL)apwszPaths[i]);
|
|
if (j < iPath)
|
|
j++;
|
|
}
|
|
else
|
|
apwszPaths[iInsert++] = apwszPaths[i]; // move to right place
|
|
}
|
|
iPath++; // setup to go through cleanupcode...
|
|
|
|
// Fall through to cleanup code...
|
|
|
|
Abort:
|
|
// Warning... Since a failure return from this function will
|
|
// release this class, most all of the allocated items up till the failure should
|
|
// be released... Also cleanup any paths we may have allocated...
|
|
for (i = 0; i < iPath; i++)
|
|
{
|
|
if (aScopesOrig[i] != aScopes[i])
|
|
LocalFree(aScopes[i]);
|
|
}
|
|
|
|
if (aDepths)
|
|
LocalFree(aDepths);
|
|
|
|
return hres;
|
|
}
|
|
|
|
#ifdef CI_ROWSETNOTIFY
|
|
|
|
// Implemention of notification callback class for above
|
|
CDFOleDBRowsetWatchNotify::CDFOleDBRowsetWatchNotify(CDFOleDBEnum *pdfoe):
|
|
_cRef(1)
|
|
{
|
|
// We did not addref as this could cause circular reference...
|
|
// need to be carefull in our managment of this pointer...
|
|
_pdfoe = pdfoe;
|
|
}
|
|
|
|
|
|
CDFOleDBRowsetWatchNotify::~CDFOleDBRowsetWatchNotify()
|
|
{
|
|
}
|
|
|
|
HRESULT CDFOleDBRowsetWatchNotify::QueryInterface(REFIID riid, void ** ppvObj)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CDFOleDBRowsetWatchNotify, IRowsetNotify), //IID_IRowsetWatchNotify
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
ULONG CDFOleDBRowsetWatchNotify::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CDFOleDBRowsetWatchNotify::Release()
|
|
{
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CDFOleDBRowsetWatchNotify::OnRowChange(
|
|
IRowset *pRowset, ULONG cColumns, const HROW rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny)
|
|
{
|
|
if (eReason == DBREASON_ROW_ASYNCHINSERT || eReason == DBREASON_ROW_INSERT)
|
|
{
|
|
_pdfoe->HandleWatchNotify(eReason);
|
|
return S_OK;
|
|
}
|
|
return DB_S_UNWANTEDREASON;
|
|
}
|
|
#endif // #ifdef CI_ROWSETNOTIFY
|
|
|
|
#endif
|