540 lines
14 KiB
C++
540 lines
14 KiB
C++
#include "private.h"
|
|
#include <shlguid.h>
|
|
|
|
#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;
|
|
}
|