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

377 lines
8.9 KiB
C++

//+-------------------------------------------------------------------------
//
// 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;
}