////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000-2001 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 HIGH_TICK_COUNT 400 //**************************************************************************** // // Static Function Prototypes // //**************************************************************************** static HRESULT HrCreateTreeItem( TVINSERTSTRUCT * ptvisOut , STreeItemLParamData * ptipdIn , HTREEITEM htiParentIn , int nImageIn , BSTR bstrTextIn ); ////////////////////////////////////////////////////////////////////////////// //++ // // CTaskTreeView::CTaskTreeView( // HWND hwndParentIn, // UINT uIDTVIn, // UINT uIDProgressIn, // UINT uIDStatusIn // ) // //-- ////////////////////////////////////////////////////////////////////////////// CTaskTreeView::CTaskTreeView( HWND hwndParentIn, UINT uIDTVIn, UINT uIDProgressIn, UINT uIDStatusIn ) { TraceFunc( "" ); Assert( m_htiSelected == NULL ); 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; TraceFuncExit(); } //*** CTaskTreeView::CTaskTreeView() ////////////////////////////////////////////////////////////////////////////// //++ // // CTaskTreeView::~CTaskTreeView( void ) // //-- ////////////////////////////////////////////////////////////////////////////// CTaskTreeView::~CTaskTreeView( void ) { TraceFunc( "" ); TreeView_DeleteAllItems( m_hwndTV ); if ( m_hImgList != NULL ) { ImageList_Destroy( m_hImgList ); } TraceFuncExit(); } //*** CTaskTreeView::CTaskTreeView() ////////////////////////////////////////////////////////////////////////////// //++ // // HRESULT // CTaskTreeView::HrOnInitDialog( void ) // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CTaskTreeView::HrOnInitDialog( void ) { TraceFunc( "" ); HRESULT hr = S_OK; DWORD sc; HICON hIcon; int idx; // // Build image list for icons in tree view. // m_hImgList = ImageList_Create( 16, 16, ILC_MASK, tsMAX, 0); if ( m_hImgList == NULL ) { sc = TW32( GetLastError() ); goto Win32Error; } // // Unknown Icon - Task Unknown. // hIcon = (HICON) LoadImage( g_hInstance, MAKEINTRESOURCE( IDI_SEL ), IMAGE_ICON, 16, 16, LR_SHARED ); if ( hIcon == NULL ) { sc = TW32( GetLastError() ); goto Win32Error; } 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 ) { sc = TW32( GetLastError() ); goto Win32Error; } 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 ) { sc = TW32( GetLastError() ); goto Win32Error; } 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 ) { sc = TW32( GetLastError() ); goto Win32Error; } 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 ) { sc = TW32( GetLastError() ); goto Win32Error; } 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 ) ); goto Cleanup; Win32Error: hr = HRESULT_FROM_WIN32( sc ); goto Cleanup; 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. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CTaskTreeView::HrAddTreeViewItem( HTREEITEM * phtiOut , UINT idsIn , REFCLSID rclsidMinorTaskIDIn , REFCLSID rclsidMajorTaskIDIn // = IID_NULL , HTREEITEM htiParentIn // = TVI_ROOT ) { TraceFunc( "" ); HRESULT hr = S_OK; STreeItemLParamData * ptipd; SYSTEMTIME systemtime; TVINSERTSTRUCT tvis; HTREEITEM hti = NULL; // // Allocate an item data structure and initialize it. // ptipd = new STreeItemLParamData; if ( ptipd == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } hr = THR( HrGetComputerName( ComputerNamePhysicalDnsFullyQualified, &ptipd->bstrNodeName ) ); if ( FAILED( hr ) ) { goto Cleanup; } hr = THR( HrLoadStringIntoBSTR( g_hInstance, idsIn, &ptipd->bstrDescription ) ); if ( FAILED( hr ) ) { goto Cleanup; } GetSystemTime( &systemtime ); if ( ! SystemTimeToFileTime( &systemtime, &ptipd->ftTime ) ) { DWORD sc = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( sc ); goto Cleanup; } CopyMemory( &ptipd->clsidMajorTaskId, &rclsidMajorTaskIDIn, sizeof( ptipd->clsidMajorTaskId ) ); CopyMemory( &ptipd->clsidMinorTaskId, &rclsidMinorTaskIDIn, sizeof( ptipd->clsidMinorTaskId ) ); // // 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; } goto Cleanup; Cleanup: if ( ptipd != NULL ) { TraceSysFreeString( ptipd->bstrNodeName ); TraceSysFreeString( ptipd->bstrDescription ); delete ptipd; } HRETURN( hr ); } //*** CTaskTreeView::HrAddTreeViewRootItem() ////////////////////////////////////////////////////////////////////////////// //++ // // 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->bstrDescription ); TraceSysFreeString( ptipd->bstrReference ); delete ptipd; } 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; #if defined( DEBUG ) ULONG_PTR ulCurrent; ulCurrent = SendMessage( m_hwndProg, PBM_GETPOS, 0, 0 ); AssertMsg( ulCurrent + 100 <= HIGH_TICK_COUNT, "Need to adjust HIGH_TICK_COUNT higher!" ); #endif 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; } SetWindowText( m_hwndStatus, bstrDescription ); Cleanup: TraceSysFreeString( bstrDescription ); HRETURN( hr ); } //*** CTaskTreeView::HrShowStatusAsDone() ////////////////////////////////////////////////////////////////////////////// //++ // // HRESULT // CTaskTreeView::HrOnNotifySetActive( void ) // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CTaskTreeView::HrOnNotifySetActive( void ) { TraceFunc( "" ); HRESULT hr = S_OK; TreeView_DeleteAllItems( m_hwndTV ); SetWindowText( m_hwndStatus, L"" ); m_ulLowNibble = 0; m_ulHighNibble = HIGH_TICK_COUNT; SendMessage( m_hwndProg, PBM_SETRANGE, 0, MAKELPARAM( m_ulLowNibble, m_ulHighNibble ) ); SendMessage( m_hwndProg, PBM_SETPOS, m_ulLowNibble, 0 ); HRETURN( hr ); } //*** CTaskTreeView::HrOnNotifySetActive() ////////////////////////////////////////////////////////////////////////////// //++ // // CTaskTreeView::HrOnSendStatusReport // // Description: // Handle a status report call. // // Arguments: // pcszNodeNameIn - // clsidTaskMajorIn - // clsidTaskMinorIn - // ulMinIn - // ulMaxIn - // ulCurrentIn - // hrStatusIn - // pcszDescriptionIn - // pftTimeIn - // pcszReferenceIn - // // Return Values: // S_OK - Operation completed successfully. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CTaskTreeView::HrOnSendStatusReport( LPCWSTR pcszNodeNameIn , CLSID clsidTaskMajorIn , CLSID clsidTaskMinorIn , ULONG ulMinIn , ULONG ulMaxIn , ULONG ulCurrentIn , HRESULT hrStatusIn , LPCWSTR pcszDescriptionIn , FILETIME * pftTimeIn , LPCWSTR pcszReferenceIn ) { TraceFunc( "" ); HRESULT hr = S_OK; DWORD sc; int nImageChild; STreeItemLParamData tipd; HTREEITEM htiRoot; BSTR bstrStatus = NULL; ////////////////////////////////////////////////////////////////////////// // // 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 ) { LPWSTR psz; // // Shorten the FDQN DNS name to only the hostname. // psz = wcschr( pcszNodeNameIn, L'.' ); if ( psz != NULL ) { *psz = L'\0'; } hr = THR( HrFormatMessageIntoBSTR( g_hInstance , IDS_FORMAT_STATUS , &bstrStatus , pcszNodeNameIn , pcszDescriptionIn ) ); // // Restore the FQDN DNS name. // if ( psz != NULL ) { *psz = L'.'; } // // Handle the formatting error if there was one. // if ( FAILED( hr ) ) { // TODO: Display default description instead of exiting this function goto Error; } } // if: node name was specified else { bstrStatus = TraceSysAllocString( pcszDescriptionIn ); if ( bstrStatus == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } } 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 ( ulCurrentIn == ulMaxIn ) { nImageChild = tsDONE; } else { nImageChild = tsPENDING; } break; case S_FALSE: nImageChild = tsWARNING; break; case E_PENDING: nImageChild = tsPENDING; break; default: if ( FAILED( hrStatusIn ) ) { nImageChild = tsFAILED; } else { nImageChild = tsWARNING; } 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.ulMin = ulMinIn; tipd.ulMax = ulMaxIn; tipd.ulCurrent = ulCurrentIn; 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. if ( pcszNodeNameIn == NULL ) { tipd.bstrNodeName = NULL; } else { tipd.bstrNodeName = TraceSysAllocString( pcszNodeNameIn ); if ( tipd.bstrNodeName == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } } if ( pcszDescriptionIn == NULL ) { tipd.bstrDescription = NULL; } else { tipd.bstrDescription = TraceSysAllocString( pcszDescriptionIn ); if ( tipd.bstrDescription == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } } if ( pcszReferenceIn == NULL ) { tipd.bstrReference = NULL; } else { tipd.bstrReference = TraceSysAllocString( pcszReferenceIn ); if ( tipd.bstrReference == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } } // Start with the first item in the tree view. htiRoot = TreeView_GetRoot( m_hwndTV ); if ( htiRoot == NULL ) { sc = TW32( ERROR_NOT_FOUND ); goto Win32Error; } // 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 ( 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 ); } #endif // DEBUG } // if: S_FALSE returned from HrInsertTaskIntoTree goto Cleanup; Win32Error: // Don't return an error result since doing so will prevent the report // from being propagated to other subscribers. // hr = HRESULT_FROM_WIN32( sc ); hr = S_OK; 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( bstrStatus ); TraceSysFreeString( tipd.bstrNodeName ); 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; DWORD sc; HTREEITEM htiParent; HTREEITEM htiChild = NULL; TVITEMEX tviParent; TVITEMEX tviChild; BOOL fReturn; BOOL fOnlyUpdateProgress; STreeItemLParamData * ptipdParent = NULL; STreeItemLParamData * ptipdChild = NULL; // // Determine if this report is only for updating progress. // fOnlyUpdateProgress = IsEqualIID( ptipdIn->clsidMinorTaskId, TASKID_Minor_Update_Progress ); // // 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 ) { sc = TW32( ERROR_NOT_FOUND ); goto Win32Error; } ptipdParent = reinterpret_cast< STreeItemLParamData * >( tviParent.lParam ); Assert( ptipdParent != NULL ); // // See if this item could be the parent. // If not, recurse through child items. // if ( IsEqualIID( ptipdIn->clsidMajorTaskId, ptipdParent->clsidMinorTaskId ) ) { // // FOUND THE PARENT ITEM // // Is this report intended only for updating progress? // if ( fOnlyUpdateProgress ) { // // REPORT ONLY TO UPDATE PROGRESS // Assert( ptipdIn->hr == S_OK ); Assert( ptipdIn->bstrReference == NULL ); // // Update the progress bar. // THR( HrUpdateProgressBar( ptipdIn, ptipdParent ) ); // ignore failure. // // Copy data from the report to the tree view item. // Since this is a progress update, only progress fields // are allowed to be specified. // ptipdParent->ulMin = ptipdIn->ulMin; ptipdParent->ulMax = ptipdIn->ulMax; ptipdParent->ulCurrent = ptipdIn->ulCurrent; CopyMemory( &ptipdParent->ftTime, &ptipdIn->ftTime, sizeof( ptipdParent->ftTime ) ); } // if: only updating progress else { BOOL fMinorTaskIdMatches; BOOL fBothNodeNamesPresent; BOOL fBothNodeNamesEmpty; BOOL fNodeNamesEqual; // // REPORT NOT JUST TO UPDATE PROGRESS // ////////////////////////////////////////////////////////////// // // 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 ) { sc = TW32( ERROR_NOT_FOUND ); goto Win32Error; } 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->bstrNodeName != NULL ) && ( ptipdChild->bstrNodeName != NULL ); fBothNodeNamesEmpty = ( ptipdIn->bstrNodeName == NULL ) && ( ptipdChild->bstrNodeName == NULL ); if ( fBothNodeNamesPresent ) { fNodeNamesEqual = ( _wcsicmp( ptipdIn->bstrNodeName, ptipdChild->bstrNodeName ) == 0 ); } else if ( fBothNodeNamesEmpty ) { fNodeNamesEqual = TRUE; } else { fNodeNamesEqual = FALSE; } if ( fMinorTaskIdMatches && fNodeNamesEqual ) { // // CHILD ITEM MATCHES. // Update the child item. // // // Update the progress bar. // THR( 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->ulMin = ptipdIn->ulMin; ptipdChild->ulMax = ptipdIn->ulMax; ptipdChild->ulCurrent = ptipdIn->ulCurrent; CopyMemory( &ptipdChild->ftTime, &ptipdIn->ftTime, sizeof( ptipdChild->ftTime ) ); // Update the error code if needed. if ( ptipdChild->hr == S_OK ) { ptipdChild->hr = ptipdIn->hr; } // // 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 ) || ( wcscmp( ptipdIn->bstrDescription, ptipdChild->bstrDescription ) != 0 ) ) ) { fReturn = TraceSysReAllocString( &ptipdChild->bstrDescription, ptipdIn->bstrDescription ); if ( ! fReturn ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } tviChild.mask = TVIF_TEXT; tviChild.pszText = bstrDescriptionIn; tviChild.cchTextMax = SysStringLen( 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 ) || ( wcscmp( ptipdChild->bstrReference, ptipdIn->bstrReference ) != 0 ) ) ) { fReturn = TraceSysReAllocString( &ptipdChild->bstrReference, ptipdIn->bstrReference ); if ( ! fReturn ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } } // 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; } // 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 ); THR( 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 ) ) { // // 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; THR( HrFormatErrorIntoBSTR( ptipdIn->hr, &bstrError ) ); hrFormat = THR( HrFormatMessageIntoBSTR( g_hInstance , IDS_TASK_RETURNED_ERROR , &bstrErrorDescription , ptipdIn->hr , ( bstrError == NULL ? L"" : bstrError ) ) ); 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: 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 , fOnlyUpdateProgress ) ); if ( FAILED( hr ) ) { goto Cleanup; } } // if: found or created a child } // else: not just updating progress // // 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; } 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 goto Cleanup; Win32Error: hr = HRESULT_FROM_WIN32( sc ); goto Cleanup; Cleanup: RETURN( hr ); } //*** CTaskTreeView::HrInsertTaskIntoTree() ////////////////////////////////////////////////////////////////////////////// //++ // // 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( "" ); Assert( ptipdNewIn != NULL ); Assert( ptipdPrevIn != NULL ); HRESULT hr = S_OK; LRESULT lr; ULONG ulCurrent; PBRANGE pbrange; // // Evaluate max value. // if ( m_ulHighNibble < ( ptipdPrevIn->ulMax - ptipdNewIn->ulMax ) ) { // // Out of space and need to expand range. // m_ulHighNibble = 0; SendMessage( m_hwndProg, PBM_GETRANGE, 0, (LPARAM) &pbrange ); pbrange.iHigh += ( ptipdPrevIn->ulMax - ptipdNewIn->ulMax ); SendMessage( m_hwndProg, PBM_SETRANGE, 0, (LPARAM) MAKELPARAM( pbrange.iLow, pbrange.iHigh ) ); } else { // // Keep nibbling away. // m_ulHighNibble -= ( ptipdPrevIn->ulMax - ptipdNewIn->ulMax ); } // // Evaluate min value. // if ( m_ulLowNibble < ( ptipdPrevIn->ulMin - ptipdNewIn->ulMin ) ) { // // Out of space and need to expand range. // m_ulLowNibble = 0; SendMessage( m_hwndProg, PBM_GETRANGE, 0, (LPARAM) &pbrange ); pbrange.iLow += ( ptipdPrevIn->ulMin - ptipdNewIn->ulMin ); SendMessage( m_hwndProg, PBM_SETRANGE, 0, (LPARAM) MAKELPARAM( pbrange.iLow, pbrange.iHigh ) ); } else { // // Keep nibbling away. // m_ulLowNibble -= ( ptipdPrevIn->ulMin - ptipdNewIn->ulMin ); } // // Update the progress bar. // lr = SendMessage( m_hwndProg, PBM_GETPOS, 0, 0 ); ulCurrent = static_cast< ULONG >( lr ); ulCurrent += ( ptipdPrevIn->ulCurrent - ptipdNewIn->ulCurrent ); SendMessage( m_hwndProg, PBM_SETPOS, ulCurrent, 0 ); 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; DWORD sc; 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; } tviParent.mask = TVIF_PARAM | TVIF_IMAGE; tviParent.hItem = htiParent; fReturn = TreeView_GetItem( m_hwndTV, &tviParent ); if ( ! fReturn ) { sc = TW32( ERROR_NOT_FOUND ); goto Win32Error; } // // 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 ) { sc = TW32( ERROR_NOT_FOUND ); goto Win32Error; } 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; } // 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; } goto Cleanup; Win32Error: hr = HRESULT_FROM_WIN32( sc ); 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: 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; } 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; } // // 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; } 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; } // // 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; } 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; } 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->ulMin = ptipdIn->ulMin; ptipdNew->ulMax = ptipdIn->ulMax; ptipdNew->ulCurrent = ptipdIn->ulCurrent; ptipdNew->hr = ptipdIn->hr; if ( ptipdIn->bstrNodeName != NULL ) { ptipdNew->bstrNodeName = TraceSysAllocString( ptipdIn->bstrNodeName ); if ( ptipdNew->bstrNodeName == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } } if ( ptipdIn->bstrDescription != NULL ) { ptipdNew->bstrDescription = TraceSysAllocString( ptipdIn->bstrDescription ); if ( ptipdNew->bstrDescription == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } } if ( ptipdIn->bstrReference != NULL ) { ptipdNew->bstrReference = TraceSysAllocString( ptipdIn->bstrReference ); if ( ptipdNew->bstrReference == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } } // // 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->bstrDescription ); TraceSysFreeString( ptipdNew->bstrReference ); delete ptipdNew; } HRETURN( hr ); } //*** HrCreateTreeItem()