#include "private.h" #include #define TF_THISMODULE TF_WEBCHECKCORE DWORD g_idSchedThread = 0; // global containing pointer to instance of CWebcheck. Needed to control // externals loading on demand. CWebCheck *g_pwc = NULL; ////////////////////////////////////////////////////////////////////////// // // CWebCheck implementation // ////////////////////////////////////////////////////////////////////////// CWebCheck::CWebCheck() { // Maintain global object count DllAddRef(); // Initialize object m_cRef = 1; // save our instance g_pwc = this; } CWebCheck::~CWebCheck() { // Maintain global object count DllRelease(); // no longer available g_pwc = NULL; } // // IUnknown members // STDMETHODIMP_(ULONG) CWebCheck::AddRef(void) { // TraceMsg(TF_THISMODULE, "CWebCheck::AddRef m_cRef=%d", m_cRef+1); return ++m_cRef; } STDMETHODIMP_(ULONG) CWebCheck::Release(void) { // TraceMsg(TF_THISMODULE, "CWebCheck::Release m_cRef=%d", m_cRef-1); if( 0L != --m_cRef ) return m_cRef; delete this; return 0L; } STDMETHODIMP CWebCheck::QueryInterface(REFIID riid, void ** ppv) { *ppv=NULL; // Validate requested interface if (IsEqualIID(riid, IID_IUnknown)) *ppv = (IUnknown *)this; else if (IsEqualIID(riid, IID_IOleCommandTarget)) *ppv = (IOleCommandTarget *)this; else return E_NOINTERFACE; // Addref through the interface ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; } // // IOleCommandTarget members // The shell will send notifications to us through this interface. // STDMETHODIMP CWebCheck::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText) { if (IsEqualGUID(*pguidCmdGroup, CGID_ShellServiceObject)) { // We like Shell Service Object notifications... return S_OK; } return(OLECMDERR_E_UNKNOWNGROUP); } STDMETHODIMP CWebCheck::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvaIn, VARIANTARG *pvaOut) { if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CGID_ShellServiceObject)) { // Handle Shell Service Object notifications here. switch (nCmdID) { case SSOCMDID_OPEN: StartService(FALSE); break; case SSOCMDID_CLOSE: StopService(); break; } return S_OK; } return(E_NOTIMPL); } // // IWebCheck members // // Starts the webcheck service in a process STDMETHODIMP CWebCheck::StartService(BOOL fForceExternals) { DBG("CWebCheck::StartService entered"); // reset offline mode for all platforms except NT5 if(FALSE == g_fIsWinNT5) { HMODULE hWininet = GetModuleHandle(TEXT("WININET.DLL")); if(hWininet) { // wininet is loaded - tell it to go online INTERNET_CONNECTED_INFO ci; memset(&ci, 0, sizeof(ci)); ci.dwConnectedState = INTERNET_STATE_CONNECTED; InternetSetOption(NULL, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci)); } else { // wininet not loaded - blow away offline reg key so we'll // be online when it does load DWORD dwOffline = 0; // FALSE => not offline WriteRegValue(HKEY_CURRENT_USER, c_szRegPathInternetSettings, TEXT("GlobalUserOffline"), &dwOffline, sizeof(DWORD), REG_DWORD); } } // create dialmon window DialmonInit(); // Fire up LCE and sens if necessary if(fForceExternals || ShouldLoadExternals()) LoadExternals(); // // Process the Infodelivery Admin Policies on user login. (User login coincides // with webcheck's StartService() call.) // ProcessInfodeliveryPolicies(); DBG("CWebCheck::StartService exiting"); return S_OK; } // Stops Webcheck if running. STDMETHODIMP CWebCheck::StopService(void) { DBG("CWebCheck::StopService entered"); // kill dialmon window DialmonShutdown(); // shut down the external bits if(FALSE == g_fIsWinNT) UnloadExternals(); DBG("CWebCheck::StopService exiting"); return S_OK; } // // load behavior: (win9x) // // "auto" Load if on a laptop // "yes" Load always // "no" Load never // static const WCHAR s_szAuto[] = TEXT("auto"); static const WCHAR s_szYes[] = TEXT("yes"); static const WCHAR s_szNo[] = TEXT("no"); BOOL CWebCheck::ShouldLoadExternals(void) { WCHAR szSens[16], szLce[16]; DWORD cbData; // // don't load on NT // if(g_fIsWinNT) { DBG("CWebCheck::ShouldLoadExternals -> NO (NT)"); return FALSE; } // // read sens/lce user settings - no setting means auto // cbData = sizeof(szLce); if(ERROR_SUCCESS != SHGetValueW(HKEY_LOCAL_MACHINE, c_szRegKey, L"LoadLCE", NULL, szLce, &cbData)) { StrCpyW(szLce, s_szAuto); } cbData = sizeof(szSens); if(ERROR_SUCCESS != SHGetValueW(HKEY_LOCAL_MACHINE, c_szRegKey, L"LoadSens", NULL, szSens, &cbData)) { StrCpyW(szSens, s_szAuto); } // // if either is yes, load // if(0 == StrCmpIW(szLce, s_szYes) || 0 == StrCmpIW(szSens, s_szYes)) { DBG("CWebCheck::ShouldLoadExternals -> YES (reg = yes)"); return TRUE; } // // if either is auto, check for laptop // if(0 == StrCmpIW(szLce, s_szAuto) || 0 == StrCmpIW(szSens, s_szAuto)) { if(SHGetMachineInfo(GMI_LAPTOP)) { // Is a laptop - load DBG("CWebCheck::ShouldLoadExternals -> YES (reg = auto, laptop)"); return TRUE; } } // don't load DBG("CWebCheck::ShouldLoadExternals -> NO"); return FALSE; } BOOL CWebCheck::AreExternalsLoaded(void) { return (_hThread != NULL); } void CWebCheck::LoadExternals(void) { DWORD dwThreadId; DBG("CWebCheck::LoadExternals"); if(_hThread) { DBG("CWebCheck::LoadExternals - already loaded"); return; } // fire up a thread to do the work _hThread = CreateThread(NULL, 4096, ExternalsThread, this, 0, &dwThreadId); if(NULL == _hThread) { DBG("LoadExternals failed to create externals thread!"); return; } // create initializion and termination events // // [darrenmi 2/7/00] Wininet now tries to find this named mutex instead of querying // dialmon. It's the A version because wininet isn't unicode and OpenEventA can't // find events created with CreateEventW. // // See GetSensLanState in inet\wininet\dll\autodial.cxx. // _hTerminateEvent = CreateEventA(NULL, TRUE, FALSE, "MS_WebcheckExternalsTerminateEvent"); if(NULL == _hTerminateEvent) { DBG("LoadExternals failed to create termination event"); return; } DBG("CWebCheck::LoadExternals exiting"); return; } void CWebCheck::UnloadExternals(void) { if(NULL == _hThread) { DBG("CWebCheck::UnloadExternals - nothing to unload"); return; } // tell externals thread to go away by setting termination event SetEvent(_hTerminateEvent); // Give thread a 10 second grace period to shut down // don't really care if it goes away or not... our process is going away! WaitForSingleObject(_hThread, 10000); // clean up CloseHandle(_hThread); CloseHandle(_hTerminateEvent); _hThread = NULL; _hTerminateEvent = NULL; return; } DWORD WINAPI ExternalsThread(LPVOID lpData) { CWebCheck * pWebCheck = (CWebCheck *)lpData; HINSTANCE hLCE, hSENS = NULL; BOOL fLCEStarted = FALSE, fSENSStarted = FALSE; DWORD dwRet; MSG msg; // sleep for 10 seconds before firing off externals Sleep(10 * 1000); // fire up com HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(FAILED(hr)) { DBG("LoadExternals: Failed to initialize COM"); return 0; } // load and start LCE hLCE = LoadLibrary(TEXT("estier2.dll")); DBGASSERT(hLCE, "LoadExternals: Failed to load estier2.dll"); if(hLCE) { LCESTART startfunc; startfunc = (LCESTART)GetProcAddress(hLCE, "LCEStartServer"); DBGASSERT(startfunc, "LoadExternals: Failed to find LCEStartServer"); if(startfunc) { hr = startfunc(); if(SUCCEEDED(hr)) fLCEStarted = TRUE; DBGASSERT(fLCEStarted, "LoadExternals: Failed to start LCE"); } } // if LCE started sucessfully, load and start SENS if(fLCEStarted) { hSENS = LoadLibrary(TEXT("sens.dll")); DBGASSERT(hSENS, "LoadExternals: Failed to load sens.dll"); if(hSENS) { SENSSTART startfunc; startfunc = (SENSSTART)GetProcAddress(hSENS, "SensInitialize"); DBGASSERT(startfunc, "LoadExternals: Failed to find SensInitialize"); if(startfunc) { if(startfunc()) fSENSStarted = TRUE; DBGASSERT(fSENSStarted, "LoadExternals: Failed to start SENS"); } } } // Wait for our shutdown event but pump messages in the mean time do { dwRet = MsgWaitForMultipleObjects(1, &(pWebCheck->_hTerminateEvent), FALSE, INFINITE, QS_ALLINPUT); if(WAIT_OBJECT_0 == dwRet) { // got our event, drop out of do loop break; } // empty the message queue... while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } while(TRUE); // shut down SENS if(fSENSStarted) { ASSERT(hSENS); SENSSTOP stopfunc; stopfunc = (SENSSTOP)GetProcAddress(hSENS, "SensUninitialize"); if(stopfunc) { stopfunc(); } } // // [darrenmi] beta-1 hack: Sens may have a thread sitting in its code // at this point so it's not safe to unload sens. Since we're in the // process of shutting down anyway, just leave it alone and let the // system unload it. // //if(hSENS) { // FreeLibrary(hSENS); //} // shut down LCE if(fLCEStarted) { ASSERT(hLCE) LCESTOP stopfunc; stopfunc = (LCESTOP)GetProcAddress(hLCE, "LCEStopServer"); if(stopfunc) { stopfunc(); } } if(hLCE) { FreeLibrary(hLCE); } // clean up com goo CoUninitialize(); return 0; } #if 0 // TODO: need similar functionality in the new world void SetNotificationMgrRestrictions(INotificationProcessMgr0 *pNotfMgrProcess) { HRESULT hr; INotificationProcessMgr0 *pNotProcess = pNotfMgrProcess; // get NotificationMgr if it wasn't passed in if (!pNotfMgrProcess) { hr = CoCreateInstance(CLSID_StdNotificationMgr, NULL, CLSCTX_INPROC_SERVER, IID_INotificationProcessMgr0, (void**)&pNotProcess); DBGASSERT(SUCCEEDED(hr), "SetNotificationMgrRestrictions - failed to create notification mgr"); } // set the restrictions if (pNotProcess) { const TCHAR c_szNoScheduledUpdates[] = TEXT("NoScheduledUpdates"); THROTTLEITEM ti = {0}; ti.NotificationType = NOTIFICATIONTYPE_AGENT_START; ti.nParallel = 3; // Has the user has disabled scheduled subscription updates? DWORD dwData; DWORD cbData = sizeof(dwData); if ((ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, c_szRegKey, c_szNoScheduledUpdates, NULL, &dwData, &cbData)) && dwData) { ti.dwFlags |= TF_DONT_DELIVER_SCHEDULED_ITEMS; } // Has the administrator has disabled scheduled subscription updates? if (SHRestricted2W(REST_NoScheduledUpdates, NULL, 0)) { ti.dwFlags |= TF_DONT_DELIVER_SCHEDULED_ITEMS; } // Has the administrator has excluded scheduled subscription updates // from this time range? DWORD dwBegin = SHRestricted2W(REST_UpdateExcludeBegin, NULL, 0); DWORD dwEnd = SHRestricted2W(REST_UpdateExcludeEnd, NULL, 0); if (dwBegin && dwEnd) { ti.dwFlags |= TF_APPLY_EXCLUDE_RANGE; ti.stBegin.wHour = (WORD)(dwBegin / 60); ti.stBegin.wMinute = (WORD)(dwBegin % 60); ti.stEnd.wHour = (WORD)(dwEnd / 60); ti.stEnd.wMinute = (WORD)(dwEnd %60); } // Has the admin set a minimum interval for scheduled subscription updates? dwData = SHRestricted2W(REST_MinUpdateInterval, NULL, 0); if (dwData) { ti.dwFlags |= TF_APPLY_UPDATEINTERVAL; ti.dwMinItemUpdateInterval = dwData; } hr = pNotProcess->RegisterThrottleNotificationType(1, &ti, 0, NULL, 0, 0); DBGASSERT(SUCCEEDED(hr), "SetNotificationMgrRestrictions - failed to register throttle type & restrictions"); } // release NotificationMgr if it wasn't passed in if (!pNotfMgrProcess) { SAFERELEASE(pNotProcess); } } #endif // // OLE bypass code // // Expose a couple of APIs to call start and stop service so loadwc doesn't // need to load up OLE at start time. // HRESULT ExtStartService( BOOL fForceExternals ) { HRESULT hr = E_FAIL; // make a webcheck object ASSERT(NULL == g_pwc); if(NULL == g_pwc) { g_pwc = new CWebCheck; if(g_pwc) { hr = g_pwc->StartService(fForceExternals); } } return hr; } HRESULT ExtStopService( void ) { HRESULT hr = E_FAIL; if(g_pwc) { hr = g_pwc->StopService(); SAFERELEASE(g_pwc); } return hr; }