/*++ (c) 1998 Seagate Software, Inc. All rights reserved. Module Name: PrDrive.cpp Abstract: Base file for HSM shell extensions on drives Author: Art Bragg [abragg] 04-Aug-1997 Revision History: --*/ #include "stdafx.h" #include "rshelpid.h" //#define RS_SHOW_ALL_PCTS // Help Ids #define RS_WINDIR_SIZE (2*MAX_PATH) static DWORD pHelpIds[] = { #ifdef RS_SHOW_ALL_PCTS IDC_STATIC_LOCAL_PCT, idh_volume_percent_local_data, IDC_STATIC_LOCAL_PCT_UNIT, idh_volume_percent_local_data, #endif IDC_STATIC_LOCAL_4DIGIT, idh_volume_capacity_local_data, IDC_STATIC_LOCAL_4DIGIT_LABEL, idh_volume_capacity_local_data, IDC_STATIC_LOCAL_4DIGIT_HELP, idh_volume_capacity_local_data, #ifdef RS_SHOW_ALL_PCTS IDC_STATIC_CACHED_PCT, idh_volume_percent_remote_data_cached, IDC_STATIC_CACHED_PCT_UNIT, idh_volume_percent_remote_data_cached, #endif IDC_STATIC_CACHED_4DIGIT, idh_volume_capacity_remote_data_cached, IDC_STATIC_CACHED_4DIGIT_LABEL, idh_volume_capacity_remote_data_cached, IDC_STATIC_FREE_PCT, idh_volume_percent_free_space, IDC_STATIC_FREE_PCT_UNIT, idh_volume_percent_free_space, IDC_STATIC_FREE_4DIGIT, idh_volume_capacity_free_space, IDC_STATIC_FREE_4DIGIT_LABEL, idh_volume_capacity_free_space, IDC_STATIC_TOTAL_4DIGIT, idh_volume_disk_capacity, IDC_STATIC_TOTAL_4DIGIT_LABEL, idh_volume_disk_capacity, IDC_STATIC_REMOTE_STORAGE_4DIGIT, idh_volume_data_remote_storage, IDC_STATIC_STATS_LABEL, idh_volume_data_remote_storage, IDC_EDIT_LEVEL, idh_desired_free_space_percent, IDC_SPIN_LEVEL, idh_desired_free_space_percent, IDC_EDIT_LEVEL_LABEL, idh_desired_free_space_percent, IDC_EDIT_LEVEL_UNIT, idh_desired_free_space_percent, IDC_EDIT_SIZE, idh_min_file_size_criteria, IDC_SPIN_SIZE, idh_min_file_size_criteria, IDC_EDIT_SIZE_LABEL, idh_min_file_size_criteria, IDC_EDIT_SIZE_UNIT, idh_min_file_size_criteria, IDC_EDIT_TIME, idh_file_access_date_criteria, IDC_SPIN_TIME, idh_file_access_date_criteria, IDC_EDIT_TIME_LABEL, idh_file_access_date_criteria, IDC_EDIT_TIME_UNIT, idh_file_access_date_criteria, 0, 0 }; ///////////////////////////////////////////////////////////////////////////// // CPrDrive // IShellExtInit STDMETHODIMP CPrDrive::Initialize( LPCITEMIDLIST /*pidlFolder*/, IDataObject * pDataObj, HKEY /*hkeyProgID*/ ) { // // Initialize can be called more than once // m_pDataObj.Release( ); // // duplicate the object pointer // m_pDataObj = pDataObj; return( NOERROR ); } ////////////////////////////////////////////////////////////////////////////// // // AddPages // // STDMETHODIMP CPrDrive::AddPages( LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam ) { AFX_MANAGE_STATE( AfxGetStaticModuleState( ) ); HRESULT hr = S_OK; HPROPSHEETPAGE hPage = NULL; // Windows property page handle TCHAR szFileSystemName [256]; TCHAR szDrive [MAX_PATH]; int nState; CComPtr pFsaRes; FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM medium; CPrDrivePg * pPageDrive = 0; CPrDriveXPg * pPageXDrive = 0; try { // // Find out how many files the user has selected... // UINT cbFiles = 0; BOOL bMountedVol = FALSE; WsbAssertPointer( m_pDataObj ); //Paranoid check, m_pDataObj should have something by now... hr = m_pDataObj->GetData( &fmte, &medium ) ; // Returns hr if (FAILED(hr)) { // // Isn't a normal volume name. Maybe it's a mounted volume. // Mounted volume names come in on a different clipboard format // so we can treat them differently from normal volume // names like "C:\". A mounted volume name will be the path // to the folder hosting the mounted volume. // For mounted volumes, the DataObject provides CF "MountedVolume". // fmte.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_MOUNTEDVOLUME); WsbAffirmHr(m_pDataObj->GetData(&fmte, &medium)); bMountedVol = TRUE; } cbFiles = DragQueryFile( ( HDROP )medium.hGlobal,( UINT )-1, NULL, 0 ); if( 1 == cbFiles ) { // // Do we have admin privileges? // if( SUCCEEDED( WsbCheckAccess( WSB_ACCESS_TYPE_ADMINISTRATOR ) ) ) { // //OK, the user has only selected a single file, so lets go ahead //and get more information // //Get the name of the file the user has clicked on // DragQueryFile( (HDROP)medium.hGlobal, 0, (USHORT *)szDrive, (sizeof szDrive)/(sizeof szDrive[0]) ); // // Is this a local drive? // if( ( GetDriveType( szDrive ) )!= DRIVE_REMOTE ) { // // Is this an NTFS drive? // GetVolumeInformation( szDrive, NULL, 0, NULL, // Serial Number NULL, // Filename length NULL, // flags szFileSystemName, 256 ); if( wcscmp( szFileSystemName, L"NTFS" ) == 0 ) { // // Make sure the Fsa is running - if not do not do anything that // could cause it to load. // if( WsbCheckService( NULL, APPID_RemoteStorageFileSystemAgent ) == S_OK ) { // // Try to get the FSA object for the drive. If we fail, we don't // put up the property page. // CWsbStringPtr computerName; WsbAffirmHr( WsbGetComputerName( computerName ) ); CString szFullResName = computerName; CString szResName = szDrive; // // If a drive letter is present, the format to connect is: // \NTFS\, no trailing '\' or ':' // i.e. RAVI\NTFS\D // If it's a sticky vol. name however the format is: // \NTFS\\ (WITH the trailing '\' // i.e. RAVI\NTFS\Volume{445a4110-60aa-11d3-0060b0ededdb\ // if (bMountedVol) { // // Remove the leading \\?\ // szResName = szResName.Right(szResName.GetLength() - 4); szFullResName = szFullResName + "\\" + "NTFS" + "\\" + szResName; } else { szFullResName = szFullResName + "\\" + "NTFS" + "\\" + szDrive; // // Remove trailing \ and or : // if( szFullResName [szFullResName.GetLength()- 1] == '\\' ) { szFullResName = szFullResName.Left( szFullResName.GetLength( ) - 1 ); } if( szFullResName [szFullResName.GetLength( )- 1] == ':' ) { szFullResName = szFullResName.Left( szFullResName.GetLength( ) - 1 ); } } if( HsmConnectFromName( HSMCONN_TYPE_RESOURCE, szFullResName, IID_IFsaResource,( void** )&pFsaRes ) == S_OK ) { // // Connected to Hsm // Is the resource managed? // if( pFsaRes->IsManaged( ) == S_OK ) { nState = MANAGED; } else { nState = NOT_MANAGED; } } else { // // Couldn't connect to Fsa // nState = NO_FSA; } } else { // // Fsa is not running // nState = NO_FSA; } } else { nState = NOT_NTFS; } } else { // // Remote volume // nState = REMOTE; } } else { nState = NOT_ADMIN; } } else { nState = MULTI_SELECT; } // // For Not admin, Remote and Multi-Select, we don't even show the page // switch( nState ) { case NOT_NTFS: case NOT_ADMIN: case REMOTE: case MULTI_SELECT: // // For Not admin, Remote and Multi-Select, we don't even show the page // break; case MANAGED: { ///////////////////////////////////////////////////////////// // Create the property page WsbAssertPointer( pFsaRes ); // // Create the Drive property page. // pPageDrive = new CPrDrivePg( ); WsbAffirmPointer( pPageDrive ); // // Assign the Fsa object to the page // pPageDrive->m_pFsaResource = pFsaRes; // // Set the state // pPageDrive->m_nState = nState; hPage = CreatePropertySheetPage( &pPageDrive->m_psp ); WsbAffirmHandle( hPage ); // // Call the callback function with the handle to the new // page // WsbAffirm( lpfnAddPage( hPage, lParam ), E_UNEXPECTED ); break; } default: { ///////////////////////////////////////////////////////////// // Create the property page pPageXDrive = new CPrDriveXPg( ); WsbAffirmPointer( pPageXDrive ); // // Set the state // pPageXDrive->m_nState = nState; hPage = CreatePropertySheetPage( &pPageXDrive->m_psp ); WsbAffirmHandle( hPage ); // Call the callback function with the handle to the new // page WsbAffirm( lpfnAddPage( hPage, lParam ), E_UNEXPECTED ); } } } WsbCatchAndDo( hr, if( pPageDrive ) delete pPageDrive; if( pPageXDrive ) delete pPageXDrive; ); return( hr ); } // // FUNCTION: CPrDrive::ReplacePage( UINT, LPFNADDPROPSHEETPAGE, LPARAM ) // // PURPOSE: Called by the shell only for Control Panel property sheet // extensions // // PARAMETERS: // uPageID - ID of page to be replaced // lpfnReplaceWith - Pointer to the Shell's Replace function // lParam - Passed as second parameter to lpfnReplaceWith // // RETURN VALUE: // // E_FAIL, since we don't support this function. It should never be // called. // COMMENTS: // STDMETHODIMP CPrDrive::ReplacePage( UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/ ) { return( E_FAIL ); } ///////////////////////////////////////////////////////////////////////////// // CPrDrivePg property page CPrDrivePg::CPrDrivePg( ): CPropertyPage( CPrDrivePg::IDD ) { //{{AFX_DATA_INIT( CPrDrivePg ) m_accessTime = 0; m_hsmLevel = 0; m_fileSize = 0; //}}AFX_DATA_INIT // // Lock the module while this object lives. // Otherwise, modules can call CoFreeUnusedLibraries( ) // and cause us to unload before our page gets destroyed, // which causes an AV in the common control. // _Module.Lock( ); // // initialize state // m_nState = NO_STATE; // // Get and save the MFC callback function. // This is so we can delete the class the dialog never gets created. // m_pMfcCallback = m_psp.pfnCallback; // // Set the call back to our callback // m_psp.pfnCallback = PropPageCallback; } CPrDrivePg::~CPrDrivePg( ) { _Module.Unlock( ); } void CPrDrivePg::DoDataExchange( CDataExchange* pDX ) { AFX_MANAGE_STATE( AfxGetStaticModuleState( ) ); CPropertyPage::DoDataExchange( pDX ); //{{AFX_DATA_MAP( CPrDrivePg ) DDX_Control( pDX, IDC_EDIT_SIZE, m_editSize ); DDX_Control( pDX, IDC_EDIT_LEVEL, m_editLevel ); DDX_Control( pDX, IDC_EDIT_TIME, m_editTime ); DDX_Control( pDX, IDC_SPIN_TIME, m_spinTime ); DDX_Control( pDX, IDC_SPIN_SIZE, m_spinSize ); DDX_Control( pDX, IDC_SPIN_LEVEL, m_spinLevel ); DDX_Text( pDX, IDC_EDIT_TIME, m_accessTime ); DDV_MinMaxUInt( pDX, m_accessTime, HSMADMIN_MIN_INACTIVITY, HSMADMIN_MAX_INACTIVITY ); DDX_Text( pDX, IDC_EDIT_LEVEL, m_hsmLevel ); DDV_MinMaxUInt( pDX, m_hsmLevel, HSMADMIN_MIN_FREESPACE, HSMADMIN_MAX_FREESPACE ); DDX_Text( pDX, IDC_EDIT_SIZE, m_fileSize ); //}}AFX_DATA_MAP // // Since we limit the number of characters in the buddy edits, we // don't expect the previous two DDV's to ever really kick in. // However, it is possible to enter bad minumum size since both // '0' and '1' can be entered, but are not in the valid range. // // // Code is equivalent to: // DDV_MinMaxDWord( pDX, m_fileSize, HSMADMIN_MIN_MINSIZE, HSMADMIN_MAX_MINSIZE ); // if( pDX->m_bSaveAndValidate && ( m_fileSize < HSMADMIN_MIN_MINSIZE || m_fileSize > HSMADMIN_MAX_MINSIZE ) ) { CString message; AfxFormatString2( message, IDS_ERR_MINSIZE_RANGE, CString( WsbLongAsString( (LONG)HSMADMIN_MIN_MINSIZE ) ), CString( WsbLongAsString( (LONG)HSMADMIN_MAX_MINSIZE ) ) ); AfxMessageBox( message, MB_OK | MB_ICONWARNING ); pDX->Fail(); } } BEGIN_MESSAGE_MAP( CPrDrivePg, CPropertyPage ) //{{AFX_MSG_MAP( CPrDrivePg ) ON_EN_CHANGE( IDC_EDIT_TIME, OnChangeEditAccess ) ON_EN_CHANGE( IDC_EDIT_LEVEL, OnChangeEditLevel ) ON_EN_CHANGE( IDC_EDIT_SIZE, OnChangeEditSize ) ON_WM_DESTROY( ) ON_WM_CONTEXTMENU() ON_WM_HELPINFO() //}}AFX_MSG_MAP END_MESSAGE_MAP( ) ///////////////////////////////////////////////////////////////////////////// // CPrDrivePg message handlers BOOL CPrDrivePg::OnInitDialog( ) { AFX_MANAGE_STATE( AfxGetStaticModuleState( ) ); HRESULT hr = S_OK; LONGLONG total = 0; LONGLONG free = 0; LONGLONG premigrated = 0; LONGLONG truncated = 0; LONGLONG remoteStorage = 0; ULONG totalMB = 0; ULONG freeMB = 0; ULONG premigratedMB = 0; ULONG truncatedMB = 0; CString sFormat; CString sBufFormat; CPropertyPage::OnInitDialog( ); try { WsbAffirmPointer( m_pFsaResource ); // // Set the spinner ranges // m_spinTime.SetRange( HSMADMIN_MIN_INACTIVITY, HSMADMIN_MAX_INACTIVITY ); m_spinSize.SetRange( HSMADMIN_MIN_MINSIZE, HSMADMIN_MAX_MINSIZE ); m_spinLevel.SetRange( HSMADMIN_MIN_FREESPACE, HSMADMIN_MAX_FREESPACE ); // // Set text limits // m_editTime.SetLimitText( 3 ); m_editSize.SetLimitText( 5 ); m_editLevel.SetLimitText( 2 ); // // Get statistics // WsbAffirmHr( m_pFsaResource->GetSizes( &total, &free, &premigrated, &truncated ) ); // // "Local" data // LONGLONG local = max( ( total - free - premigrated ),( LONGLONG )0 ); // // Calculate percents // int freePct; int premigratedPct; if( 0 == total ) { freePct = 0; premigratedPct = 0; } else { freePct = (int)( ( free * 100 )/ total ); premigratedPct = (int)( ( premigrated * 100 )/ total ); } int localPct = 100 - freePct - premigratedPct; remoteStorage = premigrated + truncated; // // Show the statistics in 4-char format // RsGuiFormatLongLong4Char( local, sBufFormat ); SetDlgItemText( IDC_STATIC_LOCAL_4DIGIT, sBufFormat ); RsGuiFormatLongLong4Char( premigrated, sBufFormat ); SetDlgItemText( IDC_STATIC_CACHED_4DIGIT, sBufFormat ); RsGuiFormatLongLong4Char( free, sBufFormat ); SetDlgItemText( IDC_STATIC_FREE_4DIGIT, sBufFormat ); RsGuiFormatLongLong4Char( total, sBufFormat ); SetDlgItemText( IDC_STATIC_TOTAL_4DIGIT, sBufFormat ); RsGuiFormatLongLong4Char( remoteStorage, sBufFormat ); SetDlgItemText( IDC_STATIC_REMOTE_STORAGE_4DIGIT, sBufFormat ); // // Show Percents // #ifdef RS_SHOW_ALL_PCTS sFormat.Format( L"%d", localPct ); SetDlgItemText( IDC_STATIC_LOCAL_PCT, sFormat ); sFormat.Format( L"%d", premigratedPct ); SetDlgItemText( IDC_STATIC_CACHED_PCT, sFormat ); #endif sFormat.Format( L"%d", freePct ); SetDlgItemText( IDC_STATIC_FREE_PCT, sFormat ); // // Get levels // ULONG hsmLevel = 0; LONGLONG fileSize = 0; BOOL isRelative = TRUE; // assumed to be TRUE FILETIME accessTime; WsbAffirmHr( m_pFsaResource->GetHsmLevel( &hsmLevel ) ); m_hsmLevel = hsmLevel / FSA_HSMLEVEL_1; WsbAffirmHr( m_pFsaResource->GetManageableItemLogicalSize( &fileSize ) ); m_fileSize = (DWORD)(fileSize / 1024); // Show KBytes WsbAffirmHr( m_pFsaResource->GetManageableItemAccessTime( &isRelative, &accessTime ) ); WsbAssert( isRelative, E_FAIL ); // We only do relative time // Convert FILETIME to days m_accessTime = (UINT)( WsbFTtoLL( accessTime ) / WSB_FT_TICKS_PER_DAY ); if(m_accessTime > HSMADMIN_MAX_INACTIVITY ) { m_accessTime = HSMADMIN_MAX_INACTIVITY; } UpdateData( FALSE ); // Get help file name CString helpFile; helpFile.LoadString(IDS_HELPFILEPOPUP); CWsbStringPtr winDir; WsbAffirmHr( winDir.Alloc( RS_WINDIR_SIZE ) ); WsbAffirmStatus( ::GetWindowsDirectory( (WCHAR*)winDir, RS_WINDIR_SIZE ) != 0 ); m_pszHelpFilePath = CString(winDir) + L"\\help\\" + helpFile; } WsbCatch( hr ) return( TRUE ); } void CPrDrivePg::OnChangeEditAccess( ) { SetModified( ); } void CPrDrivePg::OnChangeEditLevel( ) { SetModified( ); } void CPrDrivePg::OnChangeEditSize( ) { SetModified( ); } BOOL CPrDrivePg::OnApply( ) { HRESULT hr; try { // // m_pFsaResource is NULL if we didn't show any properties, in which case there is nothing // to apply.. Note that apply may have been enabled by another page in the sheet. // if( m_pFsaResource ) { LONGLONG fileSize = 0; UpdateData( TRUE ); WsbAffirmHr( m_pFsaResource->SetHsmLevel( m_hsmLevel * FSA_HSMLEVEL_1 ) ); fileSize = ((LONGLONG)m_fileSize) * 1024; WsbAffirmHr( m_pFsaResource->SetManageableItemLogicalSize( fileSize ) ); // // Convert days to FILETIME // FILETIME accessTime; accessTime = WsbLLtoFT( ( LONGLONG )m_accessTime * WSB_FT_TICKS_PER_DAY ); WsbAffirmHr( m_pFsaResource->SetManageableItemAccessTime( TRUE, accessTime ) ); } } WsbCatch( hr ); return( CPropertyPage::OnApply( ) ); } UINT CALLBACK CPrDrivePg::PropPageCallback( HWND hWnd, UINT uMessage, LPPROPSHEETPAGE ppsp ) { UINT rVal = 0; HRESULT hr = S_OK; try { WsbAffirmPointer( ppsp ); WsbAffirmPointer( ppsp->lParam ); // // Get the page object from lParam // CPrDrivePg* pPage = (CPrDrivePg*)ppsp->lParam; WsbAssertPointer( pPage->m_pMfcCallback ); rVal = ( pPage->m_pMfcCallback )( hWnd, uMessage, ppsp ); switch( uMessage ) { case PSPCB_CREATE: break; case PSPCB_RELEASE: delete pPage; break; } } WsbCatch( hr ); return( rVal ); } void CPrDrivePg::OnContextMenu(CWnd* pWnd, CPoint point) { UNREFERENCED_PARAMETER(pWnd); UNREFERENCED_PARAMETER(point); if(pHelpIds && (m_pszHelpFilePath != L"")) { AFX_MANAGE_STATE( AfxGetStaticModuleState( ) ); ::WinHelp(m_hWnd, m_pszHelpFilePath, HELP_CONTEXTMENU, (DWORD_PTR)pHelpIds); } } BOOL CPrDrivePg::OnHelpInfo(HELPINFO* pHelpInfo) { if( (HELPINFO_WINDOW == pHelpInfo->iContextType) && pHelpIds && (m_pszHelpFilePath != L"") ) { AFX_MANAGE_STATE( AfxGetStaticModuleState( ) ); // // Look through list to see if we have help for this control // If not, we want to avoid the "No Help Available" box // DWORD *pTmp = pHelpIds; DWORD helpId = 0; DWORD tmpHelpId = 0; DWORD tmpCtrlId = 0; while( pTmp && *pTmp ) { // // Array is a pairing of control ID and help ID // tmpCtrlId = pTmp[0]; tmpHelpId = pTmp[1]; pTmp += 2; if(tmpCtrlId == (DWORD)pHelpInfo->iCtrlId) { helpId = tmpHelpId; break; } } if( helpId != 0 ) { ::WinHelp(m_hWnd, m_pszHelpFilePath, HELP_CONTEXTPOPUP, helpId); } } return CPropertyPage ::OnHelpInfo(pHelpInfo); } ///////////////////////////////////////////////////////////////////////////// // CPrDriveXPg property page CPrDriveXPg::CPrDriveXPg( ): CPropertyPage( CPrDriveXPg::IDD ) { //{{AFX_DATA_INIT( CPrDriveXPg ) //}}AFX_DATA_INIT // // Lock the module while this object lives. // Otherwise, modules can call CoFreeUnusedLibraries( ) // and cause us to unload before our page gets destroyed, // which causes an AV in the common control. // _Module.Lock( ); m_nState = NO_STATE; // // Get and save the MFC callback function. // This is so we can delete the class the dialog never gets created. // m_pMfcCallback = m_psp.pfnCallback; // // Set the call back to our callback // m_psp.pfnCallback = PropPageCallback; } CPrDriveXPg::~CPrDriveXPg( ) { _Module.Unlock( ); } BEGIN_MESSAGE_MAP( CPrDriveXPg, CPropertyPage ) //{{AFX_MSG_MAP( CPrDriveXPg ) //}}AFX_MSG_MAP END_MESSAGE_MAP( ) ///////////////////////////////////////////////////////////////////////////// // CPrDriveXPg message handlers BOOL CPrDriveXPg::OnInitDialog( ) { AFX_MANAGE_STATE( AfxGetStaticModuleState( ) ); HRESULT hr = S_OK; CPropertyPage::OnInitDialog( ); try { switch( m_nState ) { case NO_FSA: m_szError.LoadString( IDS_NO_FSA ); break; case NOT_MANAGED: m_szError.LoadString( IDS_NOT_MANAGED ); break; case NOT_NTFS: m_szError.LoadString( IDS_NOT_NTFS ); break; } SetDlgItemText( IDC_STATIC_ERROR, m_szError ); } WsbCatch( hr ) return( TRUE ); } UINT CALLBACK CPrDriveXPg::PropPageCallback( HWND hWnd, UINT uMessage, LPPROPSHEETPAGE ppsp ) { UINT rVal = 0; HRESULT hr = S_OK; try { WsbAffirmPointer( ppsp ); WsbAffirmPointer( ppsp->lParam ); // // Get the page object from lParam // CPrDriveXPg* pPage = (CPrDriveXPg*)ppsp->lParam; WsbAssertPointer( pPage->m_pMfcCallback ); rVal = ( pPage->m_pMfcCallback )( hWnd, uMessage, ppsp ); switch( uMessage ) { case PSPCB_CREATE: break; case PSPCB_RELEASE: delete pPage; break; } } WsbCatch( hr ); return( rVal ); }