/*++ Copyright (C) Microsoft Corporation, 1998 - 1999 All rights reserved. Module Name: findloc.cxx Abstract: This module provides all the functions for browsing the physical location tree stored in an Active Directory Author: Lazar Ivanov (LazarI) 23-Nov-1998 Steve Kiraly (SteveKi) 24-Nov-1998 Lazar Ivanov (LazarI) Nov-28-2000 - redesign the tokenizer Weihai Chen (WeihaiC) Mar-28-2001 - use DS search to enumerate location data Revision History: --*/ #include "precomp.hxx" #pragma hdrstop #include "dsinterf.hxx" #include "findloc.hxx" #include "physloc.hxx" enum { kDSIconIndex = 1, }; /****************************************************************************** Utility functions ******************************************************************************/ /*++ Name: vRecursiveGetSel Description: Finds the selecton string denoted by parent nodes of the current treeview item, then appends string of the current item Arguments: hTree - handle to treeview control pItem - pointer to current item strSel - reference to string to fill in hStop - tree item to stop recursing Return Value: None Notes: --*/ VOID vRecursiveGetSel ( IN HWND hTree, IN const TVITEM *pItem, OUT TString &strSel, IN HTREEITEM hStop ) { TVITEM tvParent; TCHAR szItem[TPhysicalLocation::kMaxPhysicalLocation]; TStatusB bStatus; tvParent.mask = TVIF_TEXT|TVIF_HANDLE; tvParent.pszText = szItem; tvParent.cchTextMax = TPhysicalLocation::kMaxPhysicalLocation; // // If we're at the root, init the string // if ((tvParent.hItem = TreeView_GetParent (hTree, pItem->hItem))==hStop) { bStatus DBGCHK = strSel.bUpdate (pItem->pszText); } else { // // Get the string denoted by my parent nodes, then append my string // TreeView_GetItem (hTree, &tvParent); vRecursiveGetSel (hTree, &tvParent, strSel, hStop); bStatus DBGCHK = strSel.bCat (pItem->pszText); } // // Append a slash // bStatus DBGCHK = strSel.bCat (gszSlash); } /*++ Name: Tokenize Description: Replace any valid separators with NULL in the dest string Arguments: LPTSTR pszDest - where to put the dest string UNIT nMaxLength - size of the buffer of dest LPCTSTR pszSrc - source string LPTSTR *pszStart - start markup LPTSTR *pszEnd - end markup Return Value: None --*/ VOID Tokenize( OUT LPTSTR pszDest, IN UINT nMaxLength, IN LPCTSTR pszSrc, OUT LPTSTR *pszStart, OUT LPTSTR *pszEnd ) { ASSERT(pszDest); ASSERT(nMaxLength); ASSERT(pszSrc); ASSERT(pszStart); ASSERT(pszEnd); // make a copy lstrcpyn(pszDest, pszSrc, nMaxLength); // replace all the valid separators with zeros int i, iLen = lstrlen(pszDest); for( i=0; i_Location_vDelinkSelf(); delete pLoc; } } /*++ Name: bValid Description: Class valid check routine. Arguments: None Return Value: TRUE - class is valid and usable, FALSE - class is not usable. Notes: --*/ BOOL TLocData:: bValid( VOID ) const { return _bValid; } /*++ Name: TLocData::vNotifyUIDataIsReady Description: Notify the UI the background work is done Arguments: None Return Value: None Notes: --*/ VOID TLocData:: vNotifyUIDataIsReady( VOID ) { // // Check if the data is ready first // if( _bIsDataReady ) { for (HWND hWnd = FindWindow(_strWindowClass, NULL); hWnd; hWnd = GetWindow(hWnd, GW_HWNDNEXT)) { TCHAR szBuffer[MAX_PATH]; if( !GetClassName(hWnd, szBuffer, COUNTOF(szBuffer)) ) { continue; } // // check again ensure that class name is same // if we obtained this handle from GetWindow( ... ) // if( !_tcsicmp( szBuffer, _strWindowClass ) ) { HANDLE hProp = GetProp( hWnd, _strPropertyName ); // // Just verify who we are? // if( hProp == reinterpret_cast(this) ) { // // Apropriate window found - post the message // PostMessage( hWnd, _uMsgDataReady, 0, reinterpret_cast(this) ); break; } } } } } /*++ Name: TLocData::FindLocationsThread Description: static thread procedure, invokes the real worker proc Arguments: pData - TLocData object pointer to call Return Value: None Notes: --*/ VOID TLocData:: FindLocationsThread ( IN TLocData *pData ) { // // Explore the locations (this is a // slow resource access operation) // pData->vDoTheWork(); // // Data is consistent to be used here // pData->_bIsDataReady = TRUE; // // Notify UI we are ready // pData->vNotifyUIDataIsReady(); // // Unhook from data // pData->cDecRef(); } /*++ Name: TLocData::vRefZeroed Description: Called when ref count reaches zero. Deletes the object Arguments: None Return Value: None Notes: --*/ VOID TLocData:: vRefZeroed( VOID ) { // // No more clients - just kill myself, game over // delete this; } /*++ Name: TLocData::bSearchLocations Description: Opens the Active Directory and searches the configuration container to find the location property of each Subnet. Arguments: ds - The active directory object Return Value: TRUE - on success FALSE - on failure Notes: --*/ BOOL TLocData:: bSearchLocations( IN TDirectoryService &ds ) { static const DWORD kMaxPageSize = 10000; static ADS_SEARCHPREF_INFO prefInfo[3]; static LPWSTR ppszPropertyName[] = { const_cast(gszLocation) }; TString strConfig; TString strSubnet; ADS_SEARCH_HANDLE hSearch = NULL; ADS_SEARCH_COLUMN column; IDirectorySearch* pDsSearch = NULL; TStatusB bStatus; TStatusH hResult; DWORD cItems; // // Set search preferences // // // one level search // prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; prefInfo[0].vValue.dwType = ADSTYPE_INTEGER; prefInfo[0].vValue.Integer = ADS_SCOPE_ONELEVEL; // // async search // prefInfo[1].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS; prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN; prefInfo[1].vValue.Boolean = TRUE; // // paged results // prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; prefInfo[2].vValue.dwType = ADSTYPE_INTEGER; prefInfo[2].vValue.Integer = kMaxPageSize; // // Get the configuration path // bStatus DBGCHK = ds.GetConfigurationContainer (strConfig); if (bStatus) { TString strLDAPPrefix; // // Append config path to the subnet string // bStatus DBGCHK = strSubnet.bUpdate( gszLdapPrefix ) && strSubnet.bCat( gszSubnetContainter ) && strSubnet.bCat( gszComma ) && strSubnet.bCat( strConfig ); if(bStatus) { // // Get container interface for the subnets object // hResult DBGCHK = ds.ADsGetObject ( const_cast(static_cast(strSubnet)), IID_IDirectorySearch, reinterpret_cast(&pDsSearch)); if (SUCCEEDED(hResult)) { // // Search for the subnet objects // hResult DBGCHK = pDsSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo)); } if (SUCCEEDED (hResult)) { hResult DBGCHK = pDsSearch->ExecuteSearch(L"(Objectclass=*)", ppszPropertyName, ARRAYSIZE (ppszPropertyName), &hSearch); } for (cItems = 0 ; cItems < kMaxPageSize && SUCCEEDED (hResult); cItems++) { hResult DBGCHK = pDsSearch->GetNextRow(hSearch); if (hResult == S_ADS_NOMORE_ROWS) { // // we have more data so lets continue // hResult DBGCHK = S_OK; break; } // // Get the Name and display it in the list. // hResult DBGCHK = pDsSearch->GetColumn( hSearch, ppszPropertyName[0], &column ); if (SUCCEEDED(hResult)) { switch (column.dwADsType) { case ADSTYPE_CASE_IGNORE_STRING: if (column.pADsValues->CaseIgnoreString && column.pADsValues->CaseIgnoreString[0]) { // // This subnet has a location property // TLoc *pLoc = new TLoc(); if( VALID_PTR(pLoc) ) { pLoc->strLocation.bUpdate( column.pADsValues->CaseIgnoreString ); _LocationList_vAppend(pLoc); } else { hResult DBGCHK = E_OUTOFMEMORY; break; } } break; default: hResult DBGCHK = E_INVALIDARG; break; } pDsSearch->FreeColumn(&column); } else if (hResult == E_ADS_COLUMN_NOT_SET) { // // The location data is not available for this subnet, ignore // hResult DBGCHK = S_OK; } } if (hSearch) { pDsSearch->CloseSearchHandle (hSearch); } if (pDsSearch) { pDsSearch->Release(); } } } // // Make this final check // if(bStatus) { bStatus DBGCHK = SUCCEEDED(hResult); } return bStatus; } /*++ Name: TLocData::vDoTheWork Description: Opens the Active Directory and searches the configuration container to find the location property of each Subnet. Arguments: None Return Value: None Notes: --*/ VOID TLocData:: vDoTheWork( VOID ) { TStatusB bStatus; TDirectoryService ds; bStatus DBGCHK = ds.bValid(); // // Lookup for the DS name first // if( bStatus ) { bStatus DBGCHK = ds.bGetDirectoryName( _strDSName ); } // // Enumerate the subnet objects // if( bStatus ) { bStatus DBGCHK = bSearchLocations (ds); } // // Find out the exact location of the current machine // if( bStatus ) { TPhysicalLocation physLoc; if (_bFindPhysicalLocation && physLoc.Discover()) { physLoc.GetExact (_strDefault); } } } /****************************************************************************** TLocData::TLoc ******************************************************************************/ TLocData:: TLoc:: TLoc( VOID ) { } TLocData:: TLoc:: ~TLoc( VOID ) { } /****************************************************************************** TLocTree methods ******************************************************************************/ /*++ Name: TLocTree constructor Description: Creates the imagelist for the tree control and inserts the root string that describes the choices. Arguments: None Return Value: None Notes: --*/ TLocTree:: TLocTree( IN HWND hwnd ) { HICON hIcon; TCHAR szIconLocation[MAX_PATH]; INT iIconIndex; TStatusB bStatus; _hwndTree = hwnd; _iGlobe = 0; _iSite = 0; _bWaitData = TRUE; _hCursorWait = LoadCursor(NULL, IDC_WAIT); _DefProc = NULL; // // Create the simple imagelist // If this fails, the tree items just won't have icons // bStatus DBGCHK = Shell_GetImageLists( NULL, &_hIml ); if (_hIml) { IDsDisplaySpecifier *pDisplay = NULL; // // Use IDsDisplaySpecifier::GetIcon to find path to the icons // for sites and subnets. // if (SUCCEEDED(CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, reinterpret_cast(&pDisplay)))) { _iGlobe = Shell_GetCachedImageIndex (gszDSIconFile, kDSIconIndex, 0); pDisplay->GetIconLocation (gszSiteIconClass, DSGIF_GETDEFAULTICON, szIconLocation, MAX_PATH, &iIconIndex); _iSite = Shell_GetCachedImageIndex (szIconLocation, iIconIndex, 0); pDisplay->Release(); TreeView_SetImageList (_hwndTree, _hIml, TVSIL_NORMAL); } } // // Subclass tree control to handle WM_SETCURSOR message // _DefProc = reinterpret_cast( GetWindowLongPtr( _hwndTree, GWLP_WNDPROC ) ); SetWindowLongPtr( _hwndTree, GWLP_USERDATA, reinterpret_cast(this) ); SetWindowLongPtr( _hwndTree, GWLP_WNDPROC, reinterpret_cast(&TLocTree::ThunkProc) ); } /*++ Name: TLocTree destructor Description: Destroys the image list Arguments: None Return Value: None Notes: --*/ TLocTree:: ~TLocTree( VOID ) { // // Unsubclass here // SetWindowLongPtr( _hwndTree, GWLP_WNDPROC, reinterpret_cast(_DefProc) ); SetWindowLongPtr( _hwndTree, GWLP_USERDATA, 0 ); // // No longer need the shared image list // if (_hIml) { TreeView_SetImageList (_hwndTree, NULL, TVSIL_NORMAL); } } /*++ Name: TLocTree::vResetTree Description: Resets the tree content Arguments: None Return Value: None Notes: --*/ VOID TLocTree:: vResetTree( VOID ) { // // Delete all items and start waiting // new data // TreeView_DeleteAllItems( _hwndTree ); _bWaitData = TRUE; } /*++ Name: TLocTree::vBuildTree Description: Build the tree control from a ready data Arguments: pLocData - Where to get data from Return Value: None Notes: --*/ VOID TLocTree:: vBuildTree( IN const TLocData *pLocData ) { // // Check to reset the data // if( !_bWaitData ) { vResetTree( ); } if( !pLocData->_LocationList_bEmpty() ) { // // Build the tree and Fill the tree with the data. // vInsertRootString( pLocData ); vFillTree( pLocData ); } // // Stop waiting data // _bWaitData = FALSE; } /*++ Name: TLocTree::vInsertRootString Description: Inserts the root string in the tree control Arguments: None Return Value: None Notes: --*/ VOID TLocTree:: vInsertRootString( IN const TLocData *pLocData ) { TVINSERTSTRUCT tviStruct; TString strRoot; TStatusB bStatus; TDirectoryService ds; TString strDSName; // // Insert the Root-level string that describes the control // This will be a combination of "Entire " + // tviStruct.hParent = NULL; tviStruct.hInsertAfter = TVI_ROOT; tviStruct.item.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE; tviStruct.item.iImage = _iGlobe; tviStruct.item.iSelectedImage = _iGlobe; bStatus DBGCHK = strRoot.bLoadString( ghInst, IDS_LOCTREEROOT ) && strRoot.bCat( pLocData->_strDSName ); tviStruct.item.pszText = const_cast(static_cast(strRoot)); tviStruct.item.cchTextMax = strRoot.uLen(); _hRoot = TreeView_InsertItem (_hwndTree, &tviStruct); } /*++ Name: TLocTree::bInsertLocString Description: Add the given location string to the tree control. Each level of the hierarchy is indicated by the '/' delimiter. Prevents duplicate entries at each level of the tree. Arguments: strLoc - location string to insert, Must be '/' delimited Return Value: TRUE - on success FALSE - on failure Notes: --*/ BOOL TLocTree:: bInsertLocString( IN const TString &strLoc ) const { // Start at the root. // For each '/' delimited string in szLoc, // add a level to the tree and insert the // string as a child node of the current node. // At each level don't insert duplicates. // HTREEITEM hCurrent, hChild; TVINSERTSTRUCT tviItem; TVITEM* pItem = &tviItem.item; DWORD dwErr; TCHAR szBuffer[512]; LPTSTR psz, pszMax; if (!_hwndTree) { return FALSE; } // tokenize the input string Tokenize(szBuffer, ARRAYSIZE(szBuffer), strLoc, &psz, &pszMax); // initialize the item to insert memset(pItem, 0, sizeof(TVITEM)); pItem->mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE; pItem->iImage = _iSite; pItem->iSelectedImage = _iSite; tviItem.hInsertAfter = TVI_SORT; hCurrent = _hRoot; while( hCurrent ) { // find a valid token while( 0 == *psz && psz < pszMax ) { psz++; } // if we've gone past the buffer break the loop if( psz >= pszMax ) { break; } pItem->pszText = psz; tviItem.hParent = hCurrent; // if the current name already exists at this level don't insert if( hChild = IsAChild(psz, hCurrent) ) { hCurrent = hChild; } else { hCurrent = TreeView_InsertItem (_hwndTree, &tviItem); } // advance the token pointer psz += lstrlen(psz); } return hCurrent ? TRUE : FALSE; } /*++ Name: TLocTree::IsAChild Description: Determines if szLoc exists at the level of the tree whose parent is hParent. If so, return the handle of the node containing szLoc Arguments: szLoc - string to search for hParent - parent node Return Value: Handle to treeview item matching szLoc, or NULL if not found Notes: --*/ HTREEITEM TLocTree:: IsAChild( IN LPCTSTR szLoc, IN HTREEITEM hParent ) const { TVITEM tvItem; HTREEITEM hItem; TCHAR szItemText[TPhysicalLocation::kMaxPhysicalLocation]; BOOL bMatch = FALSE; tvItem.mask = TVIF_TEXT | TVIF_HANDLE; tvItem.pszText = szItemText; tvItem.cchTextMax = TPhysicalLocation::kMaxPhysicalLocation; hItem = TreeView_GetChild (_hwndTree, hParent); while (hItem && !bMatch) { tvItem.hItem = hItem; TreeView_GetItem (_hwndTree, &tvItem); bMatch = !_tcsicmp (szLoc, szItemText); if (bMatch) { break; } hItem = TreeView_GetNextSibling (_hwndTree, hItem); } if (!bMatch) { hItem = NULL; } return hItem; } /*++ Name: TLocTree::uGetSelectedLocation Description: Walks the tree control from the selected item to the root, building the slash-delimited location string name. Arguments: strLoc - string to update Return Value: TRUE - on success FALSE - otherwise Notes: --*/ BOOL TLocTree:: bGetSelectedLocation( OUT TString &strLoc ) const { TVITEM tvItem; TStatusB status; TCHAR szTemp[TPhysicalLocation::kMaxPhysicalLocation]; tvItem.mask = TVIF_TEXT|TVIF_HANDLE; tvItem.cchTextMax = TPhysicalLocation::kMaxPhysicalLocation; tvItem.pszText = szTemp; // // Get the selected item // tvItem.hItem = TreeView_GetSelection (_hwndTree); if (tvItem.hItem == _hRoot) { status DBGCHK = strLoc.bUpdate(_T("\0")); return status; } status DBGCHK = TreeView_GetItem (_hwndTree, &tvItem); if (status) { vRecursiveGetSel (_hwndTree, &tvItem, strLoc, _hRoot); } return status; } /*++ Name: TLocTree::vExpandTree Description: Expands the tree view control to make visible the node corresponding to the given location string Arguments: strExpand - slash-delimited location string to expand tree by Return Value: None Notes: --*/ VOID TLocTree:: vExpandTree( IN const TString &strExpand ) const { if( strExpand.uLen() ) { HTREEITEM hParent, hItem; TCHAR szBuffer[512]; LPTSTR psz, pszMax; // tokenize the input string Tokenize(szBuffer, ARRAYSIZE(szBuffer), strExpand, &psz, &pszMax); hParent = _hRoot; for( ;; ) { // find a valid token while( 0 == *psz && psz < pszMax ) { psz++; } // if we've gone past the buffer break the loop if( psz >= pszMax ) { break; } if( hItem = IsAChild(psz, hParent) ) { // a valid child - remember hParent = hItem; // advance to the next token psz += lstrlen(psz); } else { // not a child, bail out break; } } if (hParent) { TreeView_EnsureVisible(_hwndTree, hParent); TreeView_SelectItem(_hwndTree, hParent); } } } /*++ Name: vFillTree Description: Walks through the linked list and adds strings to the TLocTree object Arguments: pLocData - Where to get data from Return Value: None Notes: --*/ VOID TLocTree:: vFillTree( IN const TLocData *pLocData ) const { TIter Iter; TLocData::TLoc *pLoc; TStatusB status = TRUE; for( pLocData->_LocationList_vIterInit(Iter), Iter.vNext(); Iter.bValid(); Iter.vNext() ) { pLoc = pLocData->_LocationList_pConvert( Iter ); status DBGCHK = bInsertLocString( pLoc->strLocation ); } } /*++ Name: TLocTree::ThunkProc Description: Thunk control proc (for subclassing) Arguments: Standard window proc parameters Return Value: None Notes: --*/ LRESULT CALLBACK TLocTree:: ThunkProc( IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ) { LRESULT lResult = 0; TLocTree *This = reinterpret_cast( GetWindowLongPtr( hwnd, GWLP_USERDATA ) ); if( This ) { if( WM_DESTROY == uMsg || WM_NCDESTROY == uMsg ) { lResult = This->nHandleMessage( uMsg, wParam, lParam ); SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast(This->_DefProc) ); SetWindowLongPtr( hwnd, GWLP_USERDATA, 0 ); } else { SPLASSERT( hwnd == This->_hwndTree ); lResult = This->nHandleMessage( uMsg, wParam, lParam ); if( !lResult ) { // // Message is not handled go to // default processing // lResult = This->_DefProc( hwnd, uMsg, wParam, lParam ); } } } return lResult; } /*++ Name: TLocTree::nHandleMessage Description: Message handler function Arguments: Standard window proc parameters Return Value: None Notes: --*/ LRESULT TLocTree:: nHandleMessage( IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ) { // // assume message is processed // LRESULT lResult = 1; switch( uMsg ) { case WM_SETCURSOR: { if( _bWaitData ) { // // Set hourglass cursor // SetCursor( _hCursorWait ); } else { // // Message is not processed - go default processing // lResult = 0; } } break; default: { // // Message is not processed - go default processing // lResult = 0; } break; } return lResult; } /****************************************************************************** TFindLocDlg functions ******************************************************************************/ /*++ Name: TFindLocDlg::bGenerateGUIDAsString Description: This function will generate a GUID, convert it to string and return it Arguments: pstrGUID - where to place the genearted GUID Return Value: TRUE on success FALSE on failure Notes: --*/ BOOL TFindLocDlg:: bGenerateGUIDAsString( OUT TString *pstrGUID ) { static const TCHAR szRegFormat[] = _T("{%08lX-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X}"); // // Assume failue // TStatusB bStatus = FALSE; HRESULT hResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if( SUCCEEDED(hResult) ) { GUID guid; bStatus DBGCHK = SUCCEEDED(CoCreateGuid(&guid)); if( bStatus ) { // // Generate a registry format GUID string // bStatus DBGCHK = pstrGUID->bFormat( szRegFormat, // first copy... guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7], // second copy... guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); } // // Uninitialize COM after we finish // CoUninitialize( ); } return bStatus; } /*++ Name: TFindLocDlg constructor Description: Constructs TFindLocDlg object Arguments: flags - UI controlling flags (show/hide help button) Return Value: None Notes: --*/ TFindLocDlg:: TFindLocDlg( IN ELocationUI flags ) : _pLocData(NULL), _pTree(NULL), _UIFlags(flags), _bValid(TRUE), _uMsgDataReady(0) { } /*++ Name: TFindLocDlg destructor Description: Releases refcount on the location object Arguments: None Return Value: None Notes: --*/ TFindLocDlg:: ~TFindLocDlg( VOID ) { if (VALID_PTR(_pLocData)) { _pLocData->cDecRef(); } } /*++ Name: TFindLocDlg::bDoModal Description: Invokes the browse location dialog Arguments: hParent - hwnd of parent window pstrDefault - default selection string, this is optional Return Value: TRUE - if dialog creation succeeds FALSE - otherwise Notes: pstrDefault has NULL default value --*/ BOOL TFindLocDlg:: bDoModal( IN HWND hParent, OUT TString *pstrDefault OPTIONAL ) { TStatusB bStatus; bStatus DBGNOCHK = TRUE; if (pstrDefault) { bStatus DBGCHK = _strDefault.bUpdate( *pstrDefault ); } // // Register the window message, this message is used to inform the // UI thread that the data fetched by the background thread is ready. // _uMsgDataReady = RegisterWindowMessage( _T("WM_BACKGROUNDTASKISREADY") ); // // Check if we succeeded to register the message // and update the default location // if( _bValid && _uMsgDataReady && bStatus ) { // // Check if data is not obtained from a previous call // of bDoModal() function - if not then start background // thread to explore locations from the directory // service // if( !_pLocData ) { // // Generate the special property name // bStatus DBGCHK = bGenerateGUIDAsString( &_strPropertyName ); if( bStatus ) { _pLocData = new TLocData( _T("#32770"), _strPropertyName, _uMsgDataReady, _strDefault.bEmpty() ); bStatus DBGCHK = VALID_PTR( _pLocData ); if( bStatus ) { _pLocData->vIncRef( ); // // Spin the background explorer thread here // bStatus DBGCHK = bStartTheBackgroundThread(); } else { delete _pLocData; _pLocData = NULL; } } } // // Data is expected to be ready here or to be in // process of exploration in background // if( bStatus ) { // // Everything looks fine - just show the UI // bStatus DBGCHK= (BOOL)DialogBoxParam( ghInst, MAKEINTRESOURCE(DLG_BROWSE_LOC), hParent, MGenericDialog::SetupDlgProc, reinterpret_cast(this) ); } } return bStatus; } /*++ Name: TFindLocDlg::uGetLocation Description: Fills in the location string selected when the dialog was open. Arguments: strLocation - TString to update Return Value: TRUE - on success, FALSE - otherwise Notes: --*/ BOOL TFindLocDlg:: bGetLocation( OUT TString &strLocation ) { TStatusB bStatus; bStatus DBGCHK = strLocation.bUpdate ((LPCTSTR)_strSelLocation); return bStatus; } /*++ Name: TFindLocDlg::vDataIsReady Description: Called when the background thread completes its work. Always stops the wait cursor. Arguments: None Return Value: None Notes: --*/ VOID TFindLocDlg:: vDataIsReady( VOID ) { // // Make sure the animation stops // vStopAnim(); // // Fill in the tree control, and select the default string // _pTree->vBuildTree( _pLocData ); if( !_pLocData->_LocationList_bEmpty() ) { // // Expand the default location here // if (!_strDefault.bEmpty()) { _pTree->vExpandTree(_strDefault); } else { _pTree->vExpandTree(_pLocData->_strDefault); } // // Enable the OK button // EnableWindow (GetDlgItem (_hDlg, IDOK), TRUE); } // // Place the focus in the tree control // PostMessage( _hDlg, WM_NEXTDLGCTL, reinterpret_cast( GetDlgItem(_hDlg, IDC_LOCTREE) ), 1L ); } /*++ Name: TFindLocDlg::vOnOK Description: Called when the OK button is pressed. Updates current selection string Arguments: None Return Value: None Notes: --*/ VOID TFindLocDlg:: vOnOK( VOID ) { // // Get the selected location and close the dialog // _pTree->bGetSelectedLocation (_strSelLocation); EndDialog (_hDlg, IDOK); } /*++ Name: TFindLocDlg::vOnDestroy Description: Cleans up data allocated for current dialog window instance Arguments: None Return Value: None Notes: --*/ VOID TFindLocDlg:: vOnDestroy( VOID ) { if (VALID_PTR(_pTree)) { delete _pTree; _pTree = NULL; } } /*++ Name: TFindLocDlg::bHandleMessage Description: Dispatches messages to appropriate handlers. Arguments: uMsg - message value wParam, lParam - message params Return Value: TRUE - if message is handled, FALSE - otherwise Notes: --*/ BOOL TFindLocDlg:: bHandleMessage( IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ) { BOOL bHandled = TRUE; // // Check our registered message first // if( _uMsgDataReady == uMsg && reinterpret_cast(_pLocData) == lParam ) { vDataIsReady( ); bHandled = TRUE; } else { // // Standard message processing // switch (uMsg) { case WM_INITDIALOG: { bHandled = bOnInitDialog(); } break; case WM_COMMAND: { // // Handle WM_COMMAND messages // bHandled = bOnCommand( LOWORD(wParam) ); } break; case WM_NOTIFY: { // // The only WM_NOTIFY source on the dialog is the TreeView // bHandled = bOnTreeNotify (reinterpret_cast(lParam)); } break; case WM_HELP: case WM_CONTEXTMENU: { // // Help messages // PrintUIHelp( uMsg, _hDlg, wParam, lParam ); } break; case WM_DESTROY: { vOnDestroy (); } break; case WM_CTLCOLORSTATIC: { bHandled = bOnCtlColorStatic( reinterpret_cast(wParam), reinterpret_cast(lParam) ); } break; default: { bHandled = FALSE; } break; } } return bHandled; } /*++ Name: TFindLocDlg::bOnInitDialog Description: Instantiates the TLocTree object and attempts to initialize it if needed. Disable the OK button until the worker thread completes. Arguments: None Return Value: TRUE - on success FALSE - on failure Notes: --*/ BOOL TFindLocDlg:: bOnInitDialog( VOID ) { DWORD dwResult; TStatusB bStatus; _pTree = new TLocTree (GetDlgItem(_hDlg, IDC_LOCTREE)); bStatus DBGCHK = VALID_PTR(_pTree); if( !bStatus ) { delete _pTree; _pTree = NULL; } else { // // Set some special property value to us, so the // worker thread could recognize us // bStatus DBGCHK = SetProp( _hDlg, _strPropertyName, static_cast(_pLocData) ); if( bStatus ) { // // Hide the help button as requested // if (!(_UIFlags & kLocationShowHelp)) { ShowWindow (GetDlgItem(_hDlg, IDC_LOCATION_HELP), SW_HIDE); } // // Let the user know we're busy // EnableWindow (GetDlgItem (_hDlg, IDOK), FALSE); vStartAnim(); // // Just verify if the data is ready // _pLocData->vNotifyUIDataIsReady(); } } return bStatus; } /*++ Name: TFindLocDlg::bOnTreeNotify Description: When the selection is changed on the tree control, update the static control that displays the currently selected location Arguments: pTreeNotify - pointer to NMTREEVIEW struct Return Value: TRUE - if message is handled FALSE - otherwise Notes: --*/ BOOL TFindLocDlg:: bOnTreeNotify( IN LPNMTREEVIEW pTreeNotify ) { TStatusB bStatus = TRUE; TString strSel; switch (pTreeNotify->hdr.code) { case TVN_SELCHANGED: { bStatus DBGCHK = _pTree->bGetSelectedLocation (strSel); if (strSel.bEmpty()) { strSel.bLoadString (ghInst,IDS_NO_LOCATION); } bStatus DBGCHK = bSetEditText (_hDlg, IDC_CHOSEN_LOCATION, strSel); } break; default: { bStatus DBGNOCHK = FALSE; } break; } return bStatus; } /*++ Name: TFindLocDlg::vStartAnim Description: Show the animation window Arguments: None Return Value: None Notes: --*/ VOID TFindLocDlg:: vStartAnim( VOID ) { HWND hwndAni = GetDlgItem (_hDlg, IDC_BROWSELOC_ANIM); SetWindowPos (hwndAni, HWND_TOP, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW); Animate_Open(hwndAni, MAKEINTRESOURCE (IDA_SEARCH)); Animate_Play(hwndAni, 0, -1, -1); } /*++ Name: TFindLocDlg::vStopAnim Description: Hide the animation window Arguments: None Return Value: None Notes: --*/ VOID TFindLocDlg:: vStopAnim( VOID ) { // // Call LockWindowUpdate() to prevent default drawing of the // animation control after stop playing - which is gray rect // LockWindowUpdate( _hDlg ); HWND hwndAni = GetDlgItem (_hDlg, IDC_BROWSELOC_ANIM); SetWindowPos (hwndAni, HWND_BOTTOM, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE|SWP_HIDEWINDOW); Animate_Stop(hwndAni); // // Unlock window update - tree control will // repaint its content // LockWindowUpdate( NULL ); } /*++ Name: TFindLocDlg::bOnCtlColorStatic Description: Used to set background color of the animation window to the same color as the treeview window Arguments: hdc - DC to set background color Return Value: color value used to draw background Notes: --*/ BOOL TFindLocDlg:: bOnCtlColorStatic( IN HDC hdc, IN HWND hStatic ) { // // Assume default processing of this message // BOOL bResult = FALSE; if (hStatic == GetDlgItem (_hDlg, IDC_BROWSELOC_ANIM)) { // // We process this message only to have // opportunity to patch the animation control // dc just before paint. // // We do not return a brush here! // (which means that we return a NULL brush) // // We should just return TRUE here to prevent the // default message processing of this msg, which will // revert the backgound color to gray. // SetBkColor(hdc, GetSysColor(COLOR_WINDOW)); // // Message is processed. Do not perform default // processing! // bResult = TRUE; } return bResult; } /*++ Name: TFindLocDlg::bStartTheBackgroundThread Description: Starts the background thread for reading the subnet locations Arguments: None Return Value: TRUE - on success FALSE - on failure Notes: --*/ BOOL TFindLocDlg:: bStartTheBackgroundThread( VOID ) { TStatusB bStatus; DWORD dwThreadID; HANDLE hThread; // // You want to make sure the data is protected for the worker thread. // _pLocData->vIncRef(); // // Spin the background thread here // hThread = TSafeThread::Create ( NULL, 0, (LPTHREAD_START_ROUTINE)TLocData::FindLocationsThread, _pLocData, 0, &dwThreadID ); if( hThread ) { // // We don't need this handle any more. // Just leave the thread running untill // it stops // CloseHandle( hThread ); } else { // // Ensure the data is release if the thread creation fails. // _pLocData->cDecRef(); } bStatus DBGCHK = !!hThread; return bStatus; } /*++ Name: TFindLocDlg::bOnCommand Description: Handles WM_COMMAND messages Arguments: None Return Value: TRUE - on success FALSE - on failure Notes: --*/ BOOL TFindLocDlg:: bOnCommand( IN UINT uCmdID ) { // // Assume message is handled // BOOL bHandled = TRUE; switch (uCmdID) { case IDOK: vOnOK (); break; case IDCANCEL: EndDialog (_hDlg, 0); break; case IDC_LOCATION_HELP: PrintUIHtmlHelp (_hDlg, gszHtmlPrintingHlp, HH_DISPLAY_TOPIC, reinterpret_cast(gszLocationHtmFile)); break; default: { bHandled = FALSE; } break; } return bHandled; }