1218 lines
28 KiB
C++
1218 lines
28 KiB
C++
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "shitemid.h"
|
|
#include "ids.h"
|
|
#include "hwcmmn.h"
|
|
|
|
#include "mtptr.h"
|
|
|
|
#ifdef DEBUG
|
|
DWORD CMtPtRemote::_cMtPtRemote = 0;
|
|
DWORD CShare::_cShare = 0;
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Public methods
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT CMtPtRemote::SetLabel(HWND hwnd, LPCTSTR pszLabel)
|
|
{
|
|
TraceMsg(TF_MOUNTPOINT, "CMtPtRemote::SetLabel: for '%s'", _GetNameDebug());
|
|
|
|
RSSetTextValue(NULL, TEXT("_LabelFromReg"), pszLabel,
|
|
REG_OPTION_NON_VOLATILE);
|
|
|
|
// we notify for only the current drive (no folder mounted drive)
|
|
SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_PATH, _GetName(), _GetName());
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL CMtPtRemote::IsDisconnectedNetDrive()
|
|
{
|
|
return !_IsConnected();
|
|
}
|
|
|
|
// Expensive, do not call for nothing
|
|
BOOL CMtPtRemote::IsFormatted()
|
|
{
|
|
return (0xFFFFFFFF != GetFileAttributes(_GetNameForFctCall()));
|
|
}
|
|
|
|
HRESULT CMtPtRemote::_GetDefaultUNCDisplayName(LPTSTR pszLabel, DWORD cchLabel)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPTSTR pszShare, pszT;
|
|
TCHAR szTempUNCPath[MAX_PATH];
|
|
|
|
pszLabel[0] = TEXT('\0');
|
|
|
|
if (!_pshare->fFake)
|
|
{
|
|
// Why would it not be a UNC name?
|
|
if (PathIsUNC(_GetUNCName()))
|
|
{
|
|
// Now we need to handle 3 cases.
|
|
// The normal case: \\pyrex\user
|
|
// The Netware setting root: \\strike\sys\public\dist
|
|
// The Netware CD? \\stike\sys \public\dist
|
|
StringCchCopy(szTempUNCPath, ARRAYSIZE(szTempUNCPath), _GetUNCName());
|
|
pszT = StrChr(szTempUNCPath, TEXT(' '));
|
|
while (pszT)
|
|
{
|
|
pszT++;
|
|
if (*pszT == TEXT('\\'))
|
|
{
|
|
// The netware case of \\strike\sys \public\dist
|
|
*--pszT = 0;
|
|
break;
|
|
}
|
|
pszT = StrChr(pszT, TEXT(' '));
|
|
}
|
|
|
|
pszShare = StrRChr(szTempUNCPath, NULL, TEXT('\\'));
|
|
if (pszShare)
|
|
{
|
|
*pszShare++ = 0;
|
|
PathMakePretty(pszShare);
|
|
|
|
// pszServer should always start at char 2.
|
|
if (szTempUNCPath[2])
|
|
{
|
|
LPTSTR pszServer, pszSlash;
|
|
|
|
pszServer = &szTempUNCPath[2];
|
|
for (pszT = pszServer; pszT != NULL; pszT = pszSlash)
|
|
{
|
|
pszSlash = StrChr(pszT, TEXT('\\'));
|
|
if (pszSlash)
|
|
*pszSlash = 0;
|
|
|
|
PathMakePretty(pszT);
|
|
if (pszSlash)
|
|
*pszSlash++ = TEXT('\\');
|
|
}
|
|
|
|
TCHAR szDisplay[MAX_PATH];
|
|
hr = SHGetComputerDisplayName(pszServer, 0x0, szDisplay, ARRAYSIZE(szDisplay));
|
|
if (FAILED(hr))
|
|
{
|
|
*szDisplay = 0;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPTSTR pszLabel2 = ShellConstructMessageString(HINST_THISDLL,
|
|
MAKEINTRESOURCE(IDS_UNC_FORMAT), pszShare, szDisplay);
|
|
|
|
if (pszLabel2)
|
|
{
|
|
StringCchCopy(pszLabel, cchLabel, pszLabel2);
|
|
LocalFree(pszLabel2);
|
|
}
|
|
else
|
|
{
|
|
*pszLabel = TEXT('\0');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
int CMtPtRemote::GetDriveFlags()
|
|
{
|
|
// By default every drive type is ShellOpen, except CD-ROMs
|
|
UINT uDriveFlags = DRIVE_SHELLOPEN;
|
|
|
|
if (_IsAutorun())
|
|
{
|
|
uDriveFlags |= DRIVE_AUTORUN;
|
|
|
|
//FEATURE should we set AUTOOPEN based on a flag in the AutoRun.inf???
|
|
uDriveFlags |= DRIVE_AUTOOPEN;
|
|
}
|
|
|
|
if (_IsConnected())
|
|
{
|
|
if ((0 != _dwSpeed) && (_dwSpeed <= SPEED_SLOW))
|
|
{
|
|
uDriveFlags |= DRIVE_SLOW;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CMtPtRemote::_CalcPathSpeed()
|
|
{
|
|
_dwSpeed = 0;
|
|
|
|
NETCONNECTINFOSTRUCT nci = {0};
|
|
NETRESOURCE nr = {0};
|
|
TCHAR szPath[3];
|
|
|
|
nci.cbStructure = sizeof(nci);
|
|
|
|
// we are passing in a local drive and MPR does not like us to pass a
|
|
// local name as Z:\ but only wants Z:
|
|
_GetNameFirstXChar(szPath, 2 + 1);
|
|
|
|
nr.lpLocalName = szPath;
|
|
|
|
// dwSpeed is returned by MultinetGetConnectionPerformance
|
|
MultinetGetConnectionPerformance(&nr, &nci);
|
|
|
|
_dwSpeed = nci.dwSpeed;
|
|
}
|
|
|
|
// Imported from fsnotify.c
|
|
STDAPI_(void) SHChangeNotifyRegisterAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias);
|
|
//
|
|
// If a mount point is for a remote path (UNC), it needs to respond
|
|
// to shell changes identified by both UNC and local drive path (L:\).
|
|
// This function performs this registration.
|
|
//
|
|
HRESULT CMtPtRemote::ChangeNotifyRegisterAlias(void)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Don't wake up sleeping net connections
|
|
if (_IsConnected() && !(_pshare->fFake))
|
|
{
|
|
LPITEMIDLIST pidlLocal = SHSimpleIDListFromPath(_GetName());
|
|
if (NULL != pidlLocal)
|
|
{
|
|
LPITEMIDLIST pidlUNC = SHSimpleIDListFromPath(_GetUNCName());
|
|
if (NULL != pidlUNC)
|
|
{
|
|
SHChangeNotifyRegisterAlias(pidlUNC, pidlLocal);
|
|
ILFree(pidlUNC);
|
|
hr = NOERROR;
|
|
}
|
|
ILFree(pidlLocal);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Temp /////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void _UpdateGFAAndGVIInfoHelper(LPCWSTR pszDrive, CShare* pshare)
|
|
{
|
|
pshare->dwGetFileAttributes = GetFileAttributes(pszDrive);
|
|
|
|
if (-1 != pshare->dwGetFileAttributes)
|
|
{
|
|
pshare->fGVIRetValue = GetVolumeInformation(pszDrive,
|
|
pshare->szLabel, ARRAYSIZE(pshare->szLabel),
|
|
&(pshare->dwSerialNumber), &(pshare->dwMaxFileNameLen),
|
|
&(pshare->dwFileSystemFlags), pshare->szFileSysName,
|
|
ARRAYSIZE(pshare->szFileSysName));
|
|
}
|
|
}
|
|
|
|
struct GFAGVICALL
|
|
{
|
|
HANDLE hEventBegun;
|
|
HANDLE hEventFinish;
|
|
WCHAR szDrive[4];
|
|
CShare* pshare;
|
|
};
|
|
|
|
void _FreeGFAGVICALL(GFAGVICALL* pgfagvicall)
|
|
{
|
|
if (pgfagvicall->hEventBegun)
|
|
{
|
|
CloseHandle(pgfagvicall->hEventBegun);
|
|
}
|
|
|
|
if (pgfagvicall->hEventFinish)
|
|
{
|
|
CloseHandle(pgfagvicall->hEventFinish);
|
|
}
|
|
|
|
if (pgfagvicall->pshare)
|
|
{
|
|
pgfagvicall->pshare->Release();
|
|
}
|
|
|
|
if (pgfagvicall)
|
|
{
|
|
LocalFree(pgfagvicall);
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI _UpdateGFAAndGVIInfoCB(LPVOID pv)
|
|
{
|
|
GFAGVICALL* pgfagvicall = (GFAGVICALL*)pv;
|
|
|
|
SetEvent(pgfagvicall->hEventBegun);
|
|
|
|
_UpdateGFAAndGVIInfoHelper(pgfagvicall->szDrive, pgfagvicall->pshare);
|
|
|
|
SetEvent(pgfagvicall->hEventFinish);
|
|
|
|
_FreeGFAGVICALL(pgfagvicall);
|
|
|
|
return 0;
|
|
}
|
|
|
|
GFAGVICALL* CMtPtRemote::_PrepareThreadParam(HANDLE* phEventBegun,
|
|
HANDLE* phEventFinish)
|
|
{
|
|
BOOL fSucceeded = FALSE;
|
|
*phEventBegun = NULL;
|
|
*phEventFinish = NULL;
|
|
|
|
GFAGVICALL* pgfagvicall = (GFAGVICALL*)LocalAlloc(LPTR,
|
|
sizeof(GFAGVICALL));
|
|
|
|
if (pgfagvicall)
|
|
{
|
|
pgfagvicall->hEventBegun = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (pgfagvicall->hEventBegun)
|
|
{
|
|
HANDLE hCurrentProcess = GetCurrentProcess();
|
|
|
|
if (DuplicateHandle(hCurrentProcess, pgfagvicall->hEventBegun,
|
|
hCurrentProcess, phEventBegun, 0, FALSE,
|
|
DUPLICATE_SAME_ACCESS))
|
|
{
|
|
pgfagvicall->hEventFinish = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (pgfagvicall->hEventFinish)
|
|
{
|
|
if (DuplicateHandle(hCurrentProcess,
|
|
pgfagvicall->hEventFinish, hCurrentProcess,
|
|
phEventFinish, 0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
_pshare->AddRef();
|
|
pgfagvicall->pshare = _pshare;
|
|
|
|
fSucceeded = SUCCEEDED(StringCchCopy(pgfagvicall->szDrive,
|
|
ARRAYSIZE(pgfagvicall->szDrive), _GetName()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
if (*phEventBegun)
|
|
{
|
|
CloseHandle(*phEventBegun);
|
|
}
|
|
|
|
if (pgfagvicall)
|
|
{
|
|
_FreeGFAGVICALL(pgfagvicall);
|
|
pgfagvicall = NULL;
|
|
}
|
|
}
|
|
|
|
return pgfagvicall;
|
|
}
|
|
|
|
// Expiration: 35 secs (what we shipped W2K with)
|
|
BOOL CMtPtRemote::_HaveGFAAndGVIExpired(DWORD dwNow)
|
|
{
|
|
BOOL fExpired = FALSE;
|
|
|
|
// Check also for the wrapping case first.
|
|
if ((_pshare->dwGFAGVILastCall > dwNow) ||
|
|
((dwNow - _pshare->dwGFAGVILastCall) > 35 * 1000))
|
|
{
|
|
fExpired = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fExpired = FALSE;
|
|
}
|
|
|
|
return fExpired;
|
|
}
|
|
|
|
// We launch a thread so that we won't be jammed on this for more than 10 sec.
|
|
// If the thread times out, we use the cache value but do not reset the cache
|
|
// values. They're better than nothing. We do reset the cache last tick count
|
|
// so that we do not send another thread to jam here before at least 35 sec.
|
|
|
|
// Return TRUE or FALSE to tell us if timed out or not. For GFA and GVI
|
|
// success/failure check (-1 != dwGetFileAttributes) && (_fGVIRetValue)
|
|
BOOL CMtPtRemote::_UpdateGFAAndGVIInfo()
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD dwNow = GetTickCount();
|
|
|
|
if (_HaveGFAAndGVIExpired(dwNow))
|
|
{
|
|
_pshare->dwGFAGVILastCall = dwNow;
|
|
|
|
BOOL fGoSync = TRUE;
|
|
HANDLE hEventBegun;
|
|
HANDLE hEventFinish;
|
|
|
|
GFAGVICALL* pgfagvicall = _PrepareThreadParam(&hEventBegun,
|
|
&hEventFinish);
|
|
|
|
if (pgfagvicall)
|
|
{
|
|
if (SHQueueUserWorkItem(_UpdateGFAAndGVIInfoCB, pgfagvicall,
|
|
0, (DWORD_PTR)0, (DWORD_PTR*)NULL, NULL, 0))
|
|
{
|
|
DWORD dw = WaitForSingleObject(hEventFinish, 10 * 1000);
|
|
|
|
if (WAIT_TIMEOUT == dw)
|
|
{
|
|
// we timed out!
|
|
fRet = FALSE;
|
|
|
|
if (WAIT_OBJECT_0 != WaitForSingleObject(
|
|
hEventBegun, 0))
|
|
{
|
|
// since the thread started, we know that
|
|
// this call is _really_ slow!
|
|
fGoSync = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// our work item was never queued, so we
|
|
// fall through to the fGoSync case below
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_FreeGFAGVICALL(pgfagvicall);
|
|
}
|
|
|
|
CloseHandle(hEventBegun);
|
|
CloseHandle(hEventFinish);
|
|
}
|
|
|
|
if (fGoSync)
|
|
{
|
|
// we should come here if we failed to create our workitem
|
|
// or our workitem was never queued
|
|
_UpdateGFAAndGVIInfoHelper(_GetName(), _pshare);
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL CMtPtRemote::_GetFileAttributes(DWORD* pdwAttrib)
|
|
{
|
|
if (_UpdateGFAAndGVIInfo())
|
|
{
|
|
*pdwAttrib = _pshare->dwGetFileAttributes;
|
|
}
|
|
else
|
|
{
|
|
*pdwAttrib = -1;
|
|
}
|
|
|
|
return (-1 != *pdwAttrib);
|
|
}
|
|
|
|
// { DRIVE_ISCOMPRESSIBLE | DRIVE_LFN | DRIVE_SECURITY }
|
|
int CMtPtRemote::_GetGVIDriveFlags()
|
|
{
|
|
int iFlags = 0;
|
|
|
|
if (_UpdateGFAAndGVIInfo())
|
|
{
|
|
if (_pshare->fGVIRetValue)
|
|
{
|
|
// The file attrib we received at the begginning should be
|
|
// valid, do not touch the drive for nothing
|
|
if (_pshare->dwFileSystemFlags & FS_FILE_COMPRESSION)
|
|
{
|
|
iFlags |= DRIVE_ISCOMPRESSIBLE;
|
|
}
|
|
|
|
// Volume supports long filename (greater than 8.3)?
|
|
if (_pshare->dwMaxFileNameLen > 12)
|
|
{
|
|
iFlags |= DRIVE_LFN;
|
|
}
|
|
|
|
// Volume supports security?
|
|
if (_pshare->dwFileSystemFlags & FS_PERSISTENT_ACLS)
|
|
{
|
|
iFlags |= DRIVE_SECURITY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return iFlags;
|
|
}
|
|
|
|
BOOL CMtPtRemote::_GetSerialNumber(DWORD* pdwSerialNumber)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if (_UpdateGFAAndGVIInfo())
|
|
{
|
|
if (_pshare->fGVIRetValue)
|
|
{
|
|
*pdwSerialNumber = _pshare->dwSerialNumber;
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
|
|
// No reg stuff
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL CMtPtRemote::_GetGVILabel(LPTSTR pszLabel, DWORD cchLabel)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
*pszLabel = 0;
|
|
|
|
if (_UpdateGFAAndGVIInfo())
|
|
{
|
|
if (_pshare->fGVIRetValue)
|
|
{
|
|
fRet = SUCCEEDED(StringCchCopy(pszLabel, cchLabel, _pshare->szLabel));
|
|
}
|
|
}
|
|
|
|
// No reg stuff
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL CMtPtRemote::_GetGVILabelOrMixedCaseFromReg(LPTSTR pszLabel, DWORD cchLabel)
|
|
{
|
|
return _GetGVILabel(pszLabel, cchLabel);
|
|
}
|
|
|
|
BOOL CMtPtRemote::_GetFileSystemFlags(DWORD* pdwFlags)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
*pdwFlags = 0;
|
|
|
|
if (_UpdateGFAAndGVIInfo())
|
|
{
|
|
if (_pshare->fGVIRetValue)
|
|
{
|
|
*pdwFlags = _pshare->dwFileSystemFlags;
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL CMtPtRemote::_GetFileSystemName(LPTSTR pszFileSysName, DWORD cchFileSysName)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
*pszFileSysName = 0;
|
|
|
|
if (_UpdateGFAAndGVIInfo())
|
|
{
|
|
if (_pshare->fGVIRetValue)
|
|
{
|
|
fRet = SUCCEEDED(StringCchCopy(pszFileSysName, cchFileSysName, _pshare->szFileSysName));
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
DWORD CMtPtRemote::GetShellDescriptionID()
|
|
{
|
|
return SHDID_COMPUTER_NETDRIVE;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// New //////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
UINT CMtPtRemote::_GetAutorunIcon(LPTSTR pszModule, DWORD cchModule)
|
|
{
|
|
int iIcon = -1;
|
|
|
|
if (RSGetTextValue(TEXT("_Autorun\\DefaultIcon"), NULL, pszModule,
|
|
&cchModule))
|
|
{
|
|
iIcon = PathParseIconLocation(pszModule);
|
|
}
|
|
|
|
return iIcon;
|
|
}
|
|
|
|
UINT CMtPtRemote::GetIcon(LPTSTR pszModule, DWORD cchModule)
|
|
{
|
|
BOOL fFoundIt = FALSE;
|
|
UINT iIcon = II_DRIVENET;
|
|
|
|
*pszModule = 0;
|
|
|
|
// Autorun first
|
|
// Fancy icon (Autoplay) second
|
|
// Legacy drive icons last
|
|
|
|
if (_IsAutorun())
|
|
{
|
|
iIcon = _GetAutorunIcon(pszModule, cchModule);
|
|
|
|
if (-1 != iIcon)
|
|
{
|
|
fFoundIt = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!fFoundIt)
|
|
{
|
|
if (_pszLegacyRegIcon)
|
|
{
|
|
if (RSGetTextValue(TEXT("DefaultIcon"), NULL, pszModule,
|
|
&cchModule))
|
|
{
|
|
iIcon = PathParseIconLocation(pszModule);
|
|
}
|
|
else
|
|
{
|
|
*pszModule = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_IsUnavailableNetDrive())
|
|
{
|
|
iIcon = II_DRIVENETDISABLED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*pszModule)
|
|
TraceMsg(TF_MOUNTPOINT, "CMtPtRemote::GetIcon: for '%s', chose '%s', '%d'", _GetNameDebug(), pszModule, iIcon);
|
|
else
|
|
TraceMsg(TF_MOUNTPOINT, "CMtPtRemote::GetIcon: for '%s', chose '%d'", _GetNameDebug(), iIcon);
|
|
|
|
return iIcon;
|
|
}
|
|
|
|
void CMtPtRemote::GetTypeString(LPTSTR pszType, DWORD cchType)
|
|
{
|
|
int iID;
|
|
|
|
*pszType = 0;
|
|
|
|
if (_IsConnected())
|
|
{
|
|
iID = IDS_DRIVES_NETDRIVE;
|
|
}
|
|
else
|
|
{
|
|
iID = IDS_DRIVES_NETUNAVAIL;
|
|
}
|
|
|
|
LoadString(HINST_THISDLL, iID, pszType, cchType);
|
|
}
|
|
|
|
HRESULT CMtPtRemote::GetLabelNoFancy(LPTSTR pszLabel, DWORD cchLabel)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (_UpdateGFAAndGVIInfo())
|
|
{
|
|
hr = StringCchCopy(pszLabel, cchLabel, _pshare->szLabel);
|
|
}
|
|
else
|
|
{
|
|
*pszLabel = 0;
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CMtPtRemote::GetLabel(LPTSTR pszLabel, DWORD cchLabel)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
|
|
ASSERT(pszLabel);
|
|
|
|
*pszLabel = 0;
|
|
|
|
// Do we already have a label from the registry for this volume?
|
|
// (the user may have renamed this drive)
|
|
|
|
if (!_GetLabelFromReg(pszLabel, cchLabel))
|
|
{
|
|
// No
|
|
|
|
// Do we have a name from the server?
|
|
if (!_GetLabelFromDesktopINI(pszLabel, cchLabel))
|
|
{
|
|
// No
|
|
// We should build up the display name ourselves
|
|
hres = _GetDefaultUNCDisplayName(pszLabel, cchLabel);
|
|
|
|
if (SUCCEEDED(hres) && *pszLabel)
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
GetTypeString(pszLabel, cchLabel);
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CMtPtRemote::GetRemotePath(LPWSTR pszPath, DWORD cchPath)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
*pszPath = 0;
|
|
if (!_pshare->fFake && _pshare->pszRemoteName[0])
|
|
{
|
|
hr = StringCchCopy(pszPath, cchPath, _pshare->pszRemoteName);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Connection status
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// We cannot cache the connection status. This is already cached at the redirector level.
|
|
// When calling the WNetGetConnection fcts you get what's cache there, no check is actually
|
|
// done on the network to see if this information is accurate (OK/Disconnected/Unavailable).
|
|
// The information is updated only when the share is actually accessed (e.g: GetFileAttributes)
|
|
//
|
|
// So we need to always do the calls (fortunately non-expensive) so that we get the most
|
|
// up to date info. Otherwise the following was occuring: A user double click a map drive
|
|
// from the Explorer's Listview, WNetConnection gets called and we get the OK cached value
|
|
// from the redirector. Some other code actually try to access the share, and the redirector
|
|
// realize that the share is not there and set its cache to Disconnected. We are queried
|
|
// again for the state of the connection to update the icon, if we cached this info we
|
|
// return OK, if we ask for it (0.1 sec after the first call to WNetGetConnection) we get
|
|
// Disconnected. (stephstm 06/02/99)
|
|
|
|
void CMtPtRemote::_UpdateWNetGCStatus()
|
|
{
|
|
TCHAR szRemoteName[MAX_PATH];
|
|
DWORD cchRemoteName = ARRAYSIZE(szRemoteName);
|
|
TCHAR szPath[3];
|
|
|
|
// WNetConnection does not take a trailing slash
|
|
_dwWNetGCStatus = WNetGetConnection(
|
|
_GetNameFirstXChar(szPath, 2 + 1), szRemoteName, &cchRemoteName);
|
|
}
|
|
|
|
BOOL CMtPtRemote::IsUnavailableNetDrive()
|
|
{
|
|
return _IsUnavailableNetDrive();
|
|
}
|
|
|
|
BOOL CMtPtRemote::_IsUnavailableNetDrive()
|
|
{
|
|
BOOL fUnavail = TRUE;
|
|
BOOL fPrevUnavail = _IsUnavailableNetDriveFromStateVar();
|
|
|
|
_UpdateWNetGCStatus();
|
|
|
|
fUnavail = (ERROR_CONNECTION_UNAVAIL == _dwWNetGCStatus);
|
|
|
|
if (fPrevUnavail != fUnavail)
|
|
{
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, _GetName(), NULL);
|
|
}
|
|
|
|
return fUnavail;
|
|
}
|
|
|
|
BOOL CMtPtRemote::_IsUnavailableNetDriveFromStateVar()
|
|
{
|
|
return (ERROR_CONNECTION_UNAVAIL == _dwWNetGCStatus);
|
|
}
|
|
|
|
BOOL CMtPtRemote::_IsConnected()
|
|
{
|
|
BOOL fConnected = TRUE;
|
|
|
|
_UpdateWNetGCStatus();
|
|
|
|
// This whole if/else statement is the same thing as
|
|
// _IsConnectedFromStateVar() except that we will avoid calling
|
|
// WNetGetConnection3 if possible (optimization)
|
|
|
|
if (NO_ERROR != _dwWNetGCStatus)
|
|
{
|
|
fConnected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwSize = sizeof(_wngcs);
|
|
TCHAR szPath[3];
|
|
|
|
_dwWNetGC3Status = WNetGetConnection3(
|
|
_GetNameFirstXChar(szPath, 2 + 1), NULL,
|
|
WNGC_INFOLEVEL_DISCONNECTED, &_wngcs, &dwSize);
|
|
|
|
// Did we succeeded the call to WNetGetConnection 3 and it returned
|
|
// disconnected?
|
|
if (WN_SUCCESS == _dwWNetGC3Status)
|
|
{
|
|
if (WNGC_DISCONNECTED == _wngcs.dwState)
|
|
{
|
|
// Yes
|
|
fConnected = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fConnected = FALSE;
|
|
}
|
|
}
|
|
|
|
return fConnected;
|
|
}
|
|
|
|
BOOL CMtPtRemote::_IsMountedOnDriveLetter()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
void CMtPtRemote::_UpdateLabelFromDesktopINI()
|
|
{
|
|
WCHAR szLabelFromDesktopINI[MAX_MTPTCOMMENT];
|
|
|
|
if (!GetShellClassInfo(_GetName(), TEXT("NetShareDisplayName"),
|
|
szLabelFromDesktopINI, ARRAYSIZE(szLabelFromDesktopINI)))
|
|
{
|
|
szLabelFromDesktopINI[0] = 0;
|
|
}
|
|
|
|
RSSetTextValue(NULL, TEXT("_LabelFromDesktopINI"),
|
|
szLabelFromDesktopINI, REG_OPTION_NON_VOLATILE);
|
|
}
|
|
|
|
void CMtPtRemote::_UpdateAutorunInfo()
|
|
{
|
|
_pshare->fAutorun = FALSE;
|
|
|
|
if (_IsAutoRunDrive())
|
|
{
|
|
if (_ProcessAutoRunFile())
|
|
{
|
|
_pshare->fAutorun = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!_pshare->fAutorun)
|
|
{
|
|
// Make sure to delete the shell key
|
|
RSDeleteSubKey(TEXT("Shell"));
|
|
}
|
|
}
|
|
|
|
CMtPtRemote::CMtPtRemote()
|
|
{
|
|
#ifdef DEBUG
|
|
++_cMtPtRemote;
|
|
#endif
|
|
}
|
|
|
|
CMtPtRemote::~CMtPtRemote()
|
|
{
|
|
if (_pshare)
|
|
{
|
|
_pshare->Release();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
--_cMtPtRemote;
|
|
#endif
|
|
}
|
|
|
|
HRESULT CMtPtRemote::_InitWithoutShareName(LPCWSTR pszName)
|
|
{
|
|
// Let's make a name
|
|
GUID guid;
|
|
HRESULT hr = CoCreateGuid(&guid);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR szGUID[sizeof("{00000010-0000-0010-8000-00AA006D2EA4}")];
|
|
|
|
if (StringFromGUID2(guid, szGUID, ARRAYSIZE(szGUID)))
|
|
{
|
|
hr = _Init(pszName, szGUID, TRUE);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_pshare->fFake = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CMtPtRemote::_Init(LPCWSTR pszName, LPCWSTR pszShareName,
|
|
BOOL fUnavailable)
|
|
{
|
|
HRESULT hr;
|
|
|
|
_pshare = _GetOrCreateShareFromID(pszShareName);
|
|
|
|
if (_pshare)
|
|
{
|
|
if (fUnavailable)
|
|
{
|
|
_dwWNetGCStatus = ERROR_CONNECTION_UNAVAIL;
|
|
}
|
|
|
|
hr = StringCchCopy(_szName, ARRAYSIZE(_szName), pszName);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
PathAddBackslash(_szName);
|
|
|
|
// Remote drives uses the Share key for all their stuff. They do not have
|
|
// anything interesting specific to the drive letter
|
|
RSInitRoot(HKEY_CURRENT_USER, REGSTR_MTPT_ROOTKEY2, _pshare->pszKeyName,
|
|
REG_OPTION_NON_VOLATILE);
|
|
|
|
RSSetTextValue(NULL, TEXT("BaseClass"), TEXT("Drive"));
|
|
|
|
// Access the drive on first connection of the share
|
|
_InitOnlyOnceStuff();
|
|
|
|
_InitLegacyRegIconAndLabel(FALSE, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
LPCTSTR CMtPtRemote::_GetUNCName()
|
|
{
|
|
return _pshare->pszRemoteName;
|
|
}
|
|
|
|
void CMtPtRemote::_InitOnlyOnceStuff()
|
|
{
|
|
if (!RSValueExist(NULL, TEXT("_CommentFromDesktopINI")))
|
|
{
|
|
// Comment
|
|
_UpdateCommentFromDesktopINI();
|
|
|
|
// Label
|
|
_UpdateLabelFromDesktopINI();
|
|
|
|
// Autorun
|
|
_UpdateAutorunInfo();
|
|
}
|
|
}
|
|
|
|
int CMtPtRemote::_GetDriveType()
|
|
{
|
|
return DRIVE_REMOTE;
|
|
}
|
|
|
|
HRESULT CMtPtRemote::GetAssocSystemElement(IAssociationElement **ppae)
|
|
{
|
|
return AssocElemCreateForClass(&CLSID_AssocSystemElement, L"Drive.Network", ppae);
|
|
}
|
|
|
|
DWORD CMtPtRemote::_GetPathSpeed()
|
|
{
|
|
if (!_dwSpeed)
|
|
{
|
|
_CalcPathSpeed();
|
|
}
|
|
|
|
return _dwSpeed;
|
|
}
|
|
|
|
// static
|
|
HRESULT CMtPtRemote::_DeleteAllMtPtsAndShares()
|
|
{
|
|
_csDL.Enter();
|
|
|
|
for (DWORD dw = 0; dw <26; ++dw)
|
|
{
|
|
CMtPtRemote* pmtptr = CMountPoint::_rgMtPtDriveLetterNet[dw];
|
|
|
|
if (pmtptr)
|
|
{
|
|
pmtptr->Release();
|
|
CMountPoint::_rgMtPtDriveLetterNet[dw] = 0;
|
|
}
|
|
}
|
|
|
|
if (_hdpaShares)
|
|
{
|
|
DPA_Destroy(_hdpaShares);
|
|
_hdpaShares = NULL;
|
|
}
|
|
|
|
_csDL.Leave();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// static
|
|
HRESULT CMtPtRemote::_CreateMtPtRemoteWithoutShareName(LPCWSTR pszMountPoint)
|
|
{
|
|
HRESULT hr;
|
|
CMtPtRemote* pmtptr = new CMtPtRemote();
|
|
|
|
if (pmtptr)
|
|
{
|
|
hr = pmtptr->_InitWithoutShareName(pszMountPoint);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_csDL.Enter();
|
|
|
|
CMountPoint::_rgMtPtDriveLetterNet[DRIVEID(pszMountPoint)] =
|
|
pmtptr;
|
|
|
|
_csDL.Leave();
|
|
}
|
|
else
|
|
{
|
|
delete pmtptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// static
|
|
HRESULT CMtPtRemote::_CreateMtPtRemote(LPCWSTR pszMountPoint,
|
|
LPCWSTR pszShareName, BOOL fUnavailable)
|
|
{
|
|
HRESULT hr;
|
|
CMtPtRemote* pmtptr = new CMtPtRemote();
|
|
|
|
if (pmtptr)
|
|
{
|
|
hr = pmtptr->_Init(pszMountPoint, pszShareName, fUnavailable);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_csDL.Enter();
|
|
|
|
CMountPoint::_rgMtPtDriveLetterNet[DRIVEID(pszMountPoint)] =
|
|
pmtptr;
|
|
|
|
_csDL.Leave();
|
|
}
|
|
else
|
|
{
|
|
delete pmtptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// static
|
|
CShare* CMtPtRemote::_GetOrCreateShareFromID(LPCWSTR pszShareName)
|
|
{
|
|
CShare* pshare = NULL;
|
|
|
|
_csDL.Enter();
|
|
|
|
DWORD c = DPA_GetPtrCount(_hdpaShares);
|
|
|
|
for (DWORD dw = 0; dw < c; ++dw)
|
|
{
|
|
pshare = (CShare*)DPA_GetPtr(_hdpaShares, dw);
|
|
|
|
if (pshare)
|
|
{
|
|
if (!lstrcmpi(pshare->pszRemoteName, pszShareName))
|
|
{
|
|
pshare->AddRef();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pshare = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pshare)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
|
|
pshare = new CShare();
|
|
|
|
if (pshare)
|
|
{
|
|
pshare->pszRemoteName = StrDup(pszShareName);
|
|
|
|
if (pshare->pszRemoteName)
|
|
{
|
|
pshare->pszKeyName = StrDup(pszShareName);
|
|
|
|
if (pshare->pszKeyName)
|
|
{
|
|
LPWSTR psz = pshare->pszKeyName;
|
|
|
|
while (*psz)
|
|
{
|
|
if (TEXT('\\') == *psz)
|
|
{
|
|
*psz = TEXT('#');
|
|
}
|
|
|
|
++psz;
|
|
}
|
|
|
|
if (-1 != DPA_AppendPtr(_hdpaShares, pshare))
|
|
{
|
|
fSuccess = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fSuccess)
|
|
{
|
|
if (pshare)
|
|
{
|
|
if (pshare->pszKeyName)
|
|
{
|
|
LocalFree(pshare->pszKeyName);
|
|
}
|
|
|
|
if (pshare->pszRemoteName)
|
|
{
|
|
LocalFree(pshare->pszRemoteName);
|
|
}
|
|
|
|
delete pshare;
|
|
pshare = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
_csDL.Leave();
|
|
|
|
return pshare;
|
|
}
|
|
|
|
|
|
HKEY CMtPtRemote::GetRegKey()
|
|
{
|
|
TraceMsg(TF_MOUNTPOINT, "CMtPtRemote::GetRegKey: for '%s'", _GetNameDebug());
|
|
|
|
return RSDuplicateRootKey();
|
|
}
|
|
|
|
// static
|
|
void CMtPtRemote::_NotifyReconnectedNetDrive(LPCWSTR pszMountPoint)
|
|
{
|
|
_csDL.Enter();
|
|
|
|
CMtPtRemote* pmtptr = CMountPoint::_rgMtPtDriveLetterNet[
|
|
DRIVEID(pszMountPoint)];
|
|
|
|
if (pmtptr)
|
|
{
|
|
pmtptr->_pshare->dwGFAGVILastCall = GetTickCount() - 35001;
|
|
}
|
|
|
|
// ChangeNotify???
|
|
|
|
_csDL.Leave();
|
|
}
|
|
|
|
// static
|
|
HRESULT CMtPtRemote::_RemoveShareFromHDPA(CShare* pshare)
|
|
{
|
|
_csDL.Enter();
|
|
|
|
if (_hdpaShares)
|
|
{
|
|
DWORD c = DPA_GetPtrCount(_hdpaShares);
|
|
|
|
for (DWORD dw = 0; dw < c; ++dw)
|
|
{
|
|
CShare* pshare2 = (CShare*)DPA_GetPtr(_hdpaShares, dw);
|
|
|
|
if (pshare2 && (pshare2 == pshare))
|
|
{
|
|
DPA_DeletePtr(_hdpaShares, dw);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
_csDL.Leave();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD CMtPtRemote::_GetAutorunContentType()
|
|
{
|
|
return _GetMTPTContentType();
|
|
}
|
|
|
|
DWORD CMtPtRemote::_GetMTPTDriveType()
|
|
{
|
|
return DT_REMOTE;
|
|
}
|
|
|
|
DWORD CMtPtRemote::_GetMTPTContentType()
|
|
{
|
|
DWORD dwRet = CT_UNKNOWNCONTENT;
|
|
|
|
if (_IsAutorun())
|
|
{
|
|
dwRet |= CT_AUTORUNINF;
|
|
}
|
|
|
|
return dwRet;
|
|
}
|