//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: sharecnx.cpp // //-------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "sharecnx.h" // // This class is a simple cache of net share names and some status flags. // Initially, the only status maintained is to remember if there's an // an open net connection for the share. // The motivation for the cache is to avoid expensive net calls while // we're cruising through lists of files (i.e. deleting files from the // cache). After we delete a file from the cache, it is effectively // unpinned. Therefore, we like to notify the shell so it can remove // the "pinned" icon overlay from the affected file(s). However, // we don't want to hit the net with a change notify if there // isn't an open connection to a file's parent share. Before we issue // a change notify, we just query this cache for a file using // IsOpenConnectionPathUNC(). If there's no entry for the file's share, // one is added and the connection status is obtained. If there's // already an entry, then we just return the status. The public APIs // support a "refresh" flag if refresh is desired. // Additional status information could easily be added for each entry // if it's needed later. // [brianau - 12/12/98] // //----------------------------------------------------------------------------- // CShareCnxStatusCache member functions. //----------------------------------------------------------------------------- CShareCnxStatusCache::CShareCnxStatusCache( void ) : m_hdpa(NULL) { } CShareCnxStatusCache::~CShareCnxStatusCache( void ) { if (NULL != m_hdpa) { // // Delete all the entries then destroy the DPA. // int cEntries = Count(); for (int i = 0; i < cEntries; i++) { delete GetEntry(i); } DPA_Destroy(m_hdpa); } } // // Returns address of entry or NULL if not found. // CShareCnxStatusCache::Entry * CShareCnxStatusCache::FindEntry( LPCTSTR pszShare ) const { if (NULL != m_hdpa) { int cEntries = Count(); for (int i = 0; i < cEntries; i++) { Entry *pEntry = GetEntry(i); if (NULL != pEntry && NULL != pEntry->Share()) { if (0 == lstrcmpi(pszShare, pEntry->Share())) { // // Aha, we found a match. // return pEntry; } } } } return NULL; } // // Creates a new entry and adds it to the DPA of entries. // If successful, returns address of new entry. // Does not check for duplicate entry before adding new one. // CShareCnxStatusCache::Entry * CShareCnxStatusCache::AddEntry( LPCTSTR pszShare, DWORD dwStatus ) { Entry *pEntry = NULL; if (NULL == m_hdpa) { // // Must be first addition. Create the DPA. // m_hdpa = DPA_Create(8); } if (NULL != m_hdpa) { int iEntry = -1; pEntry = new Entry(pszShare, dwStatus); if (NULL != pEntry && pEntry->IsValid()) { // // We have a valid entry. Add it to the DPA. // iEntry = DPA_AppendPtr(m_hdpa, pEntry); } if (-1 == iEntry) { // // One of the following bad things happened: // // 1. Entry creation failed. Most likely couldn't alloc string. // 2. Failed to add entry to DPA (out of memory). // // Either way, destroy the entry and set the entry ptr so we // return NULL. // delete pEntry; pEntry = NULL; } } return pEntry; } // // Determine if the net share associated with a UNC path (file or folder) // has an open connection on this machine. // // Returns: // S_OK = Open connection. // S_FALSE = No open connection. // E_OUTOFMEMORY // HRESULT CShareCnxStatusCache::IsOpenConnectionPathUNC( LPCTSTR pszPathUNC, bool bRefresh // [optional]. Default = false. ) { // // Trim the path back to just the UNC share name. // Call IsOpenConnectionShare() to do the actual work. // TCHAR szShare[MAX_PATH * 2]; lstrcpyn(szShare, pszPathUNC, ARRAYSIZE(szShare)); PathStripToRoot(szShare); return IsOpenConnectionShare(szShare); } // // Determine if the net share has an open connection on this machine. // // Returns: // S_OK = Open connection. // S_FALSE = No open connection. // HRESULT CShareCnxStatusCache::IsOpenConnectionShare( LPCTSTR pszShare, bool bRefresh // [optional]. Default = false. ) { DWORD dwStatus = 0; HRESULT hr = GetShareStatus(pszShare, &dwStatus, bRefresh); if (SUCCEEDED(hr)) { if (0 != (dwStatus & Entry::StatusOpenCnx)) hr = S_OK; else hr = S_FALSE; } return hr; } // // Returns: // // E_INVALIDARG = Path was not a UNC share. // S_OK = Status is valid. // HRESULT CShareCnxStatusCache::GetShareStatus( LPCTSTR pszShare, DWORD *pdwStatus, bool bRefresh // [optional]. Default = false. ) { HRESULT hr = E_INVALIDARG; // Assume share name isn't UNC. *pdwStatus = 0; if (PathIsUNCServerShare(pszShare)) { // // We have a valid UNC "\\server\share" name string. // Entry *pEntry = FindEntry(pszShare); if (NULL == pEntry) { // // Cache miss. Get the system status for the share and try to // cache it. // hr = Entry::QueryShareStatus(pszShare, pdwStatus); if (SUCCEEDED(hr)) { // // Note that we don't return any errors from the cache attempt. // The only problem of not caching the entry is that the next // call to this function will need to re-query the system for // the information. This makes the cache meaningless but it's // not worth failing the information request. Just slows things // down a bit. // AddEntry(pszShare, *pdwStatus); } } else { // // Cache hit. // hr = S_OK; if (bRefresh) { // // Caller want's fresh info. // hr = pEntry->Refresh(); } *pdwStatus = pEntry->Status(); if (SUCCEEDED(hr)) hr = pEntry->LastResult(); } } return hr; } // // Returns number of entries in the cache. // int CShareCnxStatusCache::Count( void ) const { return (NULL != m_hdpa) ? DPA_GetPtrCount(m_hdpa) : 0; } //----------------------------------------------------------------------------- // CShareCnxStatusCache::Entry member functions. //----------------------------------------------------------------------------- CShareCnxStatusCache::Entry::Entry( LPCTSTR pszShare, DWORD dwStatus ) : m_pszShare(StrDup(pszShare)), m_dwStatus(dwStatus), m_hrLastResult(NOERROR) { if (NULL == m_pszShare) { m_hrLastResult = E_OUTOFMEMORY; } } CShareCnxStatusCache::Entry::~Entry( void ) { if (NULL != m_pszShare) { LocalFree(m_pszShare); } } // // Obtain new status info for the entry. // Returns true if no errors obtaining info, false if there were errors. // HRESULT CShareCnxStatusCache::Entry::Refresh( void ) { m_dwStatus = 0; m_hrLastResult = E_OUTOFMEMORY; if (NULL != m_pszShare) m_hrLastResult = QueryShareStatus(m_pszShare, &m_dwStatus); return m_hrLastResult; } // // Static function for obtaining entry status info from the // system. Made this a static function so the cache object // can obtain information before creating the entry. In case // entry creation fails, we still want to be able to return // valid status info. // HRESULT CShareCnxStatusCache::Entry::QueryShareStatus( LPCTSTR pszShare, DWORD *pdwStatus ) { HRESULT hr = NOERROR; *pdwStatus = 0; // // Check the open connection status for this share. // hr = ::IsOpenConnectionShare(pszShare); switch(hr) { case S_OK: // // Open connection found. // *pdwStatus |= StatusOpenCnx; break; case S_FALSE: hr = S_OK; break; default: break; } // // If any other status information is required in the future, // here's where you collect it from the system. // return hr; }