/**********************************************************************/ /** Microsoft Windows/NT **/ /** Copyright(c) Microsoft Corporation, 1997 - 1999 **/ /**********************************************************************/ /* status.cpp wins status scope pane node handler. FILE HISTORY: */ #include "stdafx.h" #include "status.h" #include "server.h" #include "statnode.h" #include "dhcp.h" #include "statndpp.h" #define WINS_MESSAGE_SIZE 576 #define ANSWER_TIMEOUT 20000 /*--------------------------------------------------------------------------- CWinsStatusHandler::CWinsStatusHandler Description ---------------------------------------------------------------------------*/ CWinsStatusHandler::CWinsStatusHandler ( ITFSComponentData * pCompData, DWORD dwUpdateInterval ) : CMTWinsHandler(pCompData), m_hListenThread(NULL), m_hMainMonThread(NULL), m_hPauseListening(NULL), m_nServersUpdated(0) { m_bExpanded = FALSE; m_nState = loaded; m_dwUpdateInterval = dwUpdateInterval; // from class ThreadHandler m_uMsgBase = 0; } /*--------------------------------------------------------------------------- CWinsStatusHandler::CWinsStatusHandler Destructor ---------------------------------------------------------------------------*/ CWinsStatusHandler::~CWinsStatusHandler() { m_listServers.RemoveAll(); if (m_uMsgBase) { ::SendMessage(m_hwndHidden, WM_HIDDENWND_REGISTER, FALSE, m_uMsgBase); m_uMsgBase = 0; } CloseSockets(); } /*--------------------------------------------------------------------------- CWinsStatusHandler::DestroyHandler Release and pointers we have here Author: EricDav ----------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::DestroyHandler ( ITFSNode * pNode ) { m_spNode.Set(NULL); return hrOK; } /*--------------------------------------------------------------------------- CWinsStatusHandler::OnCreateNodeId2 Returns a unique string for this node Author: EricDav ---------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::OnCreateNodeId2(ITFSNode * pNode, CString & strId, DWORD * dwFlags) { const GUID * pGuid = pNode->GetNodeType(); CString strGuid; StringFromGUID2(*pGuid, strGuid.GetBuffer(256), 256); strGuid.ReleaseBuffer(); strId = strGuid; return hrOK; } /*--------------------------------------------------------------------------- CWinsStatusHandler::InitializeNode Initializes node specific data ----------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::InitializeNode ( ITFSNode * pNode ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); HRESULT hr = hrOK; SPITFSNode spParent; CString strTemp; strTemp.LoadString(IDS_SERVER_STATUS_FOLDER); SetDisplayName(strTemp); // Make the node immediately visible pNode->SetData(TFS_DATA_COOKIE, (LPARAM) pNode); pNode->SetData(TFS_DATA_IMAGEINDEX, ICON_IDX_SERVER); pNode->SetData(TFS_DATA_OPENIMAGEINDEX, ICON_IDX_SERVER); pNode->SetData(TFS_DATA_USER, (LPARAM) this); pNode->SetData(TFS_DATA_TYPE, WINSSNAP_SERVER_STATUS); pNode->SetData(TFS_DATA_RELATIVE_FLAGS, SDI_FIRST); pNode->SetData(TFS_DATA_SCOPE_LEAF_NODE, TRUE); SetColumnStringIDs(&aColumns[WINSSNAP_SERVER_STATUS][0]); SetColumnWidths(&aColumnWidths[WINSSNAP_SERVER_STATUS][0]); // the event to signal the Listen thread to abort m_hAbortListen = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hAbortListen == NULL) { Trace1("WinsStatusHandler::InitializeNode - CreateEvent Failed m_hAbortListen %d\n", ::GetLastError()); return HRESULT_FROM_WIN32(::GetLastError()); } // the event to signal the main thread to abort m_hAbortMain = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hAbortListen == NULL) { Trace1("WinsStatusHandler::InitializeNode - CreateEvent Failed m_hAbortMain %d\n", ::GetLastError()); return HRESULT_FROM_WIN32(::GetLastError()); } // the event to signal the threads to wakeup m_hWaitIntervalListen = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hWaitIntervalListen == NULL) { Trace1("WinsStatusHandler::InitializeNode - CreateEvent Failed m_hWaitIntervalListen %d\n", ::GetLastError()); return HRESULT_FROM_WIN32(::GetLastError()); } m_hWaitIntervalMain = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hWaitIntervalMain == NULL) { Trace1("WinsStatusHandler::InitializeNode - CreateEvent Failed m_hWaitIntervalMain %d\n", ::GetLastError()); return HRESULT_FROM_WIN32(::GetLastError()); } // when sending a probe, the thread waits for this m_hAnswer = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hAnswer == NULL) { Trace1("WinsStatusHandler::InitializeNode - CreateEvent Failed m_hAnswer %d\n", ::GetLastError()); return HRESULT_FROM_WIN32(::GetLastError()); } return hr; } /*--------------------------------------------------------------------------- Overridden base handler functions ---------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------- CWinsStatusHandler::GetString Implementation of ITFSNodeHandler::GetString Author: KennT ---------------------------------------------------------------------------*/ STDMETHODIMP_(LPCTSTR) CWinsStatusHandler::GetString ( ITFSNode * pNode, int nCol ) { if (nCol == 0 || nCol == -1) return GetDisplayName(); else return NULL; } /*--------------------------------------------------------------------------- CWinsStatusHandler::OnAddMenuItems Description ---------------------------------------------------------------------------*/ STDMETHODIMP CWinsStatusHandler::OnAddMenuItems ( ITFSNode * pNode, LPCONTEXTMENUCALLBACK pContextMenuCallback, LPDATAOBJECT lpDataObject, DATA_OBJECT_TYPES type, DWORD dwType, long * pInsertionAllowed ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); HRESULT hr = S_OK; return hr; } /*!-------------------------------------------------------------------------- CWinsStatusHandler::HasPropertyPages Implementation of ITFSNodeHandler::HasPropertyPages NOTE: the root node handler has to over-ride this function to handle the snapin manager property page (wizard) case!!! Author: KennT ---------------------------------------------------------------------------*/ STDMETHODIMP CWinsStatusHandler::HasPropertyPages ( ITFSNode * pNode, LPDATAOBJECT pDataObject, DATA_OBJECT_TYPES type, DWORD dwType ) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = hrOK; if (dwType & TFS_COMPDATA_CREATE) { // This is the case where we are asked to bring up property // pages when the user is adding a new snapin. These calls // are forwarded to the root node to handle. hr = hrOK; } else { // we have property pages in the normal case hr = hrOK; } return hr; } /*--------------------------------------------------------------------------- CWinsStatusHandler::CreatePropertyPages Description ---------------------------------------------------------------------------*/ STDMETHODIMP CWinsStatusHandler::CreatePropertyPages ( ITFSNode * pNode, LPPROPERTYSHEETCALLBACK lpProvider, LPDATAOBJECT pDataObject, LONG_PTR handle, DWORD dwType ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); HRESULT hr = hrOK; Assert(pNode->GetData(TFS_DATA_COOKIE) != 0); // Object gets deleted when the page is destroyed SPIComponentData spComponentData; m_spNodeMgr->GetComponentData(&spComponentData); CStatusNodeProperties * pStatProp = new CStatusNodeProperties(pNode, spComponentData, m_spTFSCompData, NULL); Assert(lpProvider != NULL); return pStatProp->CreateModelessSheet(lpProvider, handle); } /*--------------------------------------------------------------------------- CWinsStatusHandler::OnPropertyChange Description ---------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::OnPropertyChange ( ITFSNode * pNode, LPDATAOBJECT pDataobject, DWORD dwType, LPARAM arg, LPARAM lParam ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); LONG_PTR changeMask = 0; CStatusNodeProperties * pProp = reinterpret_cast(lParam); // tell the property page to do whatever now that we are back on the // main thread pProp->OnPropertyChange(TRUE, &changeMask); pProp->AcknowledgeNotify(); if (changeMask) pNode->ChangeNode(changeMask); return hrOK; } HRESULT CWinsStatusHandler::OnExpand ( ITFSNode * pNode, LPDATAOBJECT pDataObject, DWORD dwType, LPARAM arg, LPARAM param ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); HRESULT hr = hrOK; if (m_bExpanded) return hr; m_spNode.Set(pNode); // build the list to hold the list of the servers BuildServerList(pNode); // Create the result pane data here CreateNodes(pNode); // start monitoring StartMonitoring(pNode); m_bExpanded = TRUE; return hr; } /*!-------------------------------------------------------------------------- CWinsStatusHandler::OnNotifyHaveData - Author: EricDav ---------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::OnNotifyHaveData ( LPARAM lParam ) { // The background wins monitoring stuff sends us a message to update the // status column information UpdateStatusColumn(m_spNode); return hrOK; } /*--------------------------------------------------------------------------- CWinsStatusHandler::OnResultRefresh Base handler override Author: v-shubk ---------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::OnResultRefresh ( ITFSComponent * pComponent, LPDATAOBJECT pDataObject, MMC_COOKIE cookie, LPARAM arg, LPARAM lParam ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); // wait up the monitoring thread SetEvent(m_hWaitIntervalMain); return hrOK; } /*--------------------------------------------------------------------------- CWinsStatusHandler::CompareItems Description Author: EricDav ---------------------------------------------------------------------------*/ STDMETHODIMP_(int) CWinsStatusHandler::CompareItems ( ITFSComponent * pComponent, MMC_COOKIE cookieA, MMC_COOKIE cookieB, int nCol ) { SPITFSNode spNode1, spNode2; m_spNodeMgr->FindNode(cookieA, &spNode1); m_spNodeMgr->FindNode(cookieB, &spNode2); int nCompare = 0; CServerStatus * pWins1 = GETHANDLER(CServerStatus, spNode1); CServerStatus * pWins2 = GETHANDLER(CServerStatus, spNode2); switch (nCol) { // name case 0: { nCompare = lstrcmp(pWins1->GetServerName(), pWins2->GetServerName()); } break; // status case 1: { CString str1; str1 = pWins1->GetStatus(); nCompare = str1.CompareNoCase(pWins2->GetStatus()); } break; } return nCompare; } /*--------------------------------------------------------------------------- CWinsStatusHandler::BuildServerList Description Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::BuildServerList(ITFSNode *pNode) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); // get the root node SPITFSNode spRootNode; m_spNodeMgr->GetRootNode(&spRootNode); // enumerate thro' all the nodes HRESULT hr = hrOK; SPITFSNodeEnum spNodeEnum; SPITFSNode spCurrentNode; ULONG nNumReturned = 0; BOOL bFound = FALSE; // get the enumerator for this node spRootNode->GetEnum(&spNodeEnum); spNodeEnum->Next(1, &spCurrentNode, &nNumReturned); while (nNumReturned) { // iterate to teh next node, if the status handler node is seen const GUID* pGuid; pGuid = spCurrentNode->GetNodeType(); if (*pGuid != GUID_WinsServerStatusNodeType) { // add to the list CServerStatus* pServ = NULL; char szBuffer[MAX_PATH]; CWinsServerHandler * pServer = GETHANDLER(CWinsServerHandler, spCurrentNode); CString strTemp = pServer->GetServerAddress(); // this should be ACP WideToMBCS(strTemp, szBuffer); pServ = new CServerStatus(m_spTFSCompData); strcpy(pServ->szServerName, szBuffer); pServ->dwIPAddress = pServer->GetServerIP(); pServ->dwMsgCount = 0; strcpy(pServ->szIPAddress, ""); m_listServers.Add(pServ); } // get the next Server in the list spCurrentNode.Release(); spNodeEnum->Next(1, &spCurrentNode, &nNumReturned); } } /*--------------------------------------------------------------------------- CWinsStatusHandler::CreateListeningSockets( ) Abstract: This function initializes Winsock and opens a socket that listens to bcasts the DHCP srv sends to UDP port 68. Arguments: pListenSockCl - reference to the socket we're going to open (argument passed by reference - clean but hidden) this socket will listen on the DHCP client port so that it can pick up bcasts on the local segmnt pListenSockSrv - reference to the socket we're going to open (argument passed by reference - clean but hidden) this socket will listen on the DHCP server port so that it can pick up unicasts to the "relay" listenNameSvcSock - reference to the socket we're going to open (argument passed by reference - clean but hidden) this socket will listen on the NBT name svc port so that it can pick up answers from the WINS srv We have to do this on the socket layer because on the NetBIOS layer we wouldn't notice that a name query has been resolved by bcasting. Return value: none --------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::CreateListeningSockets( ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); int nResult = 0; // status info returned from function calls WSADATA wsaData; // WinSock startup details buffer DWORD optionValue; // helper var for setsockopt() SOCKADDR_IN sockAddr; // struct holding source socket info // the event to signal listening thread to pause m_hPauseListening = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hPauseListening == NULL) { Trace1("WinsStatusHandler::CreateListeningSockets - CreateEvent Failed m_hPauseListening %d\n", ::GetLastError()); return HRESULT_FROM_WIN32(::GetLastError()); } // create socket to listen to the WINS servers on the client port (same subnet) listenSockCl = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( listenSockCl == INVALID_SOCKET ) { Trace1("\nError %d creating socket to listen to WINS traffic.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } optionValue = TRUE; if ( setsockopt(listenSockCl, SOL_SOCKET, SO_REUSEADDR, (const char *)&optionValue, sizeof(optionValue)) ) { Trace1("\nError %d setting SO_REUSEADDR option.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } optionValue = TRUE; if ( setsockopt(listenSockCl, SOL_SOCKET, SO_BROADCAST, (const char *)&optionValue, sizeof(optionValue)) ) { Trace1("\nError %d setting SO_REUSEADDR option.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } sockAddr.sin_family = PF_INET; sockAddr.sin_addr.s_addr = 0; // use any local address sockAddr.sin_port = htons( DHCP_CLIENT_PORT ); RtlZeroMemory( sockAddr.sin_zero, 8 ); if ( bind(listenSockCl, (LPSOCKADDR )&sockAddr, sizeof(sockAddr)) == SOCKET_ERROR ) { Trace1("\nError %d binding the listening socket.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } // create socket to listen to the WINS servers on the server port (remote subnet, fake relay) listenSockSrv = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( listenSockSrv == INVALID_SOCKET ) { Trace1("\nError %d creating socket to listen to DHCP traffic.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } optionValue = TRUE; if ( setsockopt(listenSockSrv, SOL_SOCKET, SO_REUSEADDR, (const char *)&optionValue, sizeof(optionValue)) ) { Trace1("\nError %d setting SO_REUSEADDR option.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } optionValue = TRUE; if ( setsockopt(listenSockSrv, SOL_SOCKET, SO_BROADCAST, (const char *)&optionValue, sizeof(optionValue)) ) { Trace1("\nError %d setting SO_REUSEADDR option.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } sockAddr.sin_family = PF_INET; sockAddr.sin_addr.s_addr = 0; // use any local address sockAddr.sin_port = htons( DHCP_SERVR_PORT ); RtlZeroMemory( sockAddr.sin_zero, 8 ); if ( bind(listenSockSrv, (LPSOCKADDR )&sockAddr, sizeof(sockAddr)) == SOCKET_ERROR ) { Trace1("\nError %d binding the listening socket.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } // create socket to listen to name svc responses from the WINS server listenNameSvcSock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( listenNameSvcSock == INVALID_SOCKET ) { Trace1("\nError %d creating socket to listen to WINS traffic.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } optionValue = TRUE; if ( setsockopt(listenNameSvcSock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optionValue, sizeof(optionValue)) ) { Trace1("\nError %d setting SO_REUSEADDR option.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } optionValue = FALSE; if ( setsockopt(listenNameSvcSock, SOL_SOCKET, SO_BROADCAST, (const char *)&optionValue, sizeof(optionValue)) ) { Trace1("\nError %d setting SO_REUSEADDR option.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } sockAddr.sin_family = PF_INET; sockAddr.sin_addr.s_addr = INADDR_ANY; sockAddr.sin_port = 0; RtlZeroMemory( sockAddr.sin_zero, 8 ); if ( bind(listenNameSvcSock, (LPSOCKADDR )&sockAddr, sizeof(sockAddr)) == SOCKET_ERROR ) { Trace1("\nError %d binding the listening socket.\n", WSAGetLastError() ); return HRESULT_FROM_WIN32(WSAGetLastError()); } return hrOK; } /*--------------------------------------------------------------------------- CWinsStatusHandler::ListeningThreadFunc( ) Abstract: A blocking recvfrom() is sitting in an infinite loop. Whenever we receive anything on our listening socket we do a quick sanity chk on the packet and then increment a counter. The processing is kept minimal to spare the CPU cycles. Arguments: pListenSock - pointer to the socket set we've opened to listen for xmits from the server Return value: none ---------------------------------------------------------------------------*/ DWORD WINAPI CWinsStatusHandler::ListeningThreadFunc( ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); SOCKADDR_IN senderSockAddr; int nSockAddrSize = sizeof( senderSockAddr ); int nBytesRcvd = 0; int nSocksReady; char MsgBuf[WINS_MESSAGE_SIZE]; int nSockNdx; LPBYTE MagicCookie; SOCKET listenSock; fd_set localSockSet; // to take care of reinit for select() char szOutput[MAX_PATH]; while ( TRUE ) { // check if the thread needs to be aborted if (WaitForSingleObject(m_hPauseListening, 0) == WAIT_OBJECT_0) { Trace0("CWinsStatusHandler::ListenThreadFun - going to sleep\n"); // wait until we are woken up or the next time interval expires WaitForSingleObject(m_hWaitIntervalListen, INFINITE); Trace0("CWinsStatusHandler::ListenThreadFun - waking up\n"); } if (WaitForSingleObject(m_hAbortListen, 0) == WAIT_OBJECT_0) { // we are going away.. break out man Trace0("CWinsStatusHandler::ListenThreadFun - abort detected, bye bye\n"); break; } // reinit the select set in every loop localSockSet = m_listenSockSet; timeval tm; tm.tv_sec = 5; // number of sockets ready nSocksReady = select( 0, &localSockSet, NULL, NULL, &tm ); if ( nSocksReady == SOCKET_ERROR ) { Trace1("select() failed with error %d.\n", WSAGetLastError() ); } for ( nSockNdx = 0; nSockNdx < nSocksReady; nSockNdx++ ) { listenSock = localSockSet.fd_array[nSockNdx]; nBytesRcvd = recvfrom( listenSock, MsgBuf, sizeof(MsgBuf), 0, (LPSOCKADDR )&senderSockAddr, &nSockAddrSize ); if ( nBytesRcvd == SOCKET_ERROR ) { Trace1( "recvfrom() failed with error %d.\n", WSAGetLastError() ); } strcpy(szOutput, (LPSTR)inet_ntoa(senderSockAddr.sin_addr)); CString strOutput(szOutput); Trace2("ListeningThreadFunc(): processing frame from %s port %d \n", strOutput, ntohs(senderSockAddr.sin_port)); // process incoming WINS if ( (listenSock == listenNameSvcSock) && (senderSockAddr.sin_port == NBT_NAME_SERVICE_PORT) ) { strcpy(szOutput, (LPSTR)inet_ntoa(senderSockAddr.sin_addr)); CString str(szOutput); Trace1("ListeningThreadFunc(): processing WINS frame from %s \n", str); int nCount = GetListSize(); for ( int i=0; i < nCount ; i++) { CServerStatus *pWinsSrvEntry = GetServer(i); // check if the server has been deleted in the scope pane if (IsServerDeleted(pWinsSrvEntry)) continue; // get the iP address for the server DWORD dwIPAdd = pWinsSrvEntry->dwIPAddress; CString strIP; ::MakeIPAddress(dwIPAdd, strIP); char szBuffer[MAX_PATH] = {0}; // convert to mbcs // NOTE: this should be ACP because its being handed to a winsock call WideToMBCS(strIP, szBuffer); // check if the server has been deleted in the scope pane if (IsServerDeleted(pWinsSrvEntry)) continue; if (dwIPAdd == 0) strcpy(szBuffer, pWinsSrvEntry->szIPAddress); DWORD dwSrvIPAdd = inet_addr( szBuffer ); if ( senderSockAddr.sin_addr.s_addr == dwSrvIPAdd ) { // check if the server has been deleted in the scope pane if (IsServerDeleted(pWinsSrvEntry)) continue; pWinsSrvEntry->dwMsgCount++; struct in_addr addrStruct; addrStruct.s_addr = dwSrvIPAdd; strcpy(szOutput, inet_ntoa(addrStruct)); CString str(szOutput); Trace1("ListeningThreadFunc(): WINS msg received from %s \n", str ); // notify the thread we got something SetEvent(m_hAnswer); } } } } /* END OF for() processing indicated sockets from select() */ } /* END OF while( TRUE ) */ return TRUE; } /*--------------------------------------------------------------------------- int CWinsHandler::Probe() Assembles and sends a name query to the WINS server. Arguments: none Return value: TRUE if a response has been received from the server FALSE otherwise Author: v-shubk ---------------------------------------------------------------------------*/ int CWinsStatusHandler::Probe( CServerStatus *pServer, SOCKET listenNameSvcSock ) { NM_FRAME_HDR *pNbtHeader = (NM_FRAME_HDR *)pServer->nbtFrameBuf; NM_QUESTION_SECT *pNbtQuestion = (NM_QUESTION_SECT *)( pServer->nbtFrameBuf + sizeof(NM_FRAME_HDR) ); char *pDest, *pName; struct sockaddr_in destSockAddr; // struct holding dest socket info int nBytesSent = 0; // char m_szNameToQry[MAX_PATH] = "Rhino1"; /* RFC 1002 section 4.2.12 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NAME_TRN_ID |0| 0x0 |0|0|1|0|0 0|B| 0x0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0001 | 0x0000 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0000 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | / QUESTION_NAME / / / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NB (0x0020) | IN (0x0001) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ pNbtHeader->xid = NM_QRY_XID; pNbtHeader->flags = NBT_NM_OPC_QUERY | NBT_NM_OPC_REQUEST | NBT_NM_FLG_RECURS_DESRD; pNbtHeader->question_cnt = 0x0100; pNbtHeader->answer_cnt = 0; pNbtHeader->name_serv_cnt = 0; pNbtHeader->additional_cnt = 0; // pDest is filling nbtQuestion->q_name pNbtQuestion->q_type = NBT_NM_QTYP_NB; pNbtQuestion->q_class = NBT_NM_QCLASS_IN; // // translate the name // pDest = (char *)&(pNbtQuestion->q_name); pName = pServer->szServerName; // the first byte of the name is the length field = 2*16 *pDest++ = NBT_NAME_SIZE; // step through name converting ascii to half ascii, for 32 times for ( int i = 0; i < (NBT_NAME_SIZE / 2) ; i++ ) { *pDest++ = (*pName >> 4) + 'A'; *pDest++ = (*pName++ & 0x0F) + 'A'; } *pDest++ = '\0'; *pDest = '\0'; // check if the server has been deleted in the scope pane if (IsServerDeleted(pServer)) return FALSE; CString strIP; DWORD dwIPAdd = pServer->dwIPAddress; // even then 0 means, invalid server if (dwIPAdd == 0 && strcmp(pServer->szIPAddress, "") == 0) return FALSE; ::MakeIPAddress(dwIPAdd, strIP); char szBuffer[MAX_PATH] = {0}; // convert to mbcs // NOTE: this should be ACP because it is being used in a winsock call WideToMBCS(strIP, szBuffer); // if the name is not yet resolved if (dwIPAdd == 0) { strcpy(szBuffer, pServer->szIPAddress); } DWORD dwSrvIPAdd = inet_addr( szBuffer ); // // send the name query frame // destSockAddr.sin_family = PF_INET; destSockAddr.sin_port = NBT_NAME_SERVICE_PORT; destSockAddr.sin_addr.s_addr = dwSrvIPAdd; for (int k = 0; k < 8 ; k++ ) { destSockAddr.sin_zero[k] = 0; } struct in_addr addrStruct; addrStruct.s_addr = dwSrvIPAdd; Trace1( "CWinsSrv::Probe(): sending probe Name Query to %s \n", strIP); nBytesSent = sendto( listenNameSvcSock, (PCHAR )pServer->nbtFrameBuf, sizeof(NM_FRAME_HDR) + sizeof(NM_QUESTION_SECT), 0, (struct sockaddr *)&destSockAddr, sizeof( struct sockaddr ) ); if ( nBytesSent == SOCKET_ERROR ) { Trace1("CWinsSrv::Probe(): Error %d in sendto().\n", WSAGetLastError() ); } // // the other thread should see the incoming frame and increment dwMsgCount // WaitForSingleObject(m_hAnswer, ANSWER_TIMEOUT); if ( pServer->dwMsgCount == 0 ) return FALSE; return TRUE; } /* END OF Probe() */ /*--------------------------------------------------------------------------- CWinsStatusHandler::ExecuteMonitoring() Starts monitoring thread for the servers Author: v-shubk ----------------------------------------------------------------------------*/ DWORD WINAPI CWinsStatusHandler::ExecuteMonitoring() { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); HANDLE hListeningThread; CServerStatus *pWinsSrvEntry = NULL; // create listening thread FD_ZERO( &m_listenSockSet ); FD_SET( listenSockCl, &m_listenSockSet ); FD_SET( listenSockSrv, &m_listenSockSet ); FD_SET( listenNameSvcSock, &m_listenSockSet ); m_hListenThread = CreateThread( NULL, // handle can't be inherited 0, // default stack size MonThreadProc, // thread function this, // argument to the thread function 0, // start thread immediately NULL ); if (m_hListenThread == NULL ) { Trace0("CWinsStatusHandler::ExecuteMonitoring() - Listening thread failed to start\n"); return hrOK; } // main checking loop while ( TRUE ) { // scanning the list of WINS servers POSITION pos; int nCount = GetListSize(); m_nServersUpdated = 0; for (int i = 0; i < nCount; i++) { pWinsSrvEntry = GetServer(i); if (IsServerDeleted(pWinsSrvEntry)) continue; UpdateStatus(i, IDS_ROOTNODE_STATUS_WORKING, ICON_IDX_SERVER_BUSY); NotifyMainThread(); DWORD dwIPAdd = pWinsSrvEntry->dwIPAddress; // if the server is not connected, try to get the host IP address if (dwIPAdd == 0) { // get the server name and convert to MBCS. Get the IP for this server and try // to check the status char* dest_ip=NULL; char hostname[MAX_PATH] ; struct sockaddr_in dest; unsigned addr =0; if (IsServerDeleted(pWinsSrvEntry)) continue; strcpy(hostname,pWinsSrvEntry->szServerName); HOSTENT *hp = gethostbyname(hostname); if ((!hp) && (addr == INADDR_NONE) ) { CString str(hostname); Trace1("Unable to resolve %s \n",str); SetIPAddress(i, NULL); } else if (!hp) { addr = inet_addr(hostname); SetIPAddress(i, hostname); } else { if (hp != NULL) memcpy(&(dest.sin_addr),hp->h_addr, hp->h_length); else dest.sin_addr.s_addr = addr; if (hp) dest.sin_family = hp->h_addrtype; else dest.sin_family = AF_INET; dest_ip = inet_ntoa(dest.sin_addr); SetIPAddress(i, dest_ip); } } CString strIP; if (IsServerDeleted(pWinsSrvEntry)) continue; ::MakeIPAddress(pWinsSrvEntry->dwIPAddress, strIP); // TRY to probe max 3 times if (pWinsSrvEntry->dwMsgCount == 0) { UINT uStatus = 0; UINT uImage; if (IsServerDeleted(pWinsSrvEntry)) continue; BOOL fResponding = FALSE; if (pWinsSrvEntry->dwIPAddress != 0) { for (int j = 0; j < 3; j++) { fResponding = Probe(pWinsSrvEntry, listenNameSvcSock); if (fResponding) break; if (FCheckForAbort()) { // we are going away.. break out man Trace0("CWinsStatusHandler::ExecuteMonitoring() - abort detected, bye bye \n"); break; } } } // check to see if we need to clear out if (FCheckForAbort()) { // we are going away.. break out man Trace0("CWinsStatusHandler::ExecuteMonitoring() - abort detected, bye bye \n"); break; } if (!fResponding) { Trace1("Status is DOWN for the server %s \n", strIP); uStatus = IDS_ROOTNODE_STATUS_DOWN; uImage = ICON_IDX_SERVER_LOST_CONNECTION; } else { Trace1("Status is UP for the server %s \n", strIP); uStatus = IDS_ROOTNODE_STATUS_UP; uImage = ICON_IDX_SERVER_CONNECTED; } if (IsServerDeleted(pWinsSrvEntry)) continue; UpdateStatus(i, uStatus, uImage); m_nServersUpdated++; // update the last checked time pWinsSrvEntry->m_timeLast = CTime::GetCurrentTime(); NotifyMainThread(); } else { Trace2( "%d WINS msg from server %s - zeroing counter\n", pWinsSrvEntry->dwMsgCount, strIP); if (IsServerDeleted(pWinsSrvEntry)) continue; pWinsSrvEntry->dwMsgCount = 0; } pWinsSrvEntry->dwMsgCount = 0; } // tell the listening thread to go to sleep SetEvent(m_hPauseListening); m_nServersUpdated = 0; // wait for the next interval or if we are triggered Trace1("CWinsStatusHandler::ExecuteMonitoring() - going to sleep for %d \n", m_dwUpdateInterval); WaitForSingleObject(m_hWaitIntervalMain, m_dwUpdateInterval); Trace0("CWinsStatusHandler::ExecuteMonitoring() - waking up\n"); // wake up the listening thread SetEvent(m_hWaitIntervalListen); if (FCheckForAbort()) { // we are going away.. break out man Trace0("CWinsStatusHandler::ExecuteMonitoring() - abort detected, bye bye \n"); break; } } return TRUE; } /*--------------------------------------------------------------------------- CWinsStatusHandler::CloseSockets Closes all the socket connections that were opened Author: v-shubk ---------------------------------------------------------------------------*/ BOOL CWinsStatusHandler::FCheckForAbort() { BOOL fAbort = FALSE; if (WaitForSingleObject(m_hAbortMain, 0) == WAIT_OBJECT_0) { // we are going away.. break out man fAbort = TRUE; } return fAbort; } /*--------------------------------------------------------------------------- CWinsStatusHandler::CloseSockets Closes all the socket connections that were opened Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::CloseSockets() { // final clean up if (closesocket(listenSockCl) == SOCKET_ERROR) { Trace1("closesocket(listenSockCl) failed with error %d.\n", WSAGetLastError()); } if (closesocket(listenSockSrv) == SOCKET_ERROR) { Trace1("closesocket(listenSockSrv) failed with error %d.\n", WSAGetLastError()); } if (closesocket(listenNameSvcSock) == SOCKET_ERROR) { Trace1("closesocket(listenNameSvcSock) failed with error %d \n", WSAGetLastError()); } // we're going away... Trace0("CWinsStatusHandler::CloseSockets() - Setting abort event.\n"); SetEvent(m_hAbortListen); SetEvent(m_hAbortMain); // wake everybody up Trace0("CWinsStatusHandler::CloseSockets() - waking up threads.\n"); SetEvent(m_hWaitIntervalListen); SetEvent(m_hWaitIntervalMain); SetEvent(m_hAnswer); // terminate the threads if (m_hListenThread) { if (WaitForSingleObject(m_hListenThread, 5000) != WAIT_OBJECT_0) { Trace0("CWinsStatusHandler::CloseSockets() - ListenThread failed to cleanup!\n"); } ::CloseHandle(m_hListenThread); m_hListenThread = NULL; } if (m_hMainMonThread) { if (WaitForSingleObject(m_hMainMonThread, 5000) != WAIT_OBJECT_0) { Trace0("CWinsStatusHandler::CloseSockets() - MainMonThread failed to cleanup!\n"); } ::CloseHandle(m_hMainMonThread); m_hMainMonThread = NULL; } // clean up our events if (m_hPauseListening) { ::CloseHandle(m_hPauseListening); m_hPauseListening = NULL; } if (m_hAbortListen) { ::CloseHandle(m_hAbortListen); m_hAbortListen = NULL; } if (m_hAbortMain) { ::CloseHandle(m_hAbortMain); m_hAbortMain = NULL; } if (m_hWaitIntervalListen) { ::CloseHandle(m_hWaitIntervalListen); m_hWaitIntervalListen = NULL; } if (m_hWaitIntervalMain) { ::CloseHandle(m_hWaitIntervalMain); m_hWaitIntervalMain = NULL; } if (m_hAnswer) { ::CloseHandle(m_hAnswer); m_hAnswer = NULL; } } /*--------------------------------------------------------------------------- CWinsStatusHandler::CreateNodes(ITFSNode *pNode) Displays the result pane nodes for the servers Author: v-shubk ---------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::CreateNodes(ITFSNode *pNode) { HRESULT hr = hrOK; POSITION pos = NULL; int nCount = (int)m_listServers.GetSize(); for(int i = 0; i < nCount; i++) { SPITFSNode spStatLeaf; CServerStatus *pWinsSrvEntry = m_listServers.GetAt(i); CreateLeafTFSNode(&spStatLeaf, &GUID_WinsServerStatusLeafNodeType, pWinsSrvEntry, pWinsSrvEntry, m_spNodeMgr); // Tell the handler to initialize any specific data pWinsSrvEntry->InitializeNode((ITFSNode *) spStatLeaf); // Add the node as a child to the Active Leases container pNode->AddChild(spStatLeaf); pWinsSrvEntry->Release(); } return hr; } /*--------------------------------------------------------------------------- CWinsStatusHandler::UpdateStatusColumn(ITFSNode *pNode) Updates the status column of the servers in the result pane Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::UpdateStatusColumn(ITFSNode *pNode) { HRESULT hr = hrOK; // enumerate thro' all the nodes SPITFSNodeEnum spNodeEnum; SPITFSNode spCurrentNode; ULONG nNumReturned = 0; BOOL bFound = FALSE; // get the enumerator for this node pNode->GetEnum(&spNodeEnum); spNodeEnum->Next(1, &spCurrentNode, &nNumReturned); while (nNumReturned) { CServerStatus * pStat = GETHANDLER(CServerStatus, spCurrentNode); spCurrentNode->SetData(TFS_DATA_IMAGEINDEX, pStat->m_uImage); spCurrentNode->SetData(TFS_DATA_OPENIMAGEINDEX, pStat->m_uImage); // fillup the status column spCurrentNode->ChangeNode(RESULT_PANE_CHANGE_ITEM); // get the next Server in the list spCurrentNode.Release(); spNodeEnum->Next(1, &spCurrentNode, &nNumReturned); } } /*--------------------------------------------------------------------------- CWinsStatusHandler::AddNode(ITFSNode *pNode, CWinsServerHandler *pServer) Adds a node to the result pane, used when a new server is added to tree that has to be reflected for the status node Author: v-shubk ---------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::AddNode(ITFSNode *pNode, CWinsServerHandler *pServer) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); HRESULT hr = hrOK; CServerStatus* pServ = NULL; char szBuffer[MAX_PATH] = {0}; SPITFSNode spStatLeaf; // if we haven't been expanded, don't add now. Will get done // when we are expanded. if (!m_bExpanded) return hr; // add to the list // NOTE: this should be ACP because it's being used through winsock CString strTemp = pServer->GetServerAddress(); WideToMBCS(strTemp, szBuffer); // check if the server already exists, if so, just change the // state stored to SERVER_ADDED and change the variables // appropriately if ((pServ = GetExistingServer(szBuffer)) == NULL) { pServ = new CServerStatus(m_spTFSCompData); strcpy(pServ->szServerName, szBuffer); AddServer(pServ); } else { // just add the related data to the CServerStatus and add the node // to the UI strcpy(pServ->szServerName, szBuffer); // set the flag to SERVER_ADDED MarkAsDeleted(szBuffer, FALSE); } pServ->dwIPAddress = pServer->GetServerIP(); pServ->dwMsgCount = 0; strcpy(pServ->szIPAddress, ""); // create the new node here CreateLeafTFSNode(&spStatLeaf, &GUID_WinsServerStatusLeafNodeType, pServ, pServ, m_spNodeMgr); // Tell the handler to initialize any specific data pServ->InitializeNode((ITFSNode *) spStatLeaf); // Add the node as a child to the Active Leases container pNode->AddChild(spStatLeaf); pServ->Release(); spStatLeaf->ChangeNode(RESULT_PANE_CHANGE_ITEM_DATA); pNode->ChangeNode(SCOPE_PANE_CHANGE_ITEM); return hr; } /*--------------------------------------------------------------------------- CWinsStatusHandler::DeleteNode(ITFSNode *pNode, CWinsServerHandler *pServer) Removes the particular server from tehresult pane Author: v-shubk ---------------------------------------------------------------------------*/ HRESULT CWinsStatusHandler::DeleteNode(ITFSNode *pNode, CWinsServerHandler *pServer) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); HRESULT hr = hrOK; CServerStatus* pServ = NULL; char szBuffer[MAX_PATH]; SPITFSNode spStatLeaf; // loop thro' the status nodes and set the flag to deleted so that this // server is not seen in the result pane SPITFSNodeEnum spNodeEnum; SPITFSNode spCurrentNode; ULONG nNumReturned = 0; // get the enumerator pNode->GetEnum(&spNodeEnum); spNodeEnum->Next(1, &spCurrentNode, &nNumReturned); while (nNumReturned) { char szBuffer[MAX_PATH]; // iterate thro' all the nodes and get the one that matches the // current server CServerStatus * pStat = GETHANDLER(CServerStatus, spCurrentNode); // convert to ANSI CString strTemp = pServer->GetServerAddress(); WideToMBCS(strTemp, szBuffer); // if found if (_stricmp(szBuffer, pStat->szServerName) == 0) { // mark as deleted and break MarkAsDeleted(szBuffer, TRUE); // remove this node spCurrentNode->SetVisibilityState(TFS_VIS_HIDE); spCurrentNode->ChangeNode(RESULT_PANE_DELETE_ITEM); // do the cleanup and break //spCurrentNode.Release(); //break; } // get the next server in the list spCurrentNode.Release(); spNodeEnum->Next(1, &spCurrentNode, &nNumReturned); } return hr; } /*--------------------------------------------------------------------------- CWinsStatusHandler::StartMonitoring Spawns off the monitoring thread Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::StartMonitoring(ITFSNode *pNode) { HRESULT hr = hrOK; // create the sockets, they need to be closed at the end hr = CreateListeningSockets(); if (hr != hrOK) { Trace0("CWinsStatusHandler::StartMonitoring, Initializing the sockets failed\n"); // no point continuing return; } m_hMainMonThread = CreateThread(NULL, 0, MainMonThread, this, 0, NULL ); if (m_hMainMonThread == NULL) { Trace0("CWinsStatusHandler:: Main Monitoring thread failed to start\n"); return; } } /*--------------------------------------------------------------------------- CWinsStatusHandler::GetServer(int i) Returns the Server given the index Author: v-shubk ---------------------------------------------------------------------------*/ CServerStatus* CWinsStatusHandler::GetServer(int i) { CSingleLock sl(&m_cs); sl.Lock(); return m_listServers.GetAt(i); } /*--------------------------------------------------------------------------- CWinsStatusHandler::AddServer(CServerStatus* pServer) Adds a server to the array maintained Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::AddServer(CServerStatus* pServer) { CSingleLock sl(&m_cs); sl.Lock(); m_listServers.Add(pServer); } /*--------------------------------------------------------------------------- CWinsStatusHandler::RemoveServer(int i) Removes a server from the array Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::RemoveServer(int i) { CSingleLock sl(&m_cs); sl.Lock(); m_listServers.RemoveAt(i); } /*--------------------------------------------------------------------------- CWinsStatusHandler::UpdateStatus(UINT nID, int i) Upadtes the status string for the server Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::UpdateStatus(int nIndex, UINT uStatusId, UINT uImage) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); CSingleLock sl(&m_cs); sl.Lock(); CServerStatus *pStat = m_listServers.GetAt(nIndex); pStat->m_strStatus.LoadString(uStatusId); pStat->m_uImage = uImage; } /*--------------------------------------------------------------------------- CWinsStatusHandler::GetListSize() Retruns the number of elements in the array Author: v-shubk ---------------------------------------------------------------------------*/ int CWinsStatusHandler::GetListSize() { CSingleLock sl(&m_cs); sl.Lock(); return (int)m_listServers.GetSize(); } /*--------------------------------------------------------------------------- CWinsStatusHandler::SetIPAddress(int i, LPSTR szIP) Sets the iP Address of the server, this is the case when the server is added with Do not connect option, but we still need to update the status Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::SetIPAddress(int i, LPSTR szIP) { CSingleLock sl(&m_cs); sl.Lock(); CServerStatus *pStat = m_listServers.GetAt(i); strcpy(pStat->szIPAddress, szIP); } /*--------------------------------------------------------------------------- CWinsStatusHandler::MarkAsDeleted(LPSTR szBuffer, BOOL bDelete) Marks the flag to DELETED if bDelete is TRUE, else to ADDED All the servers with the flag DELETED set are not processed and are not shown in the UI. Author: v-shubk ---------------------------------------------------------------------------*/ void CWinsStatusHandler::MarkAsDeleted(LPSTR szBuffer, BOOL bDelete) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); CSingleLock sl(&m_cs); sl.Lock(); int nCount = 0; CServerStatus *pStat = NULL; // get the list of the servers maintained nCount = (int)m_listServers.GetSize(); for(int i = 0; i < nCount; i++) { pStat = m_listServers.GetAt(i); if (_stricmp(szBuffer, pStat->szServerName) == 0) { // set the deleted flag if (bDelete) pStat->dwState = SERVER_DELETED; else pStat->dwState = SERVER_ADDED; break; } } return; } /*--------------------------------------------------------------------------- CWinsStatusHandler::GetExistingServer(LPSTR szBuffer) Gets the pointer to the existing server in the array This function is useful when the server is deletd and again added back to the scope tree. Author: v-shubk ----------------------------------------------------------------------------*/ CServerStatus * CWinsStatusHandler::GetExistingServer(LPSTR szBuffer) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); CSingleLock sl(&m_cs); sl.Lock(); int nCount = 0; CServerStatus *pStat = NULL; for(int i = 0; i < nCount; i++) { pStat = m_listServers.GetAt(i); if (_strcmpi(pStat->szServerName, szBuffer) == 0) return pStat; } return NULL; } /*--------------------------------------------------------------------------- CWinsStatusHandler::IsServerDeleted(CServerStatus *pStat) Checks if a server has been deleted, such servers sre not considered for monitoring Author: v-shubk ---------------------------------------------------------------------------*/ BOOL CWinsStatusHandler::IsServerDeleted(CServerStatus *pStat) { return (pStat->dwState == SERVER_DELETED) ? TRUE : FALSE; } /*--------------------------------------------------------------------------- CWinsStatusHandler::NotifyMainThread() Description Author: EricDav ---------------------------------------------------------------------------*/ void CWinsStatusHandler::NotifyMainThread() { if (!m_uMsgBase) { m_uMsgBase = (INT) ::SendMessage(m_spTFSCompData->GetHiddenWnd(), WM_HIDDENWND_REGISTER, TRUE, 0); } ::PostMessage(m_spTFSCompData->GetHiddenWnd(), m_uMsgBase + WM_HIDDENWND_INDEX_HAVEDATA, (WPARAM)(ITFSThreadHandler *)this, NULL); } // listening thread for the main monitoring thread DWORD WINAPI MonThreadProc(LPVOID pParam) { DWORD dwReturn; HRESULT hr = hrOK; COM_PROTECT_TRY { CWinsStatusHandler * pWinsStatus = (CWinsStatusHandler *) pParam; Trace0("MonThreadProc - Thread started.\n"); dwReturn = pWinsStatus->ListeningThreadFunc(); Trace0("MonThreadProc - Thread ending.\n"); } COM_PROTECT_CATCH return dwReturn; } // main monitoring thread DWORD WINAPI MainMonThread(LPVOID pParam) { DWORD dwReturn; HRESULT hr = hrOK; COM_PROTECT_TRY { CWinsStatusHandler * pWinsStatus = (CWinsStatusHandler *) pParam; Trace0("MainMonThread - Thread started.\n"); dwReturn = pWinsStatus->ExecuteMonitoring(); Trace0("MainMonThread - Thread ending.\n"); } COM_PROTECT_CATCH return dwReturn; }