|
|
#include "shellprv.h"
#include "commctrl.h"
#include "comctrlp.h"
#pragma hdrstop
#include "netview.h"
#include "msprintx.h"
#include "setupapi.h"
#include "ras.h"
#include "ids.h"
// a COM object to enumerate shares and printers in the shell, its acts
// as a monitor for all that is going on.
class CWorkgroupCrawler : public INetCrawler, IPersistPropertyBag { public: CWorkgroupCrawler(); ~CWorkgroupCrawler();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// INetCrawler
STDMETHOD(Update)(DWORD dwFlags);
// IPersist
STDMETHOD(GetClassID)(CLSID *pclsid) { *pclsid = CLSID_WorkgroupNetCrawler; return S_OK; }
// IPersistPropertyBag
STDMETHOD(InitNew)() { return S_OK; } STDMETHOD(Load)(IPropertyBag *ppb, IErrorLog *pel) { IUnknown_Set((IUnknown**)&_ppb, ppb); return S_OK; } STDMETHOD(Save)(IPropertyBag *ppb, BOOL fClearDirty, BOOL fSaveAll) { return S_OK; }
private: HRESULT _GetMRUs(); void _AgeOutShares(BOOL fDeleteAll); HRESULT _CreateShortcutToShare(LPCTSTR pszRemoteName); HRESULT _InstallPrinter(LPCTSTR pszRemoteName); BOOL _KeepGoing(int *pcMachines, int *pcShares, int *pcPrinters); void _EnumResources(LPNETRESOURCE pnr, int *pcMachines, HDPA hdaShares, HDPA hdaPrinters); HANDLE _AddPrinterConnectionNoUI(LPCWSTR pszRemoteName, BOOL *pfInstalled);
static int CALLBACK _DiscardCB(void *pvItem, void *pv); static int CALLBACK _InstallSharesCB(void *pvItem, void *pv); static int CALLBACK _InstallPrinterCB(void *pvItem, void *pv);
LONG _cRef; // reference count for the object
HANDLE _hPrinters; // MRU for printers
HKEY _hShares; // registry key for the printer shares
HINSTANCE _hPrintUI; // instance handle for printui.dll
IPropertyBag *_ppb; // property bag object for state
};
// constants for the MRU's and buffers
#define WORKGROUP_PATH \
REGSTR_PATH_EXPLORER TEXT("\\WorkgroupCrawler")
#define PRINTER_SUBKEY \
(WORKGROUP_PATH TEXT("\\Printers")) #define SHARE_SUBKEY \
(WORKGROUP_PATH TEXT("\\Shares"))
#define LAST_VISITED TEXT("DateLastVisited")
#define SHORTCUT_NAME TEXT("Filename")
#define MAX_MACHINES 32
#define MAX_PRINTERS 10
#define MAX_SHARES 10
#define CB_WNET_BUFFER (8*1024)
typedef HANDLE (* ADDPRINTCONNECTIONNOUI)(LPCWSTR, BOOL *);
// construction and IUnknown
CWorkgroupCrawler::CWorkgroupCrawler() : _cRef(1) { }
CWorkgroupCrawler::~CWorkgroupCrawler() { if (_hPrinters) FreeMRUList(_hPrinters);
if (_hShares) RegCloseKey(_hShares);
if (_hPrintUI) FreeLibrary(_hPrintUI);
if (_ppb) _ppb->Release(); }
STDMETHODIMP CWorkgroupCrawler::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CWorkgroupCrawler, INetCrawler), // IID_INetCrawler
QITABENT(CWorkgroupCrawler, IPersist), // IID_IPersist
QITABENT(CWorkgroupCrawler, IPersistPropertyBag), // IID_IPersistPropertyBag
{ 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CWorkgroupCrawler::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CWorkgroupCrawler::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
STDAPI CWorkgroupCrawler_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv) { CWorkgroupCrawler *pwgc = new CWorkgroupCrawler(); if (!pwgc) return E_OUTOFMEMORY;
HRESULT hr = pwgc->QueryInterface(riid, ppv); pwgc->Release(); return hr; }
// lets open the keys for the objects we are about to install
HRESULT CWorkgroupCrawler::_GetMRUs() { // get the printers MRU if we need to allocate one
if (!_hPrinters) { MRUINFO mi = { 0 }; mi.cbSize = sizeof(mi); mi.hKey = HKEY_CURRENT_USER; mi.uMax = (MAX_PRINTERS * MAX_MACHINES); mi.lpszSubKey = PRINTER_SUBKEY;
_hPrinters = CreateMRUList(&mi); if (!_hPrinters) return E_OUTOFMEMORY; }
if (!_hShares) { DWORD dwres = RegCreateKeyEx(HKEY_CURRENT_USER, SHARE_SUBKEY, 0, TEXT(""), 0, MAXIMUM_ALLOWED, NULL, &_hShares, NULL); if (WN_SUCCESS != dwres) { return E_FAIL; } }
return S_OK; // success
}
// lets create a folder shortcut to the object
HRESULT CWorkgroupCrawler::_CreateShortcutToShare(LPCTSTR pszRemoteName) { HRESULT hr = S_OK; TCHAR szTemp[MAX_PATH]; BOOL fCreateLink = FALSE; HKEY hk = NULL;
// the share information is stored in the registry as follows:
//
// Shares
// Remote Name
// value: shortcut name
// value: last seen
//
// as we add each share we update the information stored in this list in
// the registry. for each entry we have the shortcut name (so we can remove it)
// and the time and date we last visited the share.
// determine if we need to recreate the object?
StrCpyN(szTemp, pszRemoteName+2, ARRAYSIZE(szTemp)); LPTSTR pszTemp = StrChr(szTemp, TEXT('\\')); if (pszTemp) { *pszTemp = TEXT('/'); // convert the \\...\... to .../...
DWORD dwres = RegOpenKeyEx(_hShares, szTemp, 0, MAXIMUM_ALLOWED, &hk); if (WN_SUCCESS != dwres) { fCreateLink = TRUE; dwres = RegCreateKeyEx(_hShares, szTemp, 0, TEXT(""), 0, MAXIMUM_ALLOWED, NULL, &hk, NULL); }
if (WN_SUCCESS == dwres) { // if we haven't already seen the link (eg. the key didn't exist in the registry
// then lets create it now.
if (fCreateLink) { // NOTE: we must use SHCoCreateInstance() here because we are being called from a thread
// that intentionally did not initialize COM (see comment in Update())
IShellLink *psl; hr = SHCoCreateInstance(NULL, &CLSID_FolderShortcut, NULL, IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { psl->SetPath(pszRemoteName); // sotore the remote name, its kinda important
// get a description for the link, this comes either from the desktop.ini or the
// is a pretty version of teh remote name.
if (GetShellClassInfo(pszRemoteName, TEXT("InfoTip"), szTemp, ARRAYSIZE(szTemp))) { psl->SetDescription(szTemp); } else { StrCpyN(szTemp, pszRemoteName, ARRAYSIZE(szTemp)); PathMakePretty(szTemp); psl->SetDescription(szTemp); }
// some links (shared documents) can specify a shortcut name, if this is specified
// then use it, otherwise get a filename from the nethood folder (eg. foo on bah).
//
// we musst also record the name we save the shortcut as, this used when we
// age out the links from the hood folder.
if (!GetShellClassInfo(pszRemoteName, TEXT("NetShareDisplayName"), szTemp, ARRAYSIZE(szTemp))) { LPITEMIDLIST pidl; hr = SHILCreateFromPath(pszRemoteName, &pidl, NULL); if (SUCCEEDED(hr)) { hr = SHGetNameAndFlags(pidl, SHGDN_NORMAL, szTemp, ARRAYSIZE(szTemp), NULL); ILFree(pidl); } } else { hr = S_OK; }
// should we find a unique name (daviddv)
if (SUCCEEDED(hr)) { if (NO_ERROR == SHSetValue(hk, NULL, SHORTCUT_NAME, REG_SZ, szTemp, lstrlen(szTemp)*sizeof(TCHAR))) { hr = SaveShortcutInFolder(CSIDL_NETHOOD, szTemp, psl); } else { hr = E_FAIL; } }
psl->Release(); } }
// lets update the time we last saw the link into the registry - this is used for the clean up
// pass we will perform.
if (SUCCEEDED(hr)) { FILETIME ft; GetSystemTimeAsFileTime(&ft);
dwres = SHSetValue(hk, NULL, LAST_VISITED, REG_BINARY, (void*)&ft, sizeof(ft)); hr = (NO_ERROR != dwres) ? E_FAIL:S_OK; } }
if (hk) RegCloseKey(hk); } else { hr = E_UNEXPECTED; } return hr; }
// walk the list of shares stored in the registry to determine which ones should be
// removed from the file system and the list. all files older than 7 days need to
// be removed.
#define FILETIME_SECOND_OFFSET (LONGLONG)((1 * 10 * 1000 * (LONGLONG)1000))
void CWorkgroupCrawler::_AgeOutShares(BOOL fDeleteAll) { FILETIME ft; ULARGE_INTEGER ulTime; DWORD index = 0; TCHAR szFilesToDelete[1024]; int cchFilesToDelete = 0;
GetSystemTimeAsFileTime(&ft); ulTime = *((ULARGE_INTEGER*)&ft); ulTime.QuadPart -= FILETIME_SECOND_OFFSET*((60*60*24)*2);
SHQueryInfoKey(_hShares, &index, NULL, NULL, NULL); // retrieve the count of the keys
while (((LONG)(--index)) >= 0) { TCHAR szKey[MAX_PATH]; DWORD cb = ARRAYSIZE(szKey); BOOL fRemoveKey = FALSE;
if (WN_SUCCESS == SHEnumKeyEx(_hShares, index, szKey, &cb)) { // we enumerated a key name, so lets open it so we can look around inside.
HKEY hk; if (WN_SUCCESS == RegOpenKeyEx(_hShares, szKey, 0, MAXIMUM_ALLOWED, &hk)) { ULARGE_INTEGER ulLastSeen;
// when did we last crawl to this object, if it was less than the time we
// have for our threshold, then we go through the process of cleaning up the
// object.
cb = sizeof(ulLastSeen); if (ERROR_SUCCESS == SHGetValue(hk, NULL, LAST_VISITED, NULL, (void*)&ulLastSeen, &cb)) { if (fDeleteAll || (ulLastSeen.QuadPart <= ulTime.QuadPart)) { TCHAR szName[MAX_PATH]; cb = ARRAYSIZE(szName)*sizeof(TCHAR); if (ERROR_SUCCESS == SHGetValue(hk, NULL, SHORTCUT_NAME, NULL, &szName, &cb)) { TCHAR szPath[MAX_PATH];
// compose the path to the object we want to delete. if the buffer
// is full (eg. this item would over run the size) then flush the
// buffer.
SHGetFolderPath(NULL, CSIDL_NETHOOD|CSIDL_FLAG_CREATE, NULL, 0, szPath); PathAppend(szPath, szName);
if ((lstrlen(szPath)+cchFilesToDelete) >= ARRAYSIZE(szFilesToDelete)) { SHFILEOPSTRUCT shfo = { NULL, FO_DELETE, szFilesToDelete, NULL, FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI, FALSE, NULL, NULL };
szFilesToDelete[cchFilesToDelete] = 0; // double terminate
SHFileOperation(&shfo);
cchFilesToDelete = 0; }
// add this name to the buffer
StrCpyN(&szFilesToDelete[cchFilesToDelete], szPath, ARRAYSIZE(szFilesToDelete) - cchFilesToDelete); cchFilesToDelete += lstrlen(szPath)+1; }
fRemoveKey = TRUE; } }
RegCloseKey(hk);
// we can only close the key once it has been closed
if (fRemoveKey) SHDeleteKey(_hShares, szKey); } } }
// are there any trailing files in the buffer? if so then lets nuke them also
if (cchFilesToDelete) { SHFILEOPSTRUCT shfo = { NULL, FO_DELETE, szFilesToDelete, NULL, FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI, FALSE, NULL, NULL };
szFilesToDelete[cchFilesToDelete] = 0; // double terminate
SHFileOperation(&shfo); } }
// silently install printers we have discovered. we have the remote name of the
// printer share, so we then call printui to perform the printer installation
// which it does without UI (hopefully).
HANDLE CWorkgroupCrawler::_AddPrinterConnectionNoUI(LPCWSTR pszRemoteName, BOOL *pfInstalled) { HANDLE hResult = NULL;
if (!_hPrintUI) _hPrintUI = LoadLibrary(TEXT("printui.dll"));
if (_hPrintUI) { ADDPRINTCONNECTIONNOUI apc = (ADDPRINTCONNECTIONNOUI)GetProcAddress(_hPrintUI, (LPCSTR)200); if (apc) { hResult = apc(pszRemoteName, pfInstalled); } }
return hResult; }
HRESULT CWorkgroupCrawler::_InstallPrinter(LPCTSTR pszRemoteName) { if (-1 == FindMRUString(_hPrinters, pszRemoteName, NULL)) { BOOL fInstalled; HANDLE hPrinter = _AddPrinterConnectionNoUI(pszRemoteName, &fInstalled); if (hPrinter) { ClosePrinter(hPrinter); hPrinter = NULL; } } AddMRUString(_hPrinters, pszRemoteName); // promote back to the top of the list
return S_OK; }
// check the counters, if we have max'd out then lets stop enumerating
BOOL CWorkgroupCrawler::_KeepGoing(int *pcMachines, int *pcShares, int *pcPrinters) { if (pcMachines && (*pcMachines > MAX_MACHINES)) return FALSE; if (pcShares && (*pcShares > MAX_SHARES)) return FALSE; if (pcPrinters && (*pcPrinters > MAX_PRINTERS)) return FALSE;
return TRUE; }
void CWorkgroupCrawler::_EnumResources(LPNETRESOURCE pnr, int *pcMachines, HDPA hdaShares, HDPA hdaPrinters) { HANDLE hEnum = NULL; int cPrinters = 0; int cShares = 0; DWORD dwScope = RESOURCE_GLOBALNET; // if no net resource structure passed then lets enumerate the workgroup
// (this is used for debugging)
NETRESOURCE nr = { 0 }; if (!pnr) { pnr = &nr; dwScope = RESOURCE_CONTEXT; nr.dwType = RESOURCETYPE_ANY; nr.dwUsage = RESOURCEUSAGE_CONTAINER; }
// open the enumerator
DWORD dwres = WNetOpenEnum(dwScope, RESOURCETYPE_ANY, 0, pnr, &hEnum); if (NO_ERROR == dwres) { NETRESOURCE *pnrBuffer = (NETRESOURCE*)SHAlloc(CB_WNET_BUFFER); // avoid putting the buffer on the stack
if (pnrBuffer) { while ((WN_SUCCESS == dwres) || (dwres == ERROR_MORE_DATA) && _KeepGoing(pcMachines, &cShares, &cPrinters)) { DWORD cbEnumBuffer= CB_WNET_BUFFER; DWORD dwCount = -1;
// enumerate the resources for this enum context and then lets
// determine the objects which we should see.
dwres = WNetEnumResource(hEnum, &dwCount, pnrBuffer, &cbEnumBuffer); if ((WN_SUCCESS == dwres) || (dwres == ERROR_MORE_DATA)) { DWORD index; for (index = 0 ; (index != dwCount) && _KeepGoing(pcMachines, &cShares, &cPrinters) ; index++) { LPNETRESOURCE pnr = &pnrBuffer[index]; LPTSTR pszRemoteName = pnr->lpRemoteName;
switch (pnr->dwDisplayType) { case RESOURCEDISPLAYTYPE_ROOT: // ignore the entire network object
default: break;
case RESOURCEDISPLAYTYPE_NETWORK: { // ensure that we only crawl the local network providers (eg. Windows Networking)
// crawling DAV, TSCLIENT etc can cause all sorts of random pop ups.
DWORD dwType, cbProviderType = sizeof(dwType); if (WN_SUCCESS == WNetGetProviderType(pnr->lpProvider, &dwType)) { if (dwType == WNNC_NET_LANMAN) { _EnumResources(pnr, pcMachines, hdaShares, hdaPrinters); } } break; } case RESOURCEDISPLAYTYPE_DOMAIN: _EnumResources(pnr, pcMachines, hdaShares, hdaPrinters); break; case RESOURCEDISPLAYTYPE_SERVER: { *pcMachines += 1; // another machine found
if (!PathIsSlow(pszRemoteName, -1)) { SHCacheComputerDescription(pszRemoteName, pnr->lpComment); _EnumResources(pnr, pcMachines, hdaShares, hdaPrinters); }
break; } case RESOURCEDISPLAYTYPE_SHARE: { HDPA hdpa = NULL; switch (pnr->dwType) { case RESOURCETYPE_PRINT: cPrinters++; hdpa = hdaPrinters; break;
case RESOURCETYPE_DISK: cShares++; hdpa = hdaShares; break;
default: break; }
if (hdpa) { LPTSTR pszName = StrDup(pszRemoteName); if (pszName) { if (-1 == DPA_AppendPtr(hdpa, pszName)) { LocalFree(pszName); } } } break; } } } } } SHFree(pnrBuffer); }
WNetCloseEnum(hEnum); } }
// handle the clean up of the DPA's, either we are installing or
// we are releasing objects.
int CALLBACK CWorkgroupCrawler::_DiscardCB(void *pvItem, void *pv) { LPTSTR pszRemoteName = (LPTSTR)pvItem; LocalFree(pszRemoteName); return 1; }
int CALLBACK CWorkgroupCrawler::_InstallPrinterCB(void *pvItem, void *pv) { CWorkgroupCrawler* pnc = (CWorkgroupCrawler*)pv; if (pnc) { LPTSTR pszRemoteName = (LPTSTR)pvItem; pnc->_InstallPrinter(pszRemoteName); } return _DiscardCB(pvItem, pv); }
int CALLBACK CWorkgroupCrawler::_InstallSharesCB(void *pvItem, void *pv) { CWorkgroupCrawler* pnc = (CWorkgroupCrawler*)pv; if (pnc) { LPTSTR pszRemoteName = (LPTSTR)pvItem; pnc->_CreateShortcutToShare(pszRemoteName); } return _DiscardCB(pvItem, pv); }
HRESULT CWorkgroupCrawler::Update(DWORD dwFlags) { // don't crawl if we are logged in on a TS client, this will discover the shares and
// printers local to the terminal server machine, rather than the ones local to the
// users login domain - badness.
if (SHGetMachineInfo(GMI_TSCLIENT)) return S_OK;
// by default we will only crawl if there isn't a RAS connection, therefore lets
// check the status using RasEnumConnections.
RASCONN rc = { 0 }; DWORD cbConnections = sizeof(rc); DWORD cConnections = 0;
rc.dwSize = sizeof(rc); if (!RasEnumConnections(&rc, &cbConnections, &cConnections) && cConnections) return S_OK;
// check to see if we are in a domain or not, if we are then we shouldn't crawl. however
// we do a provide a "WorkgroupOnly" policy which overrides this behaviour. setting
// this causes us to skip the check, and perform a CONTEXT ENUM below...
BOOL fWorkgroupOnly = (_ppb ? SHPropertyBag_ReadBOOLDefRet(_ppb, L"WorkgroupOnly", FALSE):FALSE); if (IsOS(OS_DOMAINMEMBER) && !fWorkgroupOnly) return S_OK;
// populate the DPAs with shares and printer objects we find on the network, to
// do this enumeration lets fake up a NETRESOURCE structure for entire network
int cMachines = 0; HDPA hdaShares = DPA_Create(MAX_SHARES); HDPA hdaPrinters = DPA_Create(MAX_PRINTERS);
if (hdaShares && hdaPrinters) { NETRESOURCE nr = { 0 }; nr.dwDisplayType = RESOURCEDISPLAYTYPE_ROOT; nr.dwType = RESOURCETYPE_ANY; nr.dwUsage = RESOURCEUSAGE_CONTAINER;
_EnumResources(fWorkgroupOnly ? NULL:&nr, &cMachines, hdaShares, hdaPrinters); }
// now attempt to make connections to the shares and printers. to do this
// we need to look at the number of machines we have visited, if its less
// than our threshold then we can install.
if (SUCCEEDED(_GetMRUs()) && (cMachines < MAX_MACHINES)) { DPA_DestroyCallback(hdaShares, _InstallSharesCB, this); DPA_DestroyCallback(hdaPrinters, _InstallPrinterCB, this); _AgeOutShares(FALSE); } else { DPA_DestroyCallback(hdaShares, _DiscardCB, this); DPA_DestroyCallback(hdaPrinters, _DiscardCB, this); }
return S_OK; }
// this is the main crawler object, from this we create the protocol specific
// crawlers which handle enumerating the resources for the various network types.
#define CRAWLER_SUBKEY \
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\NetworkCrawler\\Objects")
class CNetCrawler : public INetCrawler { public: CNetCrawler();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// INetCrawler
STDMETHOD(Update)(DWORD dwFlags);
private: static DWORD CALLBACK s_DoCrawl(void* pv); DWORD _DoCrawl();
LONG _cRef; LONG _cUpdateLock; // > 0 then we are already spinning
DWORD _dwFlags; // flags from update - passed to each of the crawler sub-objects
};
CNetCrawler* g_pnc = NULL; // there is a single instance of this object
// construction / IUnknown
CNetCrawler::CNetCrawler() : _cRef(1) { }
STDMETHODIMP CNetCrawler::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CNetCrawler, INetCrawler), { 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CNetCrawler::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CNetCrawler::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
ENTERCRITICAL; g_pnc = NULL; LEAVECRITICAL; delete this; return 0; }
// there is a single instance of the object, therefore in a critical section
// lets check to see if the global exists, if so then QI it, otherwise
// create a new one and QI that instead.
STDAPI CNetCrawler_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv) { HRESULT hr = E_OUTOFMEMORY; ENTERCRITICAL; if (g_pnc) { hr = g_pnc->QueryInterface(riid, ppv); } else { g_pnc = new CNetCrawler(); if (g_pnc) { hr = g_pnc->QueryInterface(riid, ppv); g_pnc->Release(); } } LEAVECRITICAL; return hr; }
// this object has a execution count to inidicate if we are already crawling.
// only if the count is 0 when the ::Update method is called, will create a thread
// which inturn will create each of the crawler objects and allow them to
// do their enumeration.
DWORD CALLBACK CNetCrawler::s_DoCrawl(void* pv) { CNetCrawler *pnc = (CNetCrawler*)pv; return pnc->_DoCrawl(); }
DWORD CNetCrawler::_DoCrawl() { // enumrate all the keys under the crawler sub-key, from that we can then
// create the individual crawler objects.
HKEY hk; DWORD dwres = RegOpenKeyEx(HKEY_LOCAL_MACHINE, CRAWLER_SUBKEY, 0, KEY_READ, &hk); if (WN_SUCCESS == dwres) { DWORD index = 0; SHQueryInfoKey(hk, &index, NULL, NULL, NULL); // retrieve the count of the keys
while (((LONG)(--index)) >= 0) { TCHAR szKey[MAX_PATH]; DWORD cb = ARRAYSIZE(szKey); if (WN_SUCCESS == SHEnumKeyEx(hk, index, szKey, &cb)) { // given the keyname, create a property bag so we can access
IPropertyBag *ppb; HRESULT hr = SHCreatePropertyBagOnRegKey(hk, szKey, STGM_READ, IID_PPV_ARG(IPropertyBag, &ppb)); if (SUCCEEDED(hr)) { // we have a property bag mapped to the registry for the items we need
// to read back, so lets get the CLSID and create a crawler from it.
CLSID clsid; hr = SHPropertyBag_ReadGUID(ppb, L"CLSID", &clsid); if (SUCCEEDED(hr)) { INetCrawler *pnc; hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(INetCrawler, &pnc)); if (SUCCEEDED(hr)) { // if the crawler supports IPersistPropertyBag then lets allow it
// to slurp its settings into from the registry.
SHLoadFromPropertyBag(pnc, ppb);
// allow it to update and load.
pnc->Update(_dwFlags); // we don't care about the failure
pnc->Release(); } } ppb->Release(); } } }
RegCloseKey(hk); }
InterlockedDecrement(&_cUpdateLock); // release the lock that signifies that we're updating:
Release(); return 0; }
STDMETHODIMP CNetCrawler::Update(DWORD dwFlags) { // we either have policy defined to disable the crawler, or the
// users has selected that they don't want to be able to auto discover
// the world.
SHELLSTATE ss; SHGetSetSettings(&ss, SSF_NONETCRAWLING, FALSE); if (ss.fNoNetCrawling || SHRestricted(REST_NONETCRAWL)) { return S_OK; }
// increase the lock, if its >0 then we should not bother crawling again
// as we already have it covered, therefore decrease the lock counter.
//
// if the lock is ==0 then create the thread which will do the crawling
// and inturn create the objects.
HRESULT hr = S_OK; if (InterlockedIncrement(&_cUpdateLock) == 1) { _dwFlags = dwFlags; // store the flags for use later
AddRef(); if (!SHCreateThread(s_DoCrawl, (void*)this, CTF_COINIT, NULL)) { Release(); hr = E_FAIL; } } else { InterlockedDecrement(&_cUpdateLock); } return hr; }
// helper function that will invoke the net crawler to perform a async refresh,
// to ensure that we don't block we will create a thread which inturn will CoCreate
// the net crawler and then call its refresh method.
DWORD _RefreshCrawlerThreadProc(void *pv) { INetCrawler *pnc; if (SUCCEEDED(CoCreateInstance(CLSID_NetCrawler, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(INetCrawler, &pnc)))) { pnc->Update(SNCF_REFRESHLIST); pnc->Release(); } return 0; } STDAPI_(void) RefreshNetCrawler() { SHCreateThread(_RefreshCrawlerThreadProc, NULL, CTF_COINIT, NULL); }
|