//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1993
//
// File: netfind.c
//
// Description: This file contains the net specific search code that is
// needed for the find computer code.
//
//---------------------------------------------------------------------------

#include "shellprv.h"
#pragma  hdrstop

// #define FIND_TRACE
#define NET_TIMINGS

#ifdef NET_TIMINGS
int     NTF_cNoPEnum = 0;
int     NTF_dtNoPEnum = 0;
int     NTF_cNextItem = 0;
int     NTF_dtNextItem = 0;
int     NTF_dtTime = 0;
#endif


#define DFM_DEFERINIT   (WM_USER+42)
//
// REVIEW:: The recursive code in this module has been totally neutered to
// make the ITG group happy.  IE we mad this functional mostly usless and
// wasted a lot of time doing so...  The ifdefs are under #ifdef CASTRATED
//


//===========================================================================
// Define the Default data filter data structures
//===========================================================================

// Use the code from property sheet to create the dialogs
HWND WINAPI CreatePage(PROPSHEETPAGE *hpage, HWND hwndParent);

//
// Define the internal structure of our default filter
typedef struct _CNETFilter   // fff
{
    IDocFindFileFilter  dfff;
    UINT                cRef;

    HWND                hwndTabs;

    HANDLE              hMRUSpecs;

    LPITEMIDLIST        pidlStart;      // Where to start the search from.

    // Data associated with the file name.
    LPTSTR              pszCompName;   // the one we do compares with
    TCHAR               szUserInputCompName[MAX_PATH];  // User input

} CNETFilter, FAR* LPNETFILTER;


// Define common page data for each of our pages
// WARNING the fields in this must align the same as the definition
// in docfind2.c

typedef struct { // dfpsp
    PSP             hpsp;
    HANDLE          hThreadInit;
    HWND            hwndDlg;
    LPNETFILTER      pdff;
    DWORD           dwState;
} DOCFINDPROPSHEETPAGE, * LPDOCFINDPROPSHEETPAGE;


#define DFPAGE_INIT     0x0001          /* This page has been initialized */
#define DFPAGE_CHANGE   0x0002          /*  The user has modified the page */

//===========================================================================
// Prototypes of some of the internal functions.
//===========================================================================

BOOL CALLBACK DocFind_CCOMPFNameLocDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);



//===========================================================================
// Define some other module global data
//===========================================================================

#pragma data_seg(DATASEG_READONLY)
DFPAGELIST  s_CCOMPFplComp[] =
{
    {DLG_NFNAMELOC, DocFind_CCOMPFNameLocDlgProc},
};
#pragma data_seg()

// Some global strings...
const TCHAR s_szCompSpecMRU[] = REGSTR_PATH_EXPLORER TEXT("\\FindComputerMRU");




//==========================================================================
//
// Create the default filter for our find code...  They should be completly
// self contained...
//

//===========================================================================
// CNETFilter : member prototype
//===========================================================================
HRESULT STDMETHODCALLTYPE CNETFilter_QueryInterface(LPDOCFINDFILEFILTER pnetf, REFIID riid, LPVOID FAR* ppvObj);
ULONG STDMETHODCALLTYPE CNETFilter_AddRef(LPDOCFINDFILEFILTER pnetf);
ULONG STDMETHODCALLTYPE CNETFilter_Release(LPDOCFINDFILEFILTER pnetf);
STDMETHODIMP CNETFilter_GetIconsAndMenu (LPDOCFINDFILEFILTER pdfff,
        HWND hwndDlg, HICON *phiconSmall, HICON *phiconLarge, HMENU *phmenu);
STDMETHODIMP CNETFilter_GetStatusMessageIndex (LPDOCFINDFILEFILTER pdfff,
        UINT uContext, UINT *puMsgIndex);
STDMETHODIMP CNETFilter_GetFolderMergeMenuIndex (LPDOCFINDFILEFILTER pdfff,
        UINT *puMergeMenu);
STDMETHODIMP CNETFilter_AddPages(LPDOCFINDFILEFILTER pnetf, HWND hwndTabs,
        LPITEMIDLIST pidlStart);
STDMETHODIMP CNetFilter_FFilterChanged(LPDOCFINDFILEFILTER pdfff);
STDMETHODIMP CNETFilter_GenerateTitle(LPDOCFINDFILEFILTER pnetf,
        LPTSTR *ppszTitle, BOOL fFileName);
STDMETHODIMP CNETFilter_ClearSearchCriteria(LPDOCFINDFILEFILTER pnetf);
STDMETHODIMP CNETFilter_PrepareToEnumObjects(LPDOCFINDFILEFILTER pnetf, DWORD *pdwFlags);
STDMETHODIMP CNETFilter_EnableChanges(LPDOCFINDFILEFILTER pnetf, BOOL fEnable);
STDMETHODIMP CNETFilter_CreateDetails(LPDOCFINDFILEFILTER pnetf,
        HWND hwndDlg, HDPA hdpaPidf, LPVOID FAR* ppvOut);
STDMETHODIMP CNETFilter_EnumObjects (LPDOCFINDFILEFILTER pnetf, LPSHELLFOLDER psf,
            DWORD grfFlags, LPTSTR pszProgressText, IDFEnum **ppdfenum) PURE;
STDMETHODIMP CNETFilter_FDoesItemMatchFilter(LPDOCFINDFILEFILTER pnetf,
        LPTSTR pszFolder, WIN32_FIND_DATA * pfinddata,
        LPSHELLFOLDER psf, LPITEMIDLIST  pidl);
STDMETHODIMP CNETFilter_SaveCriteria(LPDOCFINDFILEFILTER pnetf, IStream *pstm, WORD fCharType);
STDMETHODIMP CNETFilter_RestoreCriteria(LPDOCFINDFILEFILTER pnetf,
        IStream * pstm, int cCriteria, WORD fCharType);
STDMETHODIMP CNETFilter_DeclareFSNotifyInterest(LPDOCFINDFILEFILTER pnetf, HWND hwndDlg, UINT uMsg);

#pragma data_seg(DATASEG_READONLY)
IDocFindFileFilterVtbl c_CCOMPFFilterVtbl =
{
    CNETFilter_QueryInterface,
    CNETFilter_AddRef,
    CNETFilter_Release,
    CNETFilter_GetIconsAndMenu,
    CNETFilter_GetStatusMessageIndex,
    CNETFilter_GetFolderMergeMenuIndex,
    CNETFilter_AddPages,
    CNetFilter_FFilterChanged,
    CNETFilter_GenerateTitle,
    CNETFilter_PrepareToEnumObjects,
    CNETFilter_ClearSearchCriteria,
    CNETFilter_EnableChanges,
    CNETFilter_CreateDetails,
    CNETFilter_EnumObjects,
    CNETFilter_FDoesItemMatchFilter,
    CNETFilter_SaveCriteria,
    CNETFilter_RestoreCriteria,
    CNETFilter_DeclareFSNotifyInterest
};

#pragma data_seg()


//==========================================================================
// Creation function to create default find filter...
//==========================================================================
IDocFindFileFilter * CreateDefaultComputerFindFilter()
{
    LPNETFILTER pfff = (void*)LocalAlloc(LPTR, SIZEOF(CNETFilter));
    if (pfff == NULL)
        return(NULL);

    pfff->dfff.lpVtbl = &c_CCOMPFFilterVtbl;
    pfff->cRef = 1;

    // We should now simply return the filter
    return &pfff->dfff;

}

//==========================================================================
// Query interface for the docfind filter interface...
//==========================================================================

HRESULT STDMETHODCALLTYPE CNETFilter_QueryInterface(LPDOCFINDFILEFILTER pnetf, REFIID riid, LPVOID FAR* ppvObj)
{
    return ResultFromScode(E_NOTIMPL);
}

//==========================================================================
// IDocFindFileFilter::AddRef
//==========================================================================
ULONG STDMETHODCALLTYPE CNETFilter_AddRef(LPDOCFINDFILEFILTER pnetf)
{
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);
    this->cRef++;
    return(this->cRef);
}

//==========================================================================
// IDocFindFileFilter::Release
//==========================================================================
ULONG STDMETHODCALLTYPE CNETFilter_Release(LPDOCFINDFILEFILTER pnetf)
{
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);
    this->cRef--;
    if (this->cRef>0)
    {
        return(this->cRef);
    }

    // Destroy the MRU Lists...

    if (this->hMRUSpecs)
        FreeMRUList(this->hMRUSpecs);

    // unless we do not have a combobox
    if (this->pidlStart)
        ILFree(this->pidlStart);

    if (this->pszCompName)
        LocalFree( this->pszCompName );

    LocalFree((HLOCAL)this);
    return(0);
}

//==========================================================================
// IDocFindFileFilter::GetIconsAndMenu
//==========================================================================
STDMETHODIMP CNETFilter_GetIconsAndMenu (LPDOCFINDFILEFILTER pdfff,
        HWND hwndDlg, HICON *phiconSmall, HICON *phiconLarge, HMENU *phmenu)
{
    *phiconSmall = LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_COMPFIND),
            IMAGE_ICON, g_cxSmIcon, g_cySmIcon, LR_DEFAULTCOLOR);
    *phiconLarge  = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(IDI_COMPFIND));

    // Now for the menu
    *phmenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(MENU_FINDCOMPDLG));

    // BUGBUG:: Still menu to process!

    return ResultFromScode(S_OK);
}

//==========================================================================
// Function to get the string resource index number that is proper for the
// current type of search.
//==========================================================================
STDMETHODIMP CNETFilter_GetStatusMessageIndex (LPDOCFINDFILEFILTER pdfff,
        UINT uContext, UINT *puMsgIndex)
{
    // Currently context is not used
    *puMsgIndex = IDS_COMPUTERSFOUND;

    return ResultFromScode(S_OK);
}

//==========================================================================
// Function to let find know which menu to load to merge for the folder
//==========================================================================
STDMETHODIMP CNETFilter_GetFolderMergeMenuIndex (LPDOCFINDFILEFILTER pdfff,
        UINT *puMergeMenu)
{
    *puMergeMenu = POPUP_NETFIND_POPUPMERGE;
    return ResultFromScode(S_OK);
}



//==========================================================================
// IDocFindFileFilter::AddPages
//==========================================================================
STDMETHODIMP CNETFilter_AddPages(LPDOCFINDFILEFILTER pnetf, HWND hwndTabs,
        LPITEMIDLIST pidlStart)
{
    HWND hwndMainDlg;
    TCHAR szTemp[20];

    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);
    hwndMainDlg = GetParent(hwndTabs);

    // save away a pointer to the filter
    this->hwndTabs = hwndTabs;

    // We want to update the animation to show the Find computer one instead
    // of the find files, so wack it here
    wsprintf(szTemp, TEXT("#%d"),IDA_FINDCOMP);
    SetDlgItemText(hwndMainDlg, IDD_ANIMATE, szTemp);

    // since we removed the browse and drop down list we need to prefill this
    this->pidlStart = SHCloneSpecialIDList(HWND_DESKTOP, CSIDL_NETWORK, FALSE);

    return DocFind_AddPages(pnetf, hwndTabs, s_CCOMPFplComp, ARRAYSIZE(s_CCOMPFplComp));
}

//==========================================================================
// IDocFindFileFilter::FFilterChanged - Returns S_OK if nothing changed.
//==========================================================================
STDMETHODIMP CNetFilter_FFilterChanged(LPDOCFINDFILEFILTER pdfff)
{
    // Currently not saving so who cares?
    return(ResultFromScode(S_FALSE));
}


//==========================================================================
// IDocFindFileFilter::GenerateTitle - Generates the title given the current
// search criteria.
//==========================================================================
STDMETHODIMP CNETFilter_GenerateTitle(LPDOCFINDFILEFILTER pnetf,
        LPTSTR *ppszTitle, BOOL fFileName)
{
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);
    LPTSTR pszMsg;
    int iRes;

    // For now lets use the default find...
    iRes = IDS_FIND_TITLE_COMPUTER;

    // Now lets construct the message from the resource
    pszMsg = ShellConstructMessageString(HINST_THISDLL,
            MAKEINTRESOURCE(iRes), fFileName? TEXT(" #") : TEXT(":"));


    *ppszTitle = pszMsg;        // Return the pointer to the caller

    return ResultFromScode(S_OK);
}

//==========================================================================
// IDocFindFileFilter::ClearSearchCriteria
//==========================================================================
STDMETHODIMP CNETFilter_ClearSearchCriteria(LPDOCFINDFILEFILTER pnetf)
{
    int cPages;
    HWND    hwndMainDlg;
    TC_DFITEMEXTRA tie;
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);

    hwndMainDlg = GetParent(this->hwndTabs);
    for (cPages = TabCtrl_GetItemCount(this->hwndTabs) -1; cPages >= 0; cPages--)
    {
        tie.tci.mask = TCIF_PARAM;
        TabCtrl_GetItem(this->hwndTabs, cPages, &tie.tci);
        SendNotify(tie.hwndPage, hwndMainDlg, PSN_RESET, NULL);
    }

    return ResultFromScode(S_OK);
}

//==========================================================================
// IDocFindFileFilter::PrepareToEnumObjects
//==========================================================================
STDMETHODIMP CNETFilter_PrepareToEnumObjects(LPDOCFINDFILEFILTER pnetf, DWORD *pdwFlags)
{
    int cPages;
    HWND    hwndMainDlg;
    TC_DFITEMEXTRA tie;
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);

    hwndMainDlg = GetParent(this->hwndTabs);
    for (cPages = TabCtrl_GetItemCount(this->hwndTabs) -1; cPages >= 0; cPages--)
    {
        tie.tci.mask = TCIF_PARAM;
        TabCtrl_GetItem(this->hwndTabs, cPages, &tie.tci);
        SendNotify(tie.hwndPage, hwndMainDlg, PSN_APPLY, NULL);
    }

    // Update the flags and buffer strings

    *pdwFlags &= ~FFLT_INCLUDESUBDIRS;
    // Also lets convert the Computer name  pattern into the strings
    // will do the compares against.
    if ((this->szUserInputCompName[0] == TEXT('\\')) &&
            (this->szUserInputCompName[1] == TEXT('\\')))
    {
        // (DavePl)
        //
        // This code used to reuse the pszCompName buffer if it was non-null, but
        // if you do a find with _no_ criteria, and then a find with a \\foo criteria,
        // the buffer will be reused, but its too short (allocated by DocFind_SetupWildCardingOnFileSpec)
        // and memory trashing occurs


        if (this->pszCompName)
        {
            LocalFree(this->pszCompName);
        }

        this->pszCompName = LocalAlloc( LPTR, (lstrlen(this->szUserInputCompName)+1)*SIZEOF(TCHAR) );

        if (this->pszCompName)
            // We are doing special unc matching
            lstrcpy(this->pszCompName, this->szUserInputCompName);
    }
    else
    {
        if (this->pszCompName)
        {
            LocalFree( this->pszCompName );
            this->pszCompName = NULL;
        }
        DocFind_SetupWildCardingOnFileSpec(this->szUserInputCompName,
               &this->pszCompName);
    }

    return ResultFromScode(S_OK);
}


//==========================================================================
// IDocFindFileFilter::EnableChanges
//==========================================================================
STDMETHODIMP CNETFilter_EnableChanges(LPDOCFINDFILEFILTER pnetf, BOOL fEnable)
{
    int cPages;
    HWND    hwndMainDlg;
    TC_DFITEMEXTRA tie;
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);

    hwndMainDlg = GetParent(this->hwndTabs);
    for (cPages = TabCtrl_GetItemCount(this->hwndTabs) -1; cPages >= 0; cPages--)
    {
        tie.tci.mask = TCIF_PARAM;
        TabCtrl_GetItem(this->hwndTabs, cPages, &tie.tci);
        SendMessage(tie.hwndPage, DFM_ENABLECHANGES, (WPARAM)fEnable, 0);
    }

    return ResultFromScode(S_OK);
}


//==========================================================================
// IDocFindFileFilter::FDoesItemMatchFilter
//==========================================================================
STDMETHODIMP CNETFilter_FDoesItemMatchFilter(LPDOCFINDFILEFILTER pnetf,
        LPTSTR pszFolder, WIN32_FIND_DATA * pfinddata,
        LPSHELLFOLDER psf, LPITEMIDLIST pidl)
{
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);
    SCODE sc = MAKE_SCODE(0, 0, 1);

    // Make sure that we only return computers...
    BYTE bType;

    bType = SIL_GetType(pidl);

    // First pass dont push anything that is below a computer...
    if ((bType & (SHID_NET | SHID_INGROUPMASK)) != SHID_NET_SERVER)
        return ResultFromScode(0);     // does not match

    // Here is where I start getting in bed with the network enumerator
    // format of IDLists.
    if (this->pszCompName && this->pszCompName[0])
    {
        // Although for now not much...
        STRRET str;

        psf->lpVtbl->GetDisplayNameOf(psf, pidl, SHGDN_NORMAL, &str);

#ifdef UNICODE
        {
            TCHAR szPath[MAX_PATH];
            if (StrRetToStrN(szPath, MAX_PATH, &str, pidl))
            {
                if (!PathMatchSpec(szPath, this->pszCompName))
                    return ResultFromScode(0);     // does not match
            }
            else
            {
                return ResultFromScode(0);  // strret conv fails => no match
            }
        }
#else
        Assert (str.uType == STRRET_OFFSET)
        if (str.uType != STRRET_OFFSET)
            return ResultFromScode(0);     // does not match

        if (!PathMatchSpec((LPTSTR)((LPBYTE)pidl + str.uOffset), this->pszCompName))
            return ResultFromScode(0);     // does not match
#endif

    }

    return ResultFromScode(sc);    // return TRUE to imply yes!
}

//==========================================================================
// IDocFindFileFilter::SaveCriteria
//==========================================================================
STDMETHODIMP CNETFilter_SaveCriteria(LPDOCFINDFILEFILTER pnetf, IStream *pstm, WORD fCharType)
{
    //
#ifdef NOT_DONE_YET
#endif

    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);
    int cCriteria = 0;

    return ResultFromScode(MAKE_SCODE(0, 0, cCriteria));
}

//==========================================================================
// IDocFindFileFilter::RestoreCriteria
//==========================================================================
STDMETHODIMP CNETFilter_RestoreCriteria(LPDOCFINDFILEFILTER pnetf,
        IStream *pstm, int cCriteria, WORD fCharType)
{
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);

#ifdef NOT_DONE_YET
#endif
    return ResultFromScode(S_OK);
}

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Now starting the code for the name and location page
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////




//==========================================================================
//
// Process the WM_SIZE of the details page
//
void DocFind_CCOMPFNameLocOnSize(HWND hwndDlg, UINT state, int cx, int cy)
{
    RECT rc;
    int cxMargin;
    if (state == SIZE_MINIMIZED)
        return;         // don't bother when we are minimized...

    // Get the location of first static to calculate margin
    GetWindowRect(GetDlgItem(hwndDlg, IDD_STATIC), &rc);
    MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rc, 2);
    cxMargin = rc.left;
    cx -= cxMargin;

    DocFind_SizeControl(hwndDlg, IDD_FILESPEC, cx, TRUE);

}


//==========================================================================
// Helper to helper to add an item to the combobox.
//==========================================================================

HRESULT _GetDisplayName(LPSHELLFOLDER psfGP, LPCITEMIDLIST pidl, LPTSTR pszRet, UINT cchMax)
{
    LPITEMIDLIST pidlParent = ILClone(pidl);
    HRESULT hres;
    VDATEINPUTBUF(pszRet, TCHAR, cchMax);

    if (pidlParent)
    {
        LPSHELLFOLDER psfParent = NULL;
        ILRemoveLastID(pidlParent);
        if (ILIsEmpty(pidlParent))
        {
            psfParent = psfGP;
            psfParent->lpVtbl->AddRef(psfParent);
            hres = S_OK;
        }
        else
        {
            hres = psfGP->lpVtbl->BindToObject(psfGP, pidlParent, NULL, &IID_IShellFolder, &psfParent);
        }

        if (SUCCEEDED(hres))
        {
            STRRET str;
            pidl = ILFindLastID(pidl);
            hres = psfParent->lpVtbl->GetDisplayNameOf(psfParent, pidl, SHGDN_NORMAL, &str);
            StrRetToStrN(pszRet, cchMax, &str, pidl);

            psfParent->lpVtbl->Release(psfParent);
        }

        ILFree(pidlParent);
    }
    else
    {
        hres = E_OUTOFMEMORY;
    }
    return hres;
}

int  DocFind_LocCBAddPidl(HWND hwndCtl, LPSHELLFOLDER psf,
       LPITEMIDLIST pidlParent, LPITEMIDLIST pidl, LPITEMIDLIST *ppidlAbs,
       BOOL fFullName)
{
    LPDFCBITEM pdfcbi;
    TCHAR    szPath[MAX_PATH];
    int     iItem = -1;
    LPITEMIDLIST pidlAbs;

    pidlAbs = ILCombine(pidlParent, pidl);
    if (!pidlAbs)
        return(-1);

    if (fFullName)
    {
        if (!SHGetPathFromIDList(pidlAbs, szPath))
        {
            ILFree(pidlAbs);
            return(-1);
        }
    }
    else if (FAILED(_GetDisplayName(psf, pidl, szPath, ARRAYSIZE(szPath))))
    {
        ILFree(pidlAbs);
        return(-1);
    }

    pdfcbi = (LPDFCBITEM)LocalAlloc(LPTR, SIZEOF(DFCBITEM));
    if (pdfcbi != NULL)
    {

        pdfcbi->pidl = pidlAbs;
        if (ppidlAbs)
            *ppidlAbs = pdfcbi->pidl;

        pdfcbi->iImage = SHMapPIDLToSystemImageListIndex(
                psf, pidl, NULL);

        pdfcbi->uFixedDrives = 0;               // no fixed drives being referenced

        if ((iItem = SendMessage(hwndCtl, CB_ADDSTRING, 0,
                (LPARAM)szPath)) != CB_ERRSPACE)
        {
            // Set the data for this item now...
            SendMessage(hwndCtl, CB_SETITEMDATA, iItem, (LPARAM)pdfcbi);
        }
        else
        {
            Assert(FALSE);
            LocalFree((HLOCAL)pdfcbi);
        }
    }
    return(iItem);
}

//==========================================================================
// Helper function to see if an Pidl is aready in the list...
//==========================================================================
int DocFind_LocCBFindPidl(HWND hwnd, LPITEMIDLIST pidl)
{
    LPDFCBITEM pdfcbi;
    int i;

    for (i = SendMessage(hwnd, CB_GETCOUNT, 0, 0); i >= 0; i--)
    {
        pdfcbi = (LPDFCBITEM)SendMessage(hwnd, CB_GETITEMDATA, i, 0);

        if ((pdfcbi != NULL) && (pdfcbi != (LPDFCBITEM)CB_ERR) &&
                ILIsEqual(pidl, pdfcbi->pidl))
            break;
    }
    return(i);

}


//==========================================================================
// Initialize the Name and loacation page
//==========================================================================
void DocFind_CCOMPFNameLocInit(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
    LPNETFILTER pdff = pdfpsp->pdff;
    TCHAR szPath[MAX_PATH];
    LPITEMIDLIST pidlWindows;

    // We want to set the default search drive to the windows drive.
    // I am going to be a bit slimmy, but...
    //
    GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
    if (szPath[1] == TEXT(':'))
        szPath[3] = TEXT('\0');

    pidlWindows = ILCreateFromPath(szPath);

    if ((pdfpsp->dwState & DFPAGE_INIT) == 0)
    {
        pdff->hMRUSpecs = DocFind_UpdateMRUItem(NULL, pdfpsp->hwndDlg, IDD_FILESPEC,
                s_szCompSpecMRU, pdff->szUserInputCompName, szNULL);

        // Update our state to let us know that we have already initialized...
        pdfpsp->dwState |= DFPAGE_INIT;
    }


    ILFree(pidlWindows);

}


//==========================================================================
// Validate the page to make sure that the data is valid.  If it is not
// we need to display a message to the user and also set the focus to
// the invalid field.
//==========================================================================
void DocFind_CCOMPFNameLocValidatePage(LPDOCFINDPROPSHEETPAGE pdfpsp)
{

    // No validation is needed here (At least not now).

}


//==========================================================================
//
// Apply any changes that happened in the name loc page to the filter
//
void DocFind_CCOMPFNameLocApply(LPDOCFINDPROPSHEETPAGE pdfpsp)
{
    LPNETFILTER pdff = pdfpsp->pdff;

    GetDlgItemText(pdfpsp->hwndDlg, IDD_FILESPEC, pdff->szUserInputCompName,
            ARRAYSIZE(pdff->szUserInputCompName));

    DocFind_UpdateMRUItem(pdff->hMRUSpecs, pdfpsp->hwndDlg, IDD_FILESPEC,
            s_szCompSpecMRU, pdff->szUserInputCompName, NULL);

}



//==========================================================================
//
// DocFind_OnCommand - Process the WM_COMMAND messages
//
void DocFind_CCOMPFNameLocOnCommand(HWND hwndDlg, UINT id, HWND hwndCtl, UINT code)
{
}
//==========================================================================
// Handle the Measure item for the ComboBox
//==========================================================================

BOOL DocFind_LocCBMeasureItem(HWND hwnd,
        MEASUREITEMSTRUCT FAR* lpMeasureItem)
{
    HWND hwndItem;
    HDC hdc;
    TEXTMETRIC tm;
    // Now lets setup the size of the structure ...
    // I assume that this is the combobox as this is the only item
    // we have that is owner drawn.
    lpMeasureItem->itemHeight = g_cySmIcon;

    // Lets check the off case that the text metrics are larger then
    // this.
    hwndItem = GetDlgItem(hwnd, lpMeasureItem->CtlID);
    hdc = GetDC(hwndItem);
    GetTextMetrics(hdc, &tm);
    if ((UINT)tm.tmHeight > lpMeasureItem->itemHeight)
        lpMeasureItem->itemHeight = (UINT)tm.tmHeight;

    lpMeasureItem->itemHeight += 2 * GetSystemMetrics(SM_CYBORDER);

    ReleaseDC(hwndItem, hdc);

    return TRUE;
}

//==========================================================================
// Handle the DrawItem for the combobox.
//==========================================================================
BOOL DocFind_LocCBDrawItem(HWND hwnd,
        const DRAWITEMSTRUCT FAR* lpdi)
{
    LPDFCBITEM pdfcbi = (LPDFCBITEM)lpdi->itemData;
    HIMAGELIST himl;
    TCHAR szDisplayName[MAX_PATH];
    RECT rc;
    int cch;
    int iBack, iText;
    SIZE sz;

    // If not drawing entire or selection bail out
    if (!((lpdi->itemAction & ODA_SELECT) ||
            (lpdi->itemAction & ODA_DRAWENTIRE)))
        return FALSE;

    // Also bail if no information...
    if (lpdi->itemID==(UINT)-1)
        return FALSE;

    // First draw the imagelist
    Shell_GetImageLists(NULL, &himl);
    rc = lpdi->rcItem;

    ImageList_Draw(himl, pdfcbi->iImage, lpdi->hDC,
            rc.left + GetSystemMetrics(SM_CXBORDER),
            (rc.bottom + rc.top - g_cySmIcon) / 2, 0);
    if (lpdi->itemState & (ODS_SELECTED | ODS_FOCUS))
    {
        iText = SetTextColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
        iBack = SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
    }

    // Now lets output the text
    rc.left += 2 * GetSystemMetrics(SM_CYBORDER) + g_cxSmIcon;

    SendMessage(lpdi->hwndItem, CB_GETLBTEXT, lpdi->itemID, (LPARAM)szDisplayName);
    GetTextExtentPoint(lpdi->hDC, szDisplayName,
            cch = lstrlen(szDisplayName), &sz);

    ExtTextOut(lpdi->hDC, rc.left, (rc.bottom + rc.top - sz.cy) / 2,
            ETO_OPAQUE, &rc, szDisplayName, cch, NULL);

    // Restore colors
    if (lpdi->itemState & (ODS_SELECTED | ODS_FOCUS))
    {
        SetTextColor(lpdi->hDC, iText);
        SetBkColor(lpdi->hDC, iBack);
    }

    return TRUE;
}

//==========================================================================
//
// This function is the dialog (or property sheet page) for the name and
// location page.
//

const static TCHAR szHelpFile[] = TEXT("network.hlp");
#pragma data_seg(DATASEG_READONLY)
const static DWORD aGeneralHelpIds[] = {  // Context Help IDs
    IDD_STATIC,    IDH_FINDCOMP_NAME,
    IDD_FILESPEC,  IDH_FINDCOMP_NAME,

    0, 0
};
#pragma data_seg()

BOOL CALLBACK DocFind_CCOMPFNameLocDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    LPDOCFINDPROPSHEETPAGE pdfpsp = (LPDOCFINDPROPSHEETPAGE)GetWindowLong(hwndDlg, DWL_USER);

    switch (msg) {
    HANDLE_MSG(hwndDlg, WM_COMMAND, DocFind_CCOMPFNameLocOnCommand);
    HANDLE_MSG(hwndDlg, WM_SIZE, DocFind_CCOMPFNameLocOnSize);

    case WM_INITDIALOG:
        SetWindowLong(hwndDlg, DWL_USER, lParam);
        pdfpsp = (LPDOCFINDPROPSHEETPAGE)lParam;
        pdfpsp->hwndDlg = hwndDlg;
        break;


    case WM_NCDESTROY:
        Free(pdfpsp);
        SetWindowLong(hwndDlg, DWL_USER, 0);
        return FALSE;   // We MUST return FALSE to avoid mem-leak

    case DFM_ENABLECHANGES:
        EnableWindow(GetDlgItem(hwndDlg, IDD_FILESPEC), (BOOL)wParam);
        break;

    case WM_HELP:
        WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, szHelpFile, HELP_WM_HELP,
            (DWORD) (LPTSTR) aGeneralHelpIds);
        break;

    case WM_CONTEXTMENU:      // right mouse click
        WinHelp((HWND) wParam, szHelpFile, HELP_CONTEXTMENU,
            (DWORD) (LPTSTR) aGeneralHelpIds);
        break;


    case WM_NOTIFY:
        switch (((NMHDR *)lParam)->code) {
        case PSN_KILLACTIVE:
            DocFind_CCOMPFNameLocValidatePage(pdfpsp);
            break;
        case PSN_SETACTIVE:
            DocFind_CCOMPFNameLocInit(pdfpsp);

            break;

        case PSN_APPLY:
            if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
                DocFind_CCOMPFNameLocApply(pdfpsp);

            break;
        case PSN_RESET:
            if ((pdfpsp->dwState & DFPAGE_INIT) != 0)
            {
                // Null the filespec
                SetDlgItemText(hwndDlg, IDD_FILESPEC, c_szNULL);

            }
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Define the Details interface used for this search.  This includes
// The column header definitions.
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

//===========================================================================
// CCOMPFDetails : member prototype - Docfind Folder implementation
//===========================================================================
ULONG STDMETHODCALLTYPE CCOMPFDetails_Release(IShellDetails * psd);
STDMETHODIMP CCOMPFDetails_GetDetailsOf(IShellDetails * psd,
        LPCITEMIDLIST pidl, UINT iCol, LPSHELLDETAILS lpDetails);

// Functions that we call out of the file systems verison of this
extern STDMETHODIMP CNETDetails_GetDetailsOf(IShellDetails * psd,
        LPCITEMIDLIST pidl, UINT iCol, LPSHELLDETAILS lpDetails);
STDMETHODIMP CCOMPFDetails_ColumnClick(IShellDetails * psd, UINT iColumn);

//===========================================================================
// CCOMPFDetails : Vtable
//===========================================================================
#pragma warning(error: 4090 4028 4047)
#pragma data_seg(DATASEG_READONLY)

extern const UINT s_auMapDFColToFSCol[];
enum
{
        IDFCOL_NAME = 0,
        IDFCOL_PATH,
        IDFCOL_COMMENT,
        IDFCOL_MAX,                     // Make sure this is the last enum item
} ;



#pragma data_seg(DATASEG_READONLY)
const  COL_DATA s_CCOMPF_cols[] = {
    {IDFCOL_NAME,     IDS_NAME_COL,     20, LVCFMT_LEFT},
    {IDFCOL_PATH,     IDS_WORKGROUP_COL,     20, LVCFMT_LEFT},
    {IDFCOL_COMMENT,  IDS_COMMENT_COL,  20, LVCFMT_LEFT},
};


IShellDetailsVtbl c_CCOMPFDetailVtbl =
{
        SH32Unknown_QueryInterface,
        SH32Unknown_AddRef,
        SH32Unknown_Release,
        CCOMPFDetails_GetDetailsOf,
        CCOMPFDetails_ColumnClick,
};

#pragma data_seg()
#pragma warning(default: 4090 4028 4047)


typedef struct _CCOMPFDetails
{
        SH32Unknown     SH32Unk;

        // Pointer to docfind folder
        HWND            hwndDlg;
        HDPA            hdpaPidf;
} CCOMPFDetails;


STDMETHODIMP CNETFilter_CreateDetails(LPDOCFINDFILEFILTER pnetf,
        HWND hwndDlg, HDPA hdpaPidf, LPVOID FAR* ppvOut)
{
        HRESULT hres = ResultFromScode(E_OUTOFMEMORY);
        CCOMPFDetails *psd;

        psd = (void*)LocalAlloc(LPTR, SIZEOF(CCOMPFDetails));
        if (!psd)
        {
                goto Error1;
        }

        psd->SH32Unk.unk.lpVtbl = (IUnknownVtbl *)&c_CCOMPFDetailVtbl;
        psd->SH32Unk.cRef = 1;
        psd->SH32Unk.riid = &IID_IShellDetails;

        psd->hwndDlg = hwndDlg;
        psd->hdpaPidf = hdpaPidf;

        *ppvOut = psd;

        return(NOERROR);

Error1:;
        return(hres);
}


STDMETHODIMP CCOMPFDetails_GetDetailsOf(IShellDetails * psd, LPCITEMIDLIST pidl,
        UINT iColumn, LPSHELLDETAILS lpDetails)
{
    CCOMPFDetails * this = IToClass(CCOMPFDetails, SH32Unk.unk, psd);

    if (iColumn >= IDFCOL_MAX)
    {
        return(ResultFromScode(E_NOTIMPL));
    }

    lpDetails->str.uType = STRRET_CSTR;
    lpDetails->str.cStr[0] = '\0';

    if (!pidl)
    {
        LoadStringA(HINST_THISDLL, s_CCOMPF_cols[iColumn].ids,
                lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
        lpDetails->fmt = s_CCOMPF_cols[iColumn].iFmt;
        lpDetails->cxChar = s_CCOMPF_cols[iColumn].cchCol;
        return(NOERROR);
    }

    if (iColumn == IDFCOL_PATH)
    {
        // We need to now get to the idlist of the items folder.
        LPDFFOLDERLISTITEM pdffli = DPA_FastGetPtr(this->hdpaPidf,
                *DF_IFLDRPTR(pidl));

        if (pdffli != NULL)
        {
            LPCITEMIDLIST pidlLast;
            // This one is not part of the standard file system view...
            // This is rather gross, but I will assume that the parent
            // Of our item will get the same input as we do..., except
            // for the root!

            Assert (pdffli->psf);
            if (pdffli->psf != NULL)
            {
                pidlLast = ILFindLastID(pdffli->pidl);
                if (pidlLast == pdffli->pidl)
                {
                    LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);
                    return psfDesktop->lpVtbl->GetDisplayNameOf(psfDesktop,
                            pidlLast, SHGDN_NORMAL, &lpDetails->str);
                }
                else
                {
                    HRESULT hres;
                    hres = pdffli->psf->lpVtbl->GetDisplayNameOf(pdffli->psf,
                        pidlLast, SHGDN_NORMAL, &lpDetails->str);
                    if (SUCCEEDED(hres) && (lpDetails->str.uType == STRRET_OFFSET))
                    {
                        // Can't deal with it being an offset as the offset
                        // is from our internal PIDL
                        //
                        lstrcpyA(lpDetails->str.cStr,
                                (LPSTR)((LPBYTE)pidlLast + lpDetails->str.uOffset));
                        lpDetails->str.uType = STRRET_CSTR;
                    }
                }
            }
            return(NOERROR);
        }

        return(ResultFromScode(E_INVALIDARG));
    }

    else
    {
        // Let the file system function do it for us...
        // BUGBUG:: This uses the hard coded network one which
        // is probably not very good, but it beats having to create
        //
        return CNETDetails_GetDetailsOf(NULL, pidl,
                s_auMapDFColToFSCol[iColumn], lpDetails);
    }
}



STDMETHODIMP CCOMPFDetails_ColumnClick(IShellDetails * psd, UINT iColumn)
{
        CCOMPFDetails * this = IToClass(CCOMPFDetails, SH32Unk.unk, psd);

        ShellFolderView_ReArrange(this->hwndDlg, iColumn);
        return(NOERROR);
}

//===========================================================================
// CNETFEnum: class definition
//===========================================================================


typedef struct _CNETFEnum       // DFENUM (Doc find Container)
{
    IDFEnum         dfenum;
    UINT            cRef;
    IShellFolder    *psf;               // Pointer to shell 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
    LPNETFILTER     pnetf;              // Pointer to the net filter...

    // enumeration state

    LPSHELLFOLDER   psfEnum;            // Pointer to shell folder for the object.
    LPENUMIDLIST    penum;              // Enumerator in use.
    LPITEMIDLIST    pidl;               // The idlist of the currently processing
    LPITEMIDLIST    pidlStart;          // Pointer to the starting point.
    int             iFolder;            // Which folder are we adding items for?
    BOOL            fAddedSubDirs;
    BOOL            fObjReturnedInDir;  // Has an object been returned in this dir?
    BOOL            fFindUNC;           // Find UNC.
    int             iPassCnt;           // Used to control when to reiterat...

} CNETFEnum, FAR* LPDFENUM;

//===========================================================================
// CNETFEnum : member prototype - Docfind Folder implementation
//===========================================================================
HRESULT STDMETHODCALLTYPE CNETFEnum_QueryInterface(
       IDFEnum * pdfenum, REFIID riid, LPVOID FAR* ppvObj);
ULONG STDMETHODCALLTYPE CNETFEnum_AddRef(IDFEnum * pdfenum);
ULONG STDMETHODCALLTYPE CNETFEnum_Release(IDFEnum * pdfenum);

STDMETHODIMP CNETFEnum_Next(IDFEnum * pdfenum, LPITEMIDLIST *ppidl,
               int *pcObjectSearched, int *pcFoldersSearched, volatile BOOL *pfContinue, int *pState, HWND hwnd);


STDMETHODIMP CDefDFEnum_Skip(IDFEnum * pdfenum, int celt);
STDMETHODIMP CDefDFEnum_Reset(IDFEnum * pdfenum);


//===========================================================================
// CNETFEnum : Vtable
//===========================================================================
#pragma warning(error: 4090 4028 4047)
#pragma data_seg(DATASEG_READONLY)

IDFEnumVtbl c_CCOMPFFIterVtbl =
{
        CNETFEnum_QueryInterface,
        CNETFEnum_AddRef,
        CNETFEnum_Release,
        CNETFEnum_Next,
        CDefDFEnum_Skip,
        CDefDFEnum_Reset,
};


//==========================================================================
// CNETFEnum::QueryInterface
//==========================================================================

HRESULT STDMETHODCALLTYPE CNETFEnum_QueryInterface(IDFEnum * pdfenum, REFIID riid, LPVOID FAR* ppvObj)
{
    return ResultFromScode(E_NOTIMPL);
}

//==========================================================================
// IDFEnum::AddRef
//==========================================================================
ULONG STDMETHODCALLTYPE CNETFEnum_AddRef(IDFEnum * pdfenum)
{
    LPDFENUM this = IToClass(CNETFEnum, dfenum, pdfenum);
    this->cRef++;
    return(this->cRef);
}
//==========================================================================
// CNETFilter_EnumObjects - Get The real recursive filtered enumerator...
//==========================================================================
HRESULT CNETFilter_EnumObjects( IDocFindFileFilter * pdfff,
        LPSHELLFOLDER psf, DWORD grfFlags, LPTSTR pszDisplayText, IDFEnum **ppdfenum)
{
    // We need to construct the iterator
    LPNETFILTER pnetf = (LPNETFILTER)pdfff;
    LPDFENUM pdfenum = LocalAlloc(LPTR, SIZEOF(CNETFEnum));
    if (pdfenum == NULL)
        return ResultFromScode(E_OUTOFMEMORY);

    // Now initialize the data structures.
    pdfenum->dfenum.lpVtbl = &c_CCOMPFFIterVtbl;
    pdfenum->cRef = 1;
    pdfenum->psf = psf;
    pdfenum->pszDisplayText = pszDisplayText;
    pdfenum->grfFlags = grfFlags;
    pdfenum->pnetf = pnetf;

    pdfenum->pidlStart = ILClone(pnetf->pidlStart);
    pdfenum->iPassCnt = 0;

    // See if this is a UNC Search
    if (pnetf->pszCompName && (pnetf->pszCompName[0] == TEXT('\\')))
        pdfenum->fFindUNC = TRUE;


    // Save away the filter pointer
    pnetf->dfff.lpVtbl->AddRef(pdfff);

    // The rest of the fields should be zero/NULL
    *ppdfenum = &pdfenum->dfenum;       // Return the appropriate value;

#ifdef FIND_TRACE
        DebugMsg(DM_TRACE, TEXT("CNETFilter::EnumObjects"));
#endif

#ifdef NET_TIMINGS
    // Reset Counters
    NTF_cNoPEnum = NTF_dtNoPEnum = 0;
    NTF_cNextItem = NTF_dtNextItem = 0;
#endif

    return NOERROR;
}

//==========================================================================
// CNETFEnum::Release
//==========================================================================
ULONG STDMETHODCALLTYPE CNETFEnum_Release(IDFEnum * pdfenum)
{
    LPDFENUM this = IToClass(CNETFEnum, dfenum, pdfenum);

    this->cRef--;
    if (this->cRef>0)
    {
        return(this->cRef);
    }

    // Release any open enumerator and open IShell folder we may have.
    if (this->psfEnum != NULL)
        this->psfEnum->lpVtbl->Release(this->psfEnum);
    if (this->penum != NULL)
        this->penum->lpVtbl->Release(this->penum);


    // Release our use of the filter
    if (this->pnetf)
        this->pnetf->dfff.lpVtbl->Release(&this->pnetf->dfff);

    if (this->pidlStart)
        ILFree(this->pidlStart);
    if (this->pidl)
        ILFree(this->pidl);

    LocalFree((HLOCAL)this);

#ifdef NET_TIMINGS
    // Output some timings.
    if (!this->fFindUNC)
    {
        DebugMsg(DM_TRACE, TEXT("CNETFEnum:: Start Enums(%d), Time(%d), Per item(%d)"),
                NTF_cNoPEnum, NTF_dtNoPEnum, NTF_dtNoPEnum/NTF_cNoPEnum);

        DebugMsg(DM_TRACE, TEXT("CNETFEnum:: Count Next(%d), Time(%d), Per item(%d)"),
                NTF_cNextItem, NTF_dtNextItem, NTF_dtNextItem/NTF_cNextItem);
    }
#endif
    return(0);
}


//===========================================================================
// Helper function that does the find next and stuff to return an IDLISt...
//===========================================================================
LPITEMIDLIST DocFind_NextIDL(LPSHELLFOLDER psf, LPENUMIDLIST penum)
{
    UINT celt;
    LPITEMIDLIST pidl = NULL;

    if (penum->lpVtbl->Next(penum, 1, &pidl, &celt)==NOERROR && celt==1)
    {
        return pidl;
    }

    return NULL;
}


//===========================================================================
// Helper function that possibly pushes a directory onto the list of this
// to be searched.
//===========================================================================


//===========================================================================
// _FindCompByUNCName -
//    Helper function to the next function to help process find computer
//    on returning computers by UNC names...
//
//
//===========================================================================
STDMETHODIMP _FindCompByUNCName(CNETFEnum * this, LPITEMIDLIST *ppidl,
               int *piState)
{
    LPTSTR pszT;
    LPITEMIDLIST pidl;

    //
    // Two cases, There is a UNC name entered.  If so we need to process
    // this by extracting everythign off after the server name...
    //
    pszT = this->pnetf->pszCompName;

    if ((pszT==NULL) || (*pszT == TEXT('\0')))
    {
        *piState = GNF_DONE;
        return NOERROR;
    }

    if (*pszT == TEXT('\\'))
    {
        for (pszT += 2; *pszT; pszT = CharNext(pszT))
        {
            if (*pszT == TEXT('\\'))
            {
                // found something after server name, so get rid of it
                *pszT = TEXT('\0');
                break;
            }
        }
    }
    else
    {
        // They did not enter a unc name, but lets try to convert to
        // unc name
        pszT = LocalReAlloc(pszT,(3 + lstrlen(pszT))*SIZEOF(TCHAR),
                                LMEM_MOVEABLE );
        if (pszT)
        {
            this->pnetf->pszCompName = pszT;
            *pszT++ = TEXT('\\');
            *pszT++ = TEXT('\\');
            lstrcpy(pszT, this->pnetf->szUserInputCompName);
        }
        else
            return E_OUTOFMEMORY;
    }

    // Now parse the displayname - Argh we convert to Unicode, such
    // that we can uncovert at the other side.
    //
    pidl = ILCreateFromPath(this->pnetf->pszCompName);
    if (pidl != NULL)
    {
        LPITEMIDLIST pidlToAdd;
        //
        // We have a pidl so lets extract off the last part and clone
        // it to return and use the part before the end to add to the
        // folder list.
        pidlToAdd = ILCombine(ILFindLastID(pidl), (LPITEMIDLIST)&s_mkidBlank);
        ILRemoveLastID(pidl);       // Remove the last id (computer)
        CDFFolder_AddFolderToFolderList(
                            this->psf, pidl, NULL, &this->iFolder);
        // Now add the right signature on...
        pidlToAdd->mkid.cb += DF_APPENDSIZE;
        *(DF_SIGPTR(pidlToAdd)) = DF_TAGSIG;
        *(DF_IFLDRPTR(pidlToAdd)) = this->iFolder;
        // Now add this to the view
        *ppidl = pidlToAdd;
        *piState = GNF_MATCH;
    }
    else
        *piState = GNF_DONE;

    // And Return;
    return NOERROR;
}


//===========================================================================
// CNETFEnum::Next Recursive Iterator that is very special to the docfind.
//             It will walk each directory, breath first, it will call the
//             defined callback function to determine if it is an
//             interesting file to us.  It will also return additional
//             information, such as:  The number of folders and files
//             searched, so we can give results back to the user.  It
//             will return control to the caller whenever:
//                  a) Finds a match.
//                  b) runs out of things to search.
//                  c) Starts searching in another directory
//                  d) when the callback says to...
//
//
//===========================================================================
STDMETHODIMP CNETFEnum_Next(IDFEnum * pdfenum, LPITEMIDLIST *ppidl,
               int *pcObjectSearched, int *pcFoldersSearched,
               volatile BOOL *pfContinue, int *piState, HWND hwnd)
{
    // If we aren't enumerating a directory, then get the next directory
    // name from the dir list, and begin enumerating its contents...
    //
    CNETFEnum * this = IToClass(CNETFEnum, dfenum, pdfenum);
    BOOL fContinue = TRUE;
    STRRET strret;
    LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);

    //
    // Special case to find UNC Names quickly.  It will ignore all other
    // things.
    if (this->fFindUNC)
    {
        // If not the first time through return that we are done!
        if (this->iPassCnt)
        {
            *piState = GNF_DONE;
            return NOERROR;
        }

        this->iPassCnt = 1;

        return _FindCompByUNCName(this, ppidl, piState);
    }

    do
    {
        if (this->penum)
        {
            LPITEMIDLIST pidl;
#ifdef NET_TIMINGS
            NTF_dtTime = GetTickCount();
#endif
            pidl = DocFind_NextIDL(this->psfEnum, this->penum);
#ifdef NET_TIMINGS
            NTF_cNextItem++;
            NTF_dtNextItem += GetTickCount() - NTF_dtTime;
#endif

            if (pidl)
            {
#ifdef FIND_TRACE
                TCHAR szTrace[MAX_PATH];
                STRRET str;
                this->psfEnum->lpVtbl->GetDisplayNameOf(this->psfEnum, pidl, SHGDN_NORMAL,
                        &str);
                StrRetToStrN(szTrace, ARRAYSIZE(szTrace), &str, pidl);
                DebugMsg(DM_TRACE, TEXT("CNETFEnum::Next: %s"), szTrace);
#endif
                // 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 (this->pnetf->dfff.lpVtbl->FDoesItemMatchFilter(
                        &this->pnetf->dfff,
                        this->pszDisplayText, NULL, this->psfEnum, pidl) != 0)
                {
                    LPITEMIDLIST pidlToAdd;
                    *piState = GNF_MATCH;

                    // Now see if we have to add this folder to our
                    // list.
                    if (!this->fObjReturnedInDir)
                    {
                        this->fObjReturnedInDir = TRUE;
                        CDFFolder_AddFolderToFolderList(
                                this->psf, ILClone(this->pidl), NULL,
                                &this->iFolder);
                    }

                    // Now lets muck up the IDList to put ur index number
                    // onto the end of the idlist.
                    pidlToAdd = ILCombine((LPITEMIDLIST)pidl,
                            (LPITEMIDLIST)&s_mkidBlank);
                    ILFree(pidl);
                    if (pidlToAdd)
                    {
                        pidlToAdd->mkid.cb += DF_APPENDSIZE;
                        *(DF_SIGPTR(pidlToAdd)) = DF_TAGSIG;
                        *(DF_IFLDRPTR(pidlToAdd)) = this->iFolder;

                        // Now add this to the view
                        *ppidl = pidlToAdd;
                        if ((this->iPassCnt == 1) && this->pnetf->pszCompName && *this->pnetf->pszCompName)
                        {
                            // 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.
                            STRRET str;

                            this->psf->lpVtbl->GetDisplayNameOf(this->psf,
                                    pidlToAdd, SHGDN_NORMAL, &str);

#ifdef UNICODE
                            {
                                TCHAR szName[MAX_PATH];
                                if (StrRetToStrN(szName, MAX_PATH, &str, pidlToAdd))
                                {
                                    if (0 == lstrcmpi(szName, this->pnetf->szUserInputCompName))
                                    {
                                        this->iPassCnt = 2;
                                    }
                                }
                            }

#else
                            Assert (str.uType == STRRET_OFFSET)

                            // Note in this test we ignore the two slashes...
                            if ((str.uType == STRRET_OFFSET) &&
                                    (ualstrcmpi((LPTSTR)((LPBYTE)pidlToAdd + str.uOffset),
                                            this->pnetf->szUserInputCompName) == 0))
                                this->iPassCnt = 2;
#endif
                        }
                        break;
                    }

                }
                else

                {
                    // Release the IDList that did not match
                    ILFree(pidl);
                    *piState = GNF_NOMATCH;
                }
            }
            else
            {
                // Close out the shell folder and the enumeration function.
                this->penum->lpVtbl->Release(this->penum);
                this->penum = NULL;

                this->psfEnum->lpVtbl->Release(this->psfEnum);
                this->psfEnum = NULL;
            }
        }
        if (!this->penum)
        {
                switch (this->iPassCnt)
                {
                case 1:
                    // We went through all of the items see if there is
                    // an exact match...
                    this->iPassCnt = 2;

                    return _FindCompByUNCName(this, ppidl, piState);

                case 2:
                    // We looped through everything so return done!
                    *piState = GNF_DONE;
                    return NOERROR;

                case 0:
                    // This is the main pass through here...
                    // Need to clone the idlist
                    this->pidl = ILClone(this->pidlStart);
                    if (this->pidl == NULL)
                    {
                       *piState = GNF_ERROR;
                       return ResultFromScode(E_OUTOFMEMORY);
                    }
                    this->iPassCnt = 1;

                    // We will do the first on in our own thread.
                    if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
                            this->pidl, NULL, &IID_IShellFolder,
                            &this->psfEnum)))
                    {

                        // BUGBUG:: Need more flags to control this!
                        if (FAILED(this->psfEnum->lpVtbl->EnumObjects(this->psfEnum,
                                (HWND)NULL, // BUGBUG: hwndOwner
                                SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &this->penum)))
                        {
                            // Failed to get iterator so release folder.
                            this->psfEnum->lpVtbl->Release(this->psfEnum);
                            this->psfEnum = NULL;
                            this->penum = NULL;
                        }
                    }
                }

            // We are now read to get the IShellfolder for this guy!
            (*pcFoldersSearched)++;

            // Need to put something here to show what is being searched!
            strret.uType = STRRET_OFFSET;
            if (SUCCEEDED(psfDesktop->lpVtbl->GetDisplayNameOf(psfDesktop,
                    this->pidl, SHGDN_NORMAL, &strret)))
            {
                 StrRetToStrN(this->pszDisplayText, MAX_PATH, &strret,
                         this->pidl);
            }

#ifdef NET_TIMINGS
            NTF_cNoPEnum++;
#endif
        }
    } while (fContinue && *pfContinue);

    return NOERROR;
}

//===========================================================================
// CNETFEnum::Skip Recursive Iterator that is very special to the docfind.
//===========================================================================
STDMETHODIMP CDefDFEnum_Skip(IDFEnum * pdfenum, int celt)
{
    return (E_NOTIMPL);
}

//===========================================================================
// CNETFEnum::Reset
//===========================================================================
STDMETHODIMP CDefDFEnum_Reset(IDFEnum * pdfenum)
{
    return (E_NOTIMPL);
}




//###########################################################################
// Code for the Browse For Starting Folder
//###########################################################################
// Structure to pass information to browse for folder dialog
typedef struct _bfsf
{
    HWND        hwndOwner;
    LPCITEMIDLIST pidlRoot;      // Root of search.  Typically desktop or my net
    LPTSTR        pszDisplayName;// Return display name of item selected.
    int          *piImage;      // where to return the Image index.
    LPCTSTR      lpszTitle;      // resource (or text to go in the banner over the tree.
    UINT         ulFlags;       // Flags that control the return stuff
    BFFCALLBACK  lpfn;
    LPARAM      lParam;
    HWND         hwndDlg;       // The window handle to the dialog
    HWND         hwndTree;      // The tree control.
    HTREEITEM    htiCurParent;  // tree item associated with Current shell folder
    LPSHELLFOLDER psfParent;    // Cache of the last IShell folder I needed...
    LPITEMIDLIST pidlCurrent;   // IDlist of current folder to select
    BOOL         fShowAllObjects; // Should we Show all ?
} BFSF, *PBFSF;


BOOL CALLBACK DocFind_BFSFDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
LPITEMIDLIST _BFSFUpdateISHCache(PBFSF pbfsf, HTREEITEM hti, LPITEMIDLIST pidlItem);

//===========================================================================
// DocFind_BrowseForStartingFolder - Browse for a folder to start the
//         search from.
//===========================================================================


// BUGBUG, give them a way to turn off the ok button.

LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi)
{
    LPITEMIDLIST lpRet;
    // NB: The ANSI Thunk (see below) does not call through this routine,
    // but rather called DialogBoxParam on its own.  If you change this
    // routine, change the A version as well!!
    BFSF bfsf =
        {
          lpbi->hwndOwner,
          lpbi->pidlRoot,
          lpbi->pszDisplayName,
          &lpbi->iImage,
          lpbi->lpszTitle,
          lpbi->ulFlags,
          lpbi->lpfn,
          lpbi->lParam,
        };
    HCURSOR hcOld = SetCursor(LoadCursor(NULL,IDC_WAIT));
    SHELLSTATE ss;

    SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
    bfsf.fShowAllObjects = ss.fShowAllObjects;

    // Now Create the dialog that will be doing the browsing.
    if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_BROWSEFORFOLDER),
                       lpbi->hwndOwner, DocFind_BFSFDlgProc, (LPARAM)&bfsf))
        lpRet = bfsf.pidlCurrent;
    else
        lpRet = NULL;

    if (hcOld)
        SetCursor(hcOld);

    return lpRet;
}

#ifdef UNICODE

LPITEMIDLIST WINAPI SHBrowseForFolderA(LPBROWSEINFOA lpbi)
{
    LPITEMIDLIST lpRet;
    WCHAR wszReturn[MAX_PATH];

    ThunkText * pThunkText = ConvertStrings(1,
                                            lpbi->lpszTitle);

    if (pThunkText)
    {
        BFSF bfsf =
        {
            lpbi->hwndOwner,
            lpbi->pidlRoot,
            wszReturn,
            &lpbi->iImage,
            pThunkText->m_pStr[0],   // UNICODE copy of lpbi->lpszTitle
            lpbi->ulFlags,
            lpbi->lpfn,
            lpbi->lParam,
        };
        HCURSOR hcOld = SetCursor(LoadCursor(NULL,IDC_WAIT));
        SHELLSTATE ss;
        BOOL fDialogResult;

        SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
        bfsf.fShowAllObjects = ss.fShowAllObjects;

        // Now Create the dialog that will be doing the browsing.

        fDialogResult = DialogBoxParam(HINST_THISDLL,
                                       MAKEINTRESOURCE(DLG_BROWSEFORFOLDER),
                                       lpbi->hwndOwner, DocFind_BFSFDlgProc,
                                       (LPARAM)&bfsf);
        LocalFree(pThunkText);
        if (hcOld)
            SetCursor(hcOld);

        if (fDialogResult)
        {
            BOOL fDefUsed;
            if (NULL != lpbi->pszDisplayName)
            {
                WideCharToMultiByte(CP_ACP,
                                     WC_COMPOSITECHECK | WC_DEFAULTCHAR,
                                     wszReturn,
                                     -1,
                                     lpbi->pszDisplayName,
                                     MAX_PATH,
                                     "_",
                                     &fDefUsed);
            }
            lpRet = bfsf.pidlCurrent;
        }
        else
        {
            lpRet = NULL;
        }
    }
    else
    {
        lpRet = NULL;
    }

    return lpRet;
}
#else
LPITEMIDLIST WINAPI SHBrowseForFolderW(LPBROWSEINFOW lpbi)
{
    return NULL;        // BUGBUG - BobDay - We should move this into SHUNIMP.C
}
#endif

void BFSFCallback(PBFSF pbfsf, UINT uMsg, LPARAM lParam)
{
    if (pbfsf->lpfn) {
        pbfsf->lpfn(pbfsf->hwndDlg, uMsg, lParam, pbfsf->lParam);
    }
}

//===========================================================================
// Some helper functions for processing the dialog
//===========================================================================
HTREEITEM _BFSFAddItemToTree(HWND hwndTree,
        HTREEITEM htiParent, LPITEMIDLIST pidl, int cChildren)
{
    TV_INSERTSTRUCT tii;

    // Initialize item to add with callback for everything
    tii.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
            TVIF_PARAM | TVIF_CHILDREN;
    tii.hParent = htiParent;
    tii.hInsertAfter = TVI_FIRST;
    tii.item.iImage = I_IMAGECALLBACK;
    tii.item.iSelectedImage = I_IMAGECALLBACK;
    tii.item.pszText = LPSTR_TEXTCALLBACK;   //
    tii.item.cChildren = cChildren; //  Assume it has children
    tii.item.lParam = (LPARAM)pidl;
    return TreeView_InsertItem(hwndTree, &tii);
}

//===========================================================================
LPITEMIDLIST _BFSFGetIDListFromTreeItem(HWND hwndTree, HTREEITEM hti)
{
    LPITEMIDLIST pidl;
    LPITEMIDLIST pidlT;
    TV_ITEM tvi;

    // If no hti passed in, get the selected on.
    if (hti == NULL)
    {
        hti = TreeView_GetSelection(hwndTree);
        if (hti == NULL)
            return(NULL);
    }

    // now lets get the information about the item
    tvi.mask = TVIF_PARAM | TVIF_HANDLE;
    tvi.hItem = hti;
    if (!TreeView_GetItem(hwndTree, &tvi))
        return(NULL);   // Failed again

    pidl = ILClone((LPITEMIDLIST)tvi.lParam);

    // Now walk up parents.
    while ((NULL != (tvi.hItem = TreeView_GetParent(hwndTree, tvi.hItem))) && pidl)
    {
        if (!TreeView_GetItem(hwndTree, &tvi))
            return(pidl);   // will assume I screwed up...
        pidlT = ILCombine((LPITEMIDLIST)tvi.lParam, pidl);

        ILFree(pidl);

        pidl = pidlT;

    }
    return(pidl);
}


int CALLBACK _BFSFTreeCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
        IShellFolder *psfParent = (IShellFolder *)lParamSort;
        HRESULT hres;

        hres = psfParent->lpVtbl->CompareIDs(psfParent,
                0, (LPITEMIDLIST)lParam1, (LPITEMIDLIST)lParam2);
        if (!SUCCEEDED(hres))
        {
                return(0);
        }

        return((short)SCODE_CODE(GetScode(hres)));
}

void _BFSFSort(PBFSF pbfsf, HTREEITEM hti, LPSHELLFOLDER psf)
{
    TV_SORTCB sSortCB;
    sSortCB.hParent = hti;
    sSortCB.lpfnCompare = _BFSFTreeCompare;

    psf->lpVtbl->AddRef(psf);
    sSortCB.lParam = (LPARAM)psf;
    TreeView_SortChildrenCB(pbfsf->hwndTree, &sSortCB, FALSE);
    psf->lpVtbl->Release(psf);
}

//===========================================================================
BOOL _BFSFHandleItemExpanding(PBFSF pbfsf, LPNM_TREEVIEW lpnmtv)
{
    LPITEMIDLIST pidlToExpand;
    LPITEMIDLIST pidl;
    LPSHELLFOLDER psf;
    LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);
    BYTE bType;
    DWORD grfFlags;
    BOOL fPrinterTest = FALSE;
    int cAdded = 0;
    TV_ITEM tvi;

    LPENUMIDLIST   penum;              // Enumerator in use.

    if (lpnmtv->action != TVE_EXPAND)
        return FALSE;

    if ((lpnmtv->itemNew.state & TVIS_EXPANDEDONCE))
        return FALSE;

    // set this bit now because we might be reentered via the wnet apis
    tvi.mask = TVIF_STATE;
    tvi.hItem = lpnmtv->itemNew.hItem;
    tvi.state = TVIS_EXPANDEDONCE;
    tvi.stateMask = TVIS_EXPANDEDONCE;
    TreeView_SetItem(pbfsf->hwndTree, &tvi);


    if (lpnmtv->itemNew.hItem == NULL)
    {
        lpnmtv->itemNew.hItem = TreeView_GetSelection(pbfsf->hwndTree);
        if (lpnmtv->itemNew.hItem == NULL)
            return FALSE;
    }

    pidlToExpand = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, lpnmtv->itemNew.hItem);

    if (pidlToExpand == NULL)
        return FALSE;

    // Now lets get the IShellFolder and iterator for this object
    // special case to handle if the Pidl is the desktop
    // This is rather gross, but the desktop appears to be simply a pidl
    // of length 0 and ILIsEqual will not work...
    if (pidlToExpand->mkid.cb == 0)
    {
        psf = psfDesktop;
        psfDesktop->lpVtbl->AddRef(psf);
    }
    else
    {
        if (FAILED(psfDesktop->lpVtbl->BindToObject(psfDesktop,
                pidlToExpand, NULL, &IID_IShellFolder, &psf)))
        {
            ILFree(pidlToExpand);
            return FALSE; // Could not get IShellFolder.
        }
    }

    // Need to do a couple of special cases here to allow us to
    // browse for a network printer.  In this case if we are at server
    // level we then need to change what we search for non folders when
    // we are the level of a server.
    if (pbfsf->ulFlags & BIF_BROWSEFORPRINTER)
    {
        grfFlags = SHCONTF_FOLDERS | SHCONTF_NETPRINTERSRCH;
        pidl = ILFindLastID(pidlToExpand);
        bType = SIL_GetType(pidl);
        fPrinterTest = ((bType & (SHID_NET|SHID_INGROUPMASK))==SHID_NET_SERVER);
        if (fPrinterTest)
            grfFlags |= SHCONTF_NONFOLDERS;
    }
    else
        grfFlags = SHCONTF_FOLDERS;

    if (pbfsf->fShowAllObjects)
        grfFlags |= SHCONTF_INCLUDEHIDDEN;



    if (FAILED(psf->lpVtbl->EnumObjects(psf, pbfsf->hwndDlg, grfFlags, &penum)))
    {
        psf->lpVtbl->Release(psf);
        ILFree(pidlToExpand);
        return FALSE;
    }
    // psf->lpVtbl->AddRef(psf);

    while (NULL != (pidl = DocFind_NextIDL(psf, penum)))
    {
        int cChildren = I_CHILDRENCALLBACK;  // Do call back for children
        //
        // We need to special case here in the netcase where we onlyu
        // browse down to workgroups...
        //
        //
        // Here is where I also need to special case to not go below
        // workgroups when the appropriate option is set.
        //
        bType = SIL_GetType(pidl);
        if ((pbfsf->ulFlags & BIF_DONTGOBELOWDOMAIN) && (bType & SHID_NET))
        {
            switch (bType & (SHID_NET | SHID_INGROUPMASK))
            {
            case SHID_NET_SERVER:
                ILFree(pidl);       // Dont want to add this one
                continue;           // Try the next one
            case SHID_NET_DOMAIN:
                cChildren = 0;      // Force to not have children;
            }
        }

        else if ((pbfsf->ulFlags & BIF_BROWSEFORCOMPUTER) && (bType & SHID_NET))
        {
            if ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER)
                cChildren = 0;  // Don't expand below it...
        }
        else if (fPrinterTest)
        {
            // Special case when we are only allowing printers.
            // for now I will simply key on the fact that it is non-FS.
            ULONG ulAttr = SFGAO_FILESYSTEM;

            psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &ulAttr);

            if ((ulAttr & SFGAO_FILESYSTEM)== 0)
            {
                cChildren = 0;      // Force to not have children;
            }
            else
            {
                ILFree(pidl);       // Dont want to add this one
                continue;           // Try the next one
            }
        }

        _BFSFAddItemToTree(pbfsf->hwndTree, lpnmtv->itemNew.hItem,
                pidl, cChildren);
        cAdded++;
    }

    // Now Cleanup after ourself
    penum->lpVtbl->Release(penum);

    _BFSFSort(pbfsf, lpnmtv->itemNew.hItem, psf);
    psf->lpVtbl->Release(psf);
    ILFree(pidlToExpand);

    // If we did not add anything we should update this item to let
    // the user know something happened.
    //
    if (cAdded == 0)
    {
        TV_ITEM tvi;
        tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;   // only change the number of children
        tvi.hItem = lpnmtv->itemNew.hItem;
        tvi.cChildren = 0;

        TreeView_SetItem(pbfsf->hwndTree, &tvi);

    }

    return TRUE;
}


//===========================================================================
void _BFSFHandleDeleteItem(PBFSF pbfsf, LPNM_TREEVIEW lpnmtv)
{
    // We need to free the IDLists that we allocated previously
    if (lpnmtv->itemOld.lParam != 0)
        ILFree((LPITEMIDLIST)lpnmtv->itemOld.lParam);
}

//===========================================================================
LPITEMIDLIST _BFSFUpdateISHCache(PBFSF pbfsf, HTREEITEM hti,
        LPITEMIDLIST pidlItem)
{
    HTREEITEM htiParent;
    LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);

    if (pidlItem == NULL)
        return(NULL);

    // Need to handle the root case here!
    htiParent = TreeView_GetParent(pbfsf->hwndTree, hti);
    if ((htiParent != pbfsf->htiCurParent) || (pbfsf->psfParent == NULL))
    {
        LPITEMIDLIST pidl;

        if (pbfsf->psfParent)
        {

            if (pbfsf->psfParent != psfDesktop)
                pbfsf->psfParent->lpVtbl->Release(pbfsf->psfParent);
            pbfsf->psfParent = NULL;
        }

        if (htiParent)
        {
            pidl = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, htiParent);
        }
        else
        {
            //
            // If No Parent then the item here is one of our roots which
            // should be fully qualified.  So try to get the parent by
            // decomposing the ID.
            //
            LPITEMIDLIST pidlT = (LPITEMIDLIST)ILFindLastID(pidlItem);
            if (pidlT != pidlItem)
            {
                pidl = ILClone(pidlItem);
                ILRemoveLastID(pidl);
                pidlItem = pidlT;
            }
            else
                pidl = NULL;
        }

        pbfsf->htiCurParent = htiParent;

        // If still NULL then we use root of evil...
        if (pidl == NULL || (pidl->mkid.cb == 0))
        {
            // Still one m
            pbfsf->psfParent = psfDesktop;

            if (pidl)
                ILFree(pidl);
        }

        else
        {
            psfDesktop->lpVtbl->BindToObject(psfDesktop,
                     pidl, NULL, &IID_IShellFolder, &pbfsf->psfParent);
            ILFree(pidl);

            if (pbfsf->psfParent == NULL)
                return NULL;
        }
    }
    return(ILFindLastID(pidlItem));
}


//===========================================================================
void _BFSFGetDisplayInfo(PBFSF pbfsf, TV_DISPINFO *lpnm)
{
    TV_ITEM ti;
    LPITEMIDLIST pidlItem = (LPITEMIDLIST)lpnm->item.lParam;

    if ((lpnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN)) == 0)
        return; // nothing for us to do here.

    pidlItem = _BFSFUpdateISHCache(pbfsf, lpnm->item.hItem, pidlItem);

    ti.mask = 0;
    ti.hItem = (HTREEITEM)lpnm->item.hItem;

    // They are asking for IconIndex.  See if we can find it now.
    // Once found update their list, such that they wont call us back for
    // it again.
    if (lpnm->item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE))
    {
        // We now need to map the item into the right image index.
        ti.iImage = lpnm->item.iImage = SHMapPIDLToSystemImageListIndex(
                pbfsf->psfParent, pidlItem, &ti.iSelectedImage);
        // we should save it back away to
        lpnm->item.iSelectedImage = ti.iSelectedImage;
        ti.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
    }
    // Also see if this guy has any child folders
    if (lpnm->item.mask & TVIF_CHILDREN)
    {
        ULONG ulAttrs;

        ulAttrs = SFGAO_HASSUBFOLDER;
        pbfsf->psfParent->lpVtbl->GetAttributesOf(pbfsf->psfParent,
                1, &pidlItem, &ulAttrs);

        ti.cChildren = lpnm->item.cChildren =
                (ulAttrs & SFGAO_HASSUBFOLDER)? 1 : 0;

        ti.mask |= TVIF_CHILDREN;

    }

    if (lpnm->item.mask & TVIF_TEXT)
    {
        STRRET str;
        pbfsf->psfParent->lpVtbl->GetDisplayNameOf(pbfsf->psfParent,
                pidlItem, SHGDN_INFOLDER, &str);

        StrRetToStrN(lpnm->item.pszText, lpnm->item.cchTextMax, &str, pidlItem);
        ti.mask |= TVIF_TEXT;
        ti.pszText = lpnm->item.pszText;
    }

    // Update the item now
    TreeView_SetItem(pbfsf->hwndTree, &ti);
}

//===========================================================================
void _BFSFHandleSelChanged(PBFSF pbfsf, LPNM_TREEVIEW lpnmtv)
{
    LPITEMIDLIST pidl;
    ULONG ulAttrs = SFGAO_FILESYSTEM;
    BYTE bType;

    // We only need to do anything if we only want to return File system
    // level objects.
    if ((pbfsf->ulFlags & (BIF_RETURNONLYFSDIRS|BIF_RETURNFSANCESTORS|BIF_BROWSEFORPRINTER|BIF_BROWSEFORCOMPUTER)) == 0)
        goto NotifySelChange;

    // We need to get the attributes of this object...
    pidl = _BFSFUpdateISHCache(pbfsf, lpnmtv->itemNew.hItem,
            (LPITEMIDLIST)lpnmtv->itemNew.lParam);

    if (pidl)
    {
        BOOL fEnable;

        bType = SIL_GetType(pidl);
        if ((pbfsf->ulFlags & (BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS)) != 0)
        {
            int i;
            // if this is the root pidl, then do a get attribs on 0
            // so that we'll get the attributes on the root, rather than
            // random returned values returned by FSFolder
            if (ILIsEmpty(pidl)) {
                i = 0;
            } else
                i = 1;

            pbfsf->psfParent->lpVtbl->GetAttributesOf(pbfsf->psfParent,
                                                      i, &pidl, &ulAttrs);

            fEnable = (((ulAttrs & SFGAO_FILESYSTEM) && (pbfsf->ulFlags & BIF_RETURNONLYFSDIRS)) ||
                ((ulAttrs & SFGAO_FILESYSANCESTOR) && (pbfsf->ulFlags & BIF_RETURNFSANCESTORS))) ||
                    ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
        }
        else if ((pbfsf->ulFlags & BIF_BROWSEFORCOMPUTER) != 0)
            fEnable = ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SERVER);
        else if ((pbfsf->ulFlags & BIF_BROWSEFORPRINTER) != 0)
        {
            // Printers are of type Share and usage Print...
            fEnable = ((bType & (SHID_NET | SHID_INGROUPMASK)) == SHID_NET_SHARE);
        }

        EnableWindow(GetDlgItem(pbfsf->hwndDlg, IDOK),fEnable);

    }

NotifySelChange:
    if (pbfsf->lpfn) {
        pidl = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, lpnmtv->itemNew.hItem);
        BFSFCallback(pbfsf, BFFM_SELCHANGED, (LPARAM)pidl);
        ILFree(pidl);
    }
}

BOOL BrowseSelectPidl(PBFSF pbfsf, LPCITEMIDLIST pidl)
{
    HTREEITEM htiParent;
    LPITEMIDLIST pidlTemp;
    LPITEMIDLIST pidlNext = NULL;
    LPITEMIDLIST pidlParent = NULL;
    BOOL fRet = FALSE;

    htiParent = TreeView_GetChild(pbfsf->hwndTree, NULL);
    if (htiParent) {

        // step through each item of the pidl
        for (;;) {
            TreeView_Expand(pbfsf->hwndTree, htiParent, TVE_EXPAND);
            pidlParent = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, htiParent);
            if (!pidlParent)
                break;

            pidlNext = ILClone(pidl);
            if (!pidlNext)
                break;

            pidlTemp = ILFindChild(pidlParent, pidlNext);
            if (!pidlTemp)
                break;

            if (ILIsEmpty(pidlTemp)) {
                // found it!
                //
                TreeView_SelectItem(pbfsf->hwndTree, htiParent);
                fRet = TRUE;
                break;
            } else {
                // loop to find the next item
                HTREEITEM htiChild;

                pidlTemp = ILGetNext(pidlTemp);
                if (!pidlTemp)
                    break;
                else
                    pidlTemp->mkid.cb = 0;


                htiChild = TreeView_GetChild(pbfsf->hwndTree, htiParent);
                while (htiChild) {
                    BOOL fEqual;
                    pidlTemp = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree, htiChild);
                    if (!pidlTemp) {
                        htiChild = NULL;
                        break;
                    }
                    fEqual = ILIsEqual(pidlTemp, pidlNext);

                    ILFree(pidlTemp);
                    if (fEqual) {
                        break;
                    } else {
                        htiChild = TreeView_GetNextSibling(pbfsf->hwndTree, htiChild);
                    }
                }

                if (!htiChild) {
                    // we didn't find the next one... bail
                    break;
                } else {
                    // the found child becomes the next parent
                    htiParent = htiChild;
                    ILFree(pidlParent);
                    ILFree(pidlNext);
                }
            }

        }
    }

    if (pidlParent) ILFree(pidlParent);
    if (pidlNext) ILFree(pidlNext);
    return fRet;
}

//===========================================================================
// DocFind_OnBFSFInitDlg - Process the init dialog
//===========================================================================
BOOL DocFind_OnBFSFInitDlg(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
    HTREEITEM hti;
    PBFSF pbfsf = (PBFSF)lParam;
    HIMAGELIST himl;
    LPTSTR lpsz;
    TCHAR szTitle[80];    // no title should be bigger than this!
    HWND hwndTree;

    lpsz = ResourceCStrToStr(HINST_THISDLL, pbfsf->lpszTitle);
    SetDlgItemText(hwnd, IDD_BROWSETITLE, lpsz);
    if (lpsz != pbfsf->lpszTitle)
    {
        LocalFree(lpsz);
        lpsz = NULL;
    }


    SetWindowLong(hwnd, DWL_USER, (LONG)lParam);
    pbfsf->hwndDlg = hwnd;
    hwndTree = pbfsf->hwndTree = GetDlgItem(hwnd, IDD_FOLDERLIST);

    if (hwndTree)
    {
        UINT swpFlags = SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
                | SWP_NOACTIVATE;
        RECT rc;
        POINT pt = {0,0};

        if (!(pbfsf->ulFlags & BIF_STATUSTEXT)) {
            HWND hwndStatus = GetDlgItem(hwnd, IDD_BROWSESTATUS);
            // nuke the status window
            ShowWindow(hwndStatus, SW_HIDE);
            MapWindowPoints(hwndStatus, hwnd, &pt, 1);
            GetClientRect(hwndTree, &rc);
            MapWindowPoints(hwndTree, hwnd, (POINT*)&rc, 2);
            rc.top = pt.y;
            swpFlags =  SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
        }

        Shell_GetImageLists(NULL, &himl);
        TreeView_SetImageList(hwndTree, himl, TVSIL_NORMAL);

        SetWindowLong(hwndTree, GWL_EXSTYLE,
                GetWindowLong(hwndTree, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);

        // Now try to get this window to know to recalc
        SetWindowPos(hwndTree, NULL, rc.left, rc.top,
                     rc.right - rc.left, rc.bottom - rc.top, swpFlags);

    }

    // If they passed in a root, add it, else add the contents of the
    // Root of evil... to the list as ROOT objects.
    if (pbfsf->pidlRoot)
    {
        LPITEMIDLIST pidl;
        if (!HIWORD(pbfsf->pidlRoot)) {
            pidl = SHCloneSpecialIDList(NULL, (UINT)pbfsf->pidlRoot, TRUE);
        } else {
            pidl = ILClone(pbfsf->pidlRoot);
        }
        // Now lets insert the Root object
        hti = _BFSFAddItemToTree(hwndTree, TVI_ROOT,
                                 pidl, 1);
        // Still need to expand below this point. to the starting location
        // That was passed in. But for now expand the first level.
        TreeView_Expand(hwndTree, hti, TVE_EXPAND);
    }
    else
    {
        LPCITEMIDLIST pidlDrives = GetSpecialFolderIDList(NULL, CSIDL_DRIVES, FALSE);
        LPITEMIDLIST pidlDesktop = SHCloneSpecialIDList(NULL, CSIDL_DESKTOP, FALSE);
        HTREEITEM htiRoot;

        htiRoot = _BFSFAddItemToTree(hwndTree, TVI_ROOT, pidlDesktop, 1);

        // Expand the first level under the desktop
        TreeView_Expand(hwndTree, htiRoot, TVE_EXPAND);

        // Lets Preexpand the Drives portion....
        hti = TreeView_GetChild(hwndTree, htiRoot);
        while (hti)
        {
            LPITEMIDLIST pidl = _BFSFGetIDListFromTreeItem(hwndTree, hti);
            if (ILIsEqual(pidl, pidlDrives))
            {

                TreeView_Expand(hwndTree, hti, TVE_EXPAND);

                TreeView_SelectItem(hwndTree, hti);
                ILFree(pidl);
                break;
            }
            ILFree(pidl);
            hti = TreeView_GetNextSibling(hwndTree, hti);
        }
    }

    // go to our internal selection changed code to do any window enabling needed
    {
        NM_TREEVIEW nmtv;
        hti = TreeView_GetSelection(hwndTree);
        if (hti) {
            TV_ITEM ti;
            ti.mask = TVIF_PARAM;
            ti.hItem = hti;
            TreeView_GetItem(hwndTree, &ti);
            nmtv.itemNew.hItem = hti;
            nmtv.itemNew.lParam = ti.lParam;

            _BFSFHandleSelChanged(pbfsf, &nmtv);
        }
    }

    if ((pbfsf->ulFlags & BIF_BROWSEFORCOMPUTER) != 0)
    {
        LoadString(HINST_THISDLL, IDS_FINDSEARCH_COMPUTER, szTitle, ARRAYSIZE(szTitle));
        SetWindowText(hwnd, szTitle);
    }
    else if ((pbfsf->ulFlags & BIF_BROWSEFORPRINTER) != 0)
    {
        LoadString(HINST_THISDLL, IDS_FINDSEARCH_PRINTER, szTitle, ARRAYSIZE(szTitle));
        SetWindowText(hwnd, szTitle);
    }

    BFSFCallback(pbfsf, BFFM_INITIALIZED, 0);

    return TRUE;
}


//
// Called when a ANSI app sends BFFM_SETSTATUSTEXT message.
//
void _BFSFSetStatusTextA(PBFSF pbfsf, LPCSTR lpszText)
{
    CHAR szText[100];
    if (!HIWORD(lpszText)) {
        LoadStringA(HINST_THISDLL, LOWORD((DWORD)lpszText), szText, ARRAYSIZE(szText));
        lpszText = szText;
    }

    SetDlgItemTextA(pbfsf->hwndDlg, IDD_BROWSESTATUS, lpszText);
}


//
// Called when a UNICODE app sends BFFM_SETSTATUSTEXT message.
//
void _BFSFSetStatusTextW(PBFSF pbfsf, LPCWSTR lpszText)
{
    WCHAR szText[100];
    if (!HIWORD(lpszText)) {
        LoadStringW(HINST_THISDLL, LOWORD((DWORD)lpszText), szText, ARRAYSIZE(szText));
        lpszText = szText;
    }

    SetDlgItemTextW(pbfsf->hwndDlg, IDD_BROWSESTATUS, lpszText);
}


//
// Called when an ANSI app sends BFFM_SETSELECTION message.
//
BOOL _BFSFSetSelectionA(PBFSF pbfsf, BOOL blParamIsPath, LPARAM lParam)
{
    BOOL fRet = FALSE;

    if (blParamIsPath) 
    {
#ifdef UNICODE
        //
        // UNICODE build.  Convert path from ansi to wide-char.
        //
        LPWSTR lpszPathW = NULL;
        INT cchPathW     = 0;

        cchPathW = MultiByteToWideChar(CP_ACP,
                                       0,
                                       (LPCSTR)lParam,
                                       -1,
                                       NULL,
                                       0);
        if (0 < cchPathW)
        {
            lpszPathW = LocalAlloc(LPTR, cchPathW * sizeof(TCHAR));
            if (NULL != lpszPathW)
            {
                MultiByteToWideChar(CP_ACP,
                                    0,
                                    (LPCSTR)lParam,
                                    -1,
                                    lpszPathW,
                                    cchPathW);

                lParam = (LPARAM)SHSimpleIDListFromPath(lpszPathW);
                LocalFree(lpszPathW);
            }
            else
                return FALSE;  // Failed buffer allocation.
        }
#else
        //
        // ANSI build.  Just use ANSI path "as is".
        //
        lParam = (LPARAM)SHSimpleIDListFromPath((LPCTSTR)lParam);
#endif

        if (!lParam)
            return FALSE;  // Failed pidl creation.
    }

    fRet = BrowseSelectPidl(pbfsf, (LPITEMIDLIST)lParam);

    if (blParamIsPath)
        ILFree((LPITEMIDLIST)lParam);

    return fRet;
}


//
// Called when a UNICODE app sends BFFM_SETSELECTION message.
//
BOOL _BFSFSetSelectionW(PBFSF pbfsf, BOOL blParamIsPath, LPARAM lParam)
{
    BOOL fRet = FALSE;

    if (blParamIsPath) 
    {

#ifndef UNICODE
        //
        // ANSI build.  Convert path from wide-char to ansi.
        //
        LPSTR lpszPathA = NULL;
        INT cchPathA    = 0;

        cchPathA = WideCharToMultiByte(CP_ACP,
                                       0,
                                       (LPWSTR)lParam,
                                       -1,
                                       NULL,
                                       0,
                                       0,
                                       0);
        if (0 < cchPathA)
        {
            lpszPathA = LocalAlloc(LPTR, cchPathA * sizeof(TCHAR));
            if (NULL != lpszPathA)
            {
                WideCharToMultiByte(CP_ACP,
                                    0,
                                    (LPWSTR)lParam,
                                    -1,
                                    lpszPathA,
                                    cchPathA,
                                    0,
                                    0);

                lParam = (LPARAM)SHSimpleIDListFromPath(lpszPathA);
                LocalFree(lpszPathA);
            }
            else
                return FALSE;  // Failed buffer allocation.
        }
#else
        //
        // UNICODE build.  Just use wide char path "as is".
        //
        lParam = (LPARAM)SHSimpleIDListFromPath((LPCTSTR)lParam);
#endif
        if (!lParam)
            return FALSE;   // Failed pidl creation.
    }

    fRet = BrowseSelectPidl(pbfsf, (LPITEMIDLIST)lParam);

    if (blParamIsPath)
    {
        //
        // Free the pidl we created from path.
        //
        ILFree((LPITEMIDLIST)lParam);
    }

    return fRet;
}


//===========================================================================
// DocFind_OnBFSFCommand - Process the WM_COMMAND message
//===========================================================================
void DocFind_OnBFSFCommand(PBFSF pbfsf, int id, HWND hwndCtl,
        UINT codeNotify)
{
    HTREEITEM hti;
    switch (id)
    {
    case IDOK:
        // We can now update the structure with the idlist of the item selected
        hti = TreeView_GetSelection(pbfsf->hwndTree);
        pbfsf->pidlCurrent = _BFSFGetIDListFromTreeItem(pbfsf->hwndTree,
                hti);
        if (pbfsf->pszDisplayName || pbfsf->piImage)
        {
            TV_ITEM tvi;
            tvi.mask = (pbfsf->pszDisplayName)? (TVIF_TEXT | TVIF_IMAGE) :
                    TVIF_IMAGE;
            tvi.hItem = hti;
            tvi.pszText = pbfsf->pszDisplayName;
            tvi.cchTextMax = MAX_PATH;
            TreeView_GetItem(pbfsf->hwndTree, &tvi);

            if (pbfsf->piImage)
                *pbfsf->piImage = tvi.iImage;
        }
        EndDialog(pbfsf->hwndDlg, 1);     // To return TRUE.
        break;
    case IDCANCEL:
        EndDialog(pbfsf->hwndDlg, 0);     // to return FALSE from this.
        break;
    }
}




//===========================================================================
// DocFind_BSFSDlgProc - The dialog procedure for processing the browse
//          for starting folder dialog.
//===========================================================================
#pragma data_seg(DATASEG_READONLY)
const static DWORD aBrowseHelpIDs[] = {  // Context Help IDs
    IDD_BROWSETITLE,  NO_HELP,
    IDD_BROWSESTATUS, NO_HELP,
    IDD_FOLDERLIST,   IDH_BROWSELIST,

    0, 0
};
#pragma data_seg()

BOOL CALLBACK DocFind_BFSFDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam,
        LPARAM lParam)
{
    PBFSF pbfsf = (PBFSF)GetWindowLong(hwndDlg, DWL_USER);

    switch (msg) {
    HANDLE_MSG(pbfsf, WM_COMMAND, DocFind_OnBFSFCommand);
    HANDLE_MSG(hwndDlg, WM_INITDIALOG, DocFind_OnBFSFInitDlg);

    case WM_DESTROY:
        if (pbfsf->psfParent && (pbfsf->psfParent != Desktop_GetShellFolder(TRUE)))
        {
            pbfsf->psfParent->lpVtbl->Release(pbfsf->psfParent);
            pbfsf->psfParent = NULL;
        }
        break;

    case BFFM_SETSTATUSTEXTA:
        _BFSFSetStatusTextA(pbfsf, (LPCSTR)lParam);
        break;

    case BFFM_SETSTATUSTEXTW:
        _BFSFSetStatusTextW(pbfsf, (LPCWSTR)lParam);
        break;

    case BFFM_SETSELECTIONW:
        return _BFSFSetSelectionW(pbfsf, (BOOL)wParam, lParam);

    case BFFM_SETSELECTIONA:
        return _BFSFSetSelectionA(pbfsf, (BOOL)wParam, lParam);

    case BFFM_ENABLEOK:
        EnableWindow(GetDlgItem(hwndDlg, IDOK), lParam);
        break;


    case WM_NOTIFY:
        switch (((NMHDR *)lParam)->code)
        {
        case TVN_GETDISPINFO:
            _BFSFGetDisplayInfo(pbfsf, (TV_DISPINFO *)lParam);
            break;

        case TVN_ITEMEXPANDING:
            SetCursor(LoadCursor(NULL, IDC_WAIT));
            _BFSFHandleItemExpanding(pbfsf, (LPNM_TREEVIEW)lParam);
            break;
        case TVN_ITEMEXPANDED:
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            break;
        case TVN_DELETEITEM:
            _BFSFHandleDeleteItem(pbfsf, (LPNM_TREEVIEW)lParam);
            break;
        case TVN_SELCHANGED:
            _BFSFHandleSelChanged(pbfsf, (LPNM_TREEVIEW)lParam);
            break;
        }
        break;

    case WM_HELP:
        WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
            HELP_WM_HELP, (DWORD)(LPTSTR) aBrowseHelpIDs);
        break;

    case WM_CONTEXTMENU:
        WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
            (DWORD)(LPVOID) aBrowseHelpIDs);
        break;

    default:
        return FALSE;
    }

    return TRUE;
}



/*----------------------------------------------------------------------------
/ CNETFilter_DeclareFSNotifyInterest implementation
/ ----------------------------------
/ Purpose:
/   Register our interest in changes to the network so that the find results
/   can be correctly refreshed.
/ 
/ Notes:
/   -
/ 
/ In:
/   pdfff -> description of the find filter
/   hwndDlg = window handle of the find dialog
/   uMsg = message to be sent to window when informing of notify
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
STDMETHODIMP CNETFilter_DeclareFSNotifyInterest(LPDOCFINDFILEFILTER pnetf, HWND hwndDlg, UINT uMsg)
{
    LPNETFILTER this = IToClass(CNETFilter, dfff, pnetf);
    SHChangeNotifyEntry fsne;

    fsne.fRecursive = TRUE;
    fsne.pidl = this ->pidlStart;

    if (fsne.pidl) 
    {
        SHChangeNotifyRegister(hwndDlg, SHCNRF_NewDelivery | SHCNRF_ShellLevel, SHCNE_DISKEVENTS, uMsg, 1, &fsne);
    }

    return NOERROR;
}