#include "precomp.h" #pragma hdrstop #include "private.h" #include "engine.h" #include "resource.h" #include "Document.h" #include "Application.h" #include "MainForm.h" #include "AboutDialog.h" #include "application.tmh" #include Application theApplication; MUsingCom usingCom; BOOL CanRunNLB(void); BEGIN_MESSAGE_MAP( Application, CWinApp ) ON_COMMAND( ID_HELP, OnHelp ) ON_COMMAND( ID_APP_ABOUT, OnAppAbout ) END_MESSAGE_MAP() // #define szNLBMGRREG_DISABLE_DISCLAIMER L"DisableNlbMgrDisclaimer" #if DBG // // Allow the DEMO cmdline option // #define ALLOW_DEMO 1 #endif //DBG BOOL NoAdminNics(void); void CNlbMgrCommandLineInfo::ParseParam( LPCTSTR lpszParam, BOOL bFlag, BOOL bLast ) // // -demo // -hostlist file.txt // -help -? // -autoresfresh // -noping { static enum Commands { None = 0, Demo, NoPing, HostList, AutoRefresh }; static UINT LastCommand = None; TRACE_VERB("-> %!FUNC! (szParm=\"%ws\", bFlag=%lu, bLast=%lu)", lpszParam, bFlag, bLast); if (m_bUsage) { goto end; } if (bFlag) { /* Throw an error if this is a flag, but the last command was the HostList command, which REQUIRES a non-flag argument following it. */ if (LastCommand == HostList) { /* Turn the host list option off to keep NLB manager from trying to open NULL filename. */ m_bHostList = FALSE; m_bUsage = TRUE; // error goto end; } #if ALLOW_DEMO if (!_wcsicmp(lpszParam, L"demo")) { if (m_bDemo) { m_bUsage = TRUE; // error goto end; } m_bDemo = TRUE; LastCommand = Demo; } else #endif // ALLOW_DEMO if (!_wcsicmp(lpszParam, L"noping")) { if (m_bNoPing) { m_bUsage = TRUE; // error goto end; } m_bNoPing = TRUE; LastCommand = NoPing; } else if (!_wcsicmp(lpszParam, L"hostlist")) { if (m_bHostList || bLast) { m_bUsage = TRUE; // error goto end; } m_bHostList = TRUE; LastCommand = HostList; } else if (!_wcsicmp(lpszParam, L"autorefresh")) { if (m_bAutoRefresh) { m_bUsage = TRUE; // error goto end; } m_bAutoRefresh = TRUE; LastCommand = AutoRefresh; } else { m_bUsage = TRUE; // error or help } } else { switch (LastCommand) { case None: m_bUsage = TRUE; // error break; case Demo: m_bUsage = TRUE; // error break; case NoPing: m_bUsage = TRUE; // error break; case HostList: m_bstrHostListFile = _bstr_t(lpszParam); // read the file name of the host list break; case AutoRefresh: m_refreshInterval = _wtoi(lpszParam); // read the refresh interval /* If the specified refresh interval is too small to be practical, re-set it. */ if (m_refreshInterval < NLBMGR_AUTOREFRESH_MIN_INTERVAL) m_refreshInterval = NLBMGR_AUTOREFRESH_MIN_INTERVAL; break; default: m_bUsage = TRUE; // error break; } /* Re-set the last command. */ LastCommand = None; } end: TRACE_VERB("%!FUNC! <-"); } BOOL Application::ProcessShellCommand( CNlbMgrCommandLineInfo& rCmdInfo ) { BOOL fRet = FALSE; LPCWSTR szFile = NULL; TRACE_CRIT("-> %!FUNC!"); fRet = CWinApp::ProcessShellCommand(rCmdInfo); if (!fRet) { goto end; } szFile = (LPCWSTR) rCmdInfo.m_bstrHostListFile; if (szFile==NULL) { szFile = L""; } TRACE_VERB("%!FUNC! bUsage=%lu bDemo=%lu bNoPing=%lu bHostList=%lu szFile=\"%ws\"", rCmdInfo.m_bUsage, rCmdInfo.m_bDemo, rCmdInfo.m_bNoPing, rCmdInfo.m_bHostList, szFile ); if (rCmdInfo.m_bUsage) { _bstr_t bstrMsg = GETRESOURCEIDSTRING( IDS_USAGE_MESSAGE ); _bstr_t bstrTitle = GETRESOURCEIDSTRING( IDS_USAGE_TITLE ); ::MessageBox( NULL, (LPCWSTR) bstrMsg, (LPCWSTR) bstrTitle, MB_ICONINFORMATION | MB_OK ); fRet = FALSE; } else { if (rCmdInfo.m_bDemo) { _bstr_t bstrMsg = GETRESOURCEIDSTRING( IDS_DEMO_MESSAGE ); _bstr_t bstrTitle = GETRESOURCEIDSTRING( IDS_DEMO_TITLE ); ::MessageBox( NULL, (LPCWSTR) bstrMsg, (LPCWSTR) bstrTitle, MB_ICONINFORMATION | MB_OK ); } fRet = TRUE; } end: TRACE_CRIT("<- %!FUNC! returns %lu", fRet); return fRet; } BOOL Application::InitInstance() { BOOL fRet = FALSE; WPP_INIT_TRACING(L"Microsoft\\NLB\\TPROV"); TRACE_INFO("------------ APPLICATION INITITIALIZATION -------------"); // // Set the current thread id as the main thread id // m_dwMainThreadId = GetCurrentThreadId(); ParseCommandLine(gCmdLineInfo); m_pSingleDocumentTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS( Document ), RUNTIME_CLASS( MainForm ), RUNTIME_CLASS( LeftView) ); AddDocTemplate( m_pSingleDocumentTemplate ); // // NOTE: ProcessShellCommand is our (Application) own version. It // calls CWinApp::ProcessShellCommand. // fRet = ProcessShellCommand( gCmdLineInfo ); if (!fRet) { goto end; } fRet = CanRunNLB(); // fall through... end: if (!fRet) { // Deinit tracing here. WPP_CLEANUP(); } return fRet; } void Application::OnAppAbout() { AboutDialog aboutDlg; aboutDlg.DoModal(); } void Application::OnHelp() { WCHAR wbuf[CVY_STR_SIZE]; /* Spawn the windows help process. */ StringCbPrintf(wbuf, sizeof(wbuf), L"%ls\\help\\%ls", _wgetenv(L"WINDIR"), CVY_HELP_FILE); _wspawnlp(P_NOWAIT, L"hh.exe", L"hh.exe", wbuf, NULL); } BOOL CanRunNLB(void) /* Checks if NLB can run on the current machine. The main check is to make sure that there is atleast one active NIC without NLB bound. */ { if (NoAdminNics()) { ::MessageBox( NULL, GETRESOURCEIDSTRING( IDS_CANTRUN_NONICS_TEXT), // Contents GETRESOURCEIDSTRING( IDS_CANTRUN_NONICS_CAPTION), // caption MB_ICONSTOP | MB_OK ); } else { // ::ShowDisclaimer(); } return TRUE; } // // This class manages NetCfg interfaces // class AppMyNetCfg { public: AppMyNetCfg(VOID) { m_pINetCfg = NULL; m_pLock = NULL; } ~AppMyNetCfg() { ASSERT(m_pINetCfg==NULL); ASSERT(m_pLock==NULL); } WBEMSTATUS Initialize( BOOL fWriteLock ); VOID Deinitialize( VOID ); WBEMSTATUS GetNlbCompatibleNics( OUT LPWSTR **ppszNics, OUT UINT *pNumNics, OUT UINT *pNumBoundToNlb // OPTIONAL ); WBEMSTATUS GetBindingIF( IN LPCWSTR szComponent, OUT INetCfgComponentBindings **ppIBinding ); private: INetCfg *m_pINetCfg; INetCfgLock *m_pLock; }; // Class AppMyNetCfg WBEMSTATUS AppMyNetCfg::Initialize( BOOL fWriteLock ) { HRESULT hr; INetCfg *pnc = NULL; INetCfgLock *pncl = NULL; WBEMSTATUS Status = WBEM_E_CRITICAL_ERROR; BOOL fLocked = FALSE; BOOL fInitialized=FALSE; if (m_pINetCfg != NULL || m_pLock != NULL) { ASSERT(FALSE); goto end; } hr = CoCreateInstance( CLSID_CNetCfg, NULL, CLSCTX_SERVER, IID_INetCfg, (void **) &pnc); if( !SUCCEEDED( hr ) ) { // failure to create instance. //TRACE_CRIT("ERROR: could not get interface to Net Config"); goto end; } // // If require, get the write lock // if (fWriteLock) { WCHAR *szLockedBy = NULL; hr = pnc->QueryInterface( IID_INetCfgLock, ( void **) &pncl ); if( !SUCCEEDED( hr ) ) { //TRACE_CRIT("ERROR: could not get interface to NetCfg Lock"); goto end; } hr = pncl->AcquireWriteLock( 1, // One Second L"NLBManager", &szLockedBy); if( hr != S_OK ) { //TRACE_CRIT("Could not get write lock. Lock held by %ws", // (szLockedBy!=NULL) ? szLockedBy : L""); goto end; } } // Initializes network configuration by loading into // memory all basic networking information // hr = pnc->Initialize( NULL ); if( !SUCCEEDED( hr ) ) { // failure to Initialize //TRACE_CRIT("INetCfg::Initialize failure "); goto end; } Status = WBEM_NO_ERROR; end: if (FAILED(Status)) { if (pncl!=NULL) { if (fLocked) { pncl->ReleaseWriteLock(); } pncl->Release(); pncl=NULL; } if( pnc != NULL) { if (fInitialized) { pnc->Uninitialize(); } pnc->Release(); pnc= NULL; } } else { m_pINetCfg = pnc; m_pLock = pncl; } return Status; } VOID AppMyNetCfg::Deinitialize( VOID ) { if (m_pLock!=NULL) { m_pLock->ReleaseWriteLock(); m_pLock->Release(); m_pLock=NULL; } if( m_pINetCfg != NULL) { m_pINetCfg->Uninitialize(); m_pINetCfg->Release(); m_pINetCfg= NULL; } } WBEMSTATUS AppMyNetCfg::GetNlbCompatibleNics( OUT LPWSTR **ppszNics, OUT UINT *pNumNics, OUT UINT *pNumBoundToNlb // OPTIONAL ) /* Returns an array of pointers to string-version of GUIDS that represent the set of alive and healthy NICS that are suitable for NLB to bind to -- basically alive ethernet NICs. Delete ppNics using the delete WCHAR[] operator. Do not delete the individual strings. */ { #define MY_GUID_LENGTH 38 WBEMSTATUS Status = WBEM_E_CRITICAL_ERROR; HRESULT hr; IEnumNetCfgComponent* pencc = NULL; INetCfgComponent *pncc = NULL; ULONG countToFetch = 1; ULONG countFetched; UINT NumNics = 0; LPWSTR *pszNics = NULL; INetCfgComponentBindings *pINlbBinding=NULL; UINT NumNlbBoundNics = 0; typedef struct _MYNICNODE MYNICNODE; typedef struct _MYNICNODE { LPWSTR szNicGuid; MYNICNODE *pNext; } MYNICNODE; MYNICNODE *pNicNodeList = NULL; MYNICNODE *pNicNode = NULL; *ppszNics = NULL; *pNumNics = 0; if (pNumBoundToNlb != NULL) { *pNumBoundToNlb = 0; } if (m_pINetCfg == NULL) { // // This means we're not initialized // ASSERT(FALSE); goto end; } hr = m_pINetCfg->EnumComponents( &GUID_DEVCLASS_NET, &pencc ); if( !SUCCEEDED( hr ) ) { // failure to Enumerate net components //TRACE_CRIT("%!FUNC! Could not enum netcfg adapters"); pencc = NULL; goto end; } // // Check if nlb is bound to the nlb component. // // // If we need to count of NLB-bound nics, get instance of the nlb component // if (pNumBoundToNlb != NULL) { Status = GetBindingIF(L"ms_wlbs", &pINlbBinding); if (FAILED(Status)) { //TRACE_CRIT("%!FUNC! WARNING: NLB doesn't appear to be installed on this machine"); pINlbBinding = NULL; } } while( ( hr = pencc->Next( countToFetch, &pncc, &countFetched ) )== S_OK ) { LPWSTR szName = NULL; hr = pncc->GetBindName( &szName ); if (!SUCCEEDED(hr)) { //TRACE_CRIT("%!FUNC! WARNING: couldn't get bind name for 0x%p, ignoring", // (PVOID) pncc); continue; } do // while FALSE -- just to allow breaking out { UINT Len = wcslen(szName); if (Len != MY_GUID_LENGTH) { //TRACE_CRIT("%!FUNC! WARNING: GUID %ws has unexpected length %ul", // szName, Len); break; } DWORD characteristics = 0; hr = pncc->GetCharacteristics( &characteristics ); if(!SUCCEEDED(hr)) { //TRACE_CRIT("%!FUNC! WARNING: couldn't get characteristics for %ws, ignoring", // szName); break; } if (((characteristics & NCF_PHYSICAL) || (characteristics & NCF_VIRTUAL)) && !(characteristics & NCF_HIDDEN)) { ULONG devstat = 0; // This is a physical or virtual miniport that is NOT hidden. These // are the same adapters that show up in the "Network Connections" // dialog. Hidden devices include WAN miniports, RAS miniports and // NLB miniports - all of which should be excluded here. // check if the nic is enabled, we are only // interested in enabled nics. // hr = pncc->GetDeviceStatus( &devstat ); if(!SUCCEEDED(hr)) { //TRACE_CRIT( // "%!FUNC! WARNING: couldn't get dev status for %ws, ignoring", // szName // ); break; } // if any of the nics has any of the problem codes // then it cannot be used. if( devstat != CM_PROB_NOT_CONFIGURED && devstat != CM_PROB_FAILED_START && devstat != CM_PROB_NORMAL_CONFLICT && devstat != CM_PROB_NEED_RESTART && devstat != CM_PROB_REINSTALL && devstat != CM_PROB_WILL_BE_REMOVED && devstat != CM_PROB_DISABLED && devstat != CM_PROB_FAILED_INSTALL && devstat != CM_PROB_FAILED_ADD ) { // // No problem with this nic and also // physical device // thus we want it. // if (pINlbBinding != NULL) { BOOL fBound = FALSE; hr = pINlbBinding->IsBoundTo(pncc); if( !SUCCEEDED( hr ) ) { //TRACE_CRIT("IsBoundTo method failed for Nic %ws", szName); goto end; } if( hr == S_OK ) { //TRACE_VERB("BOUND: %ws\n", szName); NumNlbBoundNics++; fBound = TRUE; } else if (hr == S_FALSE ) { //TRACE_VERB("NOT BOUND: %ws\n", szName); fBound = FALSE; } } // We allocate a little node to keep this string // temporarily and add it to our list of nodes. // pNicNode = new MYNICNODE; if (pNicNode == NULL) { Status = WBEM_E_OUT_OF_MEMORY; goto end; } ZeroMemory(pNicNode, sizeof(*pNicNode)); pNicNode->szNicGuid = szName; szName = NULL; // so we don't delete inside the lopp. pNicNode->pNext = pNicNodeList; pNicNodeList = pNicNode; NumNics++; } else { // There is a problem... //TRACE_CRIT( // "%!FUNC! WARNING: Skipping %ws because DeviceStatus=0x%08lx", // szName, devstat // ); break; } } else { //TRACE_VERB("%!FUNC! Ignoring non-physical device %ws", szName); } } while (FALSE); if (szName != NULL) { CoTaskMemFree( szName ); } pncc->Release(); pncc=NULL; } if (pINlbBinding!=NULL) { pINlbBinding->Release(); pINlbBinding = NULL; } if (NumNics==0) { Status = WBEM_NO_ERROR; goto end; } // // Now let's allocate space for all the nic strings and:w // copy them over.. // #define MY_GUID_LENGTH 38 pszNics = CfgUtilsAllocateStringArray(NumNics, MY_GUID_LENGTH); if (pszNics == NULL) { Status = WBEM_E_OUT_OF_MEMORY; goto end; } pNicNode= pNicNodeList; for (UINT u=0; upNext) { ASSERT(pNicNode != NULL); // because we just counted NumNics of em. UINT Len = wcslen(pNicNode->szNicGuid); if (Len != MY_GUID_LENGTH) { // // We should never get here beause we checked the length earlier. // //TRACE_CRIT("%!FUNC! ERROR: GUID %ws has unexpected length %ul", // pNicNode->szNicGuid, Len); ASSERT(FALSE); Status = WBEM_E_CRITICAL_ERROR; goto end; } CopyMemory( pszNics[u], pNicNode->szNicGuid, (MY_GUID_LENGTH+1)*sizeof(WCHAR)); ASSERT(pszNics[u][MY_GUID_LENGTH]==0); } Status = WBEM_NO_ERROR; end: // // Now release the temporarly allocated memory. // pNicNode= pNicNodeList; while (pNicNode!=NULL) { MYNICNODE *pTmp = pNicNode->pNext; CoTaskMemFree(pNicNode->szNicGuid); pNicNode->szNicGuid = NULL; delete pNicNode; pNicNode = pTmp; } if (FAILED(Status)) { // TRACE_CRIT("%!FUNC! fails with status 0x%08lx", (UINT) Status); NumNics = 0; if (pszNics!=NULL) { delete pszNics; pszNics = NULL; } } else { if (pNumBoundToNlb != NULL) { *pNumBoundToNlb = NumNlbBoundNics; } *ppszNics = pszNics; *pNumNics = NumNics; } if (pencc != NULL) { pencc->Release(); } return Status; } WBEMSTATUS AppMyNetCfg::GetBindingIF( IN LPCWSTR szComponent, OUT INetCfgComponentBindings **ppIBinding ) { WBEMSTATUS Status = WBEM_E_CRITICAL_ERROR; INetCfgComponent *pncc = NULL; INetCfgComponentBindings *pnccb = NULL; HRESULT hr; if (m_pINetCfg == NULL) { // // This means we're not initialized // ASSERT(FALSE); goto end; } hr = m_pINetCfg->FindComponent(szComponent, &pncc); if (FAILED(hr)) { // TRACE_CRIT("Error checking if component %ws does not exist\n", szComponent); pncc = NULL; goto end; } else if (hr == S_FALSE) { Status = WBEM_E_NOT_FOUND; // TRACE_CRIT("Component %ws does not exist\n", szComponent); goto end; } hr = pncc->QueryInterface( IID_INetCfgComponentBindings, (void **) &pnccb ); if( !SUCCEEDED( hr ) ) { // TRACE_CRIT("INetCfgComponent::QueryInterface failed "); pnccb = NULL; goto end; } Status = WBEM_NO_ERROR; end: if (pncc) { pncc->Release(); pncc=NULL; } *ppIBinding = pnccb; return Status; } BOOL NoAdminNics(void) /* Return TRUE IFF all NICs on this machine are bound to NLB. */ { LPWSTR *pszNics = NULL; OUT UINT NumNics = 0; OUT UINT NumBoundToNlb = 0; WBEMSTATUS Status = WBEM_NO_ERROR; BOOL fNetCfgInitialized = FALSE; AppMyNetCfg NetCfg; BOOL fRet = FALSE; // // Get and initialize interface to netcfg // Status = NetCfg.Initialize(FALSE); // TRUE == get write lock. if (FAILED(Status)) { goto end; } fNetCfgInitialized = TRUE; // // Get the total list of enabled nics and the list of nics // bound to NLB. If there are non-zero enabled nics and all are // bound to NLB, we return TRUE. // Status = NetCfg.GetNlbCompatibleNics( &pszNics, &NumNics, &NumBoundToNlb ); if (!FAILED(Status)) { fRet = NumNics && (NumNics == NumBoundToNlb); if (NumNics) { delete pszNics; pszNics = NULL; } } end: if (fNetCfgInitialized) { NetCfg.Deinitialize(); } return fRet; } void Application::ProcessMsgQueue() { MSG msg; BOOL bDoingBackgroundProcessing = FALSE; TRACE_INFO(L"-> %!FUNC!"); if (!mfn_IsMainThread()) goto end; if (InterlockedIncrement(&m_lMsgProcReentrancyCount) > 1) { InterlockedDecrement(&m_lMsgProcReentrancyCount); goto end; } while ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) { #if BUGFIX334243 if (msg.message == MYWM_DEFER_UI_MSG) { // DummyAction(L"Hey -- got DEFER_UI_MSG ProcessMsgQueue!"); } #endif // BUGFIX334243 if ( !this->PumpMessage( ) ) { bDoingBackgroundProcessing = FALSE; ::PostQuitMessage(0); break; } } // let MFC do its idle processing LONG lIdle = 0; while ( this->OnIdle(lIdle++ ) ) { } // Perform some background processing here // using another call to OnIdle this->DoWaitCursor(0); // process_msgqueue() breaks the hour glass cursor, This call restores the hour glass cursor if there was one InterlockedDecrement(&m_lMsgProcReentrancyCount); if (m_fQuit) { ::PostQuitMessage(0); } end: TRACE_INFO(L"<- %!FUNC!"); return; } // // Get application-wide lock. If main thread, while waiting to get the lock, // periodically process the msg loop. // VOID Application::Lock() { // // See notes.txt entry // 01/23/2002 JosephJ DEADLOCK in Leftview::mfn_Lock // for the reason for this convoluted implementation of mfn_Lock // if (mfn_IsMainThread()) { EnterCriticalSection(&m_crit); } else { while (!TryEnterCriticalSection(&m_crit)) { this->ProcessMsgQueue(); Sleep(100); } } } // // Get application-wide unlock // VOID Application::Unlock() { LeaveCriticalSection(&m_crit); }