mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4459 lines
136 KiB
4459 lines
136 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1991-1993
|
|
//
|
|
// File: drivesx.c
|
|
//
|
|
// History:
|
|
// 12-06-93 SatoNa Created.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef WINNT
|
|
#include <ntdddisk.h>
|
|
#include <ntddcdrm.h>
|
|
#include <shcompui.h> // NT Explorer compression UI.
|
|
#else
|
|
#define Not_VxD
|
|
#include <vwin32.h> // DeviceIOCtl calls
|
|
#endif
|
|
|
|
|
|
const TCHAR c_szRegLastCheck[] = REGSTR_PATH_LASTCHECK;
|
|
const TCHAR c_szRegLastOptimize[] = REGSTR_PATH_LASTOPTIMIZE;
|
|
const TCHAR c_szRegLastBackup[] = REGSTR_PATH_LASTBACKUP;
|
|
|
|
const TCHAR c_szDrivesNameSpace[] = TEXT("MyComputer\\NameSpace");
|
|
const TCHAR c_szDriveClass[] = TEXT("Drive");
|
|
const TCHAR c_szAudioCDClass[] = TEXT("AudioCD");
|
|
const TCHAR c_szCheckDisk[] = TEXT("scandskw.exe %c:");
|
|
const TCHAR c_szDefrag[] = TEXT("defrag.exe %c:");
|
|
#ifdef WINNT
|
|
const TCHAR c_szBackup[] = TEXT("ntbackup.exe");
|
|
#else
|
|
const TCHAR c_szBackup[] = TEXT("backup.exe");
|
|
#endif
|
|
const TCHAR c_szChkReg[] = TEXT("MyComputer\\chkdskpath");
|
|
const TCHAR c_szOptReg[] = TEXT("MyComputer\\defragpath");
|
|
const TCHAR c_szBkpReg[] = TEXT("MyComputer\\backuppath");
|
|
const TCHAR c_szCDAUDIO[] = TEXT("CDAUDIO");
|
|
|
|
#ifdef UNICODE
|
|
const char c_szMciSendString[] = "mciSendStringW";
|
|
#else
|
|
const char c_szMciSendString[] = "mciSendStringA";
|
|
#endif
|
|
|
|
extern const TCHAR * c_pszDesktopRegProperties[];
|
|
|
|
#ifdef RECREATEKEYS
|
|
const TCHAR c_szControlPanel[] = TEXT("Controls");
|
|
const TCHAR c_szCLSIDControlPanel[] = TEXT("{21EC2020-3AEA-1069-A2DD-08002B30309D}");
|
|
const TCHAR c_szCLSIDPrinters[] = TEXT("{2227A280-3AEA-1069-A2DE-08002B30309D}");
|
|
#endif // RECREATEKEYS
|
|
|
|
extern const TCHAR c_szAutoRunD[];
|
|
|
|
// Define cache of volume label names
|
|
#ifndef DRIVE_CACHE_PER_PROCESS
|
|
LPTSTR g_rgpszDriveNames[26] = {NULL};
|
|
#ifdef WNGC_DISCONNECTED
|
|
static DWORD g_rdwDisconnectTick[26] = {0};
|
|
static BOOL g_rfDisconnectResult[26] = {FALSE};
|
|
#define DISCONNECT_CACHE_TIMEOUT 15000
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#pragma data_seg(DATASEG_PERINSTANCE)
|
|
HINSTANCE g_hinstWinMM = NULL;
|
|
|
|
#define HINST_ERROR ((HINSTANCE)-1)
|
|
|
|
#ifdef DRIVE_CACHE_PER_PROCESS
|
|
LPTSTR g_rgpszDriveNames[26] = {NULL};
|
|
#ifdef WNGC_DISCONNECTED
|
|
static DWORD g_rdwDisconnectTick[26] = {0};
|
|
static BOOL g_rfDisconnectResult[26] = {FALSE};
|
|
#define DISCONNECT_CACHE_TIMEOUT 15000
|
|
#endif
|
|
#endif
|
|
#pragma data_seg()
|
|
|
|
void InitShowUglyDriveNames(void);
|
|
static BOOL s_fShowUglyDriveNames = (BOOL)42; // Preload some value to say lets calculate...
|
|
#define CHECKFORUGLYNAMES if (s_fShowUglyDriveNames == (BOOL)42) \
|
|
InitShowUglyDriveNames();
|
|
|
|
enum
|
|
{
|
|
DRIVES_ICOL_NAME = 0,
|
|
DRIVES_ICOL_TYPE,
|
|
DRIVES_ICOL_SIZE,
|
|
DRIVES_ICOL_FREE,
|
|
DRIVES_ICOL_MAX,
|
|
} ;
|
|
|
|
// BUGBUG: At some point we should check an ini switch or something
|
|
#define ShowDriveInfo(_iDrive) (!IsRemovableDrive(_iDrive))
|
|
|
|
#define Drives_IsReg(_pidd) ((_pidd)->bFlags == SHID_COMPUTER_REGITEM)
|
|
#define Drives_IsCD(_pidd) (SIL_GetType((LPITEMIDLIST)(_pidd)) == SHID_COMPUTER_CDROM)
|
|
#define Drives_IsAudioCD(_pidd) (DriveIsAudioCD(CDrives_GetDriveIndex((LPIDDRIVE)(_pidd))))
|
|
|
|
#define Drives_IsNetUnAvail(_pidd) (RealDriveTypeFlags(CDrives_GetDriveIndex((LPIDDRIVE)(_pidd)), FALSE) & DRIVE_NETUNAVAIL)
|
|
#define Drives_IsAutoRun(_pidd) (DriveIsAutoRun(CDrives_GetDriveIndex((LPIDDRIVE)(_pidd))))
|
|
|
|
#pragma pack(1)
|
|
typedef struct _IDDRIVE
|
|
{
|
|
WORD cb;
|
|
BYTE bFlags;
|
|
CHAR cName[4];
|
|
__int64 qwSize; // this is a "guess" at the disk size and free space
|
|
__int64 qwFree;
|
|
WORD wChecksum;
|
|
} IDDRIVE;
|
|
typedef const UNALIGNED IDDRIVE *LPCIDDRIVE;
|
|
typedef UNALIGNED IDDRIVE *LPIDDRIVE;
|
|
#pragma pack()
|
|
|
|
// External prototype
|
|
BOOL PathIsRemovable(LPNCTSTR pszPath);
|
|
int CDrives_GetDriveIndex(LPCIDDRIVE pidd);
|
|
|
|
|
|
BOOL IsEjectable(LPIDDRIVE pidd, BOOL fForceCDROM);
|
|
|
|
// for non-reg items only
|
|
|
|
// Internal function prototype
|
|
UINT CDrives_GetDriveType(int iDrive);
|
|
HRESULT Drives_GetDriveName(LPCIDDRIVE pidd, LPTSTR lpszName, UINT cchMax);
|
|
HRESULT CDrives_SD_Create(HWND hwndMain, LPVOID * ppvOut);
|
|
STDMETHODIMP CRegItems_CompareIDs(IShellFolder *psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
STDMETHODIMP CRegItems_BindToObject(IShellFolder *psf, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID * ppvOut);
|
|
void Drives_GetTypeString(BYTE bType, LPTSTR pszName, UINT cbName);
|
|
BOOL Drives_FillFreeSpace(LPIDDRIVE pidd);
|
|
HRESULT DrivesHandleFSNotify(LPSHELLFOLDER psf, LONG lNotification,
|
|
LPCITEMIDLIST* ppidl);
|
|
|
|
void InvalidateDriveNameCache(int iDrive);
|
|
|
|
//===========================================================================
|
|
// CDrives : member prototype
|
|
//===========================================================================
|
|
|
|
STDMETHODIMP CDrives_QueryInterface(LPSHELLFOLDER psf, REFIID riid, LPVOID * ppvObj);
|
|
STDMETHODIMP CDrives_ParseDisplayName(LPSHELLFOLDER psf, HWND hwndOwner,
|
|
LPBC pbc, LPOLESTR lpszDisplayName,
|
|
ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG* pdwAttributes);
|
|
STDMETHODIMP CDrives_EnumObjects( LPSHELLFOLDER psf, HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST * ppenumUnknown);
|
|
STDMETHODIMP CDrives_BindToObject(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPBC pbc,
|
|
REFIID riid, LPVOID * ppvOut);
|
|
STDMETHODIMP CDrives_CompareIDs(LPSHELLFOLDER psf, LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
STDMETHODIMP CDrives_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd, REFIID riid, LPVOID * ppvOut);
|
|
STDMETHODIMP CDrives_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfOut);
|
|
STDMETHODIMP CDrives_GetUIObjectOf(LPSHELLFOLDER psf, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
|
|
REFIID riid, UINT * prgfInOut, LPVOID * ppvOut);
|
|
STDMETHODIMP CDrives_GetDisplayNameOf(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, DWORD dwReserved, LPSTRRET pStrRet);
|
|
STDMETHODIMP CDrives_SetNameOf(LPSHELLFOLDER psf, HWND hwndOwner,
|
|
LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwReserved,
|
|
LPITEMIDLIST * ppidlOut);
|
|
|
|
STDMETHODIMP CDrives_PF_QueryInterface(LPPERSISTFOLDER fld, REFIID riid, LPVOID * ppvObj);
|
|
STDMETHODIMP CDrives_PF_GetClassID(LPPERSISTFOLDER fld, LPCLSID lpClassID);
|
|
STDMETHODIMP CDrives_PF_Initialize(LPPERSISTFOLDER fld, LPCITEMIDLIST pidl);
|
|
|
|
ULONG STDMETHODCALLTYPE Dummy_AddRef(LPVOID psf)
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
//
|
|
// Release
|
|
//
|
|
ULONG STDMETHODCALLTYPE Dummy_Release(LPVOID psf)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
//===========================================================================
|
|
// CDrives : Vtable
|
|
//===========================================================================
|
|
#pragma data_seg(".text", "CODE")
|
|
IShellFolderVtbl c_DrivesSFVtbl =
|
|
{
|
|
CDrives_QueryInterface,
|
|
CSIShellFolder_AddRef,
|
|
CSIShellFolder_Release,
|
|
CDrives_ParseDisplayName,
|
|
CDrives_EnumObjects,
|
|
CDrives_BindToObject,
|
|
CDefShellFolder_BindToStorage,
|
|
CDrives_CompareIDs,
|
|
CDrives_CreateViewObject,
|
|
CDrives_GetAttributesOf,
|
|
CDrives_GetUIObjectOf,
|
|
CDrives_GetDisplayNameOf,
|
|
CDrives_SetNameOf,
|
|
};
|
|
|
|
IPersistFolderVtbl c_DrivesPFVtbl =
|
|
{
|
|
CDrives_PF_QueryInterface,
|
|
Dummy_AddRef,
|
|
Dummy_Release,
|
|
CDrives_PF_GetClassID,
|
|
CDrives_PF_Initialize,
|
|
};
|
|
|
|
//
|
|
// We have a single instance of this Drives class in code segment.
|
|
//
|
|
typedef struct _DRIVESSF
|
|
{
|
|
IShellFolder sf;
|
|
IPersistFolder pf;
|
|
} DRIVESSF, *LPDRIVESSF;
|
|
DRIVESSF c_sfDrives = { {&c_DrivesSFVtbl }, {&c_DrivesPFVtbl } };
|
|
|
|
#pragma data_seg()
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
// HACKORAMA '95: In views.h you'll find a set of #defines which reference
|
|
// specific rows of this table, such as CDRIVES_REGITEM_CONTROLS (which, for
|
|
// example, would be "1" if the controls folder was the second item in the
|
|
// table below).
|
|
//
|
|
// IF YOU MODIFY THIS TABLE YOU MUST MODIFY THOSE INDEXES AS WELL
|
|
|
|
const REQREGITEM c_asDrivesReqItems[] =
|
|
{
|
|
{ &CLSID_CPrinters, IDS_PRINTERS, c_szShell32Dll, -IDI_PRNFLD, SFGAO_DROPTARGET | SFGAO_FOLDER},
|
|
{ &CLSID_CControls, IDS_CONTROLPANEL, c_szShell32Dll, -IDI_CPLFLD, SFGAO_FOLDER},
|
|
|
|
#define NOFONTS // BUGBUG (DAVEPL) Temporary removal of this folder
|
|
#ifndef NOFONTS
|
|
#endif
|
|
|
|
} ;
|
|
#pragma data_seg()
|
|
|
|
#pragma data_seg(DATASEG_PERINSTANCE)
|
|
// REVIEW: I am making this PERINSTANCE in case HKEY's are PERINSTANCE
|
|
REGITEMSINFO g_sDrivesRegInfo =
|
|
{
|
|
&c_sfDrives.sf,
|
|
NULL,
|
|
TEXT(':'),
|
|
SHID_COMPUTER_REGITEM,
|
|
(LPCITEMIDLIST)&c_idlDrives,
|
|
-1,
|
|
SFGAO_CANLINK,
|
|
ARRAYSIZE(c_asDrivesReqItems),
|
|
c_asDrivesReqItems,
|
|
} ;
|
|
|
|
IShellFolder *g_psfDrives = NULL;
|
|
#pragma data_seg()
|
|
|
|
HRESULT _Drives_InitRegItems(void)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if (!g_sDrivesRegInfo.hkRegItems)
|
|
{
|
|
// Note that if this fails, we just won't get any "extra" items
|
|
g_sDrivesRegInfo.hkRegItems = SHGetExplorerSubHkey(HKEY_LOCAL_MACHINE, c_szDrivesNameSpace, FALSE);
|
|
}
|
|
|
|
if (g_psfDrives)
|
|
{
|
|
return(NOERROR);
|
|
}
|
|
|
|
if (SHRestricted(REST_NOSETFOLDERS))
|
|
{
|
|
g_sDrivesRegInfo.iReqItems = 0;
|
|
}
|
|
|
|
hres = RegItems_AddToShellFolder(&g_sDrivesRegInfo,
|
|
&g_psfDrives);
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
// This should only be called during process detach
|
|
void CDrives_Terminate(void)
|
|
{
|
|
if (g_psfDrives)
|
|
{
|
|
IShellFolder *psfDrives = g_psfDrives;
|
|
g_psfDrives = NULL;
|
|
psfDrives->lpVtbl->Release(psfDrives);
|
|
}
|
|
|
|
if (g_sDrivesRegInfo.hkRegItems)
|
|
{
|
|
RegCloseKey(g_sDrivesRegInfo.hkRegItems);
|
|
g_sDrivesRegInfo.hkRegItems = NULL;
|
|
}
|
|
#ifdef DRIVE_CACHE_PER_PROCESS
|
|
// Invalidate the drive name cache for this process;
|
|
InvalidateDriveNameCache(-1);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
HRESULT Drives_GetName(LPCIDDRIVE pidd, LPSTRRET pStrRet)
|
|
{
|
|
#ifdef UNICODE
|
|
TCHAR szDriveName[MAX_PATH];
|
|
HRESULT hres;
|
|
|
|
// In case of error
|
|
pStrRet->uType = STRRET_CSTR;
|
|
pStrRet->cStr[0] = '\0';
|
|
|
|
hres = Drives_GetDriveName(pidd, szDriveName, ARRAYSIZE(szDriveName));
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
pStrRet->pOleStr = SHAlloc((lstrlen(szDriveName)+1)*SIZEOF(TCHAR));
|
|
if (pStrRet->pOleStr == NULL)
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
pStrRet->uType = STRRET_OLESTR;
|
|
lstrcpy(pStrRet->pOleStr,szDriveName);
|
|
hres = NOERROR;
|
|
}
|
|
}
|
|
return hres;
|
|
#else
|
|
pStrRet->uType = STRRET_CSTR;
|
|
return Drives_GetDriveName(pidd, pStrRet->cStr, ARRAYSIZE(pStrRet->cStr));
|
|
#endif
|
|
}
|
|
|
|
|
|
//
|
|
// Get the path to the specified file system object.
|
|
//
|
|
// Parameters:
|
|
// pidlRel -- Specifies the relative IDList to the file system object
|
|
// pszPath -- Specifies the string buffer (MAX_PATH)
|
|
//
|
|
BOOL Drives_GetPathFromIDList(LPCITEMIDLIST pidlRel, LPTSTR pszPath, UINT uOpts)
|
|
{
|
|
LPIDDRIVE pidd = (LPIDDRIVE)pidlRel;
|
|
|
|
if (!ILIsEmpty(pidlRel) && !Drives_IsReg(pidd))
|
|
{
|
|
#ifdef UNICODE
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
pidd->cName, -1,
|
|
pszPath, ARRAYSIZE(pidd->cName));
|
|
#else
|
|
lstrcpy(pszPath, pidd->cName);
|
|
#endif
|
|
if (FSFolder_CombinePath(_ILNext(pidlRel), pszPath, uOpts & GPFIDL_ALTNAME))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
// CDrives : Constructor
|
|
//===========================================================================
|
|
|
|
//
|
|
// Te be called from IClassFactory::CreateInstance
|
|
//
|
|
HRESULT CALLBACK CDrives_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, LPVOID * ppv)
|
|
{
|
|
Assert(punkOuter==NULL);
|
|
return c_sfDrives.sf.lpVtbl->QueryInterface(&c_sfDrives.sf, riid, ppv);
|
|
}
|
|
|
|
|
|
|
|
void CDrives_FillIDDrive(LPSHITEMID pid, int iDrive)
|
|
{
|
|
LPIDDRIVE pidd = (LPIDDRIVE)pid;
|
|
TCHAR szDriveName[MAX_PATH];
|
|
LPTSTR pszDriveName;
|
|
BYTE bCheckPlus;
|
|
BYTE bCheckXor;
|
|
|
|
pidd->bFlags = CDrives_GetDriveType(iDrive);
|
|
pidd->qwSize = 0;
|
|
pidd->qwFree = 0;
|
|
#ifdef UNICODE
|
|
PathBuildRoot(szDriveName, iDrive);
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
szDriveName, -1,
|
|
pidd->cName, ARRAYSIZE(pidd->cName),
|
|
NULL, NULL);
|
|
#else
|
|
PathBuildRoot(pidd->cName, iDrive);
|
|
#endif
|
|
Drives_GetDriveName(pidd, szDriveName, ARRAYSIZE(szDriveName));
|
|
bCheckPlus = (BYTE)szDriveName[0];
|
|
bCheckXor = (BYTE)szDriveName[0];
|
|
for (pszDriveName = CharNext(szDriveName); *pszDriveName;
|
|
pszDriveName = CharNext(pszDriveName))
|
|
{
|
|
bCheckPlus += (BYTE)*pszDriveName;
|
|
bCheckXor = (bCheckXor << 1) ^ (BYTE)*pszDriveName;
|
|
}
|
|
pidd->wChecksum = (WORD)bCheckPlus << 8 | (WORD)bCheckXor;
|
|
|
|
pidd->cb = SIZEOF(IDDRIVE);
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
// CDrives : Members
|
|
//===========================================================================
|
|
|
|
//
|
|
// PersistFolder
|
|
//
|
|
STDMETHODIMP CDrives_PF_QueryInterface(LPPERSISTFOLDER fld, REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
LPDRIVESSF this = IToClass(DRIVESSF, pf, fld);
|
|
|
|
return(this->sf.lpVtbl->QueryInterface(&this->sf, riid, ppvObj));
|
|
}
|
|
|
|
STDMETHODIMP CDrives_PF_GetClassID(LPPERSISTFOLDER fld, LPCLSID lpClassID)
|
|
{
|
|
*lpClassID = CLSID_ShellDrives;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CDrives_PF_Initialize(LPPERSISTFOLDER fld, LPCITEMIDLIST pidl)
|
|
{
|
|
HRESULT hres;
|
|
|
|
hres = _Drives_InitRegItems();
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
// Only allow the Drives root on the desktop
|
|
if (!CDesktop_IsMyComputer(pidl) || !ILIsEmpty(_ILNext(pidl)))
|
|
{
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
return(NOERROR);
|
|
}
|
|
|
|
//
|
|
// ShellFolder
|
|
//
|
|
STDMETHODIMP CDrives_QueryInterface(LPSHELLFOLDER psf, REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
LPDRIVESSF this = IToClass(DRIVESSF, sf, psf);
|
|
HRESULT hres;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown)
|
|
|| IsEqualIID(riid, &IID_IShellFolder))
|
|
{
|
|
hres = _Drives_InitRegItems();
|
|
if (FAILED(hres))
|
|
{
|
|
// We have some serious problem we need to deal with
|
|
Assert(FALSE);
|
|
return(hres);
|
|
}
|
|
|
|
*ppvObj = g_psfDrives;
|
|
g_psfDrives->lpVtbl->AddRef(g_psfDrives);
|
|
return NOERROR;
|
|
}
|
|
|
|
if (IsEqualIID(riid, &IID_IPersistFolder))
|
|
{
|
|
*ppvObj = &this->pf;
|
|
this->pf.lpVtbl->AddRef(&this->pf);
|
|
return NOERROR;
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
|
|
return(E_NOINTERFACE);
|
|
}
|
|
|
|
STDMETHODIMP CDrives_ParseDisplayName(LPSHELLFOLDER psf, HWND hwndOwner, LPBC pbc, LPOLESTR pwzDisplayName,
|
|
ULONG * pchEaten, LPITEMIDLIST * ppidlOut, ULONG * pdwAttributes)
|
|
{
|
|
HRESULT hres = E_INVALIDARG;
|
|
CHAR cDrive;
|
|
ULONG chEaten;
|
|
IShellFolder *psfDrive;
|
|
struct
|
|
{
|
|
IDDRIVE idd;
|
|
CHAR szDisplayName[MAX_PATH];
|
|
USHORT cbNext;
|
|
} idlDrive;
|
|
|
|
*ppidlOut = NULL; // assume error
|
|
|
|
if (!pwzDisplayName)
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
cDrive = (CHAR)*pwzDisplayName;
|
|
|
|
// Note that we should never get called with a Reg item
|
|
if (pwzDisplayName[1]==TEXT(':') && pwzDisplayName[2]==TEXT('\\'))
|
|
{
|
|
// Make sure this is a fully qualified path
|
|
if (InRange(cDrive, 'a', 'z'))
|
|
{
|
|
// Make sure we have upper case
|
|
cDrive = cDrive - 'a' + 'A';
|
|
}
|
|
else if (!InRange(cDrive, 'A', 'Z'))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
CDrives_FillIDDrive((LPSHITEMID)&idlDrive.idd, cDrive - 'A');
|
|
Assert(lstrlenA(idlDrive.idd.cName) == 3);
|
|
_ILNext((LPITEMIDLIST)&idlDrive.idd)->mkid.cb = 0;;
|
|
|
|
// Check if there are any subdirs
|
|
if (pwzDisplayName[3])
|
|
{
|
|
LPITEMIDLIST pidlDir;
|
|
hres = CDrives_BindToObject(psf, (LPITEMIDLIST)&idlDrive, pbc,
|
|
&IID_IShellFolder, &psfDrive);
|
|
if (!SUCCEEDED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
hres = psfDrive->lpVtbl->ParseDisplayName(psfDrive, hwndOwner, pbc,
|
|
pwzDisplayName+3, &chEaten, &pidlDir, pdwAttributes);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = SHILCombine((LPCITEMIDLIST)&idlDrive, pidlDir, ppidlOut);
|
|
SHFree(pidlDir);
|
|
}
|
|
psfDrive->lpVtbl->Release(psfDrive);
|
|
}
|
|
else
|
|
{
|
|
hres = SHILClone((LPITEMIDLIST)&idlDrive, ppidlOut);
|
|
if (pdwAttributes)
|
|
{
|
|
CDrives_GetAttributesOf(psf, 1, ppidlOut, pdwAttributes);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
|
|
//
|
|
// This function returns a real IDLIST
|
|
//
|
|
HRESULT Drives_GetRealIDL(LPSHELLFOLDER psf, LPCITEMIDLIST pidlSimple, LPITEMIDLIST *ppidlReal)
|
|
{
|
|
if (psf->lpVtbl == &c_DrivesSFVtbl)
|
|
{
|
|
WCHAR szRoot[4];
|
|
|
|
szRoot[0] = ((LPIDDRIVE)pidlSimple)->cName[0];
|
|
szRoot[1] = TEXT(':');
|
|
szRoot[2] = TEXT('\\');
|
|
szRoot[3] = 0;
|
|
|
|
return CDrives_ParseDisplayName(psf, NULL, NULL, szRoot, NULL, ppidlReal, NULL);
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// This function returns the LPITEMIDLIST of the folder pStr in MyComputer.
|
|
// Since the only folders we care about are c_szPrinters and c_szControlPanel
|
|
// (and those are the only ones requested), that's all we have to handle --
|
|
// we don't need to create an IShellFolder and enumerate through them. A
|
|
// further optimization -- since these are a fixed path off the global
|
|
// MyComputer, we can simply build them directly (because we know their
|
|
// representation is "Printers" or "Controls").
|
|
LPITEMIDLIST CDrives_CreateRegID(UINT uRegItem)
|
|
{
|
|
LPITEMIDLIST pidlReg, pidlAbs;
|
|
|
|
_Drives_InitRegItems();
|
|
|
|
// This should only be the Controls or Printers folder
|
|
if (uRegItem >= ARRAYSIZE(c_asDrivesReqItems))
|
|
{
|
|
Assert(FALSE);
|
|
return(NULL);
|
|
}
|
|
|
|
pidlReg = RegItems_CreateRelID(&g_sDrivesRegInfo,
|
|
c_asDrivesReqItems[uRegItem].pclsid);
|
|
if (!pidlReg)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
pidlAbs = ILCombine((LPCITEMIDLIST)&c_idlDrives, pidlReg);
|
|
ILFree(pidlReg);
|
|
return(pidlAbs);
|
|
}
|
|
|
|
//
|
|
// Determine whether this is a disconnected (as opposed to an OK or
|
|
// Unavailable) net connection.
|
|
//
|
|
BOOL IsDisconnectedNetDrive(int iDrive)
|
|
{
|
|
#ifdef WNGC_DISCONNECTED
|
|
TCHAR szDrive[4];
|
|
DWORD dwSize;
|
|
WNGC_CONNECTION_STATE wngcs;
|
|
DWORD dwNet;
|
|
BOOL fDisconnected;
|
|
DWORD dwCurrentTick;
|
|
|
|
if (iDrive < 0 || iDrive >= 26)
|
|
return FALSE;
|
|
|
|
dwCurrentTick = GetTickCount();
|
|
if (g_rdwDisconnectTick[iDrive] != 0
|
|
&& dwCurrentTick - g_rdwDisconnectTick[iDrive] < DISCONNECT_CACHE_TIMEOUT)
|
|
return g_rfDisconnectResult[iDrive];
|
|
|
|
// NB Vines (and PCNFS) doesn't like it when we pass "c:\"
|
|
// to WNetGetConnection() - they want just c: so don't use
|
|
// PathBuildRoot()
|
|
szDrive[0] = (TCHAR)iDrive + (TCHAR)TEXT('A');
|
|
szDrive[1] = TEXT(':');
|
|
szDrive[2] = 0;
|
|
|
|
dwSize = SIZEOF(wngcs);
|
|
dwNet = WNetGetConnection3(szDrive, NULL, WNGC_INFOLEVEL_DISCONNECTED, &wngcs, &dwSize);
|
|
if (dwNet == WN_SUCCESS && wngcs.dwState == WNGC_DISCONNECTED)
|
|
fDisconnected = TRUE;
|
|
else
|
|
fDisconnected = FALSE;
|
|
|
|
g_rfDisconnectResult[iDrive] = fDisconnected;
|
|
g_rdwDisconnectTick[iDrive] = dwCurrentTick;
|
|
return fDisconnected;
|
|
#else
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Determine whether this is an unavailable or disconnected
|
|
// net connection.
|
|
//
|
|
BOOL IsUnavailableNetDrive(int iDrive)
|
|
{
|
|
// See if we have a remembered connection for this
|
|
// drive.
|
|
TCHAR szDrive[4];
|
|
TCHAR szRemoteName[MAX_PATH];
|
|
DWORD dwRemoteName = ARRAYSIZE(szRemoteName);
|
|
|
|
// NB Vines (and PCNFS) doesn't like it when we pass "c:\"
|
|
// to WNetGetConnection() - they want just c: so don't use
|
|
// PathBuildRoot()
|
|
szDrive[0] = (TCHAR)iDrive + (TCHAR)TEXT('A');
|
|
szDrive[1] = TEXT(':');
|
|
szDrive[2] = 0;
|
|
|
|
return WNetGetConnection(szDrive, szRemoteName, &dwRemoteName) == ERROR_CONNECTION_UNAVAIL;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwDrivesMask;
|
|
int nLastFoundDrive;
|
|
DWORD dwRestricted;
|
|
} EnumDrives;
|
|
|
|
//
|
|
//
|
|
HRESULT CALLBACK CDrives_EnumCallBack(LPARAM lParam, LPVOID pvData, UINT ecid, UINT index)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
EnumDrives * pedrv = (EnumDrives *)pvData;
|
|
|
|
if (ecid == ECID_SETNEXTID)
|
|
{
|
|
int iDrive;
|
|
|
|
hres = S_FALSE; // assume "no more element"
|
|
|
|
for (iDrive = pedrv->nLastFoundDrive + 1; iDrive < 26; iDrive++)
|
|
{
|
|
if (pedrv->dwRestricted & (1 << iDrive))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("s.cd_ecb: Drive %d restricted."), iDrive);
|
|
}
|
|
else if ((pedrv->dwDrivesMask & (1 << iDrive)) || IsUnavailableNetDrive(iDrive))
|
|
{
|
|
// BUGBUG: piggy
|
|
LPITEMIDLIST pidl = _ILCreate(SIZEOF(IDDRIVE) + MAX_PATH);
|
|
if (pidl)
|
|
{
|
|
CDrives_FillIDDrive(&pidl->mkid, iDrive);
|
|
CDefEnum_SetReturn(lParam, pidl);
|
|
|
|
pedrv->nLastFoundDrive = iDrive;
|
|
return NOERROR;
|
|
}
|
|
else
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ecid == ECID_RELEASE)
|
|
{
|
|
LocalFree((HLOCAL)pedrv);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP CDrives_EnumObjects(LPSHELLFOLDER psf, HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST *ppenumUnknown)
|
|
{
|
|
// We should always create enum objects with this helper call.
|
|
EnumDrives * pedrv = (EnumDrives *)LocalAlloc(LPTR, SIZEOF(EnumDrives));
|
|
if (pedrv)
|
|
{
|
|
pedrv->dwDrivesMask = GetLogicalDrives();
|
|
pedrv->nLastFoundDrive=-1;
|
|
pedrv->dwRestricted = SHRestricted(REST_NODRIVES);
|
|
return SHCreateEnumObjects(hwndOwner, pedrv, CDrives_EnumCallBack, ppenumUnknown);
|
|
}
|
|
else
|
|
{
|
|
*ppenumUnknown = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
BOOL CDrives_IsValidID(LPCITEMIDLIST pidl)
|
|
{
|
|
return ((SIL_GetType(pidl) & SHID_GROUPMASK) == SHID_COMPUTER);
|
|
}
|
|
|
|
STDMETHODIMP CDrives_BindToObject(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPBC pbc,
|
|
REFIID riid, LPVOID *ppvOut)
|
|
{
|
|
LPIDDRIVE pidd = (LPIDDRIVE)pidl;
|
|
HRESULT hres = E_INVALIDARG;
|
|
|
|
if (CDrives_IsValidID(pidl))
|
|
{
|
|
// We should not get here unless we have initialized properly
|
|
hres = FSBindToObject(&CLSID_NULL, (LPCITEMIDLIST)&c_idlDrives, pidl, pbc, riid, ppvOut);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP Drives_CompareItemIDs(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
LPCIDDRIVE pidd1 = (LPCIDDRIVE)pidl1;
|
|
LPCIDDRIVE pidd2 = (LPCIDDRIVE)pidl2;
|
|
|
|
if (CDrives_IsValidID(pidl1) && CDrives_IsValidID(pidl2))
|
|
{
|
|
int iRes;
|
|
// Put all drives first, and RegItem's last
|
|
{
|
|
// Neither is a RegItem
|
|
// Compare the drive letter for sorting purpose.
|
|
iRes = lstrcmpiA(pidd1->cName, pidd2->cName);
|
|
|
|
// Then, compare the volume label (if any) for identity.
|
|
if (iRes == 0
|
|
&& pidd1->bFlags != SHID_COMPUTER_MISC
|
|
&& pidd2->bFlags != SHID_COMPUTER_MISC)
|
|
{
|
|
iRes = pidd1->wChecksum - pidd2->wChecksum;
|
|
if (iRes == 0)
|
|
iRes = pidd1->bFlags - pidd2->bFlags;
|
|
}
|
|
}
|
|
|
|
return ResultFromShort( iRes );
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrives_CompareIDs(LPSHELLFOLDER psf, LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
HRESULT hres;
|
|
LPCIDDRIVE pidd1 = (LPCIDDRIVE)(pidl1);
|
|
LPCIDDRIVE pidd2 = (LPCIDDRIVE)(pidl2);
|
|
|
|
switch (iCol) {
|
|
|
|
case DRIVES_ICOL_NAME:
|
|
UseNames:
|
|
hres = Drives_CompareItemIDs(pidl1, pidl2);
|
|
if (hres == ResultFromShort(0)) {
|
|
hres = ILCompareRelIDs(psf, pidl1, pidl2);
|
|
}
|
|
break;
|
|
|
|
case DRIVES_ICOL_TYPE:
|
|
{
|
|
TCHAR szName1[80];
|
|
TCHAR szName2[80];
|
|
Drives_GetTypeString(pidd1->bFlags, szName1, ARRAYSIZE(szName1));
|
|
Drives_GetTypeString(pidd2->bFlags, szName2, ARRAYSIZE(szName2));
|
|
hres = ResultFromShort(lstrcmpi(szName1, szName2));
|
|
break;
|
|
}
|
|
|
|
case DRIVES_ICOL_SIZE:
|
|
case DRIVES_ICOL_FREE:
|
|
{
|
|
BOOL fGotInfo1, fGotInfo2;
|
|
LPITEMIDLIST pid1 = ILClone((LPITEMIDLIST)pidd1);
|
|
LPITEMIDLIST pid2 = ILClone((LPITEMIDLIST)pidd2);
|
|
if (pid1 && pid2) {
|
|
fGotInfo1 = Drives_FillFreeSpace((LPIDDRIVE)pid1);
|
|
fGotInfo2 = Drives_FillFreeSpace((LPIDDRIVE)pid2);
|
|
ILFree(pid1);
|
|
ILFree(pid2);
|
|
|
|
if (fGotInfo1 && fGotInfo2) {
|
|
__int64 i1; // this is a "guess" at the disk size and free space
|
|
__int64 i2;
|
|
|
|
if (iCol == DRIVES_ICOL_SIZE) {
|
|
i1 = pidd1->qwSize;
|
|
i2 = pidd2->qwSize;
|
|
} else {
|
|
i1 = pidd1->qwFree;
|
|
i2 = pidd2->qwFree;
|
|
}
|
|
|
|
if (i1 == i2) {
|
|
hres = ResultFromShort(0);
|
|
} else if (i1 < i2) {
|
|
hres = ResultFromShort(-1);
|
|
} else
|
|
hres = ResultFromShort(1);
|
|
|
|
} else if (!fGotInfo1 && !fGotInfo2) {
|
|
hres = ResultFromShort(0);
|
|
} else {
|
|
hres = ResultFromShort(fGotInfo1 - fGotInfo2);
|
|
}
|
|
} else
|
|
hres = ResultFromShort(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if they were the same in anything but name, then let the
|
|
// name be the tie breaker.
|
|
if (ShortFromResult(hres) == 0 && iCol != DRIVES_ICOL_NAME) {
|
|
iCol = DRIVES_ICOL_NAME;
|
|
goto UseNames;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// fDoIt -- TRUE, if we make connections; FALSE, if just querying.
|
|
//
|
|
BOOL _MakeConnection(LPDATAOBJECT pDataObj, BOOL fDoIt)
|
|
{
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {g_cfNetResource, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
BOOL fAnyConnectable = FALSE;
|
|
|
|
if (SUCCEEDED(pDataObj->lpVtbl->GetData(pDataObj, &fmte, &medium)))
|
|
{
|
|
LPNETRESOURCE pnr = (LPNETRESOURCE)LocalAlloc(LPTR, 1024);
|
|
if (pnr)
|
|
{
|
|
UINT iItem, cItems = SHGetNetResource(medium.hGlobal, (UINT)-1, NULL, 0);
|
|
for (iItem = 0; iItem < cItems; iItem++)
|
|
{
|
|
if (SHGetNetResource(medium.hGlobal, iItem, pnr, 1024) &&
|
|
pnr->dwUsage & RESOURCEUSAGE_CONNECTABLE &&
|
|
!(pnr->dwType & RESOURCETYPE_PRINT))
|
|
{
|
|
fAnyConnectable = TRUE;
|
|
if (fDoIt)
|
|
{
|
|
SHNetConnectionDialog(NULL, pnr->lpRemoteName, pnr->dwType);
|
|
SHChangeNotifyHandleEvents();
|
|
}
|
|
else
|
|
{
|
|
break; // We are just querying.
|
|
}
|
|
}
|
|
}
|
|
LocalFree(pnr);
|
|
}
|
|
|
|
SHReleaseStgMedium(&medium);
|
|
}
|
|
|
|
return fAnyConnectable;
|
|
}
|
|
|
|
|
|
//
|
|
// This is the entry of "make connection thread"
|
|
//
|
|
DWORD WINAPI CDrives_MakeConnection(LPVOID pvDataObj)
|
|
{
|
|
LPDATAOBJECT pDataObj = (LPDATAOBJECT)pvDataObj;
|
|
_MakeConnection(pDataObj, TRUE);
|
|
pDataObj->lpVtbl->Release(pDataObj);
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
extern UINT g_cRefExtra;
|
|
g_cRefExtra--;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// puts DROPEFFECT_LINK in *pdwEffect, only if the data object
|
|
// contains one or more net resource.
|
|
//
|
|
HRESULT CDrivesIDLDropTarget_DragEnter(IDropTarget *pdropt, LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
|
|
// Call the base class first.
|
|
CIDLDropTarget_DragEnter(pdropt, pDataObj, grfKeyState, pt, pdwEffect);
|
|
|
|
*pdwEffect &= _MakeConnection(pDataObj, FALSE) ? DROPEFFECT_LINK : DROPEFFECT_NONE;
|
|
|
|
this->dwEffectLastReturned = *pdwEffect;
|
|
|
|
return NOERROR; // Notes: we should NOT return hres as it.
|
|
}
|
|
|
|
//
|
|
// creates a connection to a dropped net resource object.
|
|
//
|
|
HRESULT CDrivesIDLDropTarget_Drop(IDropTarget * pdropt, LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
HRESULT hres;
|
|
|
|
if (this->dwData & DTID_NETRES)
|
|
{
|
|
*pdwEffect = DROPEFFECT_LINK;
|
|
|
|
hres = CIDLDropTarget_DragDropMenu(this, DROPEFFECT_LINK, pDataObj,
|
|
pt, pdwEffect, NULL, NULL, POPUP_DRIVES_NONDEFAULTDD, grfKeyState);
|
|
|
|
if (hres == S_FALSE)
|
|
{
|
|
//
|
|
// Note that we need to create another thread to avoid
|
|
// blocking the source thread.
|
|
//
|
|
DWORD idThread;
|
|
HANDLE hthread;
|
|
LPDATAOBJECT pdtobjClone = NULL;
|
|
|
|
if (CIDLData_IsOurs(pDataObj))
|
|
{
|
|
// This is our dataobject, no need to marshal.
|
|
pdtobjClone = pDataObj;
|
|
pdtobjClone->lpVtbl->AddRef(pdtobjClone);
|
|
}
|
|
else
|
|
{
|
|
// This is from other app, we need to quick-marshal it.
|
|
extern HRESULT CIDLData_Clone(LPDATAOBJECT pdtobjIn, UINT acf[], UINT ccf, LPDATAOBJECT *ppdtobjOut);
|
|
UINT acf[] = { g_cfHIDA, g_cfNetResource };
|
|
CIDLData_Clone(pDataObj, acf, ARRAYSIZE(acf), &pdtobjClone);
|
|
}
|
|
|
|
if (pdtobjClone && (NULL != (hthread = CreateThread(NULL, 0, CDrives_MakeConnection, pdtobjClone, 0, &idThread))))
|
|
{
|
|
#ifdef DEBUG
|
|
{
|
|
extern UINT g_cRefExtra;
|
|
g_cRefExtra++;
|
|
}
|
|
#endif
|
|
// We don't need to communicate with this thread any more.
|
|
// Close the handle and let it run and terminate itself.
|
|
//
|
|
// Notes: In this case, pszCopy will be freed by the thread.
|
|
//
|
|
CloseHandle(hthread);
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// Thread creation failed, we should release pdtobjClone.
|
|
if (pdtobjClone)
|
|
{
|
|
pdtobjClone->lpVtbl->Release(pdtobjClone);
|
|
}
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Because QueryGetData() failed, we don't call CIDLDropTarget_
|
|
// DragDropMenu(). Therefore, we must call this explicitly.
|
|
//
|
|
CDefView_UnlockWindow();
|
|
hres = E_FAIL;
|
|
}
|
|
|
|
//
|
|
// We MUST clean up.
|
|
//
|
|
CIDLDropTarget_DragLeave(pdropt);
|
|
|
|
return hres;
|
|
}
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
IDropTargetVtbl c_CDrivesDropTargetVtbl =
|
|
{
|
|
CIDLDropTarget_QueryInterface,
|
|
CIDLDropTarget_AddRef,
|
|
CIDLDropTarget_Release,
|
|
CDrivesIDLDropTarget_DragEnter,
|
|
CIDLDropTarget_DragOver,
|
|
CIDLDropTarget_DragLeave,
|
|
CDrivesIDLDropTarget_Drop,
|
|
};
|
|
|
|
//
|
|
// To be called back from within CDefFolderMenu
|
|
//
|
|
HRESULT CALLBACK CDrives_DFMCallBackBG(LPSHELLFOLDER psf, HWND hwndOwner,
|
|
LPDATAOBJECT pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
LPQCMINFO pqcm;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case DFM_MERGECONTEXTMENU:
|
|
pqcm = (LPQCMINFO)lParam;
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DRIVES_BACKGROUND,
|
|
POPUP_DRIVES_POPUPMERGE, pqcm);
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
{
|
|
UINT idRes = IDS_MH_FSIDM_FIRST + LOWORD(wParam);
|
|
if (idRes == (IDS_MH_FSIDM_FIRST + FSIDM_SORTBYDATE))
|
|
idRes = IDS_MH_SORTBYFREESPACE;
|
|
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
|
|
break;
|
|
}
|
|
|
|
case DFM_GETHELPTEXTW:
|
|
{
|
|
UINT idRes = IDS_MH_FSIDM_FIRST + LOWORD(wParam);
|
|
if (idRes == (IDS_MH_FSIDM_FIRST + FSIDM_SORTBYDATE))
|
|
idRes = IDS_MH_SORTBYFREESPACE;
|
|
|
|
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
|
|
break;
|
|
}
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
switch(wParam)
|
|
{
|
|
case FSIDM_SORTBYNAME:
|
|
ShellFolderView_ReArrange(hwndOwner, 0);
|
|
break;
|
|
|
|
case FSIDM_SORTBYTYPE:
|
|
ShellFolderView_ReArrange(hwndOwner, 1);
|
|
break;
|
|
|
|
case FSIDM_SORTBYSIZE:
|
|
ShellFolderView_ReArrange(hwndOwner, 2);
|
|
break;
|
|
|
|
case FSIDM_SORTBYDATE:
|
|
ShellFolderView_ReArrange(hwndOwner, 3);
|
|
break;
|
|
|
|
case FSIDM_PROPERTIESBG:
|
|
SHRunControlPanel(MAKEINTRESOURCE(IDS_SYSDMCPL), hwndOwner);
|
|
break;
|
|
|
|
default:
|
|
// This is one of view menu items, use the default code.
|
|
hres = S_FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
BOOL Drives_FillFreeSpace(LPIDDRIVE pidd)
|
|
{
|
|
DWORD dwSPC, dwBPS, dwFC, dwC;
|
|
|
|
if (!Drives_IsReg(pidd) && ShowDriveInfo(DRIVEID(pidd->cName))) {
|
|
if (!pidd->qwSize && !pidd->qwFree)
|
|
{
|
|
int iDrive = CDrives_GetDriveIndex(pidd);
|
|
|
|
// Don't wake up sleeping net connections
|
|
if (IsRemoteDrive(iDrive) && IsDisconnectedNetDrive(iDrive))
|
|
return FALSE;
|
|
|
|
if (GetDiskFreeSpaceA(pidd->cName, &dwSPC, &dwBPS, &dwFC, &dwC)) {
|
|
pidd->qwSize = (__int64)dwC * (__int64)dwSPC * (__int64)dwBPS;
|
|
pidd->qwFree = (__int64)dwFC * (__int64)dwSPC * (__int64)dwBPS;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
return 1; // must be 1
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
void Drives_OnRefresh(LPSHELLFOLDER psf, HWND hwndOwner)
|
|
{
|
|
int iCount;
|
|
HWND hwndStatus = NULL;
|
|
LPSHELLBROWSER psb = FileCabinet_GetIShellBrowser(hwndOwner);
|
|
|
|
|
|
if (psb) {
|
|
psb->lpVtbl->GetControlWindow(psb, FCW_STATUS, &hwndStatus);
|
|
if (hwndStatus) {
|
|
TCHAR szTemp[30];
|
|
TCHAR szTemplate[80];
|
|
TCHAR szStatus[128];
|
|
|
|
LoadString(HINST_THISDLL,
|
|
IDS_FSSTATUSNOHIDDENTEMPLATE,
|
|
szTemplate, ARRAYSIZE(szTemplate));
|
|
|
|
iCount = ShellFolderView_GetObjectCount(hwndOwner) ;
|
|
#ifdef WINDOWS_ME
|
|
szStatus[0] = szStatus[1] = TEXT('\t');
|
|
wsprintf(&szStatus[2], szTemplate, AddCommas(iCount, szTemp));
|
|
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)SB_RTLREADING, (LPARAM)szStatus);
|
|
#else
|
|
wsprintf(szStatus, szTemplate, AddCommas(iCount, szTemp));
|
|
SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)szStatus);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void Drives_OnSelChange(LPSHELLFOLDER psf, HWND hwndOwner)
|
|
{
|
|
int i;
|
|
LPTSTR lpsz = (LPTSTR)c_szNULL;
|
|
|
|
i = ShellFolderView_GetSelectedCount(hwndOwner);
|
|
if (i == 1)
|
|
{
|
|
LPITEMIDLIST *apidl;
|
|
LPIDDRIVE pidd;
|
|
TCHAR szFree[30];
|
|
TCHAR szTotal[30];
|
|
TCHAR szTemplate[40];
|
|
TCHAR szStatus[80];
|
|
|
|
ShellFolderView_GetSelectedObjects(hwndOwner, &apidl);
|
|
pidd = (LPIDDRIVE)apidl[0];
|
|
if (Drives_FillFreeSpace(pidd)) {
|
|
ShortSizeFormat64(pidd->qwSize, szTotal);
|
|
ShortSizeFormat64(pidd->qwFree, szFree);
|
|
LoadString(HINST_THISDLL, IDS_DRIVESSTATUSTEMPLATE, szTemplate, ARRAYSIZE(szTemplate));
|
|
wsprintf(szStatus, szTemplate, szFree, szTotal);
|
|
lpsz = szStatus;
|
|
}
|
|
LocalFree(apidl);
|
|
}
|
|
|
|
FSSetStatusText(hwndOwner, &lpsz, 1, 1);
|
|
}
|
|
|
|
//
|
|
// Callback from SHCreateShellFolderViewEx
|
|
//
|
|
HRESULT CALLBACK CDrives_FNVCallBack(LPSHELLVIEW psvOuter,
|
|
LPSHELLFOLDER psf, HWND hwndOwner, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hres = NOERROR; // assume no error
|
|
|
|
psf = RegItems_GetInnerShellFolder(psf);
|
|
|
|
switch(uMsg)
|
|
{
|
|
case DVM_MERGEMENU:
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_DRIVES_POPUPMERGE, (LPQCMINFO)lParam);
|
|
break;
|
|
|
|
case DVM_INVOKECOMMAND:
|
|
hres = CDrives_DFMCallBackBG(psf, hwndOwner, NULL, DFM_INVOKECOMMAND, wParam, lParam);
|
|
break;
|
|
|
|
case DVM_GETHELPTEXT:
|
|
#ifdef UNICODE
|
|
hres = CDrives_DFMCallBackBG(psf, hwndOwner, NULL, DFM_GETHELPTEXTW, wParam, lParam);
|
|
#else
|
|
hres = CDrives_DFMCallBackBG(psf, hwndOwner, NULL, DFM_GETHELPTEXT, wParam, lParam);
|
|
#endif
|
|
break;
|
|
|
|
case DVM_INSERTITEM:
|
|
{
|
|
LPIDDRIVE pidd = (LPIDDRIVE)wParam;
|
|
if (pidd && !Drives_IsReg(pidd) && ShowDriveInfo(DRIVEID(pidd->cName))) {
|
|
// clear the size info
|
|
pidd->qwSize = pidd->qwFree = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DVM_BACKGROUNDENUM:
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case DVM_DEFITEMCOUNT:
|
|
//
|
|
// If DefView times out enumerating items, let it know we probably only
|
|
// have about 20 items
|
|
//
|
|
|
|
*(int *)lParam = 20;
|
|
break;
|
|
|
|
case DVM_UPDATESTATUSBAR:
|
|
if (wParam) {
|
|
// are we initializing?
|
|
FSInitializeStatus(hwndOwner, -1, NULL);
|
|
Drives_OnRefresh(psf, hwndOwner);
|
|
} else {
|
|
hres = E_FAIL; // if we're not initializing, we want def view to add it's selected count
|
|
}
|
|
Drives_OnSelChange(psf, hwndOwner);
|
|
break;
|
|
|
|
case DVM_SELCHANGE:
|
|
Drives_OnSelChange(psf, hwndOwner);
|
|
hres = E_FAIL; // we want defview to do status stuff
|
|
break;
|
|
|
|
case DVM_FSNOTIFY:
|
|
hres = DrivesHandleFSNotify(psf, lParam, (LPCITEMIDLIST*)wParam);
|
|
break;
|
|
|
|
default:
|
|
hres = E_FAIL;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP CDrives_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd, REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
// We should not get here unless we have initialized properly
|
|
|
|
if (IsEqualIID(riid, &IID_IShellView))
|
|
{
|
|
CSFV csfv = {
|
|
SIZEOF(CSFV), // cbSize
|
|
g_psfDrives ? g_psfDrives : psf, // pshf
|
|
NULL, // psvOuter
|
|
(LPCITEMIDLIST)&c_idlDrives, // pidl
|
|
SHCNE_DRIVEADD | // lEvents
|
|
SHCNE_DRIVEREMOVED |
|
|
SHCNE_MEDIAINSERTED |
|
|
SHCNE_MEDIAREMOVED |
|
|
SHCNE_NETSHARE |
|
|
SHCNE_NETUNSHARE |
|
|
SHCNE_RENAMEFOLDER |
|
|
SHCNE_UPDATEITEM,
|
|
CDrives_FNVCallBack, // pfnCallback
|
|
0,
|
|
};
|
|
|
|
return SHCreateShellFolderViewEx(&csfv, (LPSHELLVIEW *)ppvOut);
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IDropTarget))
|
|
{
|
|
return CIDLDropTarget_Create(hwnd, &c_CDrivesDropTargetVtbl,
|
|
(LPCITEMIDLIST)&c_idlDrives, (IDropTarget **)ppvOut);
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IShellDetails))
|
|
{
|
|
return CDrives_SD_Create(hwnd, ppvOut);
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IContextMenu))
|
|
{
|
|
Assert(g_psfDrives)
|
|
return CDefFolderMenu_Create((LPCITEMIDLIST)&c_idlDrives, hwnd,
|
|
0, NULL, g_psfDrives, CDrives_DFMCallBackBG,
|
|
NULL, NULL, (LPCONTEXTMENU *)ppvOut);
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
// BUGBUG: What do we do for RegItem's?
|
|
//
|
|
STDMETHODIMP CDrives_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * prgfInOut)
|
|
{
|
|
UINT rgfOut = SFGAO_HASSUBFOLDER | SFGAO_CANLINK
|
|
| SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_FOLDER
|
|
| SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER;
|
|
|
|
if (cidl == 0 && (*prgfInOut & SFGAO_VALIDATE))
|
|
{
|
|
// nuke all the cached drive types on a real refresh (F5)
|
|
InvalidateDriveType(-1);
|
|
}
|
|
|
|
if (cidl == 1)
|
|
{
|
|
LPIDDRIVE pidd = (LPIDDRIVE)apidl[0];
|
|
|
|
#ifdef UNICODE
|
|
TCHAR szDrive[MAX_PATH];
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
pidd->cName, -1,
|
|
szDrive, ARRAYSIZE(szDrive));
|
|
#endif
|
|
|
|
// If caller wants compression status, we need to ask the filesystem
|
|
|
|
if (*prgfInOut & SFGAO_COMPRESSED)
|
|
{
|
|
int iDrive = CDrives_GetDriveIndex(pidd);
|
|
|
|
// Don't wake up sleeping net connections
|
|
if (!IsRemoteDrive(iDrive) || !IsDisconnectedNetDrive(iDrive))
|
|
{
|
|
if (DriveIsCompressed(DRIVEID(pidd->cName))) {
|
|
rgfOut |= SFGAO_COMPRESSED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*prgfInOut & SFGAO_SHARE)
|
|
{
|
|
//
|
|
// Don't call the server code unless we need to.
|
|
//
|
|
#ifdef UNICODE
|
|
if (IsShared(szDrive, FALSE))
|
|
#else
|
|
if (IsShared(pidd->cName, FALSE))
|
|
#endif
|
|
{
|
|
rgfOut |= SFGAO_SHARE;
|
|
}
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
if ((*prgfInOut & SFGAO_REMOVABLE) && PathIsRemovable(szDrive)) {
|
|
#else
|
|
if ((*prgfInOut & SFGAO_REMOVABLE) && PathIsRemovable(pidd->cName)) {
|
|
#endif
|
|
rgfOut |= SFGAO_REMOVABLE;
|
|
}
|
|
}
|
|
|
|
*prgfInOut = rgfOut;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
typedef struct {
|
|
LPDATAOBJECT pdtobj;
|
|
LPCTSTR pStartPage;
|
|
} DRIVEPROPSTUFF;
|
|
|
|
DWORD CALLBACK _CDrives_PropertiesThread(DRIVEPROPSTUFF * pps)
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pps->pdtobj, &medium);
|
|
if (medium.hGlobal)
|
|
{
|
|
LPCITEMIDLIST pidlFirst = IDA_GetIDListPtr(pida, 0);
|
|
LPTSTR pszCaption;
|
|
|
|
HKEY ahkeys[2] = { NULL, NULL };
|
|
|
|
// Get the hkeyProgID and hkeyBaseProgID from the first item.
|
|
SHRegOpenKey(HKEY_CLASSES_ROOT, c_szDriveClass, &ahkeys[1]);
|
|
SHRegOpenKey(HKEY_CLASSES_ROOT, c_szFolderClass, &ahkeys[0]);
|
|
|
|
// REVIEW: psb?
|
|
pszCaption = SHGetCaption(medium.hGlobal);
|
|
SHOpenPropSheet(pszCaption, ahkeys, 2,
|
|
&CLSID_ShellDrvDefExt,
|
|
pps->pdtobj, NULL, pps->pStartPage);
|
|
|
|
if (ahkeys[0])
|
|
SHRegCloseKey(ahkeys[0]);
|
|
if (ahkeys[1])
|
|
SHRegCloseKey(ahkeys[1]);
|
|
|
|
if (pszCaption)
|
|
SHFree(pszCaption);
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("no HIDA in data obj"));
|
|
}
|
|
|
|
pps->pdtobj->lpVtbl->Release(pps->pdtobj);
|
|
|
|
LocalFree((HLOCAL)pps);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// BUGBUG: we need to protect this with a try/except thing
|
|
|
|
HRESULT CDrives_Properties(LPDATAOBJECT pdtobj, LPCTSTR pStartPage)
|
|
{
|
|
HANDLE hThread;
|
|
DWORD idThread;
|
|
UINT cbStartPage = HIWORD(pStartPage) ? ((lstrlen(pStartPage)+1) * SIZEOF(TCHAR)) : 0 ;
|
|
DRIVEPROPSTUFF * pps = (void*)LocalAlloc(LPTR, SIZEOF(DRIVEPROPSTUFF) + cbStartPage);
|
|
if (pps)
|
|
{
|
|
pps->pdtobj = pdtobj;
|
|
pdtobj->lpVtbl->AddRef(pdtobj);
|
|
pps->pStartPage = pStartPage;
|
|
if (HIWORD(pStartPage))
|
|
{
|
|
pps->pStartPage = (LPTSTR)(pps+1);
|
|
lstrcpy((LPTSTR)(pps->pStartPage), pStartPage);
|
|
}
|
|
|
|
hThread = CreateThread(NULL, 0, _CDrives_PropertiesThread, pps, 0, &idThread);
|
|
|
|
if (hThread) {
|
|
CloseHandle(hThread);
|
|
return NOERROR;
|
|
} else {
|
|
pdtobj->lpVtbl->Release(pdtobj);
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
const TCHAR c_szVWIN32[] = TEXT("\\\\.\\vwin32");
|
|
|
|
|
|
#ifdef WINNT
|
|
// in:
|
|
// iDrive - 0 base drive number.
|
|
//
|
|
// returns:
|
|
// if function succeeds, it will return a handle
|
|
// to the drive, to be used in UnlockAndEject(), that should
|
|
// be closed with CloseHandle(). If the function fails, it
|
|
// will return NULL.
|
|
//
|
|
|
|
HANDLE LockDrive( int iDrive )
|
|
{
|
|
#define DRIVENAME_FORMAT TEXT("\\\\.\\A:")
|
|
TCHAR chDevName[ARRAYSIZE(DRIVENAME_FORMAT)];
|
|
HANDLE h;
|
|
BOOL fSuccess;
|
|
DWORD bytesRead;
|
|
|
|
lstrcpy(chDevName,DRIVENAME_FORMAT);
|
|
|
|
chDevName[4] = TEXT('A') + iDrive;
|
|
|
|
h = CreateFile(chDevName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return NULL;
|
|
|
|
//
|
|
// Now try to lock the drive...
|
|
//
|
|
fSuccess = DeviceIoControl(h, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesRead, NULL);
|
|
|
|
//
|
|
// In theory, if no filesystem was mounted, the previous command could go
|
|
// to the device, which would fail with ERROR_INVALID_FUNCTION. If that
|
|
// occured, we would still want to proceed, since the device might still
|
|
// be able to handle the IOCTL_DISK_EJECT_MEDIA command below.
|
|
//
|
|
if ((fSuccess == TRUE) || (GetLastError() == ERROR_INVALID_FUNCTION))
|
|
{
|
|
return h;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(h);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// in:
|
|
// h - handle to drive to eject
|
|
//
|
|
// returns:
|
|
// TRUE success
|
|
// FALSE failure
|
|
//
|
|
// Enables removal of the drive (unlocks it) and then sends an EJECT to the drive
|
|
//
|
|
BOOL UnlockAndEjectDrive( HANDLE h )
|
|
{
|
|
BOOL fSuccess;
|
|
DWORD bytesRead;
|
|
PREVENT_MEDIA_REMOVAL pmr;
|
|
|
|
// Tell the drive to allow removal
|
|
pmr.PreventMediaRemoval = FALSE;
|
|
if (FALSE != (fSuccess = DeviceIoControl(h, IOCTL_DISK_MEDIA_REMOVAL, &pmr, SIZEOF(pmr), NULL, 0, &bytesRead, NULL)))
|
|
{
|
|
// Now eject the drive...
|
|
fSuccess = DeviceIoControl(h, IOCTL_DISK_EJECT_MEDIA, NULL, 0, NULL, 0, &bytesRead, NULL);
|
|
}
|
|
CloseHandle(h);
|
|
|
|
return fSuccess;
|
|
}
|
|
#endif
|
|
|
|
// in:
|
|
// iDrive 0 based drive number
|
|
//
|
|
// returns:
|
|
// TRUE success
|
|
// FALSE failure
|
|
|
|
BOOL DriveIOCTL(int iDrive, int cmd, void *pvIn, DWORD dwIn, void *pvOut, DWORD dwOut)
|
|
{
|
|
#ifdef WINNT
|
|
#define DRIVENAME_FORMAT TEXT("\\\\.\\A:")
|
|
TCHAR chDevName[ARRAYSIZE(DRIVENAME_FORMAT)];
|
|
HANDLE h;
|
|
BOOL fSuccess;
|
|
DWORD bytesRead;
|
|
|
|
lstrcpy(chDevName,DRIVENAME_FORMAT);
|
|
|
|
chDevName[4] = TEXT('A') + iDrive;
|
|
|
|
h = CreateFile(chDevName,
|
|
0, // Having 0 here prevents disk spin-up
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
//
|
|
// On NT, we issue DEVIOCTLs by cmd id.
|
|
//
|
|
fSuccess = DeviceIoControl(h, cmd, pvIn, dwIn, pvOut, dwOut, &bytesRead, NULL);
|
|
CloseHandle(h);
|
|
|
|
return fSuccess;
|
|
#else
|
|
DWORD reg[7];
|
|
DWORD cbBytes;
|
|
HANDLE h;
|
|
|
|
//
|
|
// On non-NT, we talk to VWIN32, issuing reads (which are converted
|
|
// internally to DEVIOCTLs
|
|
//
|
|
// BUGBUG: this is a real hack (talking to VWIN32) on NT we can just
|
|
// open the device, we dont have to go through VWIN32
|
|
//
|
|
reg[0] = iDrive + 1; // make 1 based drive number
|
|
reg[1] = (DWORD)pvOut; // out buffer
|
|
reg[2] = cmd; // device specific command code
|
|
reg[3] = 0x440D; // generic read ioctl
|
|
reg[6] = 0x0001; // flags, assume error (carry)
|
|
|
|
h = CreateFile(c_szVWIN32, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
DeviceIoControl(h, 1, ®, SIZEOF(reg), ®, SIZEOF(reg), &cbBytes, 0);
|
|
CloseHandle(h);
|
|
}
|
|
|
|
return !(reg[6] & 0x0001);
|
|
#endif
|
|
}
|
|
|
|
// Above function callable from 16 bit thunk side...
|
|
BOOL SH16To32DriveIOCTL(int iDrive, int cmd, void *pv)
|
|
{
|
|
return DriveIOCTL(iDrive, cmd, NULL, 0, pv, 0);
|
|
}
|
|
|
|
#ifndef WINNT
|
|
// This function allows the 16 bit side to do int26 releasing win16 lock...
|
|
int SH16To32Int2526(int iDrive, int iInt, LPVOID pv, WORD count, DWORD ssector)
|
|
{
|
|
DIOC_REGISTERS reg;
|
|
DWORD cbBytes;
|
|
HANDLE h;
|
|
|
|
reg.reg_EAX = iDrive; // 0 based drive number
|
|
reg.reg_ECX = count; // Count of sectors
|
|
reg.reg_EDX = (DWORD)ssector; // which sector to write to
|
|
reg.reg_EBX = (DWORD)pv; // pointer to buffer to output
|
|
reg.reg_Flags = 0x0001; // flags, assume error (carry)
|
|
|
|
h = CreateFile(c_szVWIN32, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
DeviceIoControl(h, (iInt == 25)? VWIN32_DIOC_DOS_INT25 : VWIN32_DIOC_DOS_INT26,
|
|
®, SIZEOF(reg), ®, SIZEOF(reg), &cbBytes, 0);
|
|
CloseHandle(h);
|
|
}
|
|
|
|
return (reg.reg_Flags & 0x0001) ? (int)reg.reg_EAX : 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
#pragma pack(1)
|
|
// from dos\inc\ioctl.inc
|
|
typedef struct {
|
|
TCHAR dmiAllocationLength; // db ? ; length of the buffer provided by caller
|
|
TCHAR dmiInfoLength; // db ? ; length of information returned
|
|
TCHAR dmiFlags; // db ? ; DRIVE_MAP_INFO flags
|
|
TCHAR dmiInt13Unit; // db ? ; int 13 drive number. FFh if the drive
|
|
// ; does not map to an int 13 drive
|
|
DWORD dmiAssociatedDriveMap; // dd ? ; bit map of logical drive numbers that
|
|
// ; are associated with the given drive
|
|
// ; (i.e. parent/child volumes of compressed
|
|
// ; volume files)
|
|
DWORD dmiPartitionStartRBA[2]; // dq ? ; starting RBA offset of the given
|
|
// ; partition
|
|
} DRIVE_MAP_INFO;
|
|
#pragma pack()
|
|
|
|
// Flags definitions for dmiFlags
|
|
|
|
#define PROT_MODE_LOGICAL_DRIVE 0x01 // ; indicates a protect mode driver
|
|
// ; is in use for this logical drive
|
|
#define PROT_MODE_PHYSICAL_DRIVE 0x02 // ; indicates a protect mode driver
|
|
// ; is in use for the physical drive
|
|
// ; corresponding to this logical
|
|
// ; drive
|
|
#define PROT_MODE_ONLY_DRIVE 0x04 // ; indicates a drive is not available
|
|
// ; under DOS, only under IOS
|
|
#define PROT_MODE_EJECT 0x08 // ; indicates a protect mode drive
|
|
// ; supports electronic eject
|
|
// zero based drive leter A=0, B=1
|
|
BOOL IsEjectable(LPIDDRIVE pidd, BOOL fForceCDROM)
|
|
{
|
|
int iDrive;
|
|
#ifdef WINNT
|
|
DISK_GEOMETRY disk_g;
|
|
#endif
|
|
|
|
if (fForceCDROM && Drives_IsCD(pidd))
|
|
return(TRUE);
|
|
|
|
iDrive = CDrives_GetDriveIndex(pidd);
|
|
|
|
#ifdef WINNT
|
|
//
|
|
// Call down to get the drive's geometry. We can then see if it's a
|
|
// removeable (or ejectable) drive. We can't just use GetDriveType
|
|
// because it tells us that floppies are removeable, which they aren't
|
|
// (by software). Doing it this way gives us the information we need
|
|
// to make the correct determination
|
|
//
|
|
|
|
if (DriveIOCTL( iDrive,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL, 0,
|
|
&disk_g, SIZEOF(disk_g) )
|
|
)
|
|
{
|
|
return disk_g.MediaType == RemovableMedia;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#else
|
|
{
|
|
DRIVE_MAP_INFO dmi;
|
|
dmi.dmiAllocationLength = SIZEOF(dmi);
|
|
|
|
return DriveIOCTL(iDrive, 0x86F, NULL, 0, &dmi, SIZEOF(dmi)) && (dmi.dmiFlags & PROT_MODE_EJECT);
|
|
}
|
|
// avoid disk hit and say all removable media are ejectable
|
|
// return IsRemovableDrive(iDrive) || (LockUnlockDrive(iDrive, STATUS) == 0);
|
|
#endif
|
|
}
|
|
|
|
|
|
typedef MCIERROR (WINAPI *MCISENDSTRINGAFN)(LPCTSTR lpstrCommand, LPTSTR lpstrReturnString, UINT uReturnLength, HWND hwndCallback);
|
|
|
|
|
|
void EjectDrive(LPIDDRIVE pidd)
|
|
{
|
|
int iDrive;
|
|
TCHAR szMCI[256];
|
|
MCISENDSTRINGAFN pfnMciSendString;
|
|
#ifdef WINNT
|
|
HANDLE h;
|
|
TCHAR szVolume[ MAX_PATH ];
|
|
#endif
|
|
|
|
iDrive = CDrives_GetDriveIndex(pidd);
|
|
|
|
if (IsEjectable(pidd, FALSE))
|
|
{
|
|
// it is a protect mode drive that we can tell directly...
|
|
#ifdef WINNT
|
|
if (Drives_IsCD(pidd))
|
|
{
|
|
DriveIOCTL(iDrive, IOCTL_DISK_EJECT_MEDIA, NULL, 0, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
// For removable drives, we want to do all the calls on a single
|
|
// handle, thus we can't do lots of calls to DriveIOCTL. Instead,
|
|
// use the helper routines to do our work...
|
|
|
|
h = LockDrive( iDrive );
|
|
if (NULL == h)
|
|
{
|
|
Drives_GetDriveName( pidd, szVolume, ARRAYSIZE(szVolume) );
|
|
ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE( IDS_UNMOUNT_TEXT ),
|
|
MAKEINTRESOURCE( IDS_UNMOUNT_TITLE ),
|
|
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND, szVolume );
|
|
return;
|
|
}
|
|
|
|
if (!UnlockAndEjectDrive( h ))
|
|
{
|
|
Drives_GetDriveName( pidd, szVolume, ARRAYSIZE(szVolume) );
|
|
ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE( IDS_EJECT_TEXT ),
|
|
MAKEINTRESOURCE( IDS_EJECT_TITLE ),
|
|
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND, szVolume );
|
|
}
|
|
}
|
|
|
|
#else
|
|
DriveIOCTL(iDrive, 0x849, NULL, 0, NULL, 0);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// else we will let MCICDA try to eject it for us...
|
|
if (g_hinstWinMM == HINST_ERROR)
|
|
return; // Could not load Multimedia...
|
|
|
|
if (!g_hinstWinMM)
|
|
{
|
|
g_hinstWinMM = LoadLibrary(c_szWinMMDll);
|
|
if (!g_hinstWinMM)
|
|
{
|
|
g_hinstWinMM = HINST_ERROR;
|
|
return;
|
|
}
|
|
}
|
|
|
|
pfnMciSendString = (MCISENDSTRINGAFN)GetProcAddress(g_hinstWinMM,
|
|
c_szMciSendString);
|
|
|
|
if (!pfnMciSendString)
|
|
return;
|
|
|
|
wsprintf(szMCI, TEXT("Open %c: type cdaudio alias foo shareable"),
|
|
pidd->cName[0]);
|
|
|
|
if (pfnMciSendString(szMCI, NULL, 0, 0L) == MMSYSERR_NOERROR)
|
|
{
|
|
pfnMciSendString(TEXT("set foo door open"), NULL, 0, 0L);
|
|
pfnMciSendString(TEXT("close foo"), NULL, 0, 0L);
|
|
}
|
|
}
|
|
|
|
#ifdef WINNT
|
|
// in:
|
|
// iDrive 0 based drive number
|
|
//
|
|
// returns:
|
|
// TRUE success
|
|
// FALSE failure
|
|
//
|
|
// This is a variant of DriveIOCTL, currently used only by IsAudioDisc on NT.
|
|
// On NT, we use GENERIC_READ (as opposed to 0) in the CreateFile call, so we
|
|
// get a handle to the filesystem (CDFS), not the device itself. But we can't
|
|
// change DriveIOCTL to do this, since that causes the floppy disks to spin
|
|
// up, and we don't want to do that. Thus, the two very similar functions.
|
|
//
|
|
|
|
BOOL FileSystemIOCTL(int iDrive, int cmd, void *pvIn, DWORD dwIn, void *pvOut, DWORD dwOut)
|
|
{
|
|
#define DRIVENAME_FORMAT TEXT("\\\\.\\A:")
|
|
TCHAR chDevName[ARRAYSIZE(DRIVENAME_FORMAT)];
|
|
HANDLE h;
|
|
BOOL fSuccess;
|
|
DWORD bytesRead;
|
|
|
|
lstrcpy(chDevName,DRIVENAME_FORMAT);
|
|
|
|
chDevName[4] = TEXT('A') + iDrive;
|
|
|
|
h = CreateFile(chDevName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
//
|
|
// On NT, we issue DEVIOCTLs by cmd id.
|
|
//
|
|
fSuccess = DeviceIoControl(h, cmd, pvIn, dwIn, pvOut, dwOut, &bytesRead, NULL);
|
|
CloseHandle(h);
|
|
|
|
return fSuccess;
|
|
}
|
|
#endif
|
|
|
|
BOOL IsAudioDisc(int iDrive)
|
|
{
|
|
#ifdef WINNT
|
|
|
|
#define TRACK_TYPE_MASK 0x04
|
|
#define AUDIO_TRACK 0x00
|
|
#define DATA_TRACK 0x04
|
|
|
|
PCDROM_TOC pTOC;
|
|
INT nTracks;
|
|
INT iTrack;
|
|
BOOL fAudio;
|
|
|
|
// To be compatible with Win95, we'll only return TRUE from this
|
|
// function if the disc has ONLY audio tracks (and NO data tracks).
|
|
|
|
// BUGBUG: Post NT-SUR beta 1, we should consider adding a new
|
|
// DriveType flag for "contains data tracks" and revamp the commands
|
|
// available on a CD-ROM drive. The current code doesn't handle
|
|
// mixed audio/data and audio/autorun discs very usefully. --JonBe
|
|
|
|
// First try the new IOCTL which gives us a ULONG with bits indicating
|
|
// the presence of either/both data & audio tracks
|
|
|
|
CDROM_DISK_DATA data;
|
|
|
|
if (FileSystemIOCTL(iDrive, IOCTL_CDROM_DISK_TYPE, NULL, 0, &data, SIZEOF(data)))
|
|
{
|
|
if ((data.DiskData & CDROM_DISK_AUDIO_TRACK) && !(data.DiskData & CDROM_DISK_DATA_TRACK))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// else that failed, so try to look for audio tracks the old way, by
|
|
// looking throught the table of contents manually. Note that data tracks
|
|
// are supposed to be hidden in the TOC by CDFS now on mixed audio/data
|
|
// discs (at least if the data tracks follow the audio tracks).
|
|
|
|
pTOC = LocalAlloc(LPTR, SIZEOF(CDROM_TOC));
|
|
if (!pTOC)
|
|
return FALSE;
|
|
|
|
if (!FileSystemIOCTL(iDrive, IOCTL_CDROM_READ_TOC, NULL, 0, pTOC, SIZEOF(*pTOC)))
|
|
{
|
|
SUB_Q_CHANNEL_DATA subq;
|
|
CDROM_SUB_Q_DATA_FORMAT df;
|
|
|
|
LocalFree(pTOC);
|
|
|
|
//
|
|
// We might not have been able to read the TOC because the drive
|
|
// was busy playing audio. Lets try querying the audio position.
|
|
//
|
|
df.Format = IOCTL_CDROM_CURRENT_POSITION;
|
|
if (!DriveIOCTL(iDrive, IOCTL_CDROM_READ_Q_CHANNEL, &df, SIZEOF(df),
|
|
&subq, SIZEOF(subq)))
|
|
return FALSE; // No position data
|
|
else
|
|
return TRUE; // Yes, position data
|
|
}
|
|
|
|
// Now iterate through the tracks looking for Audio data
|
|
nTracks = (pTOC->LastTrack - pTOC->FirstTrack) + 1;
|
|
iTrack = 0;
|
|
fAudio = FALSE;
|
|
|
|
while (iTrack < nTracks)
|
|
{
|
|
if ((pTOC->TrackData[iTrack].Control & TRACK_TYPE_MASK) == AUDIO_TRACK)
|
|
{
|
|
fAudio = TRUE;
|
|
}
|
|
else if ((pTOC->TrackData[iTrack].Control & TRACK_TYPE_MASK) == DATA_TRACK)
|
|
{
|
|
fAudio = FALSE;
|
|
break;
|
|
}
|
|
iTrack++;
|
|
}
|
|
LocalFree(pTOC);
|
|
return fAudio;
|
|
#else
|
|
#pragma pack(1)
|
|
typedef struct {
|
|
WORD InfoLevel; // information level
|
|
DWORD SerialNum; // serial number
|
|
TCHAR VolLabel[11]; // ASCII volume label
|
|
TCHAR FileSysType[8]; // file system type
|
|
} MediaID;
|
|
#pragma pack()
|
|
|
|
MediaID mid;
|
|
|
|
Assert(RealDriveType(iDrive, FALSE /* fOkToHitNet */) == DRIVE_CDROM);
|
|
|
|
mid.FileSysType[0] = 0;
|
|
DriveIOCTL(iDrive, 0x0866, NULL, 0, &mid, SIZEOF(mid));
|
|
mid.FileSysType[7] = 0;
|
|
|
|
return lstrcmp(mid.FileSysType, c_szCDAUDIO) == 0;
|
|
#endif
|
|
}
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
const ICONMAP c_aicmpDrive[] = {
|
|
{ SHID_COMPUTER_REMOVABLE, II_DRIVEREMOVE },
|
|
{ SHID_COMPUTER_DRIVE525 , II_DRIVE525 },
|
|
{ SHID_COMPUTER_DRIVE35 , II_DRIVE35 },
|
|
{ SHID_COMPUTER_FIXED , II_DRIVEFIXED },
|
|
{ SHID_COMPUTER_REMOTE , II_DRIVEFIXED },
|
|
{ SHID_COMPUTER_CDROM , II_DRIVECD },
|
|
{ SHID_COMPUTER_RAMDISK , II_DRIVERAM },
|
|
};
|
|
#pragma data_seg()
|
|
|
|
|
|
int CDrives_GetDriveIndex(LPCIDDRIVE pidd)
|
|
{
|
|
TCHAR szRoot[4];
|
|
|
|
if ((pidd->bFlags & SHID_GROUPMASK) != SHID_COMPUTER)
|
|
return -1;
|
|
|
|
szRoot[0] = (TCHAR)(pidd->cName[0]);
|
|
szRoot[1] = TEXT(':');
|
|
szRoot[2] = TEXT('\\');
|
|
szRoot[3] = 0;
|
|
|
|
return PathGetDriveNumber(szRoot);
|
|
}
|
|
|
|
extern int SHCopyDisk(HWND hwnd, UINT nSrcDrive, UINT nDestDrive);
|
|
|
|
//
|
|
// To be called back from within CDefFolderMenu
|
|
//
|
|
HRESULT CALLBACK CDrives_DFMCallBack(LPSHELLFOLDER psf, HWND hwndView,
|
|
LPDATAOBJECT pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case DFM_MERGECONTEXTMENU:
|
|
//
|
|
// This is from JoeB. We should treat all the menuitems (Format, Eject, ...)
|
|
// as verbs.
|
|
//
|
|
#if 1
|
|
if (pdtobj)
|
|
#else
|
|
if (!(wParam & CMF_VERBSONLY) && !(wParam & CMF_DVFILE) && pdtobj)
|
|
#endif
|
|
{
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
|
|
// Check if only file system objects are selected.
|
|
if (pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte) == NOERROR)
|
|
{
|
|
LPIDDRIVE pidd;
|
|
int iDrive;
|
|
UINT idCmdBase;
|
|
|
|
#define pqcm ((LPQCMINFO)lParam)
|
|
|
|
STGMEDIUM medium;
|
|
// Yes, only file system objects are selected.
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
|
|
|
|
pidd = (LPIDDRIVE)IDA_GetIDListPtr(pida, 0);
|
|
iDrive = CDrives_GetDriveIndex(pidd);
|
|
|
|
idCmdBase = pqcm->idCmdFirst; // store it away
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DRIVES_ITEM, 0, pqcm);
|
|
|
|
if ((pidd->bFlags != SHID_COMPUTER_NETDRIVE) &&
|
|
(pidd->bFlags != SHID_COMPUTER_NETUNAVAIL))
|
|
{
|
|
DeleteMenu(pqcm->hmenu, idCmdBase + FSIDM_DISCONNECT, MF_BYCOMMAND);
|
|
}
|
|
|
|
if ((pida->cidl != 1) ||
|
|
(pidd->bFlags != SHID_COMPUTER_REMOVABLE &&
|
|
pidd->bFlags != SHID_COMPUTER_FIXED &&
|
|
pidd->bFlags != SHID_COMPUTER_DRIVE525 &&
|
|
pidd->bFlags != SHID_COMPUTER_DRIVE35))
|
|
{
|
|
// Don't even try to format more than one disk
|
|
// Or a net drive, or a CD-ROM, or a RAM drive ...
|
|
// Note we are going to show the Format command on the
|
|
// boot drive, Windows drive, System drive, compressed
|
|
// drives, etc. An appropriate error should be shown
|
|
// after the user chooses this command
|
|
DeleteMenu(pqcm->hmenu, idCmdBase + FSIDM_FORMAT, MF_BYCOMMAND);
|
|
}
|
|
|
|
if ((pida->cidl != 1) || (iDrive < 0) || !IsEjectable(pidd, TRUE))
|
|
DeleteMenu(pqcm->hmenu, idCmdBase + FSIDM_EJECT, MF_BYCOMMAND);
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
|
|
#undef pqcm
|
|
}
|
|
#ifndef WINNT
|
|
//
|
|
// WINNT doesn't support these dialogs.
|
|
//
|
|
else // Printers folder needs Un/Map Printer Port
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (medium.hGlobal)
|
|
{
|
|
UINT cidl = HIDA_GetCount(medium.hGlobal);
|
|
if (cidl == 1)
|
|
{
|
|
LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, 0);
|
|
LPCITEMIDLIST pidlPrinters = GetSpecialFolderIDList(NULL, CSIDL_PRINTERS, FALSE);
|
|
pidlPrinters = ILFindLastID(pidlPrinters);
|
|
|
|
if (ResultFromShort(0) ==
|
|
CRegItems_CompareIDs(g_psfDrives, 0, pidl, pidlPrinters))
|
|
{
|
|
// (iff network is installed)
|
|
if (GetSystemMetrics(SM_NETWORK) && RNC_NETWORKS)
|
|
{
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DRIVES_PRINTERS, 0, (LPQCMINFO)lParam);
|
|
}
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Note that we always return NOERROR from this function so that
|
|
// default processing of menu items will occur
|
|
Assert(hres == NOERROR);
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
|
|
break;
|
|
|
|
case DFM_GETHELPTEXTW:
|
|
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
// Yes.
|
|
switch(wParam)
|
|
{
|
|
case DFM_CMD_PROPERTIES:
|
|
// lParam contains the page name to open
|
|
hres = CDrives_Properties(pdtobj, (LPCTSTR)lParam);
|
|
break;
|
|
|
|
case FSIDM_EJECT:
|
|
case FSIDM_FORMAT:
|
|
{
|
|
LPIDDRIVE pidd;
|
|
UINT iDrive;
|
|
STGMEDIUM medium;
|
|
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
|
|
Assert(HIDA_GetCount(medium.hGlobal) == 1);
|
|
|
|
pidd = (LPIDDRIVE)IDA_GetIDListPtr(pida, 0);
|
|
|
|
iDrive = CDrives_GetDriveIndex(pidd);
|
|
|
|
Assert(iDrive >= 0);
|
|
|
|
switch (wParam) {
|
|
case FSIDM_FORMAT:
|
|
// SHCopyDisk(hwndView, 0, 0);
|
|
SHFormatDrive(hwndView, iDrive, SHFMT_ID_DEFAULT, 0);
|
|
break;
|
|
|
|
case FSIDM_EJECT:
|
|
EjectDrive(pidd);
|
|
break;
|
|
}
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
break;
|
|
}
|
|
|
|
case FSIDM_DISCONNECT:
|
|
|
|
if (pdtobj)
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (medium.hGlobal)
|
|
{
|
|
DISCDLGSTRUCT discd = {
|
|
SIZEOF(DISCDLGSTRUCT), // cbStructure
|
|
hwndView, // hwndOwner
|
|
NULL, // lpLocalName
|
|
NULL, // lpRemoteName
|
|
DISC_UPDATE_PROFILE // dwFlags
|
|
};
|
|
UINT iidl;
|
|
for (iidl = 0 ; iidl < pida->cidl ; iidl++)
|
|
{
|
|
LPIDDRIVE pidd = (LPIDDRIVE)IDA_GetIDListPtr(pida, iidl);
|
|
if ((pidd->bFlags == SHID_COMPUTER_NETDRIVE) ||
|
|
(pidd->bFlags == SHID_COMPUTER_NETUNAVAIL))
|
|
{
|
|
BOOL fUnavailable = IsUnavailableNetDrive(
|
|
CDrives_GetDriveIndex(pidd));
|
|
|
|
TCHAR szDrive[4];
|
|
#ifdef WINNT
|
|
TCHAR szPath[4];
|
|
szDrive[0] = (TCHAR)pidd->cName[0];
|
|
szDrive[1] = TEXT(':');
|
|
szDrive[2] = TEXT('\0');
|
|
|
|
szPath[0] = szDrive[0];
|
|
szPath[1] = szDrive[1];
|
|
szPath[2] = TEXT('\\');
|
|
szPath[3] = TEXT('\0');
|
|
#else // WINNT
|
|
szDrive[0] = (TCHAR)pidd->cName[0];
|
|
szDrive[1] = (TCHAR)pidd->cName[1];
|
|
szDrive[2] = (TCHAR)pidd->cName[2];
|
|
szDrive[3] = (TCHAR)pidd->cName[3];
|
|
#endif // WINNT
|
|
discd.lpLocalName = szDrive;
|
|
if (WNetDisconnectDialog1(&discd) == WN_SUCCESS)
|
|
{
|
|
// If it is a unavailable drive we get no
|
|
// file system notification and as such
|
|
// the drive will not disappear, so lets
|
|
// set up to do it ourself...
|
|
if (fUnavailable)
|
|
{
|
|
SHChangeNotify(
|
|
SHCNE_DRIVEREMOVED,
|
|
SHCNF_PATH,
|
|
#ifdef WINNT
|
|
szPath,
|
|
#else // WINNT
|
|
szDrive,
|
|
#endif // WINNT
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// flush them altogether
|
|
SHChangeNotifyHandleEvents();
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FSIDM_CONNECT_PRN:
|
|
SHNetConnectionDialog(hwndView, NULL, RESOURCETYPE_PRINT);
|
|
break;
|
|
|
|
case FSIDM_DISCONNECT_PRN:
|
|
WNetDisconnectDialog(hwndView, RESOURCETYPE_PRINT);
|
|
break;
|
|
|
|
default:
|
|
// This is one of view menu items, use the default code.
|
|
hres = S_FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
void CDrives_GetKeys(LPCIDDRIVE pidd, HKEY *keys)
|
|
{
|
|
keys[0] = NULL;
|
|
keys[1] = NULL;
|
|
keys[2] = NULL;
|
|
|
|
if (Drives_IsReg(pidd))
|
|
{
|
|
RegItems_GetClassKeys(g_psfDrives, (LPITEMIDLIST)pidd, &keys[0], &keys[1]);
|
|
return;
|
|
}
|
|
|
|
if (Drives_IsAudioCD((LPITEMIDLIST)pidd))
|
|
{
|
|
SHRegOpenKey(HKEY_CLASSES_ROOT, c_szAudioCDClass, &keys[0]);
|
|
}
|
|
else if (Drives_IsAutoRun(pidd))
|
|
{
|
|
TCHAR szAutoRunKey[40];
|
|
wsprintf(szAutoRunKey, c_szAutoRunD, CDrives_GetDriveIndex((LPIDDRIVE)pidd));
|
|
SHRegOpenKey(HKEY_CLASSES_ROOT, szAutoRunKey, &keys[0]);
|
|
Assert(keys[0]);
|
|
}
|
|
|
|
SHRegOpenKey(HKEY_CLASSES_ROOT, c_szDriveClass, &keys[1]);
|
|
SHRegOpenKey(HKEY_CLASSES_ROOT, c_szFolderClass, &keys[2]);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrives_GetUIObjectOf(LPSHELLFOLDER psf, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
|
|
REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
|
|
{
|
|
HRESULT hres=E_INVALIDARG;
|
|
LPCITEMIDLIST pidl;
|
|
IDDRIVE pidDrive;
|
|
HKEY keys[3];
|
|
|
|
if (cidl == 0)
|
|
{
|
|
return hres;
|
|
}
|
|
|
|
pidl = apidl[0];
|
|
|
|
// BUGBUG: Not fully implemented.
|
|
// We need to handle the case of a simply pidl being passed
|
|
// in to us by first converting it to the real pidl for
|
|
// the drive...
|
|
if (SIL_GetType(pidl) == SHID_COMPUTER_MISC)
|
|
{
|
|
pidDrive.cb=SIZEOF(pidDrive);
|
|
pidDrive.cName[0] = ((LPIDDRIVE)pidl)->cName[0];
|
|
pidDrive.cName[1] = TEXT(':');
|
|
pidDrive.cName[2] = TEXT('\0');
|
|
pidDrive.bFlags = CDrives_GetDriveType(
|
|
CDrives_GetDriveIndex((LPIDDRIVE)pidl));
|
|
pidl = (LPCITEMIDLIST)&pidDrive;
|
|
}
|
|
|
|
|
|
// We should not get here unless we have initialized properly
|
|
|
|
if (IsEqualIID(riid, &IID_IExtractIcon)
|
|
#ifdef UNICODE
|
|
|| IsEqualIID(riid, &IID_IExtractIconA)
|
|
#endif
|
|
)
|
|
{
|
|
if (cidl==1 && CDrives_IsValidID(pidl))
|
|
{
|
|
UINT iIndex;
|
|
|
|
switch (SIL_GetType(pidl))
|
|
{
|
|
case SHID_COMPUTER_CDROM:
|
|
if (Drives_IsAudioCD(pidl))
|
|
iIndex = II_CDAUDIO;
|
|
else
|
|
iIndex = II_DRIVECD;
|
|
|
|
break;
|
|
|
|
case SHID_COMPUTER_NETDRIVE:
|
|
case SHID_COMPUTER_NETUNAVAIL:
|
|
if (Drives_IsNetUnAvail(pidl))
|
|
iIndex = II_DRIVENETDISABLED;
|
|
else
|
|
iIndex = II_DRIVENET;
|
|
break;
|
|
|
|
default:
|
|
iIndex = SILGetIconIndex(pidl, c_aicmpDrive, ARRAYSIZE(c_aicmpDrive));
|
|
}
|
|
|
|
CDrives_GetKeys((LPCIDDRIVE)pidl, keys);
|
|
|
|
hres = SHCreateDefExtIconKey(keys[0], // use DefaultIcon in this key
|
|
NULL, // This DLL
|
|
iIndex, // normal icon
|
|
iIndex, // open icon
|
|
GIL_PERCLASS, // meaningless
|
|
(LPEXTRACTICON *)ppvOut);
|
|
#ifdef UNICODE
|
|
if (SUCCEEDED(hres) && IsEqualIID(riid, &IID_IExtractIconA))
|
|
{
|
|
LPEXTRACTICON pxicon = *ppvOut;
|
|
hres = pxicon->lpVtbl->QueryInterface(pxicon,riid,ppvOut);
|
|
pxicon->lpVtbl->Release(pxicon);
|
|
}
|
|
#endif
|
|
|
|
if ( keys[0] )
|
|
SHCloseClassKey(keys[0]);
|
|
if ( keys[1] )
|
|
SHCloseClassKey(keys[1]);
|
|
if ( keys[2] )
|
|
SHCloseClassKey(keys[2]);
|
|
}
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IContextMenu))
|
|
{
|
|
CDrives_GetKeys((LPCIDDRIVE)pidl, keys);
|
|
|
|
Assert(g_psfDrives)
|
|
hres = CDefFolderMenu_Create2((LPCITEMIDLIST)&c_idlDrives, hwndOwner,
|
|
cidl, apidl, g_psfDrives, CDrives_DFMCallBack,
|
|
3, keys, (LPCONTEXTMENU *)ppvOut);
|
|
|
|
SHCloseClassKey(keys[0]);
|
|
SHCloseClassKey(keys[1]);
|
|
SHCloseClassKey(keys[2]);
|
|
}
|
|
else if (cidl>0 && IsEqualIID(riid, &IID_IDataObject))
|
|
{
|
|
hres = FS_CreateFSIDArray((LPCITEMIDLIST)&c_idlDrives, cidl, apidl, NULL, (LPDATAOBJECT*)ppvOut);
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IDropTarget))
|
|
{
|
|
// IDropTarget must be a single object.
|
|
if (cidl == 1)
|
|
{
|
|
LPSHELLFOLDER psfT;
|
|
hres = CRegItems_BindToObject(g_psfDrives, apidl[0], NULL, &IID_IShellFolder, &psfT);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = psfT->lpVtbl->CreateViewObject(psfT,
|
|
hwndOwner, &IID_IDropTarget, ppvOut);
|
|
psfT->lpVtbl->Release(psfT);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrives_GetDisplayNameOf(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, DWORD uFlags,
|
|
LPSTRRET pStrRet)
|
|
{
|
|
HRESULT hres=E_INVALIDARG;
|
|
LPCIDDRIVE pidd = (LPCIDDRIVE)pidl;
|
|
|
|
if (CDrives_IsValidID(pidl))
|
|
{
|
|
// Check if pidl contains more than one ID
|
|
LPCITEMIDLIST pidlNext = _ILNext(pidl);
|
|
if (!(uFlags & SHGDN_FORPARSING))
|
|
{
|
|
if (ILIsEmpty(pidlNext))
|
|
{
|
|
hres = Drives_GetName(pidd, pStrRet);
|
|
}
|
|
else
|
|
{
|
|
#ifdef UNICODE
|
|
TCHAR szTmp[MAX_PATH];
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
pidd->cName, -1,
|
|
szTmp, ARRAYSIZE(szTmp));
|
|
hres = ILGetRelDisplayName(psf, pStrRet, pidl, szTmp, NULL);
|
|
#else
|
|
hres = ILGetRelDisplayName(psf, pStrRet, pidl, pidd->cName, NULL);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = SHGetPathHelper((LPCITEMIDLIST)&c_idlDrives, pidl, pStrRet);
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrives_SetNameOf(LPSHELLFOLDER psf, HWND hwndOwner,
|
|
LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwReserved,
|
|
LPITEMIDLIST * ppidlOut)
|
|
{
|
|
if (ppidlOut) {
|
|
*ppidlOut = NULL;
|
|
}
|
|
|
|
return E_NOTIMPL; // not supported
|
|
}
|
|
|
|
#define GROUPOF_IDL(pidl) (SIL_GetType(pidl) & SHID_GROUPMASK)
|
|
#define IS_FSIDL(pidl) (GROUPOF_IDL(pidl)==SHID_FS)
|
|
#define IS_DRIVEIDL(pidl) (GROUPOF_IDL(pidl)==SHID_COMPUTER)
|
|
#define IS_PATHIDL(pidl) (IS_FSIDL(pidl) || IS_DRIVEIDL(pidl))
|
|
|
|
//===========================================================================
|
|
// CDrives : Helper API
|
|
//===========================================================================
|
|
|
|
//===========================================================================
|
|
// Removeable drive detectoin code
|
|
//===========================================================================
|
|
//
|
|
// Helper function to get the device type for the specified drive
|
|
//
|
|
#define DEVPB_DEVTYP_525_0360 0
|
|
#define DEVPB_DEVTYP_525_1200 1
|
|
#define DEVPB_DEVTYP_350_0720 2
|
|
#define DEVPB_DEVTYP_350_1440 7
|
|
#define DEVPB_DEVTYP_350_2880 9
|
|
#define DEVPB_DEVTYP_FIXED 5
|
|
#define DEVPB_DEVTYP_NECHACK 4 // for 3rd FE floppy
|
|
#define DEVPB_DEVTYP_350_120M 6
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Helper function for 32 bit cabinet to see which type device
|
|
// a particular drive is
|
|
//
|
|
// Note: this is temporary and will be replaced by a more general
|
|
// IOCTL support by Kernel32
|
|
//
|
|
BYTE _GetDiskDeviceType(int iDrive)
|
|
{
|
|
#ifdef WINNT
|
|
DISK_GEOMETRY SupportedGeometry[20]; // s/b big enough for all
|
|
BYTE byte;
|
|
|
|
if (DriveIOCTL(iDrive, IOCTL_DISK_GET_MEDIA_TYPES, NULL, 0,
|
|
SupportedGeometry, SIZEOF(SupportedGeometry)))
|
|
{
|
|
switch(SupportedGeometry[0].MediaType)
|
|
{
|
|
case F5_1Pt2_512: byte = DEVPB_DEVTYP_525_1200; break;
|
|
case F3_1Pt44_512: byte = DEVPB_DEVTYP_350_1440; break;
|
|
case F3_2Pt88_512: byte = DEVPB_DEVTYP_350_2880; break;
|
|
case F3_20Pt8_512: byte = DEVPB_DEVTYP_350_2880; break; // Hack
|
|
case F3_720_512: byte = DEVPB_DEVTYP_350_0720; break;
|
|
case F5_360_512: byte = DEVPB_DEVTYP_525_0360; break;
|
|
case F5_320_512: byte = DEVPB_DEVTYP_525_0360; break; // Hack
|
|
case F5_320_1024: byte = DEVPB_DEVTYP_525_0360; break; // Hack
|
|
case F5_180_512: byte = DEVPB_DEVTYP_525_0360; break; // Hack
|
|
case F5_160_512: byte = DEVPB_DEVTYP_525_0360; break; // Hack
|
|
case RemovableMedia: byte = 0xFF; break;
|
|
case FixedMedia: byte = DEVPB_DEVTYP_FIXED; break;
|
|
case F3_120M_512: byte = DEVPB_DEVTYP_350_120M; break;
|
|
default: byte = 0xFF; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
byte = 0xFF;
|
|
}
|
|
return byte;
|
|
#else
|
|
#define MAX_SEC_PER_TRACK 64
|
|
#pragma pack(1)
|
|
typedef struct {
|
|
BYTE SplFunctions;
|
|
BYTE devType;
|
|
WORD devAtt;
|
|
WORD NumCyls;
|
|
BYTE bMediaType; /* 0=>1.2MB and 1=>360KB */
|
|
WORD cbSec; // Bytes per sector
|
|
BYTE secPerClus; // Sectors per cluster
|
|
WORD cSecRes; // Reserved sectors
|
|
BYTE cFAT; // FATS
|
|
WORD cDir; // Root Directory Entries
|
|
WORD cSec; // Total number of sectors in image
|
|
BYTE bMedia; // Media descriptor
|
|
WORD secPerFAT; // Sectors per FAT
|
|
WORD secPerTrack; // Sectors per track
|
|
WORD cHead; // Heads
|
|
WORD cSecHidden; // Hidden sectors
|
|
WORD cSecHidden_HiWord; // The high word of no of hidden sectors
|
|
DWORD cTotalSectors; // Total sectors, if BPB_cSec is zero
|
|
BYTE A_BPB_Reserved[6]; // Unused 6 BPB bytes
|
|
BYTE TrackLayout[MAX_SEC_PER_TRACK * 4 + 2];
|
|
} DevPB;
|
|
#pragma pack()
|
|
|
|
DevPB devpb;
|
|
|
|
devpb.SplFunctions = 0; // Get for the default one (don't hit the drive!)
|
|
|
|
if (!DriveIOCTL(iDrive, 0x860, NULL, 0, &devpb, SIZEOF(devpb)))
|
|
return 0xFF;
|
|
|
|
return devpb.devType;
|
|
#endif
|
|
}
|
|
|
|
BYTE _GetDeviceType(int iDrive)
|
|
{
|
|
static BYTE s_bDeviceTypes[26] = "??????????????????????????";
|
|
|
|
if (iDrive < 0 || iDrive >= 26)
|
|
return 0xff;
|
|
|
|
// See if we have already cached out the device type for this drive number
|
|
|
|
if (s_bDeviceTypes[iDrive] != '?')
|
|
return s_bDeviceTypes[iDrive];
|
|
// Call off to 16 bit code to get this for us!
|
|
return s_bDeviceTypes[iDrive] = _GetDiskDeviceType(iDrive);
|
|
}
|
|
|
|
// REVIEW: We could cache the uType directly.
|
|
UINT CDrives_GetDriveType(int iDrive)
|
|
{
|
|
UINT uType;
|
|
|
|
#ifdef DEBUG
|
|
#define MSEC_MAXDELAY 2000
|
|
DWORD dwTick = GetCurrentTime();
|
|
DWORD dwDelay;
|
|
#endif
|
|
|
|
Assert(iDrive>=0 && iDrive<26);
|
|
|
|
uType = (SHID_COMPUTER | RealDriveType(iDrive, FALSE /* fOKToHitNet */ ));
|
|
|
|
#ifdef DEBUG
|
|
dwDelay = GetCurrentTime()-dwTick;
|
|
if (dwDelay > MSEC_MAXDELAY) {
|
|
DebugMsg(DM_WARNING, TEXT("sh WA - GetDeviceType(%d) took %d msec!"), iDrive, dwDelay);
|
|
}
|
|
#endif
|
|
|
|
switch (uType)
|
|
{
|
|
case SHID_COMPUTER_REMOVABLE:
|
|
{
|
|
switch (_GetDeviceType(iDrive)) {
|
|
case DEVPB_DEVTYP_525_0360:
|
|
case DEVPB_DEVTYP_525_1200:
|
|
case DEVPB_DEVTYP_NECHACK:
|
|
uType = SHID_COMPUTER_DRIVE525;
|
|
break;
|
|
|
|
case DEVPB_DEVTYP_350_0720:
|
|
case DEVPB_DEVTYP_350_1440:
|
|
case DEVPB_DEVTYP_350_2880:
|
|
case DEVPB_DEVTYP_350_120M:
|
|
uType = SHID_COMPUTER_DRIVE35;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SHID_COMPUTER_CDROM:
|
|
case SHID_COMPUTER_FIXED:
|
|
case SHID_COMPUTER_RAMDISK:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
TCHAR szDrive[4];
|
|
TCHAR szRemoteName[MAX_PATH];
|
|
DWORD dwRemoteName = ARRAYSIZE(szRemoteName);
|
|
DWORD dwConnectType;
|
|
#ifdef DEBUG
|
|
dwTick = GetCurrentTime();
|
|
#endif
|
|
// NB Vines (and PCNFS) doesn't like it when we pass "c:\"
|
|
// to WNetGetConnection() - they want just c: so don't use
|
|
// PathBuildRoot()
|
|
szDrive[0] = (TCHAR)iDrive + (TCHAR)TEXT('A');
|
|
szDrive[1] = TEXT(':');
|
|
szDrive[2] = 0;
|
|
|
|
dwConnectType = WNetGetConnection(szDrive, szRemoteName, &dwRemoteName);
|
|
|
|
if (dwConnectType == NO_ERROR)
|
|
{
|
|
// DebugMsg(DM_TRACE, "c.cd_gdt: Drive %s is a net drive.", szDrive);
|
|
uType = SHID_COMPUTER_NETDRIVE;
|
|
}
|
|
else if (dwConnectType == ERROR_CONNECTION_UNAVAIL)
|
|
{
|
|
// DebugMsg(DM_TRACE, "c.cd_gdt: Drive %s is an unavailable net drive.", szDrive);
|
|
// Don't cache this in the pidl as it can change!
|
|
// uType = SHID_COMPUTER_NETUNAVAIL;
|
|
uType = SHID_COMPUTER_NETDRIVE;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
dwDelay = GetCurrentTime()-dwTick;
|
|
if (dwDelay > MSEC_MAXDELAY)
|
|
{
|
|
DebugMsg(DM_WARNING, TEXT("sh WA - WNetGetConnection(%s) took %d msec!"), szDrive, dwDelay);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return uType;
|
|
}
|
|
|
|
|
|
//
|
|
// A full path is required
|
|
//
|
|
HRESULT Drives_SimpleIDListFromPath(LPCTSTR pszPath, LPITEMIDLIST *ppidl)
|
|
{
|
|
int iDrive;
|
|
//
|
|
// WARNING: This pragma is very important. Bug 19596 (page fault in
|
|
// ILGetSize) was caused by missing pragma.
|
|
//
|
|
#pragma pack(1)
|
|
struct
|
|
{
|
|
IDDRIVE idd;
|
|
USHORT cbNext;
|
|
} iddl =
|
|
{
|
|
{ SIZEOF(IDDRIVE), SHID_COMPUTER_MISC,
|
|
{ 'a', ':', '\\', '\0' }, }, 0,
|
|
};
|
|
#pragma pack()
|
|
|
|
LPITEMIDLIST pidl, pidlRight;
|
|
HRESULT hres;
|
|
|
|
if (PathIsUNC(pszPath))
|
|
return(E_INVALIDARG);
|
|
|
|
if (pszPath[1]!=TEXT(':') || pszPath[2]!=TEXT('\\'))
|
|
return(E_INVALIDARG);
|
|
|
|
iDrive = PathGetDriveNumber(pszPath);
|
|
if (iDrive == -1)
|
|
return(E_INVALIDARG);
|
|
|
|
iddl.idd.cName[0] = iDrive + TEXT('A'); // make sure it's upcased
|
|
// a simple PIDL must have a "unknown" type
|
|
//iddl.idd.bFlags = CDrives_GetDriveType(iDrive);
|
|
|
|
if (pszPath[3] == TEXT('\0'))
|
|
{
|
|
pidl = ILClone((LPITEMIDLIST)&iddl);
|
|
if (!pidl)
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
goto GetOut;
|
|
}
|
|
|
|
// Skip "A:\\" and get the IDList from FSTree
|
|
|
|
hres = FSTree_SimpleIDListFromPath(pszPath+3, &pidlRight);
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
return(hres);
|
|
}
|
|
|
|
pidl = ILAppendID(pidlRight, (LPCSHITEMID)&iddl.idd, FALSE);
|
|
if (!pidl)
|
|
{
|
|
ILFree(pidlRight);
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
GetOut:
|
|
*ppidl = pidl;
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
//
|
|
// Invalidate the drive cache entry for the specified Drive
|
|
void InvalidateDriveNameCache(int iDrive)
|
|
{
|
|
if (iDrive < 26) { // sanity check
|
|
int iEnd = iDrive;
|
|
|
|
if (iDrive < 0) {
|
|
iDrive = 0;
|
|
iEnd = 25;
|
|
}
|
|
ENTERCRITICAL
|
|
|
|
for (; iDrive <= iEnd; iDrive++)
|
|
{
|
|
if (g_rgpszDriveNames[iDrive])
|
|
{
|
|
#ifdef DRIVE_CACHE_PER_PROCESS
|
|
LocalFree((HLOCAL)g_rgpszDriveNames[iDrive]);
|
|
#else
|
|
SHFree(g_rgpszDriveNames[iDrive]);
|
|
#endif
|
|
g_rgpszDriveNames[iDrive] = NULL;
|
|
#ifdef WNGC_DISCONNECTED
|
|
g_rdwDisconnectTick[iDrive] = 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
LEAVECRITICAL
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// get the friendly name for a given drive thing
|
|
//
|
|
// for example:
|
|
// Floppy (A:)
|
|
// Volume Name (D:)
|
|
// User on 'Pyrex' (V:)
|
|
// Dist on Strike\sys\public (Netware case)
|
|
|
|
HRESULT Drives_GetDriveName(LPCIDDRIVE pidd, LPTSTR lpszName, UINT cchMax)
|
|
{
|
|
TCHAR szVol[MAX_PATH];
|
|
LPCTSTR pszFormat;
|
|
LPTSTR pszMsg;
|
|
LPTSTR pszShare = NULL;
|
|
LPTSTR pszServer = NULL;
|
|
int iDrive = DRIVEID(pidd->cName);
|
|
VDATEINPUTBUF(lpszName, TCHAR, cchMax);
|
|
|
|
pszFormat = MAKEINTRESOURCE(IDS_VOL_FORMAT);
|
|
|
|
// First See if we have a cached version
|
|
ENTERCRITICAL
|
|
if (g_rgpszDriveNames[iDrive])
|
|
{
|
|
lstrcpyn(lpszName, g_rgpszDriveNames[iDrive], cchMax);
|
|
LEAVECRITICAL
|
|
return NOERROR;
|
|
}
|
|
// I will temporarily leave it as to not hold things up for long times
|
|
// when possibly hitting the net...
|
|
LEAVECRITICAL
|
|
|
|
if (ShowDriveInfo(iDrive))
|
|
{
|
|
DWORD cch = ARRAYSIZE(szVol);
|
|
TCHAR szDriveName[4];
|
|
DWORD dwResult;
|
|
BYTE bType = pidd->bFlags & SHID_TYPEMASK;
|
|
|
|
//
|
|
// The drive name is always ANSI, so on Unicode builds we convert it to Unicode
|
|
// for use in the WNetGetConnection API. For ANSI, we just do a strcpy, which
|
|
// is totally redundant, but if people want to write if clauses with 6 parts
|
|
// spanning 5 lines, that's what they get... I'm not ifdef'ing that stuff
|
|
//
|
|
|
|
#ifdef UNICODE
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pidd->cName, -1, szDriveName, ARRAYSIZE(szDriveName));
|
|
|
|
#else
|
|
|
|
lstrcpy(szDriveName, pidd->cName);
|
|
|
|
#endif
|
|
|
|
// Stomp out the backslash
|
|
|
|
szDriveName[2] = TEXT('\0');
|
|
|
|
// We need to handle both connected and unconnected volumes however
|
|
// for unconncected volumes we have to be careful as a real drive
|
|
// may have taken it's spot. Ie a new CDROM or the like.
|
|
// I hate this long if, but...
|
|
|
|
if ( ((bType == SHID_COMPUTER_NETDRIVE) ||
|
|
(bType == SHID_COMPUTER_NETUNAVAIL) ||
|
|
(bType == SHID_COMPUTER_MISC))
|
|
&& ( ((dwResult = WNetGetConnection(szDriveName, szVol, &cch))
|
|
== NO_ERROR) || (dwResult == ERROR_CONNECTION_UNAVAIL)))
|
|
{
|
|
if (PathIsUNC(szVol))
|
|
{
|
|
LPTSTR pszT;
|
|
// 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
|
|
pszT = StrChr(szVol, TEXT(' '));
|
|
while (pszT)
|
|
{ pszT++;
|
|
if (*pszT == TEXT('\\'))
|
|
{
|
|
// The netware case of \\strike\sys \public\dist
|
|
*--pszT = TEXT('\0');
|
|
break;
|
|
}
|
|
|
|
pszT = StrChr(pszT, TEXT(' '));
|
|
}
|
|
|
|
// The Strrchr should never fail as this is a UNC which
|
|
// checked the first two characters for \'s
|
|
|
|
pszShare = StrRChr(szVol, NULL, TEXT('\\'));
|
|
*pszShare++ = 0;
|
|
PathMakePretty(pszShare);
|
|
|
|
// PszServer should always start at char 2.
|
|
if (szVol[2] != TEXT('\0'))
|
|
{
|
|
LPTSTR pszSlash;
|
|
pszServer = &szVol[2];
|
|
pszFormat = MAKEINTRESOURCE(IDS_UNC_FORMAT);
|
|
|
|
for (pszT=pszServer; pszT != NULL; pszT = pszSlash)
|
|
{
|
|
pszSlash = StrChr(pszT, TEXT('\\'));
|
|
if (pszSlash)
|
|
{
|
|
*pszSlash = TEXT('\0');
|
|
}
|
|
PathMakePretty(pszT);
|
|
if (pszSlash)
|
|
*pszSlash++ = TEXT('\\');
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we are going to call GetVolumeInformation() so might as well
|
|
// get the drive flags now. this prevents CD-ROM changers from
|
|
// hiting all the volumes twice.
|
|
//
|
|
DriveTypeFlags(iDrive);
|
|
szVol[0] = TEXT('\0'); // Handle case of no label.
|
|
|
|
#ifdef UNICODE
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
pidd->cName, -1,
|
|
szPath, ARRAYSIZE(szPath));
|
|
GetVolumeInformation(szPath, szVol, ARRAYSIZE(szVol),
|
|
NULL, NULL, NULL, NULL, 0);
|
|
}
|
|
#else
|
|
GetVolumeInformation(pidd->cName, szVol, ARRAYSIZE(szVol),
|
|
NULL, NULL, NULL, NULL, 0);
|
|
#endif
|
|
PathMakePretty(szVol);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int idsDrive;
|
|
|
|
switch (_GetDeviceType(iDrive)) {
|
|
case DEVPB_DEVTYP_525_0360:
|
|
case DEVPB_DEVTYP_525_1200:
|
|
CHECKFORUGLYNAMES;
|
|
idsDrive = s_fShowUglyDriveNames ?
|
|
IDS_525_FLOPPY_DRIVE_UGLY : IDS_525_FLOPPY_DRIVE;
|
|
break;
|
|
case DEVPB_DEVTYP_350_0720:
|
|
case DEVPB_DEVTYP_350_1440:
|
|
case DEVPB_DEVTYP_350_2880:
|
|
case DEVPB_DEVTYP_350_120M:
|
|
CHECKFORUGLYNAMES;
|
|
idsDrive = s_fShowUglyDriveNames ?
|
|
IDS_35_FLOPPY_DRIVE_UGLY : IDS_35_FLOPPY_DRIVE;
|
|
break;
|
|
default:
|
|
idsDrive = IDS_UNK_FLOPPY_DRIVE;
|
|
break;
|
|
}
|
|
|
|
pszFormat = MAKEINTRESOURCE(idsDrive);
|
|
}
|
|
|
|
pszMsg = ShellConstructMessageString(HINST_THISDLL, pszFormat,
|
|
pidd->cName[0], szVol, pszShare, pszServer);
|
|
if (pszMsg) {
|
|
lstrcpyn(lpszName, pszMsg, cchMax);
|
|
|
|
// Save away the cached name to use later...
|
|
ENTERCRITICAL
|
|
#ifdef DRIVE_CACHE_PER_PROCESS
|
|
if (g_rgpszDriveNames[iDrive])
|
|
LocalFree(g_rgpszDriveNames[iDrive]);
|
|
g_rgpszDriveNames[iDrive] = LocalAlloc(LPTR, (lstrlen(pszMsg)+1) * SIZEOF(TCHAR));
|
|
if (g_rgpszDriveNames[iDrive])
|
|
lstrcpy(g_rgpszDriveNames[iDrive], pszMsg);
|
|
LEAVECRITICAL
|
|
|
|
SHFree(pszMsg);
|
|
#else
|
|
if (g_rgpszDriveNames[iDrive])
|
|
SHFree(g_rgpszDriveNames[iDrive]);
|
|
g_rgpszDriveNames[iDrive] = pszMsg;
|
|
LEAVECRITICAL
|
|
#endif
|
|
return NOERROR;
|
|
} else {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
// CFSDetails : Vtable
|
|
//===========================================================================
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
struct _DRIVECOLS
|
|
{
|
|
UINT uID;
|
|
int fmt;
|
|
int cxChar;
|
|
} s_drives_cols[] =
|
|
{
|
|
{ IDS_DRIVES_NAME , LVCFMT_LEFT , 20, },
|
|
{ IDS_DRIVES_TYPE , LVCFMT_LEFT , 25, },
|
|
{ IDS_DRIVES_SIZE , LVCFMT_RIGHT, 15, },
|
|
{ IDS_DRIVES_FREE , LVCFMT_RIGHT, 15, },
|
|
} ;
|
|
|
|
struct _DRIVETYPE
|
|
{
|
|
BYTE bType;
|
|
UINT uID;
|
|
UINT uIDUgly;
|
|
} c_drives_type[] =
|
|
{
|
|
{ SHID_COMPUTER_REMOVABLE, IDS_DRIVES_REMOVABLE , IDS_DRIVES_REMOVABLE },
|
|
{ SHID_COMPUTER_DRIVE525 , IDS_DRIVES_DRIVE525 , IDS_DRIVES_DRIVE525_UGLY },
|
|
{ SHID_COMPUTER_DRIVE35 , IDS_DRIVES_DRIVE35 , IDS_DRIVES_DRIVE35_UGLY },
|
|
{ SHID_COMPUTER_FIXED , IDS_DRIVES_FIXED , IDS_DRIVES_FIXED },
|
|
{ SHID_COMPUTER_REMOTE , IDS_DRIVES_REMOTE , IDS_DRIVES_REMOTE },
|
|
{ SHID_COMPUTER_CDROM , IDS_DRIVES_CDROM , IDS_DRIVES_CDROM },
|
|
{ SHID_COMPUTER_RAMDISK , IDS_DRIVES_RAMDISK , IDS_DRIVES_RAMDISK },
|
|
{ SHID_COMPUTER_NETDRIVE , IDS_DRIVES_NETDRIVE , IDS_DRIVES_NETDRIVE },
|
|
{ SHID_COMPUTER_NETUNAVAIL, IDS_DRIVES_NETUNAVAIL, IDS_DRIVES_NETUNAVAIL},
|
|
{ SHID_COMPUTER_REGITEM , IDS_DRIVES_REGITEM , IDS_DRIVES_REGITEM },
|
|
} ;
|
|
#pragma data_seg()
|
|
|
|
ULONG STDMETHODCALLTYPE CDrives_SD_Release(IShellDetails * psd);
|
|
STDMETHODIMP CDrives_SD_GetDetailsOf(IShellDetails * psd, LPCITEMIDLIST pidl, UINT iCol, LPSHELLDETAILS lpDetails);
|
|
STDMETHODIMP CDrives_SD_ColumnClick(IShellDetails * psd, UINT iColumn);
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
IShellDetailsVtbl c_DrivesSDVtbl =
|
|
{
|
|
SH32Unknown_QueryInterface,
|
|
SH32Unknown_AddRef,
|
|
SH32Unknown_Release,
|
|
CDrives_SD_GetDetailsOf,
|
|
CDrives_SD_ColumnClick,
|
|
};
|
|
#pragma data_seg()
|
|
|
|
typedef struct _CDrivesSD
|
|
{
|
|
SH32Unknown SH32Unk;
|
|
|
|
HWND hwndMain;
|
|
} CDrivesSD;
|
|
|
|
|
|
|
|
HRESULT CDrives_SD_Create(HWND hwndMain, LPVOID * ppvOut)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
CDrivesSD *psd;
|
|
|
|
psd = (void*)LocalAlloc(LPTR, SIZEOF(CDrivesSD));
|
|
if (!psd)
|
|
{
|
|
goto Error1;
|
|
}
|
|
|
|
psd->SH32Unk.unk.lpVtbl = (IUnknownVtbl *)&c_DrivesSDVtbl;
|
|
psd->SH32Unk.cRef = 1;
|
|
psd->SH32Unk.riid = &IID_IShellDetails;
|
|
|
|
psd->hwndMain = hwndMain;
|
|
|
|
*ppvOut = psd;
|
|
|
|
return(NOERROR);
|
|
|
|
Error1:;
|
|
return(hres);
|
|
}
|
|
|
|
|
|
void InitShowUglyDriveNames()
|
|
{
|
|
TCHAR szACP[MAX_PATH]; // Nice large buffer
|
|
int iACP;
|
|
int iRet;
|
|
|
|
iRet = GetLocaleInfo(GetUserDefaultLCID(),
|
|
LOCALE_IDEFAULTANSICODEPAGE,
|
|
szACP,
|
|
ARRAYSIZE(szACP));
|
|
|
|
if (iRet)
|
|
{
|
|
iACP = StrToInt(szACP);
|
|
|
|
if (iACP == 1252 || (iACP >= 1254 && iACP <= 1258))
|
|
s_fShowUglyDriveNames = FALSE;
|
|
else
|
|
s_fShowUglyDriveNames = TRUE;
|
|
}
|
|
else
|
|
{
|
|
TCHAR szTemp[10];
|
|
|
|
LoadString(HINST_THISDLL, IDS_DRIVES_UGLY_TEST, szTemp, ARRAYSIZE(szTemp));
|
|
|
|
// If the characters did not come through properly set ugly mode...
|
|
// May want to later test the second character also?
|
|
s_fShowUglyDriveNames = (szTemp[0] == TEXT('_'));
|
|
}
|
|
}
|
|
|
|
void Drives_GetTypeString(BYTE bType, LPTSTR pszName, UINT cbName)
|
|
{
|
|
int i;
|
|
|
|
// See if we need to worry about ugly names...
|
|
CHECKFORUGLYNAMES;
|
|
|
|
*pszName = TEXT('\0');
|
|
|
|
for (i = 0; i < ARRAYSIZE(c_drives_type); ++i)
|
|
{
|
|
if (c_drives_type[i].bType == bType)
|
|
{
|
|
LoadString(HINST_THISDLL,
|
|
s_fShowUglyDriveNames ? c_drives_type[i].uIDUgly
|
|
: c_drives_type[i].uID,
|
|
pszName, cbName);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDrives_SD_GetDetailsOf(IShellDetails * psd, LPCITEMIDLIST pidl,
|
|
UINT iColumn, LPSHELLDETAILS lpDetails)
|
|
{
|
|
CDrivesSD * this = IToClass(CDrivesSD, SH32Unk.unk, psd);
|
|
LPIDDRIVE pidd = (LPIDDRIVE)pidl;
|
|
HRESULT hres = NOERROR;
|
|
#ifdef UNICODE
|
|
TCHAR szTemp[MAX_PATH];
|
|
#endif
|
|
|
|
if (iColumn >= DRIVES_ICOL_MAX)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
lpDetails->str.uType = STRRET_CSTR;
|
|
lpDetails->str.cStr[0] = '\0';
|
|
|
|
if (!pidd)
|
|
{
|
|
#ifdef UNICODE
|
|
LoadString(HINST_THISDLL, s_drives_cols[iColumn].uID,
|
|
szTemp, ARRAYSIZE(szTemp));
|
|
lpDetails->str.pOleStr = SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
|
|
if (lpDetails->str.pOleStr == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr,szTemp);
|
|
}
|
|
#else
|
|
LoadString(HINST_THISDLL, s_drives_cols[iColumn].uID,
|
|
lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
|
|
#endif
|
|
lpDetails->fmt = s_drives_cols[iColumn].fmt;
|
|
lpDetails->cxChar = s_drives_cols[iColumn].cxChar;
|
|
return(NOERROR);
|
|
|
|
}
|
|
|
|
switch (iColumn)
|
|
{
|
|
case DRIVES_ICOL_NAME:
|
|
if (Drives_IsReg(pidd))
|
|
{
|
|
hres = RegItems_GetName(&g_sDrivesRegInfo, pidl,
|
|
&lpDetails->str);
|
|
}
|
|
else
|
|
{
|
|
hres = Drives_GetName(pidd, &lpDetails->str);
|
|
}
|
|
break;
|
|
|
|
case DRIVES_ICOL_TYPE:
|
|
#ifdef UNICODE
|
|
{
|
|
TCHAR szTypeName[MAX_PATH];
|
|
Drives_GetTypeString(pidd->bFlags, szTypeName, ARRAYSIZE(szTypeName));
|
|
lpDetails->str.pOleStr = SHAlloc((lstrlen(szTypeName)+1)*SIZEOF(TCHAR));
|
|
if (lpDetails->str.pOleStr == NULL)
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr,szTypeName);
|
|
}
|
|
}
|
|
#else
|
|
|
|
Drives_GetTypeString(pidd->bFlags, lpDetails->str.cStr,
|
|
ARRAYSIZE(lpDetails->str.cStr));
|
|
#endif
|
|
break;
|
|
|
|
case DRIVES_ICOL_SIZE:
|
|
case DRIVES_ICOL_FREE:
|
|
if (Drives_FillFreeSpace(pidd)) {
|
|
#ifdef UNICODE
|
|
TCHAR szSizeText[MAX_PATH];
|
|
ShortSizeFormat64((iColumn == DRIVES_ICOL_SIZE) ? pidd->qwSize : pidd->qwFree, szSizeText);
|
|
lpDetails->str.pOleStr = SHAlloc((lstrlen(szSizeText)+1)*SIZEOF(TCHAR));
|
|
if (lpDetails->str.pOleStr == NULL)
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
lpDetails->str.uType = STRRET_OLESTR;
|
|
lstrcpy(lpDetails->str.pOleStr,szSizeText);
|
|
}
|
|
#else
|
|
ShortSizeFormat64((iColumn == DRIVES_ICOL_SIZE) ? pidd->qwSize : pidd->qwFree, lpDetails->str.cStr);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
|
|
STDMETHODIMP CDrives_SD_ColumnClick(IShellDetails * psd, UINT iColumn)
|
|
{
|
|
CDrivesSD * this = IToClass(CDrivesSD, SH32Unk.unk, psd);
|
|
|
|
ShellFolderView_ReArrange(this->hwndMain, iColumn);
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
void Drives_CommonPrefix(LPCITEMIDLIST *ppidl1, LPCITEMIDLIST *ppidl2)
|
|
{
|
|
LPCIDDRIVE pidd1 = (LPCIDDRIVE)(*ppidl1);
|
|
LPCIDDRIVE pidd2 = (LPCIDDRIVE)(*ppidl2);
|
|
|
|
if (lstrcmpiA(pidd1->cName, pidd2->cName) != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
*ppidl1 = _ILNext(*ppidl1);
|
|
*ppidl2 = _ILNext(*ppidl2);
|
|
|
|
if (!Drives_IsReg(pidd1) && !Drives_IsReg(pidd2))
|
|
{
|
|
FS_CommonPrefix(ppidl1, ppidl2);
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct { // dpsp
|
|
PROPSHEETPAGE psp;
|
|
|
|
HWND hDlg;
|
|
|
|
int iDrive;
|
|
|
|
DWORD dwFC;
|
|
DWORD dwC;
|
|
|
|
DWORD dwPieShadowHgt;
|
|
#ifdef WINNT
|
|
int iInitCompressedState; // Record of initial compress state.
|
|
// 0 = uncompressed.
|
|
// 1 = compressed.
|
|
// -1 = device doesn't support.
|
|
#endif
|
|
} DRIVEPROPSHEETPAGE;
|
|
|
|
#define MAXLEN_NTFS_LABEL 32
|
|
#define MAXLEN_FAT_LABEL 11
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: _DrvPrshtUpdateSpaceValues
|
|
//
|
|
// DESCRIPTION:
|
|
// Updates the Used space, Free space and Capacity values on the drive
|
|
// general property page..
|
|
//
|
|
// NOTE:
|
|
// This function was separated from _DrvPrshtInit because drive space values
|
|
// must be updated after a compression/uncompression operation as well as
|
|
// during dialog initialization.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void _DrvPrshtUpdateSpaceValues(DRIVEPROPSHEETPAGE *pdpsp)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
_int64 qwTot = 0;
|
|
_int64 qwFree = 0;
|
|
DWORD dwSPC = 0;
|
|
DWORD dwBPS = 0;
|
|
TCHAR szTemp[80];
|
|
TCHAR szTemp2[30];
|
|
TCHAR szFormat[30];
|
|
HWND hDlg = pdpsp->hDlg;
|
|
struct
|
|
{
|
|
IDDRIVE idd;
|
|
CHAR szDisplayName[MAX_PATH];
|
|
USHORT cbNext;
|
|
} idlDrive;
|
|
|
|
CDrives_FillIDDrive((LPSHITEMID)&idlDrive.idd, pdpsp->iDrive);
|
|
PathBuildRoot(szTemp2, pdpsp->iDrive);
|
|
|
|
fResult = GetDiskFreeSpaceA(idlDrive.idd.cName, &dwSPC, &dwBPS, &pdpsp->dwFC, &pdpsp->dwC);
|
|
|
|
if (fResult)
|
|
{
|
|
qwTot = (__int64)pdpsp->dwC * (__int64)dwSPC * (__int64)dwBPS;
|
|
qwFree = (__int64)pdpsp->dwFC * (__int64)dwSPC * (__int64)dwBPS;
|
|
}
|
|
else
|
|
{
|
|
// If network drive show the type as unavalable if
|
|
// the drive fails to get free space...
|
|
#ifdef DEBUG
|
|
DWORD dwError = GetLastError();
|
|
// see if we should special case the different bugs that
|
|
// might come here. Currently when it is unshared it looks
|
|
// like ERROR_INVALID_DRIVE is returned...
|
|
#endif
|
|
|
|
// Clear these for use when drawing the pie.
|
|
pdpsp->dwFC = pdpsp->dwC = 0;
|
|
if (idlDrive.idd.bFlags == SHID_COMPUTER_NETDRIVE)
|
|
{
|
|
LoadString(HINST_THISDLL, IDS_DRIVES_NETUNAVAIL, szTemp,
|
|
ARRAYSIZE(szTemp));
|
|
SetDlgItemText(hDlg, IDC_DRV_TYPE, szTemp);
|
|
}
|
|
qwTot = 0;
|
|
qwFree = 0;
|
|
}
|
|
|
|
if (LoadString(HINST_THISDLL, IDS_BYTES, szFormat, ARRAYSIZE(szFormat)))
|
|
{
|
|
|
|
#ifdef WINNT
|
|
//
|
|
// NT must be able to display 64-bit numbers; at least as much
|
|
// as is realistic. We've made the decision
|
|
// that volumes up to 100 Terrabytes will display the byte value
|
|
// and the short-format value. Volumes of greater size will display
|
|
// "---" in the byte field and the short-format value. Note that the
|
|
// short format is always displayed.
|
|
//
|
|
TCHAR szNumStr[MAX_PATH + 1] = {TEXT('\0')}; // For 64-bit int format.
|
|
NUMBERFMT NumFmt; // For 64-bit int format.
|
|
TCHAR szLocaleInfo[20]; // For 64-bit int format.
|
|
TCHAR szDecimalSep[5]; // Locale-specific.
|
|
TCHAR szThousandSep[5]; // Locale-specific.
|
|
const _int64 MaxDisplayNumber = 99999999999999; // 100TB - 1.
|
|
|
|
//
|
|
// Prepare number format info for current locale.
|
|
//
|
|
NumFmt.NumDigits = 0; // This is locale-insensitive.
|
|
NumFmt.LeadingZero = 0; // So is this.
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szLocaleInfo, ARRAYSIZE(szLocaleInfo));
|
|
NumFmt.Grouping = StrToLong(szLocaleInfo);
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSep, ARRAYSIZE(szDecimalSep));
|
|
NumFmt.lpDecimalSep = szDecimalSep;
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szThousandSep, ARRAYSIZE(szThousandSep));
|
|
NumFmt.lpThousandSep = szThousandSep;
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szLocaleInfo, ARRAYSIZE(szLocaleInfo));
|
|
NumFmt.NegativeOrder = StrToLong(szLocaleInfo);
|
|
|
|
#if 0
|
|
//
|
|
// Use this to test range of display behaviors.
|
|
// Total bytes displays "---" for too-large number.
|
|
// Used bytes displays max displayable number.
|
|
// Free bytes displays 1.
|
|
//
|
|
qwTot = MaxDisplayNumber + 1;
|
|
qwFree = 1;
|
|
#endif
|
|
|
|
if (qwTot-qwFree <= MaxDisplayNumber)
|
|
{
|
|
Int64ToString(qwTot-qwFree, szNumStr, ARRAYSIZE(szNumStr), TRUE, &NumFmt, NUMFMT_ALL);
|
|
wsprintf(szTemp, szFormat, szNumStr, szTemp2);
|
|
SetDlgItemText(hDlg, IDC_DRV_USEDBYTES, szTemp);
|
|
}
|
|
|
|
if (qwFree <= MaxDisplayNumber)
|
|
{
|
|
Int64ToString(qwFree, szNumStr, ARRAYSIZE(szNumStr), TRUE, &NumFmt, NUMFMT_ALL);
|
|
wsprintf(szTemp, szFormat, szNumStr, szTemp2);
|
|
SetDlgItemText(hDlg, IDC_DRV_FREEBYTES, szTemp);
|
|
}
|
|
|
|
if (qwTot <= MaxDisplayNumber)
|
|
{
|
|
Int64ToString(qwTot, szNumStr, ARRAYSIZE(szNumStr), TRUE, &NumFmt, NUMFMT_ALL);
|
|
wsprintf(szTemp, szFormat, szNumStr, szTemp2);
|
|
SetDlgItemText(hDlg, IDC_DRV_TOTBYTES, szTemp);
|
|
}
|
|
#else
|
|
if (!HIDWORD(qwTot-qwFree))
|
|
{
|
|
wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot) - LODWORD(qwFree), szTemp2));
|
|
SetDlgItemText(hDlg, IDC_DRV_USEDBYTES, szTemp);
|
|
}
|
|
|
|
if (!HIDWORD(qwFree))
|
|
{
|
|
wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwFree), szTemp2));
|
|
SetDlgItemText(hDlg, IDC_DRV_FREEBYTES, szTemp);
|
|
}
|
|
|
|
if (!HIDWORD(qwTot))
|
|
{
|
|
wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot), szTemp2));
|
|
SetDlgItemText(hDlg, IDC_DRV_TOTBYTES, szTemp);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ShortSizeFormat64(qwTot-qwFree, szTemp);
|
|
SetDlgItemText(hDlg, IDC_DRV_USEDMB, szTemp);
|
|
|
|
ShortSizeFormat64(qwFree, szTemp);
|
|
SetDlgItemText(hDlg, IDC_DRV_FREEMB, szTemp);
|
|
|
|
ShortSizeFormat64(qwTot, szTemp);
|
|
SetDlgItemText(hDlg, IDC_DRV_TOTMB, szTemp);
|
|
}
|
|
|
|
#ifdef WINNT
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: _DrvPrshtUpdateCompressStatus
|
|
//
|
|
// DESCRIPTION:
|
|
// Updates the "Compress" checkbox on the drive General property page to
|
|
// indicate the current compression state of the associated volume.
|
|
//
|
|
// Only required for NT Shell.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void _DrvPrshtUpdateCompressStatus(DRIVEPROPSHEETPAGE *pdpsp)
|
|
{
|
|
DWORD dwVolumeFlags = 0;
|
|
TCHAR szTemp[30];
|
|
|
|
PathBuildRoot(szTemp, pdpsp->iDrive);
|
|
if (GetVolumeInformation(szTemp, NULL, 0, NULL, NULL,
|
|
&dwVolumeFlags, NULL, 0))
|
|
{
|
|
//
|
|
// If volume supports compression, show the "Compress" checkbox and the
|
|
// separator between the checkbox and pie chart.
|
|
// Check/uncheck the box to indicate compression state of the
|
|
// volume (root directory).
|
|
//
|
|
pdpsp->iInitCompressedState = -1; // Assume compression not supported.
|
|
if (dwVolumeFlags & FS_FILE_COMPRESSION)
|
|
{
|
|
DWORD dwAttributes = 0;
|
|
TCHAR szFormat[30];
|
|
|
|
if (LoadString(HINST_THISDLL, IDS_DRIVES_COMPRESS, szFormat, ARRAYSIZE(szFormat)))
|
|
{
|
|
//
|
|
// Build the title for the checkbox control."Compress C:\".
|
|
//
|
|
LPTSTR pszText = ShellConstructMessageString(HINST_THISDLL, szFormat, szTemp);
|
|
if (pszText)
|
|
{
|
|
SetDlgItemText(pdpsp->hDlg, IDC_DRV_COMPRESS, pszText);
|
|
SHFree(pszText);
|
|
}
|
|
}
|
|
|
|
pdpsp->iInitCompressedState = 0; // Assume uncompressed.
|
|
|
|
if ( ((dwAttributes = GetFileAttributes(szTemp)) != (DWORD)-1) &&
|
|
(dwAttributes & FILE_ATTRIBUTE_COMPRESSED))
|
|
{
|
|
pdpsp->iInitCompressedState = 1;
|
|
}
|
|
|
|
CheckDlgButton(pdpsp->hDlg, IDC_DRV_COMPRESS, pdpsp->iInitCompressedState);
|
|
ShowWindow(GetDlgItem(pdpsp->hDlg, IDC_DRV_COMPRESS), SW_SHOW);
|
|
ShowWindow(GetDlgItem(pdpsp->hDlg, IDC_DRV_COMPRESS_SEP), SW_SHOW);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void _DrvPrshtInit(DRIVEPROPSHEETPAGE * pdpsp)
|
|
{
|
|
struct
|
|
{
|
|
IDDRIVE idd;
|
|
CHAR szDisplayName[MAX_PATH];
|
|
USHORT cbNext;
|
|
} idlDrive;
|
|
HWND hDlg = pdpsp->hDlg;
|
|
HWND hCtl;
|
|
TCHAR szLabel[MAXLEN_NTFS_LABEL+1];
|
|
TCHAR szFormat[30];
|
|
TCHAR szTemp[80];
|
|
TCHAR szTemp2[30];
|
|
HCURSOR hcOld;
|
|
HDC hDC;
|
|
SIZE size;
|
|
SHFILEINFO sfi;
|
|
HICON hiconT;
|
|
TCHAR szFileSystem[64];
|
|
|
|
hcOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
hDC = GetDC(pdpsp->hDlg);
|
|
GetTextExtentPoint(hDC, TEXT("W"), 1, &size);
|
|
pdpsp->dwPieShadowHgt = size.cy*2/3;
|
|
ReleaseDC(pdpsp->hDlg, hDC);
|
|
|
|
CDrives_FillIDDrive((LPSHITEMID)&idlDrive.idd, pdpsp->iDrive);
|
|
PathBuildRoot(szTemp2, pdpsp->iDrive);
|
|
|
|
//
|
|
// get the icon
|
|
//
|
|
SHGetFileInfo(szTemp2, 0, &sfi, SIZEOF(sfi), SHGFI_ICON|SHGFI_LARGEICON);
|
|
|
|
if (sfi.hIcon)
|
|
{
|
|
hiconT = Static_SetIcon(GetDlgItem(hDlg, IDC_DRV_ICON), sfi.hIcon);
|
|
|
|
if (hiconT)
|
|
{
|
|
DestroyIcon(hiconT);
|
|
}
|
|
}
|
|
|
|
|
|
hCtl = GetDlgItem(hDlg, IDC_DRV_LABEL);
|
|
|
|
{
|
|
if (!GetVolumeInformation(szTemp2, szLabel, ARRAYSIZE(szLabel), NULL, NULL,
|
|
NULL, szFileSystem, ARRAYSIZE(szFileSystem)))
|
|
{
|
|
szLabel[0] = TEXT('\0');
|
|
Edit_SetReadOnly(hCtl, TRUE);
|
|
}
|
|
else
|
|
{
|
|
#ifdef WINNT
|
|
SetDlgItemText(hDlg, IDC_DRV_FS, szFileSystem);
|
|
#endif
|
|
|
|
if (0 == lstrcmpi(szFileSystem, TEXT("NTFS")) || 0 == lstrcmpi(szFileSystem, TEXT("OFS")))
|
|
{
|
|
Edit_LimitText(hCtl, MAXLEN_NTFS_LABEL);
|
|
}
|
|
else
|
|
{
|
|
Edit_LimitText(hCtl, MAXLEN_FAT_LABEL);
|
|
}
|
|
}
|
|
SetWindowText(hCtl, szLabel);
|
|
}
|
|
|
|
Edit_SetModify(hCtl, FALSE);
|
|
|
|
if ((idlDrive.idd.bFlags == SHID_COMPUTER_NETDRIVE) ||
|
|
(idlDrive.idd.bFlags == SHID_COMPUTER_NETDRIVE))
|
|
{
|
|
Edit_SetReadOnly(hCtl, TRUE);
|
|
}
|
|
|
|
Drives_GetTypeString(idlDrive.idd.bFlags, szTemp, ARRAYSIZE(szTemp));
|
|
|
|
SetDlgItemText(hDlg, IDC_DRV_TYPE, szTemp);
|
|
|
|
_DrvPrshtUpdateSpaceValues(pdpsp);
|
|
|
|
#ifdef WINNT
|
|
_DrvPrshtUpdateCompressStatus(pdpsp);
|
|
#endif
|
|
|
|
if (LoadString(HINST_THISDLL, IDS_DRIVELETTER, szFormat, ARRAYSIZE(szFormat)))
|
|
{
|
|
wsprintf(szTemp, szFormat, pdpsp->iDrive+TEXT('A'));
|
|
SetDlgItemText(hDlg, IDC_DRV_LETTER, szTemp);
|
|
}
|
|
|
|
SetCursor(hcOld);
|
|
|
|
}
|
|
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
|
|
COLORREF c_crPieColors[] =
|
|
{
|
|
RGB( 0, 0, 255), // Blue
|
|
RGB(255, 0, 255), // Red-Blue
|
|
RGB( 0, 0, 128), // 1/2 Blue
|
|
RGB(128, 0, 128), // 1/2 Red-Blue
|
|
} ;
|
|
|
|
#pragma data_seg()
|
|
|
|
|
|
void _DrvPrshtDrawItem(DRIVEPROPSHEETPAGE *pdpsp, const DRAWITEMSTRUCT * lpdi)
|
|
{
|
|
COLORREF crDraw;
|
|
RECT rcItem = lpdi->rcItem;
|
|
HBRUSH hbDraw, hbOld;
|
|
|
|
switch (lpdi->CtlID)
|
|
{
|
|
case IDC_DRV_PIE:
|
|
{
|
|
DWORD dwPctX10 = pdpsp->dwC ?
|
|
(DWORD)((__int64)1000 *
|
|
(__int64)(pdpsp->dwC-pdpsp->dwFC) /
|
|
(__int64)pdpsp->dwC) : 1000;
|
|
|
|
DrawPie(lpdi->hDC, &lpdi->rcItem,
|
|
dwPctX10,
|
|
pdpsp->dwFC==0 || pdpsp->dwFC==pdpsp->dwC,
|
|
pdpsp->dwPieShadowHgt, c_crPieColors);
|
|
}
|
|
break;
|
|
|
|
case IDC_DRV_USEDCOLOR:
|
|
crDraw = c_crPieColors[DP_USEDCOLOR];
|
|
goto DrawColor;
|
|
|
|
case IDC_DRV_FREECOLOR:
|
|
crDraw = c_crPieColors[DP_FREECOLOR];
|
|
goto DrawColor;
|
|
|
|
DrawColor:
|
|
hbDraw = CreateSolidBrush(crDraw);
|
|
if (hbDraw)
|
|
{
|
|
hbOld = SelectObject(lpdi->hDC, hbDraw);
|
|
if (hbOld)
|
|
{
|
|
PatBlt(lpdi->hDC, rcItem.left, rcItem.top,
|
|
rcItem.right-rcItem.left,
|
|
rcItem.bottom-rcItem.top,
|
|
PATCOPY);
|
|
|
|
SelectObject(lpdi->hDC, hbOld);
|
|
}
|
|
|
|
DeleteObject(hbDraw);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL _DrvPrshtApply(DRIVEPROPSHEETPAGE *pdpsp)
|
|
{
|
|
HWND hCtl = GetDlgItem(pdpsp->hDlg, IDC_DRV_LABEL);
|
|
TCHAR szLabel[MAXLEN_NTFS_LABEL+1];
|
|
TCHAR szRoot[5];
|
|
|
|
if (Edit_GetModify(hCtl))
|
|
{
|
|
GetWindowText(hCtl, szLabel, ARRAYSIZE(szLabel));
|
|
|
|
// Make up the drive root path
|
|
PathBuildRoot(szRoot, pdpsp->iDrive);
|
|
|
|
if (!SetVolumeLabel(szRoot, szLabel))
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
SHSysErrorMessageBox(
|
|
pdpsp->hDlg,
|
|
NULL,
|
|
IDS_ERR_SETVOLUMELABEL,
|
|
dwError,
|
|
szLabel,
|
|
MB_ICONSTOP|MB_OK);
|
|
return(FALSE);
|
|
}
|
|
|
|
// Make sure the name cache gets invalidated...
|
|
InvalidateDriveNameCache(DRIVEID(szRoot));
|
|
SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_PATH, szRoot, szRoot);
|
|
}
|
|
#ifdef WINNT
|
|
//
|
|
// If the drive supports compression.
|
|
//
|
|
if (pdpsp->iInitCompressedState != -1)
|
|
{
|
|
int iCompressCbxState = Button_GetCheck(GetDlgItem(pdpsp->hDlg,
|
|
IDC_DRV_COMPRESS));
|
|
//
|
|
// Drive checkbox should only be 2-state.
|
|
//
|
|
Assert(iCompressCbxState == 0 || iCompressCbxState == 1);
|
|
|
|
//
|
|
// If compressed checkbox state has changed since initialization,
|
|
// load the compression UI DLL and compress/uncompress the drive.
|
|
// Note that the compression function will ask the user if
|
|
// all subdirectories are to be compressed also. By default,
|
|
// only the FILES in the root directory are affected.
|
|
//
|
|
if (iCompressCbxState != pdpsp->iInitCompressedState)
|
|
{
|
|
HINSTANCE hinstCompressDll = LoadLibrary(SZ_SHCOMPUI_DLLNAME);
|
|
|
|
if (hinstCompressDll != NULL)
|
|
{
|
|
FARPROC lpfCompress = GetProcAddress((HMODULE)hinstCompressDll,
|
|
SZ_COMPRESS_PROCNAME);
|
|
if (lpfCompress)
|
|
{
|
|
TCHAR szPath[_MAX_PATH + 1];
|
|
SCCA_CONTEXT Context;
|
|
|
|
SCCA_CONTEXT_INIT(&Context);
|
|
|
|
//
|
|
// Compress/Uncompress with full UI.
|
|
//
|
|
PathBuildRoot(szPath, pdpsp->iDrive);
|
|
(*lpfCompress)(pdpsp->hDlg, szPath, &Context, iCompressCbxState, TRUE);
|
|
_DrvPrshtUpdateSpaceValues(pdpsp);
|
|
_DrvPrshtUpdateCompressStatus(pdpsp);
|
|
}
|
|
else
|
|
Assert(0); // Something wrong with export of function.
|
|
|
|
FreeLibrary(hinstCompressDll);
|
|
}
|
|
else
|
|
{
|
|
ShellMessageBox(HINST_THISDLL, pdpsp->hDlg,
|
|
MAKEINTRESOURCE(IDS_NOSHCOMPUI),
|
|
MAKEINTRESOURCE(IDS_EXPLORER_NAME),
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
//
|
|
// Win95 doesn't use the "Compress" checkbox on the drive property page.
|
|
//
|
|
#endif
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
const static DWORD aDrvPrshtHelpIDs[] = { // Context Help IDs
|
|
IDC_DRV_ICON, IDH_FCAB_DRV_ICON,
|
|
IDC_DRV_LABEL, IDH_FCAB_DRV_LABEL,
|
|
IDC_DRV_TYPE_TXT, IDH_FCAB_DRV_TYPE,
|
|
IDC_DRV_TYPE, IDH_FCAB_DRV_TYPE,
|
|
IDC_DRV_FS_TXT, IDH_FCAB_DRV_FS,
|
|
IDC_DRV_FS, IDH_FCAB_DRV_FS,
|
|
IDC_DRV_USEDCOLOR, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_USEDBYTES_TXT, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_USEDBYTES, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_USEDMB, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREECOLOR, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREEBYTES_TXT, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREEBYTES, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREEMB, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_TOTSEP, NO_HELP,
|
|
IDC_DRV_TOTBYTES_TXT, IDH_FCAB_DRV_TOTSEP,
|
|
IDC_DRV_TOTBYTES, IDH_FCAB_DRV_TOTSEP,
|
|
IDC_DRV_TOTMB, IDH_FCAB_DRV_TOTSEP,
|
|
IDC_DRV_PIE, IDH_FCAB_DRV_PIE,
|
|
IDC_DRV_LETTER, IDH_FCAB_DRV_LETTER,
|
|
IDC_DRV_COMPRESS, IDH_FCAB_DRV_COMPRESS,
|
|
|
|
0, 0
|
|
};
|
|
#pragma data_seg()
|
|
|
|
//
|
|
// Descriptions:
|
|
// This is the dialog procedure for the "general" page of a property sheet.
|
|
//
|
|
BOOL CALLBACK _DrvGeneralDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DRIVEPROPSHEETPAGE * pdpsp = (DRIVEPROPSHEETPAGE *)GetWindowLong(hDlg, DWL_USER);
|
|
|
|
switch (uMessage) {
|
|
case WM_INITDIALOG:
|
|
// REVIEW, we should store more state info here, for example
|
|
// the hIcon being displayed and the FILEINFO pointer, not just
|
|
// the file name ptr
|
|
SetWindowLong(hDlg, DWL_USER, lParam);
|
|
pdpsp = (DRIVEPROPSHEETPAGE *)lParam;
|
|
pdpsp->hDlg = hDlg;
|
|
|
|
_DrvPrshtInit(pdpsp);
|
|
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
HICON hIcon;
|
|
|
|
hIcon = Static_GetIcon(GetDlgItem(hDlg, IDC_DRV_ICON), NULL);
|
|
if (hIcon)
|
|
DestroyIcon(hIcon);
|
|
break;
|
|
}
|
|
|
|
case WM_DRAWITEM:
|
|
_DrvPrshtDrawItem(pdpsp, (DRAWITEMSTRUCT *)lParam);
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (DWORD)(LPTSTR) aDrvPrshtHelpIDs);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
|
|
(DWORD)(LPVOID)aDrvPrshtHelpIDs);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDC_DRV_LABEL:
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
|
|
{
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
|
|
}
|
|
break;
|
|
|
|
#ifdef WINNT
|
|
case IDC_DRV_COMPRESS:
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
|
|
{
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR *)lParam)->code) {
|
|
case PSN_SETACTIVE:
|
|
break;
|
|
|
|
case PSN_APPLY:
|
|
if (!_DrvPrshtApply(pdpsp))
|
|
{
|
|
SetWindowLong(hDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// BUGBUG: the compiler supports and _int64 type, we should use it.
|
|
|
|
void _Divide64(LPFILETIME pft, DWORD dwDivisor)
|
|
{
|
|
LARGE_INTEGER Temp;
|
|
|
|
Temp.LowPart = pft->dwLowDateTime;
|
|
Temp.HighPart = pft->dwHighDateTime;
|
|
|
|
Temp.QuadPart = Temp.QuadPart / dwDivisor;
|
|
|
|
pft->dwHighDateTime = Temp.LowPart;
|
|
pft->dwLowDateTime = Temp.HighPart;
|
|
}
|
|
|
|
|
|
int _GetDaysDelta(HKEY hkRoot, LPCTSTR pszSubKey, LPCTSTR pszValName)
|
|
{
|
|
int nDays = -1;
|
|
SYSTEMTIME lastst;
|
|
SYSTEMTIME nowst;
|
|
FILETIME nowftU, nowft, lastft;
|
|
DWORD dwType, dwSize;
|
|
|
|
if (RegOpenKey(hkRoot, pszSubKey, &hkRoot) != ERROR_SUCCESS)
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
dwSize = SIZEOF(lastst);
|
|
if (RegQueryValueEx(hkRoot, (LPTSTR)pszValName, 0, &dwType,
|
|
(LPBYTE)&lastst, &dwSize)==ERROR_SUCCESS
|
|
&& dwType==REG_BINARY && dwSize==SIZEOF(lastst))
|
|
{
|
|
GetSystemTime(&nowst);
|
|
|
|
SystemTimeToFileTime(&nowst, &nowftU);
|
|
FileTimeToLocalFileTime(&nowftU, &nowft);
|
|
SystemTimeToFileTime(&lastst, &lastft);
|
|
|
|
// Get the number of seconds
|
|
_Divide64(&nowft, 10000000);
|
|
_Divide64(&lastft, 10000000);
|
|
|
|
// Get the number of days
|
|
_Divide64(&nowft, 60*60*24);
|
|
_Divide64(&lastft, 60*60*24);
|
|
|
|
nDays = nowft.dwLowDateTime - lastft.dwLowDateTime;
|
|
}
|
|
|
|
// Note this is not the root passed in
|
|
RegCloseKey(hkRoot);
|
|
|
|
return(nDays);
|
|
}
|
|
|
|
|
|
void Drives_ShowDays(DRIVEPROPSHEETPAGE * pdpsp, UINT idCtl,
|
|
LPCTSTR pszRegKey, UINT idsDays, UINT idsUnknown)
|
|
{
|
|
TCHAR szFormat[256];
|
|
TCHAR szTitle[256];
|
|
int nDays;
|
|
|
|
szTitle[0] = TEXT('A') + pdpsp->iDrive;
|
|
szTitle[1] = TEXT('\0');
|
|
|
|
if (RealDriveType(pdpsp->iDrive, FALSE /* fOKToHitNet */)==DRIVE_FIXED
|
|
&& (nDays=_GetDaysDelta(HKEY_LOCAL_MACHINE, pszRegKey, szTitle)) >= 0)
|
|
{
|
|
LoadString(HINST_THISDLL, idsDays, szFormat, ARRAYSIZE(szFormat));
|
|
wsprintf(szTitle, szFormat, nDays);
|
|
}
|
|
else
|
|
{
|
|
LoadString(HINST_THISDLL, idsUnknown, szTitle, ARRAYSIZE(szTitle));
|
|
}
|
|
SetDlgItemText(pdpsp->hDlg, idCtl, szTitle);
|
|
}
|
|
|
|
|
|
void _DiskToolsPrshtInit(DRIVEPROPSHEETPAGE * pdpsp)
|
|
{
|
|
#ifdef WINNT
|
|
{
|
|
|
|
HKEY hkExp;
|
|
BOOL bFoundFmt = FALSE;
|
|
|
|
//
|
|
// Several things separate NT from Win95 here.
|
|
// 1. NT doesn't currently provide a defragmentation utility.
|
|
// If there isn't a 3rd party one identified in the registry,
|
|
// we disable the Defrag button and display an appropriate message.
|
|
// 2. The NT Check Disk and Backup utilities don't write the
|
|
// "last time run" information into the registry. Therefore
|
|
// we can't display a meaningful "last time..." message.
|
|
// We replace the "last time..." message with generic feature
|
|
// description. i.e. "This option will..."
|
|
//
|
|
bFoundFmt = FALSE;
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szRegExplorer, &hkExp)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
TCHAR szFmt[MAX_PATH + 20];
|
|
LONG lLen = ARRAYSIZE(szFmt);
|
|
|
|
if ((RegQueryValue(hkExp, c_szOptReg, szFmt, &lLen) == ERROR_SUCCESS) &&
|
|
(lstrlen(szFmt) > 0))
|
|
{
|
|
bFoundFmt = TRUE;
|
|
}
|
|
RegCloseKey(hkExp);
|
|
}
|
|
|
|
//
|
|
// If no defrag utility is installed, replace the default defrag text with
|
|
// the "No defrag installed" message. Also grey out the "defrag now" button.
|
|
//
|
|
if (!bFoundFmt)
|
|
{
|
|
TCHAR szMessage[50]; // WARNING: IDS_DRIVES_NOOPTINSTALLED is currently 47
|
|
// characters long. Resize this buffer if
|
|
// the string resource is lengthened.
|
|
|
|
LoadString(HINST_THISDLL, IDS_DRIVES_NOOPTINSTALLED, szMessage, ARRAYSIZE(szMessage));
|
|
SetDlgItemText(pdpsp->hDlg, IDC_DISKTOOLS_OPTDAYS, szMessage);
|
|
Button_Enable(GetDlgItem(pdpsp->hDlg, IDC_DISKTOOLS_OPTNOW), FALSE);
|
|
}
|
|
|
|
}
|
|
#else
|
|
Drives_ShowDays(pdpsp, IDC_DISKTOOLS_CHKDAYS, c_szRegLastCheck,
|
|
IDS_DRIVES_LASTCHECKDAYS, IDS_DRIVES_LASTCHECKUNK);
|
|
Drives_ShowDays(pdpsp, IDC_DISKTOOLS_BKPDAYS, c_szRegLastBackup,
|
|
IDS_DRIVES_LASTBACKUPDAYS, IDS_DRIVES_LASTBACKUPUNK);
|
|
Drives_ShowDays(pdpsp, IDC_DISKTOOLS_OPTDAYS, c_szRegLastOptimize,
|
|
IDS_DRIVES_LASTOPTIMIZEDAYS, IDS_DRIVES_LASTOPTIMIZEUNK);
|
|
#endif
|
|
}
|
|
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
const static DWORD aDiskToolsHelpIDs[] = { // Context Help IDs
|
|
IDC_DISKTOOLS_TRLIGHT, IDH_FCAB_DISKTOOLS_CHKNOW,
|
|
IDC_DISKTOOLS_CHKDAYS, IDH_FCAB_DISKTOOLS_CHKNOW,
|
|
IDC_DISKTOOLS_CHKNOW, IDH_FCAB_DISKTOOLS_CHKNOW,
|
|
IDC_DISKTOOLS_BKPTXT, IDH_FCAB_DISKTOOLS_BKPNOW,
|
|
IDC_DISKTOOLS_BKPDAYS, IDH_FCAB_DISKTOOLS_BKPNOW,
|
|
IDC_DISKTOOLS_BKPNOW, IDH_FCAB_DISKTOOLS_BKPNOW,
|
|
IDC_DISKTOOLS_OPTDAYS, IDH_FCAB_DISKTOOLS_OPTNOW,
|
|
IDC_DISKTOOLS_OPTNOW, IDH_FCAB_DISKTOOLS_OPTNOW,
|
|
|
|
0, 0
|
|
};
|
|
#pragma data_seg()
|
|
|
|
|
|
BOOL _DiskToolsCommand(DRIVEPROPSHEETPAGE * pdpsp, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Add 20 for extra formatting
|
|
TCHAR szFmt[MAX_PATH + 20];
|
|
TCHAR szCmd[MAX_PATH + 20];
|
|
LONG lLen;
|
|
LPCTSTR pszRegName, pszDefFmt;
|
|
HKEY hkExp;
|
|
BOOL bFoundFmt;
|
|
int nErrMsg = 0;
|
|
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDC_DISKTOOLS_CHKNOW:
|
|
|
|
#ifdef WINNT
|
|
SHChkDskDrive(pdpsp->hDlg, pdpsp->iDrive);
|
|
return FALSE;
|
|
#else
|
|
pszRegName = c_szChkReg;
|
|
pszDefFmt = c_szCheckDisk;
|
|
#endif
|
|
|
|
nErrMsg = IDS_NO_DISKCHECK_APP;
|
|
break;
|
|
|
|
case IDC_DISKTOOLS_OPTNOW:
|
|
pszRegName = c_szOptReg;
|
|
pszDefFmt = c_szDefrag;
|
|
nErrMsg = IDS_NO_OPTIMISE_APP;
|
|
break;
|
|
|
|
case IDC_DISKTOOLS_BKPNOW:
|
|
pszRegName = c_szBkpReg;
|
|
pszDefFmt = c_szBackup;
|
|
nErrMsg = IDS_NO_BACKUP_APP;
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
bFoundFmt = FALSE;
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szRegExplorer, &hkExp)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
lLen = ARRAYSIZE(szFmt);
|
|
if (RegQueryValue(hkExp, pszRegName, szFmt, &lLen) == ERROR_SUCCESS)
|
|
{
|
|
bFoundFmt = TRUE;
|
|
}
|
|
RegCloseKey(hkExp);
|
|
}
|
|
|
|
if (!bFoundFmt)
|
|
{
|
|
lstrcpy(szFmt, pszDefFmt);
|
|
}
|
|
|
|
// Plug in the drive letter in case they want it
|
|
wsprintf(szCmd, szFmt, pdpsp->iDrive + TEXT('A'));
|
|
|
|
if (!ShellExecCmdLine(pdpsp->hDlg, szCmd, NULL, SW_SHOWNORMAL,
|
|
NULL, SECL_USEFULLPATHDIR | SECL_NO_UI))
|
|
{
|
|
// Something went wrong - app's probably not installed.
|
|
if (nErrMsg)
|
|
{
|
|
ShellMessageBox(HINST_THISDLL, pdpsp->hDlg,
|
|
MAKEINTRESOURCE(nErrMsg),
|
|
NULL,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Descriptions:
|
|
// This is the dialog procedure for the "Tools" page of a property sheet.
|
|
//
|
|
BOOL CALLBACK _DiskToolsDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DRIVEPROPSHEETPAGE * pdpsp = (DRIVEPROPSHEETPAGE *)GetWindowLong(hDlg, DWL_USER);
|
|
|
|
switch (uMessage) {
|
|
case WM_INITDIALOG:
|
|
// REVIEW, we should store more state info here, for example
|
|
// the hIcon being displayed and the FILEINFO pointer, not just
|
|
// the file name ptr
|
|
SetWindowLong(hDlg, DWL_USER, lParam);
|
|
pdpsp = (DRIVEPROPSHEETPAGE *)lParam;
|
|
pdpsp->hDlg = hDlg;
|
|
|
|
_DiskToolsPrshtInit(pdpsp);
|
|
|
|
break;
|
|
|
|
#if 0
|
|
case WM_DESTROY:
|
|
{
|
|
HICON hIcon;
|
|
|
|
hIcon = Static_GetIcon(GetDlgItem(hDlg, IDC_DRV_ICON), NULL);
|
|
if (hIcon)
|
|
DestroyIcon(hIcon);
|
|
break;
|
|
}
|
|
|
|
case WM_DRAWITEM:
|
|
_DrvPrshtDrawItem(pdpsp, (DRAWITEMSTRUCT *)lParam);
|
|
break;
|
|
#endif
|
|
|
|
case WM_ACTIVATE:
|
|
if (GET_WM_ACTIVATE_STATE(wParam, lParam)!=WA_INACTIVE && pdpsp)
|
|
{
|
|
_DiskToolsPrshtInit(pdpsp);
|
|
}
|
|
|
|
// Let DefDlgProc know we did not handle this
|
|
return(FALSE);
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (DWORD)(LPTSTR) aDiskToolsHelpIDs);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
|
|
(DWORD)(LPVOID)aDiskToolsHelpIDs);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
return(_DiskToolsCommand(pdpsp, wParam, lParam));
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR *)lParam)->code) {
|
|
case PSN_SETACTIVE:
|
|
break;
|
|
|
|
case PSN_APPLY:
|
|
return(TRUE);
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// We check if any of the IDList's points to a drive root. If so, we use the
|
|
// drives property page.
|
|
// Note that drives should not be mixed with folders and files, even in a
|
|
// search window.
|
|
//
|
|
BOOL WINAPI Drives_AddPages(LPVOID lp, LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
|
|
{
|
|
LPDATAOBJECT pdtobj = (LPDATAOBJECT)lp;
|
|
TCHAR szPath[10]; // doesn't need to be large
|
|
BOOL bRet = FALSE;
|
|
int i, cItems;
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
HDROP hDrop;
|
|
DRIVEPROPSHEETPAGE dpsp;
|
|
HPROPSHEETPAGE hpage;
|
|
BOOL bResult = FALSE;
|
|
|
|
if (FAILED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium)))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
hDrop = (HDROP)medium.hGlobal;
|
|
|
|
// Check if any of the items is a drive root
|
|
cItems = DragQueryFile(hDrop, (UINT)-1, NULL, 0);
|
|
for (i = 0; i < cItems; ++i)
|
|
{
|
|
BOOL fOk;
|
|
|
|
// Check that the file name length is 3 (fully qualified)
|
|
if (DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)) > 3)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Create a property sheet page for the drive.
|
|
//
|
|
dpsp.psp.dwSize = SIZEOF(dpsp); // extra data
|
|
dpsp.psp.dwFlags = PSP_DEFAULT;
|
|
dpsp.psp.hInstance = HINST_THISDLL;
|
|
dpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_DRV_GENERAL);
|
|
dpsp.psp.pfnDlgProc = _DrvGeneralDlgProc,
|
|
// fpsp.psp.lParam = 0; // unused
|
|
dpsp.iDrive = DRIVEID(szPath);
|
|
|
|
fOk = TRUE;
|
|
|
|
if (cItems > 1)
|
|
{
|
|
STRRET strRet;
|
|
struct
|
|
{
|
|
IDDRIVE idd;
|
|
|
|
// These next 2 fields are just so we have enough room for
|
|
// the RegItem name and the terminating NULL.
|
|
CHAR cName[MAX_PATH];
|
|
USHORT cbNext;
|
|
} idlDrive;
|
|
|
|
|
|
CDrives_FillIDDrive((LPSHITEMID)&idlDrive.idd, dpsp.iDrive);
|
|
if (SUCCEEDED(Drives_GetName(&idlDrive.idd, &strRet)))
|
|
{
|
|
dpsp.psp.dwFlags = PSP_USETITLE;
|
|
#ifdef UNICODE
|
|
switch(strRet.uType)
|
|
{
|
|
case STRRET_CSTR:
|
|
{
|
|
UINT cchLen = lstrlenA(strRet.cStr)+1;
|
|
|
|
dpsp.psp.pszTitle = SHAlloc(cchLen*SIZEOF(TCHAR));
|
|
if (dpsp.psp.pszTitle)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
strRet.cStr, cchLen,
|
|
(LPWSTR)dpsp.psp.pszTitle, cchLen);
|
|
}
|
|
else
|
|
{
|
|
fOk = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case STRRET_OFFSET:
|
|
{
|
|
LPSTR lpText = (LPSTR)(((LPBYTE)&idlDrive.idd) + strRet.uOffset);
|
|
UINT cchLen = lstrlenA(lpText)+1;
|
|
dpsp.psp.pszTitle = SHAlloc(cchLen*SIZEOF(TCHAR));
|
|
if (dpsp.psp.pszTitle)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
lpText, cchLen,
|
|
(LPWSTR)dpsp.psp.pszTitle, cchLen);
|
|
}
|
|
else
|
|
{
|
|
fOk = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case STRRET_OLESTR:
|
|
dpsp.psp.pszTitle = strRet.pOleStr;
|
|
break;
|
|
default:
|
|
DebugMsg(DM_ERROR,TEXT("d.ap: Bad strret type %d"), strRet.uType);
|
|
fOk = FALSE;
|
|
break;
|
|
}
|
|
#else
|
|
switch(strRet.uType)
|
|
{
|
|
case STRRET_CSTR:
|
|
dpsp.psp.pszTitle = strRet.cStr;
|
|
break;
|
|
case STRRET_OFFSET:
|
|
dpsp.psp.pszTitle = (LPTSTR)
|
|
(((LPBYTE)&idlDrive.idd) + strRet.uOffset);
|
|
break;
|
|
case STRRET_OLESTR:
|
|
DebugMsg(DM_ERROR,TEXT("d.ap: strret type OLESTR"));
|
|
fOk = FALSE;
|
|
break;
|
|
default:
|
|
DebugMsg(DM_ERROR,TEXT("d.ap: Bad strret type %d"), strRet.uType);
|
|
fOk = FALSE;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (fOk)
|
|
{
|
|
hpage = CreatePropertySheetPage(&dpsp.psp);
|
|
#ifdef UNICODE
|
|
if (cItems > 1)
|
|
{
|
|
// CreatePropertySheetPage makes its own copy of this
|
|
SHFree((LPSTR)dpsp.psp.pszTitle);
|
|
}
|
|
#endif
|
|
if (hpage)
|
|
{
|
|
if (lpfnAddPage(hpage, lParam))
|
|
{
|
|
// Return TRUE if any pages were added
|
|
bResult = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DestroyPropertySheetPage(hpage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cItems == 1)
|
|
{
|
|
switch(RealDriveType(DRIVEID(szPath), FALSE /* fOKToHitNet */))
|
|
{
|
|
case DRIVE_REMOTE:
|
|
case DRIVE_CDROM:
|
|
break;
|
|
|
|
default:
|
|
dpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_DISKTOOLS);
|
|
dpsp.psp.pfnDlgProc = _DiskToolsDlgProc;
|
|
|
|
hpage = CreatePropertySheetPage(&dpsp.psp);
|
|
if (hpage)
|
|
{
|
|
if (!lpfnAddPage(hpage, lParam))
|
|
{
|
|
DestroyPropertySheetPage(hpage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
SHReleaseStgMedium(&medium);
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
HRESULT DrivesHandleFSNotify(LPSHELLFOLDER psf, LONG lNotification, LPCITEMIDLIST *ppidl)
|
|
{
|
|
// Get to the last part of this id list...
|
|
DWORD dwRestricted;
|
|
LPCIDDRIVE pidd;
|
|
|
|
if ((lNotification != SHCNE_DRIVEADD) || (ppidl == NULL) || (*ppidl == NULL))
|
|
return(NOERROR);
|
|
|
|
dwRestricted = SHRestricted(REST_NODRIVES);
|
|
if (dwRestricted == 0)
|
|
return(NOERROR); // no drives restricted... (majority case)
|
|
|
|
pidd = (LPCIDDRIVE)ILFindLastID(*ppidl);
|
|
|
|
if ((1 << CDrives_GetDriveIndex(pidd)) & dwRestricted)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("Drive not added due to restrictions"));
|
|
return(S_FALSE);
|
|
}
|
|
// Else no restriction
|
|
return(NOERROR);
|
|
}
|