|
|
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2002 Microsoft Corporation
//
// Module Name:
// TaskTreeView.cpp
//
// Maintained By:
// Galen Barbee (GalenB) 22-MAY-2000
//
//////////////////////////////////////////////////////////////////////////////
#include "Pch.h"
#include "TaskTreeView.h"
#include "DetailsDlg.h"
DEFINE_THISCLASS( "CTaskTreeView" )
//****************************************************************************
//
// Constants
//
//****************************************************************************
#define PROGRESSBAR_CONTROL_TICK_COUNT 1000
#define PROGRESSBAR_INITIAL_POSITION 10
#define PROGRESSBAR_RESIZE_PERCENT 5
//****************************************************************************
//
// Static Function Prototypes
//
//****************************************************************************
static HRESULT HrCreateTreeItem( TVINSERTSTRUCT * ptvisOut , STreeItemLParamData * ptipdIn , HTREEITEM htiParentIn , int nImageIn , BSTR bstrTextIn );
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::CTaskTreeView
//
//--
//////////////////////////////////////////////////////////////////////////////
CTaskTreeView::CTaskTreeView( HWND hwndParentIn , UINT uIDTVIn , UINT uIDProgressIn , UINT uIDStatusIn , size_t nInitialTickCount ) { TraceFunc( "" );
m_hwndParent = hwndParentIn; m_hwndTV = GetDlgItem( hwndParentIn, uIDTVIn ); Assert( m_hwndTV != NULL );
m_hwndProg = GetDlgItem( hwndParentIn, uIDProgressIn ); Assert( m_hwndProg != NULL );
m_hwndStatus = GetDlgItem( hwndParentIn, uIDStatusIn ); Assert( m_hwndStatus != NULL );
m_hImgList = NULL;
Assert( m_htiSelected == NULL ); Assert( m_bstrClientMachineName == NULL ); Assert( m_fDisplayErrorsAsWarnings == FALSE );
//
// Most of these get set in HrOnNotifySetActive, so just init them to zero.
//
m_nInitialTickCount = (ULONG) nInitialTickCount; m_nCurrentPos = 0; m_nRealPos = 0; m_fThresholdBroken = FALSE;
m_cPASize = 0; m_cPACount = 0; m_ptipdProgressArray = NULL;
TraceFuncExit();
} //*** CTaskTreeView::CTaskTreeView()
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::~CTaskTreeView( void )
//
//--
//////////////////////////////////////////////////////////////////////////////
CTaskTreeView::~CTaskTreeView( void ) { TraceFunc( "" );
size_t idx; STreeItemLParamData * ptipdTemp;
TreeView_DeleteAllItems( m_hwndTV );
if ( m_hImgList != NULL ) { ImageList_Destroy( m_hImgList ); } // if:
TraceSysFreeString( m_bstrClientMachineName );
// Cleanup the progress array and delete any allocated entries.
for ( idx = 0; idx < m_cPASize; idx++ ) { if ( m_ptipdProgressArray[ idx ] != NULL ) { ptipdTemp = m_ptipdProgressArray[ idx ]; TraceSysFreeString( ptipdTemp->bstrNodeName ); delete ptipdTemp; } // if: element is not NULL
} // for: each element of the array
delete [] m_ptipdProgressArray;
TraceFuncExit();
} //*** CTaskTreeView::~CTaskTreeView
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrOnInitDialog
//
// Description:
// Handle the WM_INITDIALOG message.
//
// Arguments:
// None.
//
// Return Values:
// S_OK - Success.
// Other HRESULTS.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrOnInitDialog( void ) { TraceFunc( "" );
HRESULT hr = S_OK; HICON hIcon; int idx;
Assert( m_bstrClientMachineName == NULL ); hr = THR( HrGetComputerName( ComputerNameDnsHostname , &m_bstrClientMachineName , TRUE // fBestFit
) ); if ( FAILED( hr ) ) { goto Cleanup; } // if:
//
// Build image list for icons in tree view.
//
m_hImgList = ImageList_Create( 16, 16, ILC_MASK, tsMAX, 0); if ( m_hImgList == NULL ) { hr = HRESULT_FROM_WIN32( TW32( GetLastError() ) ); goto Cleanup; } // if:
//
// Unknown Icon - Task Unknown.
//
hIcon = (HICON) LoadImage( g_hInstance, MAKEINTRESOURCE( IDI_SEL ), IMAGE_ICON, 16, 16, LR_SHARED ); if ( hIcon == NULL ) { hr = HRESULT_FROM_WIN32( TW32( GetLastError() ) ); goto Cleanup; } // if:
idx = ImageList_AddIcon( m_hImgList, hIcon ); Assert( idx == tsUNKNOWN );
//
// Pending Icon - Task Pending.
//
hIcon = (HICON) LoadImage( g_hInstance, MAKEINTRESOURCE( IDI_PENDING ), IMAGE_ICON, 16, 16, LR_SHARED ); if ( hIcon == NULL ) { hr = HRESULT_FROM_WIN32( TW32( GetLastError() ) ); goto Cleanup; } // if:
idx = ImageList_AddIcon( m_hImgList, hIcon ); Assert( idx == tsPENDING );
//
// Checkmark Icon - Task Done.
//
hIcon = (HICON) LoadImage( g_hInstance, MAKEINTRESOURCE( IDI_CHECK ), IMAGE_ICON, 16, 16, LR_SHARED ); if ( hIcon == NULL ) { hr = HRESULT_FROM_WIN32( TW32( GetLastError() ) ); goto Cleanup; } // if:
idx = ImageList_AddIcon( m_hImgList, hIcon ); Assert( idx == tsDONE );
//
// Warning Icon - Task Warning.
//
hIcon = (HICON) LoadImage( g_hInstance, MAKEINTRESOURCE( IDI_WARN ), IMAGE_ICON, 16, 16, LR_SHARED ); if ( hIcon == NULL ) { hr = HRESULT_FROM_WIN32( TW32( GetLastError() ) ); goto Cleanup; } // if:
idx = ImageList_AddIcon( m_hImgList, hIcon ); Assert( idx == tsWARNING );
//
// Fail Icon - Task Failed.
//
hIcon = (HICON) LoadImage( g_hInstance, MAKEINTRESOURCE( IDI_FAIL ), IMAGE_ICON, 16, 16, LR_SHARED ); if ( hIcon == NULL ) { hr = HRESULT_FROM_WIN32( TW32( GetLastError() ) ); goto Cleanup; } // if:
idx = ImageList_AddIcon( m_hImgList, hIcon ); Assert( idx == tsFAILED );
Assert( ImageList_GetImageCount( m_hImgList ) == tsMAX );
//
// Set the image list and background color.
//
TreeView_SetImageList( m_hwndTV, m_hImgList, TVSIL_NORMAL ); TreeView_SetBkColor( m_hwndTV, GetSysColor( COLOR_3DFACE ) );
Cleanup:
HRETURN( hr );
} //*** CTaskTreeView::HrOnInitDialog
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrAddTreeViewItem
//
// Description:
// Add a tree view item. This method will return the item handle and
// allows the caller to specify the parent item.
//
// Arguments:
// phtiOut
// Handle to the item being added (optional).
//
// idsIn
// String resource ID for description of the new item.
//
// rclsidMinorTaskIDIn
// Minor task ID for the item.
//
// rclsidMajorTaskIDIn
// Major task ID for the item. Defaults to IID_NULL.
//
// htiParentIn
// Parent item. Defaults to the root.
//
// fParentToAllNodeTasksIn
// TRUE = allow item to be parent to tasks from all nodes.
// FALSE = only allow item to be parent to tasks from the local node.
// Defaults to FALSE.
//
// Return Values:
// S_OK - Success.
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrAddTreeViewItem( HTREEITEM * phtiOut , UINT idsIn , REFCLSID rclsidMinorTaskIDIn , REFCLSID rclsidMajorTaskIDIn // = IID_NULL
, HTREEITEM htiParentIn // = TVI_ROOT
, BOOL fParentToAllNodeTasksIn // = FALSE
) { TraceFunc( "" );
HRESULT hr = S_OK; STreeItemLParamData * ptipd; SYSTEMTIME systemtime; TVINSERTSTRUCT tvis; HTREEITEM hti = NULL;
//
// Allocate an item data structure.
//
ptipd = new STreeItemLParamData; if ( ptipd == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
//
// Set the node name to the local computer name.
//
hr = THR( HrGetComputerName( ComputerNamePhysicalDnsFullyQualified , &ptipd->bstrNodeName , TRUE // fBestFitIn
) ); if ( FAILED( hr ) ) { goto Cleanup; } // if:
hr = THR( HrGetFQNDisplayName( ptipd->bstrNodeName, &ptipd->bstrNodeNameWithoutDomain ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if:
//
// Set the desription from the string resource ID.
//
hr = THR( HrLoadStringIntoBSTR( g_hInstance, idsIn, &ptipd->bstrDescription ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if:
//
// Set the date/time to the current date/time.
//
GetSystemTime( &systemtime ); if ( ! SystemTimeToFileTime( &systemtime, &ptipd->ftTime ) ) { DWORD sc = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( sc ); goto Cleanup; } // if:
//
// Set the task IDs.
//
CopyMemory( &ptipd->clsidMajorTaskId, &rclsidMajorTaskIDIn, sizeof( ptipd->clsidMajorTaskId ) ); CopyMemory( &ptipd->clsidMinorTaskId, &rclsidMinorTaskIDIn, sizeof( ptipd->clsidMinorTaskId ) );
//
// Set the flag describing which items this item can be a parent to.
//
ptipd->fParentToAllNodeTasks = fParentToAllNodeTasksIn;
//
// Initialize the insert structure and insert the item into the tree.
//
tvis.hParent = htiParentIn; tvis.hInsertAfter = TVI_LAST; tvis.itemex.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; tvis.itemex.cchTextMax = SysStringLen( ptipd->bstrDescription ); tvis.itemex.pszText = ptipd->bstrDescription; tvis.itemex.iImage = tsUNKNOWN; tvis.itemex.iSelectedImage = tsUNKNOWN; tvis.itemex.lParam = reinterpret_cast< LPARAM >( ptipd );
hti = TreeView_InsertItem( m_hwndTV, &tvis ); Assert( hti != NULL );
ptipd = NULL;
if ( phtiOut != NULL ) { *phtiOut = hti; } // if:
goto Cleanup;
Cleanup:
if ( ptipd != NULL ) { TraceSysFreeString( ptipd->bstrNodeName ); TraceSysFreeString( ptipd->bstrNodeNameWithoutDomain ); TraceSysFreeString( ptipd->bstrDescription ); delete ptipd; } // if: ptipd != NULL
HRETURN( hr );
} //*** CTaskTreeView::HrAddTreeViewItem
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::OnNotify
//
// Description:
// Handler for the WM_NOTIFY message.
//
// Arguments:
// pnmhdrIn - Notification structure.
//
// Return Values:
// Notification-specific return code.
//
//--
//////////////////////////////////////////////////////////////////////////////
LRESULT CTaskTreeView::OnNotify( LPNMHDR pnmhdrIn ) { TraceFunc( "" );
LRESULT lr = TRUE;
switch( pnmhdrIn->code ) { case TVN_DELETEITEM: OnNotifyDeleteItem( pnmhdrIn ); break;
case TVN_SELCHANGED: OnNotifySelChanged( pnmhdrIn ); break;
} // switch: notify code
RETURN( lr );
} //*** CTaskTreeView::OnNotify
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::OnNotifyDeleteItem
//
// Description:
// Handler for the TVN_DELETEITEM notification message.
//
// Arguments:
// pnmhdrIn - Notification structure for the item being deleted.
//
// Return Values:
// None.
//
//--
//////////////////////////////////////////////////////////////////////////////
void CTaskTreeView::OnNotifyDeleteItem( LPNMHDR pnmhdrIn ) { TraceFunc( "" );
LPNMTREEVIEW pnmtv = reinterpret_cast< LPNMTREEVIEW >( pnmhdrIn );
if ( pnmtv->itemOld.lParam != NULL ) { STreeItemLParamData * ptipd = reinterpret_cast< STreeItemLParamData * >( pnmtv->itemOld.lParam ); TraceSysFreeString( ptipd->bstrNodeName ); TraceSysFreeString( ptipd->bstrNodeNameWithoutDomain ); TraceSysFreeString( ptipd->bstrDescription ); TraceSysFreeString( ptipd->bstrReference ); delete ptipd; } // if: lParam != NULL
TraceFuncExit();
} //*** CTaskTreeView::OnNotifyDeleteItem
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::OnNotifySelChanged
//
// Description:
// Handler for the TVN_SELCHANGED notification message.
//
// Arguments:
// pnmhdrIn - Notification structure for the item being deleted.
//
// Return Values:
// None.
//
//--
//////////////////////////////////////////////////////////////////////////////
void CTaskTreeView::OnNotifySelChanged( LPNMHDR pnmhdrIn ) { TraceFunc( "" );
LPNMTREEVIEW pnmtv = reinterpret_cast< LPNMTREEVIEW >( pnmhdrIn );
Assert( pnmtv->itemNew.mask & TVIF_HANDLE );
m_htiSelected = pnmtv->itemNew.hItem;
TraceFuncExit();
} //*** CTaskTreeView::OnNotifySelChanged
//////////////////////////////////////////////////////////////////////////////
//++
//
// HRESULT
// CTaskTreeView::HrShowStatusAsDone( void )
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrShowStatusAsDone( void ) { TraceFunc( "" );
HRESULT hr = S_OK; BSTR bstrDescription = NULL; PBRANGE pbrange;
SendMessage( m_hwndProg, PBM_GETRANGE, FALSE, (LPARAM) &pbrange ); SendMessage( m_hwndProg, PBM_SETPOS, pbrange.iHigh, 0 );
hr = THR( HrLoadStringIntoBSTR( g_hInstance, IDS_TASKS_COMPLETED, &bstrDescription ) ); if ( FAILED( hr ) ) { SetWindowText( m_hwndStatus, L"" ); goto Cleanup; } // if:
SetWindowText( m_hwndStatus, bstrDescription );
Cleanup:
TraceSysFreeString( bstrDescription );
HRETURN( hr );
} //*** CTaskTreeView::HrShowStatusAsDone
//////////////////////////////////////////////////////////////////////////////
//++
//
// HRESULT
// CTaskTreeView::HrOnNotifySetActive( void )
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrOnNotifySetActive( void ) { TraceFunc( "" );
STreeItemLParamData * ptipdTemp; HRESULT hr = S_OK; size_t idx;
TreeView_DeleteAllItems( m_hwndTV ); SetWindowText( m_hwndStatus, L"" );
// Cleanup the progress array and delete any allocated entries.
for ( idx = 0; idx < m_cPASize; idx++ ) { if ( m_ptipdProgressArray[ idx ] != NULL ) { ptipdTemp = m_ptipdProgressArray[ idx ]; TraceSysFreeString( ptipdTemp->bstrNodeName ); delete ptipdTemp; } // if: element != NULL
} // for: each element in the array
m_cPASize = 0; m_cPACount = 0; delete [] m_ptipdProgressArray; m_ptipdProgressArray = NULL;
m_nRangeHigh = 1; // We don't have any reported tasks yet. Choose 1 to avoid any div/0 errors.
m_nCurrentPos = PROGRESSBAR_INITIAL_POSITION; m_nRealPos = PROGRESSBAR_INITIAL_POSITION; m_fThresholdBroken = FALSE;
SendMessage( m_hwndProg, PBM_SETRANGE, 0, MAKELPARAM( 0, PROGRESSBAR_CONTROL_TICK_COUNT ) ); SendMessage( m_hwndProg, PBM_SETPOS, m_nCurrentPos, 0 );
HRETURN( hr );
} //*** CTaskTreeView::HrOnNotifySetActive
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrOnSendStatusReport
//
// Description:
// Handle a status report call.
//
// Arguments:
// pcszNodeNameIn -
// clsidTaskMajorIn -
// clsidTaskMinorIn -
// nMinIn -
// nMaxIn -
// nCurrentIn -
// hrStatusIn -
// pcszDescriptionIn -
// pftTimeIn -
// pcszReferenceIn -
//
// Return Values:
// S_OK - Operation completed successfully.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrOnSendStatusReport( LPCWSTR pcszNodeNameIn , CLSID clsidTaskMajorIn , CLSID clsidTaskMinorIn , ULONG nMinIn , ULONG nMaxIn , ULONG nCurrentIn , HRESULT hrStatusIn , LPCWSTR pcszDescriptionIn , FILETIME * pftTimeIn , LPCWSTR pcszReferenceIn ) { TraceFunc( "" ); Assert( pcszNodeNameIn != NULL );
HRESULT hr = S_OK; int nImageChild; STreeItemLParamData tipd; HTREEITEM htiRoot; BSTR bstrStatus = NULL; BSTR bstrDisplayName = NULL; LPCWSTR pcszNameToUse = pcszNodeNameIn;
ZeroMemory( &tipd, sizeof( tipd ) );
//
// If no node name was supplied then provide this machine's name so
// we have something to use as the node name key part when placing
// this tree item in the tree.
//
if ( pcszNodeNameIn == NULL ) { pcszNodeNameIn = m_bstrClientMachineName; } // if:
//
// If the node name is fully-qualified, use just the prefix.
//
hr = STHR( HrGetFQNDisplayName( pcszNodeNameIn, &bstrDisplayName ) ); if ( SUCCEEDED( hr ) ) { pcszNameToUse = bstrDisplayName; } // if:
//////////////////////////////////////////////////////////////////////////
//
// Update status text.
// Don't do this if it is a log-only message.
//
//////////////////////////////////////////////////////////////////////////
if ( ( pcszDescriptionIn != NULL ) && ! IsEqualIID( clsidTaskMajorIn, TASKID_Major_Client_Log ) && ! IsEqualIID( clsidTaskMajorIn, TASKID_Major_Server_Log ) && ! IsEqualIID( clsidTaskMajorIn, TASKID_Major_Client_And_Server_Log ) ) { BOOL fReturn;
if ( pcszNodeNameIn != NULL ) { hr = THR( HrFormatMessageIntoBSTR( g_hInstance , IDS_FORMAT_STATUS , &bstrStatus , pcszNameToUse , pcszDescriptionIn ) ); //
// Handle the formatting error if there was one.
//
if ( FAILED( hr ) ) { // TODO: Display default description instead of exiting this function
goto Error; } // if:
} // if: node name was specified
else { bstrStatus = TraceSysAllocString( pcszDescriptionIn ); if ( bstrStatus == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // if:
} // else: node name wasn't specified
Assert( bstrStatus!= NULL ); Assert( *bstrStatus!= L'\0' ); fReturn = SetWindowText( m_hwndStatus, bstrStatus ); Assert( fReturn ); } // if: description specified, not log-only
//////////////////////////////////////////////////////////////////////////
//
// Select the right icon.
//
//////////////////////////////////////////////////////////////////////////
switch ( hrStatusIn ) { case S_OK: if ( nCurrentIn == nMaxIn ) { nImageChild = tsDONE; } // if:
else { nImageChild = tsPENDING; } // else:
break;
case S_FALSE: nImageChild = tsWARNING; break;
case E_PENDING: nImageChild = tsPENDING; break;
default: if ( FAILED( hrStatusIn ) && ( m_fDisplayErrorsAsWarnings == FALSE ) ) { nImageChild = tsFAILED; } // if:
else { nImageChild = tsWARNING; } // else:
break; } // switch: hrStatusIn
//////////////////////////////////////////////////////////////////////////
//
// Loop through each item at the top of the tree looking for an item
// whose minor ID matches this report's major ID.
//
//////////////////////////////////////////////////////////////////////////
//
// Fill in the param data structure.
//
tipd.hr = hrStatusIn; tipd.nMin = nMinIn; tipd.nMax = nMaxIn; tipd.nCurrent = nCurrentIn;
CopyMemory( &tipd.clsidMajorTaskId, &clsidTaskMajorIn, sizeof( tipd.clsidMajorTaskId ) ); CopyMemory( &tipd.clsidMinorTaskId, &clsidTaskMinorIn, sizeof( tipd.clsidMinorTaskId ) ); CopyMemory( &tipd.ftTime, pftTimeIn, sizeof( tipd.ftTime ) );
// tipd.bstrDescription is set above.
tipd.bstrNodeName = TraceSysAllocString( pcszNodeNameIn ); if ( tipd.bstrNodeName == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // if:
tipd.bstrNodeNameWithoutDomain = bstrDisplayName; bstrDisplayName = NULL; // tipd now owns this string.
if ( pcszDescriptionIn == NULL ) { tipd.bstrDescription = NULL; } // if:
else { tipd.bstrDescription = TraceSysAllocString( pcszDescriptionIn ); if ( tipd.bstrDescription == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // if:
} // else: pcszDescritionIn != NULL
if ( pcszReferenceIn == NULL ) { tipd.bstrReference = NULL; } // if:
else { tipd.bstrReference = TraceSysAllocString( pcszReferenceIn ); if ( tipd.bstrReference == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // if:
} // else: pcszReferenceIn != NULL
if ( IsEqualIID( tipd.clsidMajorTaskId, TASKID_Major_Update_Progress ) ) { // It's an update_progress task so just call HrProcessUpdateProgressTask.
hr = STHR( HrProcessUpdateProgressTask( &tipd ) ); if ( FAILED( hr ) ) { goto Error; } // if:
} // if: major == update_progress
else { // Start with the first item in the tree view.
htiRoot = TreeView_GetRoot( m_hwndTV ); if ( htiRoot == NULL ) { TW32( ERROR_NOT_FOUND ); // Don't return an error result since doing so will prevent the report
// from being propagated to other subscribers.
hr = S_OK; goto Cleanup; } // if: htiRoot is NULL
// Insert the status report into the tree view.
hr = STHR( HrInsertTaskIntoTree( htiRoot, &tipd, nImageChild, bstrStatus ) ); if ( FAILED( hr ) ) { LogMsg( "[WIZ] Error inserting status report into the tree control. hr=%.08x, %ws", tipd.hr, pcszDescriptionIn ); goto Error; } // if:
} // else: not an update_progress task
if ( hr == S_FALSE ) { // Don't return S_FALSE to the caller since it won't mean anything there.
hr = S_OK; // TODO: Should this be written to the log?
#if defined( DEBUG )
//
// Check to make sure that if the major task ID wasn't recognized
// that it is one of the known exceptions.
//
if ( ! IsEqualIID( clsidTaskMajorIn, TASKID_Major_Client_Log ) && ! IsEqualIID( clsidTaskMajorIn, TASKID_Major_Server_Log ) && ! IsEqualIID( clsidTaskMajorIn, TASKID_Major_Client_And_Server_Log ) ) { BSTR bstrMsg = NULL;
THR( HrFormatStringIntoBSTR( g_hInstance , IDS_UNKNOWN_TASK , &bstrMsg , clsidTaskMajorIn.Data1 // 1
, clsidTaskMajorIn.Data2 // 2
, clsidTaskMajorIn.Data3 // 3
, clsidTaskMajorIn.Data4[ 0 ] // 4
, clsidTaskMajorIn.Data4[ 1 ] // 5
, clsidTaskMajorIn.Data4[ 2 ] // 6
, clsidTaskMajorIn.Data4[ 3 ] // 7
, clsidTaskMajorIn.Data4[ 4 ] // 8
, clsidTaskMajorIn.Data4[ 5 ] // 9
, clsidTaskMajorIn.Data4[ 6 ] // 10
, clsidTaskMajorIn.Data4[ 7 ] // 11
) ); AssertString( 0, bstrMsg );
TraceSysFreeString( bstrMsg ); } // if: log only
#endif // DEBUG
} // if: S_FALSE returned from HrInsertTaskIntoTree
goto Cleanup;
Error: // Don't return an error result since doing so will prevent the report
// from being propagated to other subscribers.
hr = S_OK; goto Cleanup;
Cleanup:
TraceSysFreeString( bstrDisplayName ); TraceSysFreeString( bstrStatus ); TraceSysFreeString( tipd.bstrNodeName ); TraceSysFreeString( tipd.bstrNodeNameWithoutDomain ); TraceSysFreeString( tipd.bstrDescription ); TraceSysFreeString( tipd.bstrReference );
HRETURN( hr );
} //*** CTaskTreeView::HrOnSendStatusReport
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrInsertTaskIntoTree
//
// Description:
// Insert the specified task into the tree based on the node, major
// task, and minor task.
//
// Arguments:
// htiFirstIn - First tree item to examine.
// ptipdIn - Tree item parameter data for the task to be inserted.
// nImageIn - Image identifier for the child item.
// bstrDescriptionIn - Description string to display.
//
// Return Values:
// S_OK - Task inserted successfully.
// S_FALSE - Task not inserted.
// hr - The operation failed.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrInsertTaskIntoTree( HTREEITEM htiFirstIn , STreeItemLParamData * ptipdIn , int nImageIn , BSTR bstrDescriptionIn ) { TraceFunc( "" );
Assert( htiFirstIn != NULL ); Assert( ptipdIn != NULL );
//
// LOCAL VARIABLES
//
HRESULT hr; HTREEITEM htiParent; HTREEITEM htiChild = NULL; TVITEMEX tviParent; TVITEMEX tviChild; BOOL fReturn; BOOL fSameNode; STreeItemLParamData * ptipdParent = NULL; STreeItemLParamData * ptipdChild = NULL;
//
// Loop through each item to determine if the task should be added below
// that item. If not, attempt to traverse its children.
//
for ( htiParent = htiFirstIn, hr = S_FALSE ; ( htiParent != NULL ) && ( hr == S_FALSE ) ; ) { //
// Get the information about this item in the tree.
//
tviParent.mask = TVIF_PARAM | TVIF_IMAGE; tviParent.hItem = htiParent;
fReturn = TreeView_GetItem( m_hwndTV, &tviParent ); if ( fReturn == FALSE ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_NOT_FOUND ) ); goto Cleanup; } // if:
ptipdParent = reinterpret_cast< STreeItemLParamData * >( tviParent.lParam ); Assert( ptipdParent != NULL );
//
// Determine if either the parent can be a parent to tasks from any
// node or, if not, the parent and input tasks are from the same node.
//
if ( ptipdParent->fParentToAllNodeTasks == TRUE ) { fSameNode = TRUE; } // if: parent task can host tasks from any node
else { fSameNode = ( NBSTRCompareNoCase( ptipdIn->bstrNodeNameWithoutDomain, ptipdParent->bstrNodeNameWithoutDomain ) == 0 ); } // else: parent task's node must match input task's node
//
// Reset hr to S_FALSE because it is the return value when a tree item is
// to be added to the tree because it was not found.
//
hr = S_FALSE;
//
// See if this item could be the parent.
// If not, recurse through child items.
//
if ( IsEqualIID( ptipdIn->clsidMajorTaskId, ptipdParent->clsidMinorTaskId ) && ( fSameNode == TRUE ) ) { //
// FOUND THE PARENT ITEM
//
BOOL fMinorTaskIdMatches; BOOL fBothNodeNamesPresent; BOOL fBothNodeNamesEmpty; BOOL fNodeNamesEqual;
//////////////////////////////////////////////////////////////
//
// Loop through the child items looking for an item with
// the same minor ID.
//
//////////////////////////////////////////////////////////////
htiChild = TreeView_GetChild( m_hwndTV, htiParent ); while ( htiChild != NULL ) { //
// Get the child item details.
//
tviChild.mask = TVIF_PARAM | TVIF_IMAGE; tviChild.hItem = htiChild;
fReturn = TreeView_GetItem( m_hwndTV, &tviChild ); if ( fReturn == FALSE ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_NOT_FOUND ) ); goto Cleanup; } // if:
ptipdChild = reinterpret_cast< STreeItemLParamData * >( tviChild.lParam ); Assert( ptipdChild != NULL );
//
// Does this child item match the minor ID and node name?
//
fMinorTaskIdMatches = IsEqualIID( ptipdIn->clsidMinorTaskId, ptipdChild->clsidMinorTaskId ); fBothNodeNamesPresent = ( ptipdIn->bstrNodeNameWithoutDomain != NULL ) && ( ptipdChild->bstrNodeNameWithoutDomain != NULL ); fBothNodeNamesEmpty = ( ptipdIn->bstrNodeNameWithoutDomain == NULL ) && ( ptipdChild->bstrNodeNameWithoutDomain == NULL );
if ( fBothNodeNamesPresent == TRUE ) { fNodeNamesEqual = ( NBSTRCompareNoCase( ptipdIn->bstrNodeNameWithoutDomain, ptipdChild->bstrNodeNameWithoutDomain ) == 0 ); } // if:
else if ( fBothNodeNamesEmpty == TRUE ) { fNodeNamesEqual = TRUE; } // else if:
else { fNodeNamesEqual = FALSE; } // else:
if ( ( fMinorTaskIdMatches == TRUE ) && ( fNodeNamesEqual == TRUE ) ) { //
// CHILD ITEM MATCHES.
// Update the child item.
//
//
// Update the progress bar.
//
STHR( HrUpdateProgressBar( ptipdIn, ptipdChild ) ); // ignore failure.
//
// Copy data from the report.
// This must be done after the call to
// HrUpdateProgressBar so that the previous values
// can be compared to the new values.
//
ptipdChild->nMin = ptipdIn->nMin; ptipdChild->nMax = ptipdIn->nMax; ptipdChild->nCurrent = ptipdIn->nCurrent; CopyMemory( &ptipdChild->ftTime, &ptipdIn->ftTime, sizeof( ptipdChild->ftTime ) );
// Update the error code if needed.
if ( ptipdChild->hr == S_OK ) { ptipdChild->hr = ptipdIn->hr; } // if:
//
// If the new state is worse than the last state,
// update the state of the item.
//
if ( tviChild.iImage < nImageIn ) { tviChild.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; tviChild.iImage = nImageIn; tviChild.iSelectedImage = nImageIn; TreeView_SetItem( m_hwndTV, &tviChild ); } // if: new state is worse than the last state
//
// Update the text of the child item if needed.
//
if ( ( ptipdIn->bstrDescription != NULL ) && ( ( ptipdChild->bstrDescription == NULL ) || ( NBSTRCompareCase( ptipdIn->bstrDescription, ptipdChild->bstrDescription ) != 0 ) ) ) { fReturn = TraceSysReAllocString( &ptipdChild->bstrDescription, ptipdIn->bstrDescription ); if ( fReturn == FALSE ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
tviChild.mask = TVIF_TEXT; tviChild.pszText = bstrDescriptionIn; tviChild.cchTextMax = (int) wcslen( tviChild.pszText ); TreeView_SetItem( m_hwndTV, &tviChild ); } // if: description was specified and is different
//
// Copy the reference if it is different.
//
if ( ( ptipdIn->bstrReference != NULL ) && ( ( ptipdChild->bstrReference == NULL ) || ( NBSTRCompareCase( ptipdChild->bstrReference, ptipdIn->bstrReference ) != 0 ) ) ) { fReturn = TraceSysReAllocString( &ptipdChild->bstrReference, ptipdIn->bstrReference ); if ( fReturn == FALSE ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
} // if: reference is different
break; // exit loop
} // if: found a matching child item
//
// Get the next item.
//
htiChild = TreeView_GetNextSibling( m_hwndTV, htiChild );
} // while: more child items
//////////////////////////////////////////////////////////////
//
// If the tree item was not found and the description was
// specified, then we need to create the child item.
//
//////////////////////////////////////////////////////////////
if ( ( htiChild == NULL ) && ( ptipdIn->bstrDescription != NULL ) ) { //
// ITEM NOT FOUND AND DESCRIPTION WAS SPECIFIED
//
// Insert a new item in the tree under the major's task.
//
TVINSERTSTRUCT tvis;
// Create the item.
hr = THR( HrCreateTreeItem( &tvis , ptipdIn , htiParent , nImageIn , bstrDescriptionIn ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if:
// Insert the item in the tree.
htiChild = TreeView_InsertItem( m_hwndTV, &tvis ); Assert( htiChild != NULL );
//
// Update the progress bar.
//
ptipdChild = reinterpret_cast< STreeItemLParamData * >( tvis.itemex.lParam ); Assert( ptipdChild != NULL ); STHR( HrUpdateProgressBar( ptipdIn, ptipdChild ) ); // ignore failure.
} // if: need to add new child
//////////////////////////////////////////////////////////////
//
// If the child item was created and the child has an error
// condition, then create a child of the child item
// indicating the error code and system string.
//
//////////////////////////////////////////////////////////////
if ( ( ptipdChild != NULL ) && ( FAILED( ptipdIn->hr ) || ( ( ptipdIn->hr != S_OK ) && ( ptipdIn->hr != S_FALSE ) ) ) ) { //
// CHILD ITEM FOUND OR CREATED FOR ERROR REPORT
// CREATE ERROR ITEM IN THE TREE
//
BSTR bstrError = NULL; BSTR bstrErrorDescription = NULL; HRESULT hrFormat; TVINSERTSTRUCT tvis; HTREEITEM htiChildStatus; DWORD dwSeverity; HRESULT hrNewStatus = S_OK;
dwSeverity = SCODE_SEVERITY( ptipdIn->hr );
THR( HrFormatErrorIntoBSTR( ptipdIn->hr, &bstrError, &hrNewStatus ) );
//
// If we got an updated status code then we need to choose
// a new format string that shows both the old and the new
// status codes.
//
if ( hrNewStatus != ptipdIn->hr ) { Assert( bstrError != NULL );
hrFormat = THR( HrFormatMessageIntoBSTR( g_hInstance , dwSeverity == 0 ? IDS_TASK_RETURNED_NEW_STATUS : IDS_TASK_RETURNED_NEW_ERROR , &bstrErrorDescription , ptipdIn->hr , hrNewStatus , bstrError ) ); } // if:
else { hrFormat = THR( HrFormatMessageIntoBSTR( g_hInstance , dwSeverity == 0 ? IDS_TASK_RETURNED_STATUS : IDS_TASK_RETURNED_ERROR , &bstrErrorDescription , ptipdIn->hr , ( bstrError == NULL ? L"" : bstrError ) ) ); } // else:
if ( SUCCEEDED( hrFormat ) ) { //
// Insert a new item in the tree under the minor's
// task explaining the ptipdIn->hr.
//
// Create the item.
hr = THR( HrCreateTreeItem( &tvis , ptipdIn , htiChild , nImageIn , bstrErrorDescription ) ); if ( SUCCEEDED( hr ) ) { //
// Failures are handled below to make sure we free
// all the strings allocated by this section of
// the code.
//
// Insert the item.
htiChildStatus = TreeView_InsertItem( m_hwndTV, &tvis ); Assert( htiChildStatus != NULL ); } // if: tree item created successfully
TraceSysFreeString( bstrErrorDescription );
} // if: message formatted successfully
TraceSysFreeString( bstrError );
//
// This error handling is for the return value from
// HrCreateTreeItem above. It is here so that all the strings
// can be cleaned up without having to resort to hokey
// boolean variables or move the bstrs to a more global scope.
//
if ( FAILED( hr ) ) { goto Cleanup; } // if:
} // if: child and error
//////////////////////////////////////////////////////////////
//
// If a child was found or created, propagate its state to
// the parent items.
//
//////////////////////////////////////////////////////////////
if ( htiChild != NULL ) { hr = STHR( HrPropagateChildStateToParents( htiChild , nImageIn , FALSE // fOnlyUpdateProgressIn
) ); if ( FAILED( hr ) ) { goto Cleanup; } // if:
} // if: found or created a child
//
// Return success since we found the parent for this report.
//
hr = S_OK; break;
} // if: found an item to be the parent
else { //
// PARENT ITEM NOT FOUND
//
// Recurse through all the child items.
//
htiChild = TreeView_GetChild( m_hwndTV, htiParent ); while ( htiChild != NULL ) { hr = STHR( HrInsertTaskIntoTree( htiChild, ptipdIn, nImageIn, bstrDescriptionIn ) ); if ( hr == S_OK ) { // Found a match, so exit the loop.
break; } // if:
htiChild = TreeView_GetNextSibling( m_hwndTV, htiChild ); } // while: more child items
} // else: item not the parent
//
// Get the next sibling of the parent.
//
htiParent = TreeView_GetNextSibling( m_hwndTV, htiParent );
} // for: each item at this level in the tree
Cleanup:
RETURN( hr );
} //*** CTaskTreeView::HrInsertTaskIntoTree
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrProcessUpdateProgressTask
//
// Description:
// Update the progress bar based on new tree item data.
//
// Arguments:
// ptipdNewIn - New values of the tree item data.
//
// Return Values:
// S_OK - Operation completed successfully.
// E_OUTOFMEMORY - Failure allocating memory
// Other HRESULTs
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrProcessUpdateProgressTask( const STreeItemLParamData * ptipdIn ) { TraceFunc( "" );
HRESULT hr = S_OK; STreeItemLParamData * ptipdPrev = NULL; size_t idx; size_t cPassed; size_t idxPrev = 0; BOOL fNewTask = FALSE; // Are ptipdPrev && ptipdIn the same task?
Assert( ptipdIn != NULL ); Assert( IsEqualIID( ptipdIn->clsidMajorTaskId, TASKID_Major_Update_Progress ) );
//
// Is this a one-off event? min == max == current
//
if ( ( ptipdIn->nMin == ptipdIn->nMax ) && ( ptipdIn->nMax == ptipdIn->nCurrent ) ) { // Yes - don't bother mucking with the array.
STHR( HrUpdateProgressBar( ptipdIn, ptipdIn ) ); hr = S_OK; goto Cleanup; } // if: one-off event
//
// Check to see if this task is in the array.
//
for ( idx = 0, cPassed = 0; ( idx < m_cPASize ) && ( cPassed < m_cPACount ); idx++ ) { if ( m_ptipdProgressArray[ idx ] != NULL ) { // Check the minors and make sure this is the same node.
if ( IsEqualIID( m_ptipdProgressArray[ idx ]->clsidMinorTaskId, ptipdIn->clsidMinorTaskId ) && ( NBSTRCompareNoCase( m_ptipdProgressArray[ idx ]->bstrNodeName, ptipdIn->bstrNodeName ) == 0 ) ) { ptipdPrev = m_ptipdProgressArray[ idx ]; idxPrev = idx; break; } // if:
// If the array is X elements long and we have Y elements, stop after looking at Y elements.
cPassed++; } // if: current slot is null
} // for: each item in the array until we find a match
if ( ptipdPrev == NULL ) { //
// We didn't find it in the list - we'll have to insert it.
//
if ( m_ptipdProgressArray == NULL ) { Assert( m_cPACount == 0 ); //
// We have to allocate the array.
//
m_cPASize = 10; // Pick a reasonable initial array size.
m_ptipdProgressArray = new PSTreeItemLParamData[ m_cPASize ]; if ( m_ptipdProgressArray == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
ZeroMemory( m_ptipdProgressArray, m_cPASize * sizeof( m_ptipdProgressArray[ 0 ] ) );
// We just allocated the array so we know the first slot is open.
idx = 0; } // if: we need to allocate the array
else if ( m_cPACount == m_cPASize ) { STreeItemLParamData ** ptipdTempArray = NULL;
Assert( m_cPASize != 0 );
//
// We need to increase the size of the array.
//
ptipdTempArray = new STreeItemLParamData* [ m_cPASize * 2 ]; if ( ptipdTempArray == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
// Copy the old array over the first half of the new array.
CopyMemory( ptipdTempArray, m_ptipdProgressArray, m_cPASize * sizeof( m_ptipdProgressArray[ 0 ] ) );
// Zero out the second half of the new array.
ZeroMemory( &ptipdTempArray[ m_cPASize ], m_cPASize * sizeof( ptipdTempArray[ 0 ] ) );
//
// Update member variables to reflect the changes.
//
m_cPASize *= 2; // We doubled the array length.
delete [] m_ptipdProgressArray; m_ptipdProgressArray = ptipdTempArray; ptipdTempArray = NULL;
// We know the first open slot is at index m_cPACount.
idx = m_cPACount; } // else: we've used all available slots
else { // Else we have a spot open somewhere in the existing array. Start searching from 0.
idx = 0; } // else: there's an available slot
//
// Find an empty slot and allocate a new task to put in it.
//
for ( ; idx < m_cPASize; idx++ ) { if ( m_ptipdProgressArray[ idx ] == NULL ) { //
// Found an empty slot - allocate the new task.
//
ptipdPrev = new STreeItemLParamData; if ( ptipdPrev == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
ptipdPrev->bstrNodeName = TraceSysAllocString( ptipdIn->bstrNodeName ); if ( ( ptipdIn->bstrNodeName != NULL ) && ( ptipdPrev->bstrNodeName == NULL ) ) { hr = THR( E_OUTOFMEMORY ); delete ptipdPrev; ptipdPrev = NULL; goto Cleanup; } // if:
CopyMemory( &ptipdPrev->clsidMajorTaskId, &ptipdIn->clsidMajorTaskId, sizeof( ptipdIn->clsidMajorTaskId ) ); CopyMemory( &ptipdPrev->clsidMinorTaskId, &ptipdIn->clsidMinorTaskId, sizeof( ptipdIn->clsidMinorTaskId ) ); ptipdPrev->nMin = ptipdIn->nMin; ptipdPrev->nMax = ptipdIn->nMax; ptipdPrev->nCurrent = ptipdIn->nCurrent; ptipdPrev->fParentToAllNodeTasks = ptipdIn->fParentToAllNodeTasks;
m_ptipdProgressArray[ idx ] = ptipdPrev; m_cPACount++; fNewTask = TRUE; idxPrev = idx; break; } // if:
} // for: find an emtpy slot
} // if: couldn't find a matching task in the array
Assert( ptipdPrev != NULL ); Assert( ptipdIn->bstrReference == NULL ); Assert( ptipdIn->nMin < ptipdIn->nMax ); Assert( ptipdIn->nMin <= ptipdIn->nCurrent ); Assert( ptipdIn->nCurrent <= ptipdIn->nMax );
//
// Update the progress bar.
//
STHR( HrUpdateProgressBar( ptipdIn, ptipdPrev ) ); // Ignore failure.
//
// If the task has completed, remove it from the array.
//
if ( ptipdIn->nMax == ptipdIn->nCurrent ) { TraceSysFreeString( ptipdPrev->bstrNodeName ); delete ptipdPrev; m_ptipdProgressArray[ idxPrev ] = NULL; m_cPACount--; } // if: task complete
else if ( fNewTask == FALSE ) { //
// Else, update ptipdPrev only if we didn't just copy the array.
//
// This could have been a range-changing event, so copy the min, max, current.
ptipdPrev->nMin = ptipdIn->nMin; ptipdPrev->nMax = ptipdIn->nMax; ptipdPrev->nCurrent = ptipdIn->nCurrent; } // else if: not a new task
Cleanup:
RETURN( hr );
} //*** CTaskTreeView::HrProcessUpdateProgressTask
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrUpdateProgressBar
//
// Description:
// Update the progress bar based on new tree item data.
//
// Arguments:
// ptipdNewIn - New values of the tree item data.
// ptipdPrevIn - Previous values of the tree item data.
//
// Return Values:
// S_OK - Operation completed successfully.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrUpdateProgressBar( const STreeItemLParamData * ptipdNewIn , const STreeItemLParamData * ptipdPrevIn ) { TraceFunc( "" );
HRESULT hr = S_OK; ULONG nRealPos = 0; ULONG nShrink = 0; ULONG nExpand = 0; ULONG nProgress = 0;
//
// Verify parameters.
//
Assert( m_hwndProg != NULL ); Assert( ptipdNewIn != NULL ); Assert( ptipdPrevIn != NULL ); Assert( (ptipdPrevIn->nCurrent <= ptipdPrevIn->nMax) && (ptipdNewIn->nCurrent <= ptipdNewIn->nMax) ); Assert( (ptipdPrevIn->nCurrent >= ptipdPrevIn->nMin) && (ptipdNewIn->nCurrent >= ptipdNewIn->nMin) );
//
// Make sure we're only passed a task update that we can analyze.
//
Assert( IsEqualIID( ptipdPrevIn->clsidMajorTaskId, IID_NULL ) == FALSE ); Assert( IsEqualIID( ptipdPrevIn->clsidMajorTaskId, ptipdNewIn->clsidMajorTaskId ) == TRUE ) // Majors match
Assert( IsEqualIID( ptipdPrevIn->clsidMinorTaskId, ptipdNewIn->clsidMinorTaskId ) == TRUE ) // && Minors match
if ( IsEqualIID( ptipdPrevIn->clsidMajorTaskId, ptipdNewIn->clsidMajorTaskId ) == FALSE // Majors don't match
|| IsEqualIID( ptipdPrevIn->clsidMinorTaskId, ptipdNewIn->clsidMinorTaskId ) == FALSE ) // or Minors don't match
{ // This update is meaningless because we don't know how to compare the two IN params.
WCHAR szNewMajor[ 64 ]; WCHAR szNewMinor[ 64 ]; WCHAR szPrevMajor[ 64 ]; WCHAR szPrevMinor[ 64 ]; int cch;
cch = StringFromGUID2( ptipdNewIn->clsidMajorTaskId, szNewMajor, RTL_NUMBER_OF( szNewMajor ) ); Assert( cch > 0 ); // 64 chars should always hold a guid!
cch = StringFromGUID2( ptipdNewIn->clsidMinorTaskId, szNewMinor, RTL_NUMBER_OF( szNewMinor ) ); Assert( cch > 0 ); // 64 chars should always hold a guid!
cch = StringFromGUID2( ptipdPrevIn->clsidMajorTaskId, szPrevMajor, RTL_NUMBER_OF( szPrevMajor ) ); Assert( cch > 0 ); // 64 chars should always hold a guid!
cch = StringFromGUID2( ptipdPrevIn->clsidMinorTaskId, szPrevMinor, RTL_NUMBER_OF( szPrevMinor ) ); Assert( cch > 0 ); // 64 chars should always hold a guid!
LogMsg( L"[WIZ] Ignoring invalid progress -- major and minor task IDs do not match. new major = %ws, new minor = %ws, prev major = %ws, prev minor = %ws" , szNewMajor , szNewMinor , szPrevMajor , szPrevMinor ); hr = THR( S_FALSE ); goto Cleanup; } // if: the two tipd's don't match
if ( ( ptipdNewIn->nMin == ptipdNewIn->nMax ) && ( ptipdNewIn->nCurrent == ptipdNewIn->nMax ) ) { // This is a one-off: min == max && current == max.
nExpand = ptipdNewIn->nCurrent; nProgress = ptipdNewIn->nCurrent; } // else: this is a one-off
else if ( ( ptipdNewIn->nMax != ptipdPrevIn->nMax ) || ( ptipdNewIn->nMin != ptipdPrevIn->nMin ) ) { //
// The min's and/or maxes changed. Verify that it follows the rules.
//
// Rules:
// Max: If the max changes then min and current need to stay the same.
// Resizing max can not cause the current to exceed max.
//
// Min: If the min changes then max can't change and the difference
// between current and min has to remain the same.
//
// In no case should a resizing cause min == max == current - this
// will be treated as a one-off event and may orphan a task in the
// update progress array.
//
if ( ptipdNewIn->nMax != ptipdPrevIn->nMax ) // Maxes changed
{ if ( ( ptipdNewIn->nCurrent != ptipdPrevIn->nCurrent ) // Currents changed
|| ( ptipdNewIn->nMin != ptipdPrevIn->nMin ) // or Mins changed
|| ( ptipdNewIn->nCurrent > ptipdNewIn->nMax ) ) // or current exceeded the max
{ LogMsg( L"[WIZ] Ignoring invalid progress -- mins and/or maxes are invalid. new min = %d, prev min = %d, new max = %d, prev max = %d, new current = %d, prev current = %d" , ptipdNewIn->nMin , ptipdPrevIn->nMin , ptipdNewIn->nMax , ptipdPrevIn->nMax , ptipdNewIn->nCurrent , ptipdPrevIn->nCurrent ); hr = THR( S_FALSE ); goto Cleanup; } // if:
//
// The max changed, meaning we'll need to modify the range by that much.
//
if ( ptipdNewIn->nMax > ptipdPrevIn->nMax ) { nExpand = ptipdNewIn->nMax - ptipdPrevIn->nMax; } // if:
else { nShrink = ptipdPrevIn->nMax - ptipdNewIn->nMax; } // else:
} // if: maxes differ
else // Mins changed
{ // If the difference between min & current varies between the two, or if the new current > new max then fail.
if ( ( ptipdNewIn->nCurrent - ptipdNewIn->nMin ) != ( ptipdPrevIn->nCurrent - ptipdPrevIn->nMin ) || ( ptipdNewIn->nCurrent > ptipdNewIn->nMax ) ) { LogMsg( L"[WIZ] Ignoring invalid progress -- the range between current and max is incorrect. new current - new min = %d, prev current - prev min = %d, new current %d > new max %d" , ( ptipdNewIn->nCurrent - ptipdNewIn->nMin ) , ( ptipdPrevIn->nCurrent - ptipdPrevIn->nMin ) , ptipdNewIn->nCurrent , ptipdNewIn->nMax ); hr = THR( S_FALSE ); goto Cleanup; } // if:
//
// The min changed, meaning we need to modify the range by the difference.
//
if ( ptipdNewIn->nMin > ptipdPrevIn->nMin ) { nShrink = ptipdNewIn->nMin - ptipdPrevIn->nMin; } // if:
else { nExpand = ptipdPrevIn->nMin - ptipdNewIn->nMin; } // else:
} // else: mins changed
} // else if: min or max changed
else if ( ( ptipdNewIn->nMax == ptipdPrevIn->nMax ) // max didn't change
&& ( ptipdNewIn->nMin == ptipdPrevIn->nMin ) // and min didn't change
&& ( ptipdNewIn->nCurrent == ptipdPrevIn->nCurrent ) // and current didn't change
&& ( ptipdNewIn->nCurrent == ptipdNewIn->nMin ) ) // and current equals min
{ nExpand = ptipdNewIn->nMax - ptipdNewIn->nMin; // We have a new task.
} // else if: we're adding a new task
else if ( ( ptipdNewIn->nMax == ptipdPrevIn->nMax ) // max didn't change
&& ( ptipdNewIn->nMin == ptipdPrevIn->nMin ) // and min didn't change
&& ( ptipdNewIn->nCurrent > ptipdPrevIn->nCurrent ) // and current increased
&& ( ptipdNewIn->nCurrent <= ptipdNewIn->nMax ) ) // and current didn't exceed max
{ nProgress = ptipdNewIn->nCurrent - ptipdPrevIn->nCurrent; // We have an update.
} // else if: we're updating a known task
else { // This event broke the rules - toss it out.
LogMsg( L"[WIZ] Ignoring invalid progress -- min, max or current are invalid. new min = %d, prev min = %d, new max = %d, prev max = %d, new current = %d, prev current = %d" , ptipdNewIn->nMin , ptipdPrevIn->nMin , ptipdNewIn->nMax , ptipdPrevIn->nMax , ptipdNewIn->nCurrent , ptipdPrevIn->nCurrent ); hr = THR( S_FALSE ); goto Cleanup; } // else if: the two tipd's don't conform to the rules of a progress bar update
m_nRangeHigh += nExpand; Assert( m_nRangeHigh >= nShrink ) m_nRangeHigh -= nShrink; if ( m_nRangeHigh > m_nInitialTickCount ) { m_fThresholdBroken = TRUE; } // if: exceeded the initial tick count
m_nCurrentPos += nProgress;
if ( m_nCurrentPos >= m_nRangeHigh ) { //
// Something went wrong - our position is now somehow greater than
// the range (multi-threading issue?). Simple fix - set our new range
// to be PROGRESSBAR_RESIZE_PERCENT percent greater than our new position
//
m_nRangeHigh = ( m_nCurrentPos * (100 + PROGRESSBAR_RESIZE_PERCENT) ) / 100; } // if: current pos caught up to the upper range
// Adjust to the progress bar's scale (PROGRESSBAR_CONTROL_TICK_COUNT).
nRealPos = m_nCurrentPos * PROGRESSBAR_CONTROL_TICK_COUNT; if ( m_fThresholdBroken ) { nRealPos /= m_nRangeHigh; } // if: use threshold
else { nRealPos /= m_nInitialTickCount; } // else: use initial tick count
//
// If our progress bar position actually moved forward - update the control.
// This isn't always the case because we may have a new task come in and
// report a large number of steps, thereby moving our real position
// backwards, but we don't want to show reverse progress - just a steady
// advancement towards being done.
//
if ( nRealPos > m_nRealPos ) { m_nRealPos = nRealPos; SendMessage( m_hwndProg, PBM_SETPOS, m_nRealPos, 0 ); } // if: forward progress
Cleanup:
HRETURN( hr );
} //*** CTaskTreeView::HrUpdateProgressBar
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrPropagateChildStateToParents
//
// Description:
// Extend the state of a child item to its parent items.
// If the state of the child is worse (higher priority) than the
// parent's, update the state of the parent.
//
// Arguments:
// htiChildIn - Child item whose state is to be extended.
// nImageIn - Image of the child item.
// fOnlyUpdateProgressIn - TRUE = only updating progress.
//
// Return Values:
// S_OK - Operation completed successfully.
// S_FALSE - No parent item.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrPropagateChildStateToParents( HTREEITEM htiChildIn , int nImageIn , BOOL fOnlyUpdateProgressIn ) { TraceFunc( "" );
Assert( htiChildIn != NULL );
HRESULT hr = S_OK; BOOL fReturn; TVITEMEX tviParent; TVITEMEX tviChild; HTREEITEM htiParent; HTREEITEM htiChild;
//
// Get the parent item.
//
htiParent = TreeView_GetParent( m_hwndTV, htiChildIn ); if ( htiParent == NULL ) { hr = S_FALSE; goto Cleanup; } // if:
tviParent.mask = TVIF_PARAM | TVIF_IMAGE; tviParent.hItem = htiParent;
fReturn = TreeView_GetItem( m_hwndTV, &tviParent ); if ( ! fReturn ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_NOT_FOUND ) ); goto Cleanup; } // if:
//
// If the state of the child is worse (higher priority) than the
// parent's, update the state of the parent.
//
if ( ( tviParent.iImage < nImageIn ) || ( ( tviParent.iImage == tsDONE ) && ( nImageIn == tsPENDING ) ) ) { //
// Special Case: For the parent to be set to tsDONE, all
// the children must be set to tsDONE as well.
//
if ( ( nImageIn == tsDONE ) && ! fOnlyUpdateProgressIn ) { //
// Enum the children to see if they all have tsDONE as their images.
//
htiChild = TreeView_GetChild( m_hwndTV, tviParent.hItem ); while ( htiChild != NULL ) { tviChild.mask = TVIF_IMAGE; tviChild.hItem = htiChild;
fReturn = TreeView_GetItem( m_hwndTV, &tviChild ); if ( ! fReturn ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_NOT_FOUND ) ); goto Cleanup; } // if:
if ( tviChild.iImage != tsDONE ) { //
// Not all tsDONE! Skip setting parent's image!
// This can occur if the child is displaying a warning
// or error state image.
//
goto Cleanup; } // if:
// Get next child
htiChild = TreeView_GetNextSibling( m_hwndTV, htiChild ); } // while: more children
} // if: special case (see above)
//
// Set the parent's icon.
//
tviParent.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; tviParent.iImage = nImageIn; tviParent.iSelectedImage = nImageIn; TreeView_SetItem( m_hwndTV, &tviParent ); } // if: need to update parent's image
//
// Traverse up the tree.
//
hr = STHR( HrPropagateChildStateToParents( htiParent, nImageIn, fOnlyUpdateProgressIn ) ); if ( hr == S_FALSE ) { // S_FALSE means that there wasn't a parent.
hr = S_OK; } // if:
goto Cleanup;
Cleanup: HRETURN( hr );
} //*** CTaskTreeView::HrPropagateChildStateToParents
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrDisplayDetails
//
// Description:
// Display the Details dialog box.
//
// Arguments:
// None.
//
// Return Values:
// S_OK
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrDisplayDetails( void ) { TraceFunc( "" );
HRESULT hr = S_OK; HTREEITEM hti; HWND hwndPropertyPage;
//
// If no item is selected, select the first item.
//
if ( m_htiSelected == NULL ) { hti = TreeView_GetRoot( m_hwndTV ); Assert( hti != NULL ); hr = THR( HrSelectItem( hti ) ); if ( FAILED( hr ) ) { // TODO: Display message box
goto Cleanup; } // if:
} // if: no items are selected
//
// Display the dialog box.
//
hwndPropertyPage = GetParent( m_hwndTV ); Assert( hwndPropertyPage != NULL ); hr = THR( CDetailsDlg::S_HrDisplayModalDialog( hwndPropertyPage, this, m_htiSelected ) );
SetFocus( m_hwndTV );
Cleanup: HRETURN( hr );
} //*** CTaskTreeView::HrDisplayDetails
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::FGetItem
//
// Description:
// Get the data for an item.
//
// Arguments:
// htiIn - Handle for the item to get.
// pptipdOut - Pointer in which to return the data structure.
//
// Return Values:
// TRUE - Item returned successfully.
// FALSE - Item not returned.
//
//--
//////////////////////////////////////////////////////////////////////////////
BOOL CTaskTreeView::FGetItem( HTREEITEM htiIn , STreeItemLParamData ** pptipd ) { TraceFunc( "" );
Assert( htiIn != NULL ); Assert( pptipd != NULL );
BOOL fRet; TVITEMEX tvi;
ZeroMemory( &tvi, sizeof( tvi ) );
tvi.mask = TVIF_PARAM; tvi.hItem = htiIn;
fRet = TreeView_GetItem( m_hwndTV, &tvi ); if ( fRet == FALSE ) { goto Cleanup; } // if:
Assert( tvi.lParam != NULL ); *pptipd = reinterpret_cast< STreeItemLParamData * >( tvi.lParam );
Cleanup: RETURN( fRet );
} //*** CTaskTreeView::FGetItem
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrFindPrevItem
//
// Description:
// Find the previous item. The previous item could be at a deeper
// level than this item.
//
// Arguments:
// phtiOut - Handle to previous item (optional).
//
// Return Values:
// S_OK - Previous item found successfully.
// S_FALSE - No previous item found.
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrFindPrevItem( HTREEITEM * phtiOut ) { TraceFunc( "" );
HRESULT hr = S_FALSE; HTREEITEM htiCur; HTREEITEM htiPrev;
htiCur = m_htiSelected;
if ( phtiOut != NULL ) { *phtiOut = NULL; } // if:
//
// Find the previous sibling item.
//
htiPrev = TreeView_GetPrevSibling( m_hwndTV, htiCur ); if ( htiPrev == NULL ) { //
// NO PREVIOUS SIBLING ITEM FOUND.
//
// Find the parent item.
// If there isn't a parent, then there isn't a previous item.
//
htiPrev = TreeView_GetParent( m_hwndTV, htiCur ); if ( htiPrev == NULL ) { goto Cleanup; } // if: no parent item
//
// The parent is the previous item.
//
} // if: no previous sibling
else { //
// PREVIOUS SIBLING ITEM FOUND.
//
// Find the deepest child of the last child item.
//
for ( ;; ) { //
// Find the first child item.
//
htiCur = TreeView_GetChild( m_hwndTV, htiPrev ); if ( htiCur == NULL ) { //
// NO CHILD ITEM FOUND.
//
// This is the previous item.
//
break;
} // if: no children
//
// CHILD ITEM FOUND.
//
// Find the last sibling of this child item.
//
for ( ;; ) { //
// Find the next sibling item.
//
htiPrev = TreeView_GetNextSibling( m_hwndTV, htiCur ); if ( htiPrev == NULL ) { //
// No next sibling item found.
// Exit this loop and continue the outer loop
// to find this item's children.
//
htiPrev = htiCur; break; } // if: no next sibling item found
//
// Found a next sibling item.
//
htiCur = htiPrev; } // forever: find the last child item
} // forever: find the deepest child item
} // else: previous sibling item found
//
// Return the item we found.
//
Assert( htiPrev != NULL );
if ( phtiOut != NULL ) { *phtiOut = htiPrev; } // if:
hr = S_OK;
Cleanup: HRETURN( hr );
} //*** CTaskTreeView::HrFindPrevItem
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrFindNextItem
//
// Description:
// Find the next item. The next item could be at a different level than
// this item.
//
// Arguments:
// phtiOut - Handle to next item (optional).
//
// Return Values:
// S_OK - Next item found successfully.
// S_FALSE - No next item found.
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrFindNextItem( HTREEITEM * phtiOut ) { TraceFunc( "" );
HRESULT hr = S_FALSE; HTREEITEM htiCur; HTREEITEM htiNext;
htiCur = m_htiSelected;
if ( phtiOut != NULL ) { *phtiOut = NULL; } // if:
//
// Find the first child item.
//
htiNext = TreeView_GetChild( m_hwndTV, htiCur ); if ( htiNext == NULL ) { //
// NO CHILD ITEM FOUND.
//
for ( ;; ) { //
// Get the next sibling item.
//
htiNext = TreeView_GetNextSibling( m_hwndTV, htiCur ); if ( htiNext == NULL ) { //
// NO SIBLING ITEM FOUND.
//
// Find the parent item so we can find its next sibling.
//
htiNext = TreeView_GetParent( m_hwndTV, htiCur ); if ( htiNext == NULL ) { //
// NO PARENT ITEM FOUND.
//
// At the end of the tree.
//
goto Cleanup; } // if: no parent found
//
// PARENT ITEM FOUND.
//
// Find the parent item's next sibling.
//
htiCur = htiNext; continue; } // if: no next sibling item
//
// SIBLING ITEM FOUND.
//
// Found the next item.
//
break; } // forever: find the next sibling or parent's sibling
} // if: no child item found
else { //
// CHILD ITEM FOUND.
//
// Found the next item.
//
} // else: child item found
//
// Return the item we found.
//
Assert( htiNext != NULL );
if ( phtiOut != NULL ) { *phtiOut = htiNext; } // if:
hr = S_OK;
Cleanup: HRETURN( hr );
} //*** CTaskTreeView::HrFindNextItem
//////////////////////////////////////////////////////////////////////////////
//++
//
// CTaskTreeView::HrSelectItem
//
// Description:
// Select the specified item.
//
// Arguments:
// htiIn - Handle to item to select.
//
// Return Values:
// S_OK - Item selected successfully.
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CTaskTreeView::HrSelectItem( HTREEITEM htiIn ) { TraceFunc( "" );
Assert( htiIn != NULL );
HRESULT hr = S_OK;
TreeView_SelectItem( m_hwndTV, htiIn );
HRETURN( hr );
} //*** CTaskTreeView::HrSelectItem
//****************************************************************************
//
// Static Functions
//
//****************************************************************************
//////////////////////////////////////////////////////////////////////////////
//++
//
// HrCreateTreeItem
//
// Description:
// Create a tree item.
//
// Arguments:
// ptvisOut - Tree view insert structure to fill in.
// ptipdIn - Input tree item LParam data to create this item from.
// htiParentIn - Parent tree view item.
// nImageIn - Image index.
// bstrTextIn - Text to display.
//
// Return Values:
// S_OK - Operation was successful.
// E_OUTOFMEMORY - Error allocating memory.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT HrCreateTreeItem( TVINSERTSTRUCT * ptvisOut , STreeItemLParamData * ptipdIn , HTREEITEM htiParentIn , int nImageIn , BSTR bstrTextIn ) { TraceFunc( "" );
Assert( ptvisOut != NULL ); Assert( ptipdIn != NULL ); Assert( htiParentIn != NULL ); Assert( bstrTextIn != NULL );
// LOCAL VARIABLES
HRESULT hr = S_OK; STreeItemLParamData * ptipdNew = NULL;
//
// Allocate the tree view LParam data and initialize it.
//
ptipdNew = new STreeItemLParamData; if ( ptipdNew == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
CopyMemory( &ptipdNew->clsidMajorTaskId, &ptipdIn->clsidMajorTaskId, sizeof( ptipdNew->clsidMajorTaskId ) ); CopyMemory( &ptipdNew->clsidMinorTaskId, &ptipdIn->clsidMinorTaskId, sizeof( ptipdNew->clsidMinorTaskId ) ); CopyMemory( &ptipdNew->ftTime, &ptipdIn->ftTime, sizeof( ptipdNew->ftTime ) ); ptipdNew->nMin = ptipdIn->nMin; ptipdNew->nMax = ptipdIn->nMax; ptipdNew->nCurrent = ptipdIn->nCurrent; ptipdNew->hr = ptipdIn->hr;
if ( ptipdIn->bstrNodeName != NULL ) { ptipdNew->bstrNodeName = TraceSysAllocString( ptipdIn->bstrNodeName ); if ( ptipdNew->bstrNodeName == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
Assert( ptipdIn->bstrNodeNameWithoutDomain != NULL ); ptipdNew->bstrNodeNameWithoutDomain = TraceSysAllocString( ptipdIn->bstrNodeNameWithoutDomain ); if ( ptipdNew->bstrNodeNameWithoutDomain == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
} // if: node name specified
if ( ptipdIn->bstrDescription != NULL ) { ptipdNew->bstrDescription = TraceSysAllocString( ptipdIn->bstrDescription ); if ( ptipdNew->bstrDescription == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
} // if: description specified
if ( ptipdIn->bstrReference != NULL ) { ptipdNew->bstrReference = TraceSysAllocString( ptipdIn->bstrReference ); if ( ptipdNew->bstrReference == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if:
} // if: reference specified
//
// Initialize the tree view insert structure.
//
ptvisOut->hParent = htiParentIn; ptvisOut->hInsertAfter = TVI_LAST; ptvisOut->itemex.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; ptvisOut->itemex.cchTextMax = SysStringLen( bstrTextIn ); ptvisOut->itemex.pszText = bstrTextIn; ptvisOut->itemex.iImage = nImageIn; ptvisOut->itemex.iSelectedImage = nImageIn; ptvisOut->itemex.lParam = reinterpret_cast< LPARAM >( ptipdNew );
Assert( ptvisOut->itemex.cchTextMax > 0 );
// Release ownership to the tree view insert structure.
ptipdNew = NULL;
goto Cleanup;
Cleanup:
if ( ptipdNew != NULL ) { TraceSysFreeString( ptipdNew->bstrNodeName ); TraceSysFreeString( ptipdNew->bstrNodeNameWithoutDomain ); TraceSysFreeString( ptipdNew->bstrDescription ); TraceSysFreeString( ptipdNew->bstrReference ); delete ptipdNew; } // if:
HRETURN( hr );
} //*** HrCreateTreeItem
|