//***************************************************************************
//
//  ACTION.CPP
//
//  Module: HEALTHMON SERVER AGENT
//
//  Purpose: To act as the coordinator of actions. WMI actually provides the
//			code and support to carry out the actions (like email). This class
//			does the scheduling, and throttling of them.
//
//  Copyright (c)1999 Microsoft Corporation, All Rights Reserved
//
//***************************************************************************

#include <stdio.h>
#include <tchar.h>
#include "system.h"
#include "action.h"

extern CSystem* g_pSystem;
extern HMODULE g_hWbemComnModule;
static BYTE LocalSystemSID[] = {1,1,0,0,0,0,0,5,18,0,0,0};


//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CAction::CAction()
{

	MY_OUTPUT(L"ENTER ***** CAction::CAction...", 4);

	m_szGUID = NULL;
	m_szName = NULL;
	m_szDescription = NULL;
	m_szTypeGUID = NULL;
	m_pszStatusGUID = NULL;
	m_lCurrState = HM_GOOD;
	m_hmStatusType = HMSTATUS_ACTION;
	m_bValidLoad = FALSE;

	wcscpy(m_szDTTime, m_szDTCurrTime);
	wcscpy(m_szTime, m_szCurrTime);

	MY_OUTPUT(L"EXIT ***** CAction::CAction...", 4);
}

CAction::~CAction()
{
	MY_OUTPUT(L"ENTER ***** CAction::~CAction...", 4);

	Cleanup(TRUE);
	if (m_szGUID)
	{
		delete [] m_szGUID;
		m_szGUID = NULL;
	}

	m_bValidLoad = FALSE;

	MY_OUTPUT(L"EXIT ***** CAction::~CAction...", 4);
}

//
// Load a single Action
//
HRESULT CAction::LoadInstanceFromMOF(IWbemClassObject* pActionConfigInst, BOOL bModifyPass/*FALSE*/)
{
	HRESULT hRetRes = S_OK;
	TCHAR szTemp[1024];
	GUID guid;
	LPTSTR pszStr;
	LPTSTR pszTemp;
	BOOL bRetValue = TRUE;
	QSTRUCT Q;
	BSTR Language = NULL;
	BSTR Query = NULL;
	HRESULT hRes;
	ULONG uReturned;
	IWbemClassObject *pAssocObj = NULL;
	IEnumWbemClassObject *pEnum = NULL;
	LPTSTR pszUpper = NULL;

	MY_OUTPUT(L"ENTER ***** CAction::LoadInstanceFromMOF...", 4);


	m_bValidLoad = TRUE;
	if (m_szGUID == NULL)
	{
		// Get the GUID property
		hRetRes = GetStrProperty(pActionConfigInst, L"GUID", &m_szGUID);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) return hRetRes;
	}

	if (bModifyPass==FALSE)
	{
		Cleanup(TRUE);
		hRetRes = CoCreateGuid(&guid);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
		m_pszStatusGUID = new TCHAR[100];
		MY_ASSERT(m_pszStatusGUID); if (!m_pszStatusGUID) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
		StringFromGUID2(guid, m_pszStatusGUID, 100);
	}
	else
	{
		Cleanup(FALSE);
	}

	// Get the Name. If it is NULL then we use the qualifier
	hRetRes = GetStrProperty(pActionConfigInst, L"Name", &m_szName);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

	// Get the Description. If it is NULL then we use the qualifier
	hRetRes = GetStrProperty(pActionConfigInst, L"Description", &m_szDescription);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

	hRetRes = GetBoolProperty(pActionConfigInst, L"Enabled", &m_bEnabled);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

	hRetRes = GetUint8Property(pActionConfigInst, L"ActiveDays", &m_iActiveDays);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

	// The time format looks as follows "********0600**.******+***"; hh is hours and mm is minutes
	// All else is ignored.
	hRetRes = GetStrProperty(pActionConfigInst, L"BeginTime", &pszTemp);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pszStr = wcschr(pszTemp, '.');
	if (pszStr)
	{
		// Back up to look at the minute
		pszStr -= 2;
		*pszStr = '\0';
		pszStr -= 2;
		m_lBeginMinuteTime= _wtol(pszStr);
		// Back up to look at the hour
		*pszStr = '\0';
		pszStr -= 2;
		m_lBeginHourTime= _wtol(pszStr);
	}
	else
	{
		m_lBeginMinuteTime= -1;
		m_lBeginHourTime= -1;
	}
	delete [] pszTemp;

	hRetRes = GetStrProperty(pActionConfigInst, L"EndTime", &pszTemp);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pszStr = wcschr(pszTemp, '.');
	if (pszStr)
	{
		// Back up to look at the minute
		pszStr -= 2;
		*pszStr = '\0';
		pszStr -= 2;
		m_lEndMinuteTime= _wtol(pszStr);
		// Back up to look at the hour
		*pszStr = '\0';
		pszStr -= 2;
		m_lEndHourTime= _wtol(pszStr);
	}
	else
	{
		m_lEndMinuteTime= -1;
		m_lEndHourTime= -1;
	}
	delete [] pszTemp;

	hRetRes = GetStrProperty(pActionConfigInst, L"TypeGUID", &m_szTypeGUID);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

	//
	// Need to create a temporary consumer for each configuration object associated to
	// this Action. These are the real events that are happening to cause the actions
	// to fire.
	//
	if (!bModifyPass)
	{
		//
		// Loop through all Associations to this Action
		//
		Language = SysAllocString(L"WQL");
		MY_ASSERT(Language); if (!Language) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
		wcscpy(szTemp, L"REFERENCES OF {MicrosoftHM_ActionConfiguration.GUID=\"");
		lstrcat(szTemp, m_szGUID);
		lstrcat(szTemp, L"\"} WHERE ResultClass=MicrosoftHM_ConfigurationActionAssociation");
		Query = SysAllocString(szTemp);
		MY_ASSERT(Query); if (!Query) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

		// Initialize IEnumWbemClassObject pointer
		pEnum = NULL;

		// Issue query
    	hRetRes = g_pIWbemServices->ExecQuery(Language, Query, WBEM_FLAG_FORWARD_ONLY, 0, &pEnum);

		SysFreeString(Query);
		Query = NULL;
		SysFreeString(Language);
		Language = NULL;

		if (hRetRes != 0)
		{
			MY_HRESASSERT(hRetRes);
		}
		else
		{
			// Retrieve objects in result set
			while (TRUE)
			{
				pAssocObj = NULL;
				uReturned = 0;

				hRes = pEnum->Next(0, 1, &pAssocObj, &uReturned);
				if (uReturned == 0)
				{
					break;
				}

				//
				// Next, setup the temporary consumer for the event(s) that the actions is
				// triggered by, so we can do throttling.
				//
				hRetRes = GetStrProperty(pAssocObj, L"ParentPath", &Q.szUserConfigPath);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
				Q.pBase = g_pSystem->GetParentPointerFromPath(Q.szUserConfigPath);
				if (!Q.pBase || (Q.pBase && Q.pBase->m_hmStatusType==HMSTATUS_THRESHOLD))
				{
					if (Q.szUserConfigPath)
						delete [] Q.szUserConfigPath;
					pAssocObj->Release();
					pAssocObj = NULL;
					continue;
				}

//				hRetRes = GetUint32Property(pAssocObj, L"ThrottleTime", &Q.lThrottleTime);
//				MY_HRESASSERT(hRetRes);
				Q.lThrottleTime = 0;

				hRetRes = GetUint32Property(pAssocObj, L"ReminderTime", &Q.lReminderTime);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				hRetRes = GetStrProperty(pAssocObj, L"Query", &Q.szQuery);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				//
				// Look for "...State=0 OR State=9"
				//
				pszUpper = _wcsdup(Q.szQuery);
				MY_ASSERT(pszUpper); if (!pszUpper) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
				_wcsupr(pszUpper);
				Q.ulTriggerStates = 0;
				pszStr = wcsstr(pszUpper, L"STATE");
				if (pszStr)
				{
					if (wcschr(pszStr, '0'))
					{
						Q.ulTriggerStates |= 1<<0;
					}
					if (wcschr(pszStr, '4'))
					{
						Q.ulTriggerStates |= 1<<4;
					}
					if (wcschr(pszStr, '5'))
					{
						Q.ulTriggerStates |= 1<<5;
					}
					if (wcschr(pszStr, '8'))
					{
						Q.ulTriggerStates |= 1<<8;
					}
					if (wcschr(pszStr, '9'))
					{
						Q.ulTriggerStates |= 1<<9;
					}
				}
				free(pszUpper);
				pszUpper = NULL;

				hRetRes = GetStrProperty(pAssocObj, L"__PATH", &Q.szConfigActionAssocPath);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				hRetRes = GetStrProperty(pAssocObj, L"ChildPath", &Q.szChildPath);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				if (m_bEnabled)
				{
					//
					// Setup the event query. We need to be able to identify each individual
					// use of this Action! Pass in this unique property.
					//
					Q.pTempSink = new CTempConsumer(Q.szConfigActionAssocPath);
					MY_ASSERT(Q.pTempSink); if (!Q.pTempSink) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
					Q.hRes = 0;

					Language = SysAllocString(L"WQL");
					MY_ASSERT(Language); if (!Language) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
					Query = SysAllocString(Q.szQuery);
					MY_ASSERT(Query); if (!Query) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
					hRes = 1;
					if (g_pIWbemServices != NULL)
					{
						hRes = g_pIWbemServices->ExecNotificationQueryAsync(
													Language,
													Query,
													0,
													NULL,
													Q.pTempSink);
						Q.hRes = hRes;
					}

					SysFreeString(Query);
					Query = NULL;
					SysFreeString(Language);
					Language = NULL;
				}
				else
				{
					Q.pTempSink = 0;
					Q.hRes = 0;
				}

				Q.startTick = 0;
				Q.reminderTimeTick = 0;
				Q.bThrottleOn = FALSE;
				MY_ASSERT(Q.pBase);
				m_qList.push_back(Q);

				// Release it.
				pAssocObj->Release();
				pAssocObj = NULL;
			}

			// All done
			pEnum->Release();
			pEnum = NULL;
		}
	}

	m_bValidLoad = TRUE;
	MY_OUTPUT(L"EXIT ***** CAction::LoadInstanceFromMOF...", 4);
	return S_OK;

error:
	MY_ASSERT(FALSE);
	if (Query)
		SysFreeString(Query);
	if (Language)
		SysFreeString(Language);
	if (pszUpper)
		free(pszUpper);
	if (pAssocObj)
		pAssocObj->Release();
	if (pEnum)
		pEnum->Release();
	Cleanup(TRUE);
	m_bValidLoad = FALSE;
	return hRetRes;
}

//
// In the case of Actions, they don't actually have an interval to them, but they
// get called here for each action every second (the base agent interval).
//
BOOL CAction::OnAgentInterval(void)
{
	HRESULT hRetRes = S_OK;
	HRESULT hRes;
	IWbemClassObject* pInstance = NULL;
	HRESULT hRes2;
	IWbemClassObject* pInstance2 = NULL;
	DWORD currTick;
	BOOL bTimeOK;
	int i, iSize;
	QSTRUCT *pQ;
	BSTR Language = NULL;
	BSTR Query = NULL;
	GUID guid;

	if (m_bValidLoad == FALSE)
		return FALSE;

	//
	// Make sure that we are in a valid time to run.
	//
	bTimeOK = checkTime();

	// Remember that the DISABLED state overrides SCHEDULEDOUT.
	if ((m_bEnabled==FALSE && m_lCurrState==HM_DISABLED) ||
		(bTimeOK==FALSE && m_lCurrState==HM_SCHEDULEDOUT && m_bEnabled))
	{
		return TRUE;
	}
	else if (m_bEnabled==FALSE && m_lCurrState!=HM_DISABLED ||
			bTimeOK==FALSE && m_lCurrState!=HM_SCHEDULEDOUT)
	{
		//
		// Going into Scheduled Outage OR DISABLED.
		// What if we are going from ScheduledOut to Disabled?
		// Or from Disabled to ScheduledOut?
		// Possible transitions:
		// GOOD -> Disabled
		// GOOD -> ScheduledOut
		// Disabled -> ScheduledOut
		// ScheduledOut -> Disabled
		//
		iSize = m_qList.size();
		for (i=0; i<iSize; i++)
		{
			MY_ASSERT(i<m_qList.size());
			pQ = &m_qList[i];

			if (pQ->pTempSink)
			{
				g_pIWbemServices->CancelAsyncCall((IWbemObjectSink*)pQ->pTempSink);
				pQ->pTempSink->Release();
				pQ->pTempSink = NULL;
			}
			pQ->hRes = 0;
		}
		if (m_bEnabled==FALSE)
		{
			m_lCurrState = HM_DISABLED;
			FireEvent(-1, NULL, HMRES_ACTION_DISABLE);
		}
		else
		{
			m_lCurrState = HM_SCHEDULEDOUT;
			FireEvent(-1, NULL, HMRES_ACTION_OUTAGE);
		}
		return TRUE;
	}
	else if (m_lCurrState==HM_DISABLED || m_lCurrState==HM_SCHEDULEDOUT)
	{
		//
		// Comming out of Scheduled Outage OR DISABLED.
		// Might be going from ScheduledOut/Disabled to Disabled,
		// or disabled to ScheduledOut.
		//
		m_lCurrState = HM_GOOD;
		FireEvent(-1, NULL, HMRES_ACTION_ENABLE);

		// Re-setup the event consumer
		iSize = m_qList.size();
		for (i=0; i<iSize; i++)
		{
			MY_ASSERT(i<m_qList.size());
			pQ = &m_qList[i];
			pQ->pTempSink = new CTempConsumer(pQ->szConfigActionAssocPath);
			MY_ASSERT(pQ->pTempSink); if (!pQ->pTempSink) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
			pQ->hRes = 0;

			Language = SysAllocString(L"WQL");
			MY_ASSERT(Language); if (!Language) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
			Query = SysAllocString(pQ->szQuery);
			MY_ASSERT(Query); if (!Query) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
			hRes = 1;
			if (g_pIWbemServices != NULL)
			{
				hRes = g_pIWbemServices->ExecNotificationQueryAsync(
											Language,
											Query,
											0,
											NULL,
											pQ->pTempSink);
				pQ->hRes = hRes;
			}

			SysFreeString(Query);
			Query = NULL;
			SysFreeString(Language);
			Language = NULL;

			if (hRes != 0)
			{
				MY_HRESASSERT(hRes);
			}
			else
			{
				pQ->startTick = 0;
				pQ->reminderTimeTick = 0;
				pQ->bThrottleOn = FALSE;
			}
		}
	}

	//
	// Determine if the throttle time needs to come into play.
	//
	currTick = GetTickCount();
	iSize = m_qList.size();
	for (i=0; i<iSize; i++)
	{
		MY_ASSERT(i<m_qList.size());
		pQ = &m_qList[i];

		if (0<pQ->lThrottleTime)
		{
			// May have not gone off yet
			if ((0<pQ->startTick))
			{
				// Check to see if alloted time has passed
				if ((pQ->lThrottleTime*1000) < (currTick-pQ->startTick))
				{
					pQ->bThrottleOn = FALSE;
					pQ->startTick = 0;
				}
				else
				{
					pQ->bThrottleOn = TRUE;
				}
			}
		}
	}

	// Try and prevent problems where we may have had a query fail to register
	iSize = m_qList.size();
	for (i=0; i<iSize; i++)
	{
		MY_ASSERT(i<m_qList.size());
		pQ = &m_qList[i];

		if (pQ->hRes!=0)
		{
			Language = SysAllocString(L"WQL");
			MY_ASSERT(Language); if (!Language) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
			Query = SysAllocString(pQ->szQuery);
			MY_ASSERT(Query); if (!Query) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
			hRes = 1;
			if (g_pIWbemServices != NULL && pQ->pTempSink && pQ->hRes!=0)
			{
				hRes = g_pIWbemServices->ExecNotificationQueryAsync(
											Language,
											Query,
											0,
											NULL,
											pQ->pTempSink);
				MY_HRESASSERT(hRes);
				pQ->hRes = hRes;
			}

			SysFreeString(Query);
			Query = NULL;
			SysFreeString(Language);
			Language = NULL;
			if (hRes == 0)
			{
				pQ->startTick = 0;
				pQ->reminderTimeTick = 0;
				pQ->bThrottleOn = FALSE;
			}
		}
	}

	//
	// Determine if we need to fire off a reminder event.
	// We first need to be in the violated state.
	//
	iSize = m_qList.size();
	for (i=0; i<iSize; i++)
	{
		MY_ASSERT(i<m_qList.size());
		pQ = &m_qList[i];
		MY_ASSERT(pQ->pBase);
		if ((g_pActionEventSink || g_pActionTriggerEventSink) && pQ->pBase &&
			pQ->lReminderTime!=0 && pQ->reminderTimeTick==pQ->lReminderTime)
		{
			// Fire event to console, and another one to the event consumer
			wcscpy(m_szDTTime, m_szDTCurrTime);
			wcscpy(m_szTime, m_szCurrTime);
			m_lCurrState = HM_GOOD;
			if (m_pszStatusGUID)
			{
				delete [] m_pszStatusGUID;
				m_pszStatusGUID = NULL;
			}
			hRetRes = CoCreateGuid(&guid);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
			m_pszStatusGUID = new TCHAR[100];
			MY_ASSERT(m_pszStatusGUID); if (!m_pszStatusGUID) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
			StringFromGUID2(guid, m_pszStatusGUID, 100);
			hRes = GetHMActionStatus(&pInstance, NULL, L"MicrosoftHM_ActionStatusEvent", HMRES_ACTION_FIRED);
			hRes2 = GetHMActionStatus(&pInstance2, NULL, L"MicrosoftHM_ActionTriggerEvent", HMRES_ACTION_FIRED);
			if (SUCCEEDED(hRes) && SUCCEEDED(hRes2))
			{
				pQ->pBase->SendReminderActionIfStateIsSame(g_pActionEventSink, g_pActionTriggerEventSink, pInstance, pInstance2, pQ->ulTriggerStates);
			}
			else
			{
				MY_OUTPUT(L"failed to get instance!", 1);
				MY_HRESASSERT(hRes);
				MY_HRESASSERT(hRes2);
			}
			if (pInstance)
			{
				pInstance->Release();
				pInstance = NULL;
			}
			if (pInstance2)
			{
				pInstance2->Release();
				pInstance2 = NULL;
			}
			pQ->reminderTimeTick = 0;
		}
		if (pQ->lReminderTime!=0 && pQ->reminderTimeTick==pQ->lReminderTime)
			pQ->reminderTimeTick = 0;
		pQ->reminderTimeTick++;
	}

	return TRUE;

error:
	MY_ASSERT(FALSE);
	if (Query)
		SysFreeString(Query);
	if (Language)
		SysFreeString(Language);
	m_bValidLoad = FALSE;
	Cleanup(TRUE);
	return FALSE;
}

//
// Here we get an event, and since we act as the gatekeeper to pass on the final event
// that the EventConsumer will get fired from, we decide if we need to throttle.
// To send on the event, we embed the incomming event into ours.
//
BOOL CAction::HandleTempEvent(LPTSTR szConfigActionAssocPath, IWbemClassObject* pObj)
{
	BOOL bRetValue = TRUE;
	IWbemClassObject* pInstance = NULL;
	HRESULT hRes;
	BOOL bFound;
	QSTRUCT *pQ;
	int i, iSize;

	MY_OUTPUT(L"ENTER ***** CAction::HandleTempEvent...", 2);

	if (m_bValidLoad == FALSE)
		return FALSE;

	m_lCurrState = HM_GOOD;
	//
	// See if it is one of these
	//
	bFound = FALSE;
	iSize = m_qList.size();
	for (i=0; i<iSize; i++)
	{
		MY_ASSERT(i<m_qList.size());
		pQ = &m_qList[i];
		if (!_wcsicmp(pQ->szConfigActionAssocPath, szConfigActionAssocPath))
		{
			bFound = TRUE;
			break;
		}
	}

	//
	// We capture the time that this happened, so that we can throttle in the
	// OnAgentInterval call we can disable the action from happening (until time again).
	//
	if (bFound==FALSE)
	{
		return FALSE;
	}

	if (pQ->startTick == 0)
	{
		pQ->startTick = GetTickCount();
	}

	MY_OUTPUT2(L"HandleTempEvent GUID=%s", m_szGUID, 4);
	MY_OUTPUT2(L"szConfigActionAssocPath=%s", szConfigActionAssocPath, 4);

	if (pQ->bThrottleOn == FALSE)
	{
		if (pQ->lReminderTime != 0)
		{
			pQ->reminderTimeTick = 0;
		}

		// Don't send if no-one is listening!
		if (g_pActionTriggerEventSink != NULL)
		{
			wcscpy(m_szDTTime, m_szDTCurrTime);
			wcscpy(m_szTime, m_szCurrTime);
			FireEvent(-1, NULL, HMRES_ACTION_FIRED);
			hRes = GetHMActionStatus(&pInstance, pObj, L"MicrosoftHM_ActionTriggerEvent", HMRES_ACTION_FIRED);
			if (SUCCEEDED(hRes))
			{
				if (g_pActionTriggerEventSink) 
				{
					hRes = g_pActionTriggerEventSink->Indicate(1, &pInstance);
					//WBEM_E_SERVER_TOO_BUSY is Ok. Wbem will deliver.
					if (FAILED(hRes) && hRes != WBEM_E_SERVER_TOO_BUSY)
					{
						bRetValue = FALSE;
						MY_OUTPUT(L"Failed on Indicate!", 4);
					}
				}
				pInstance->Release();
				pInstance = NULL;
			}
			else
			{
				MY_OUTPUT(L"failed to get instance!", 1);
				MY_HRESASSERT(hRes);
			}
		}
	}

	MY_OUTPUT(L"EXIT ***** CAction::HandleTempEvent...", 2);
	return bRetValue;
}

//oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

HRESULT CAction::FindAndModAction(BSTR szGUID, IWbemClassObject* pObj)
{
	HRESULT hRetRes = S_OK;

	//
	// Is this us we are looking for?
	//
	if (!_wcsicmp(m_szGUID, szGUID))
	{
		hRetRes = LoadInstanceFromMOF(pObj, TRUE);
		return hRetRes;
	}

	return WBEM_S_DIFFERENT;
}

BOOL CAction::FindAndCreateActionAssociation(BSTR szGUID, IWbemClassObject* pObj)
{
	HRESULT hRes;
	HRESULT hRetRes = S_OK;
	int i, iSize;
	QSTRUCT *pQ;
	QSTRUCT Q;
	BOOL bFound;
	BOOL bTimeOK;
	LPTSTR pszStr;
	BSTR Language = NULL;
	BSTR Query = NULL;
	LPTSTR pszConfigActionAssocPath = NULL;
	LPTSTR pszUpper = NULL;

	if (m_bValidLoad == FALSE)
		return FALSE;

	//
	// Is this the Action we are looking for?
	//
	if (!_wcsicmp(m_szGUID, szGUID))
	{
		hRetRes = GetStrProperty(pObj, L"__PATH", &pszConfigActionAssocPath);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

		MY_OUTPUT2(L"Association to Action GUID=%s", szGUID, 4);
		MY_OUTPUT2(L"To PATH=%s", pszConfigActionAssocPath, 4);
		// Now check to see if this association already exists
		bFound = FALSE;
		iSize = m_qList.size();
		for (i=0; i<iSize; i++)
		{
			MY_ASSERT(i<m_qList.size());
			pQ = &m_qList[i];
			if (!_wcsicmp(pQ->szConfigActionAssocPath, pszConfigActionAssocPath))
			{
				bFound = TRUE;
				break;
			}
		}

		delete [] pszConfigActionAssocPath;
		pszConfigActionAssocPath = NULL;


		if (bFound == FALSE)
		{
			MY_OUTPUT(L"OK: Not found yet.", 4);
			Q.szUserConfigPath = NULL;
			Q.szQuery = NULL;
			Q.szConfigActionAssocPath = NULL;
			Q.szChildPath = NULL;
			//
			// Next, setup the temporary consumer for the event(s) that the actions is
			// triggered by, so we can do throttling.
			//
			hRetRes = GetStrProperty(pObj, L"ParentPath", &Q.szUserConfigPath);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
			Q.pBase = g_pSystem->GetParentPointerFromPath(Q.szUserConfigPath);
			if (!Q.pBase || (Q.pBase && Q.pBase->m_hmStatusType==HMSTATUS_THRESHOLD))
			{
				if (Q.szUserConfigPath)
				{
					delete [] Q.szUserConfigPath;
				}
			}
			else
			{
//				hRetRes = GetUint32Property(pObj, L"ThrottleTime", &Q.lThrottleTime);
//				MY_HRESASSERT(hRetRes);
				Q.lThrottleTime = 0;

				hRetRes = GetUint32Property(pObj, L"ReminderTime", &Q.lReminderTime);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				hRetRes = GetStrProperty(pObj, L"Query", &Q.szQuery);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				//
				// Look for "...State=0 OR State=9"
				//
				pszUpper = _wcsdup(Q.szQuery);
				MY_ASSERT(pszUpper); if (!pszUpper) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
				_wcsupr(pszUpper);
				Q.ulTriggerStates = 0;
				pszStr = wcsstr(pszUpper, L"STATE");
				if (pszStr)
				{
					if (wcschr(pszStr, '0'))
					{
						Q.ulTriggerStates |= 1<<0;
					}
					if (wcschr(pszStr, '4'))
					{
						Q.ulTriggerStates |= 1<<4;
					}
					if (wcschr(pszStr, '5'))
					{
						Q.ulTriggerStates |= 1<<5;
					}
					if (wcschr(pszStr, '8'))
					{
						Q.ulTriggerStates |= 1<<8;
					}
					if (wcschr(pszStr, '9'))
					{
						Q.ulTriggerStates |= 1<<9;
					}
				}
				free(pszUpper);
				pszUpper = NULL;

				hRetRes = GetStrProperty(pObj, L"__PATH", &Q.szConfigActionAssocPath);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				hRetRes = GetStrProperty(pObj, L"ChildPath", &Q.szChildPath);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				TCHAR msgbuf[1024];
				wsprintf(msgbuf, L"ACTIONASSOCIATION: AGUID=%s parentpath=%s childpath=%s", szGUID, Q.szUserConfigPath, Q.szChildPath);
				MY_OUTPUT(msgbuf, 4);
				//
				// Setup the event query. We need to be able to identify each individual
				// use of this Action! Pass in this unique property.
				//
				bTimeOK = checkTime();
				if (m_bEnabled==TRUE && bTimeOK==TRUE)
				{
					Q.pTempSink = new CTempConsumer(Q.szConfigActionAssocPath);
					MY_ASSERT(Q.pTempSink); if (!Q.pTempSink) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

					Language = SysAllocString(L"WQL");
					MY_ASSERT(Language); if (!Language) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
					Query = SysAllocString(Q.szQuery);
					MY_ASSERT(Query); if (!Query) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
					hRes = 1;
					Q.hRes = 0;
					if (g_pIWbemServices != NULL)
					{
						hRes = g_pIWbemServices->ExecNotificationQueryAsync(
													Language,
													Query,
													0,
													NULL,
													Q.pTempSink);
						Q.hRes = hRes;
					}

					SysFreeString(Query);
					Query = NULL;
					SysFreeString(Language);
					Language = NULL;

					Q.startTick = 0;
					Q.reminderTimeTick = 0;
					Q.bThrottleOn = FALSE;
					MY_ASSERT(Q.pBase);
					m_qList.push_back(Q);
				}
				else
				{
					Q.pTempSink = NULL;
					Q.startTick = 0;
					Q.reminderTimeTick = 0;
					Q.bThrottleOn = FALSE;
					MY_ASSERT(Q.pBase);
					m_qList.push_back(Q);
				}
			}
			return TRUE;
		}
		else
		{
			MY_OUTPUT(L"WHY?: Already There.", 4);
			return FALSE;
		}

	}
	else
	{
		return FALSE;
	}

error:
	MY_ASSERT(FALSE);
	if (pszUpper)
		free(pszUpper);
	if (pszConfigActionAssocPath)
		delete [] pszConfigActionAssocPath;
	if (Q.szUserConfigPath)
		delete [] Q.szUserConfigPath;
	if (Q.szQuery)
		delete [] Q.szQuery;
	if (Q.szConfigActionAssocPath)
		delete [] Q.szConfigActionAssocPath;
	if (Q.szChildPath)
		delete [] Q.szChildPath;
	if (Query)
		SysFreeString(Query);
	if (Language)
		SysFreeString(Language);
	return TRUE;
}

BOOL CAction::FindAndModActionAssociation(BSTR szGUID, IWbemClassObject* pObj)
{
	HRESULT hRes;
	HRESULT hRetRes;
	int i, iSize;
	QSTRUCT *pQ;
	LPTSTR pszTemp;
	BOOL bFound;
	BSTR Language = NULL;
	BSTR Query = NULL;
	BOOL bSameQuery = FALSE;
	BOOL bTimeOK;
	LPTSTR pszUpper = NULL;
	LPTSTR pszStr;

	if (m_bValidLoad == FALSE)
		return FALSE;

	//
	// Is this the Action we are looking for?
	//
	if (!_wcsicmp(m_szGUID, szGUID))
	{
		hRetRes = GetStrProperty(pObj, L"__PATH", &pszTemp);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

		// Now check to see if this association already exists
		bFound = FALSE;
		iSize = m_qList.size();
		for (i=0; i<iSize; i++)
		{
			MY_ASSERT(i<m_qList.size());
			pQ = &m_qList[i];
			if (!_wcsicmp(pQ->szConfigActionAssocPath, pszTemp))
			{
				bFound = TRUE;
				break;
			}
		}

		delete [] pszTemp;
		pszTemp = NULL;

		if (bFound == TRUE)
		{
			MY_OUTPUT2(L"MODACTIONASSOCIATION: AGUID=%s", szGUID, 4);
			MY_OUTPUT2(L"parentpath=%s", pQ->szUserConfigPath, 4);
			MY_OUTPUT2(L"childpath=%s", pQ->szChildPath, 4);

			if (pQ->szQuery)
			{
				hRetRes = GetStrProperty(pObj, L"Query", &pszTemp);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

				if (!wcscmp(pQ->szQuery, pszTemp))
				{
					bSameQuery = TRUE;
					pQ->reminderTimeTick = 0;
				}

				// Check to See if we have to register a new query.
				if (!bSameQuery)
				{
					delete [] pQ->szQuery;
					pQ->szQuery = NULL;
					hRetRes = GetStrProperty(pObj, L"Query", &pQ->szQuery);
					MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

					//
					// Look for "...State=0 OR State=9"
					//
					pszUpper = _wcsdup(pQ->szQuery);
					MY_ASSERT(pszUpper); if (!pszUpper) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
					_wcsupr(pszUpper);
					pQ->ulTriggerStates = 0;
					pszStr = wcsstr(pszUpper, L"STATE");
					if (pszStr)
					{
						if (wcschr(pszStr, '0'))
						{
							pQ->ulTriggerStates |= 1<<0;
						}
						if (wcschr(pszStr, '4'))
						{
							pQ->ulTriggerStates |= 1<<4;
						}
						if (wcschr(pszStr, '5'))
						{
							pQ->ulTriggerStates |= 1<<5;
						}
						if (wcschr(pszStr, '8'))
						{
							pQ->ulTriggerStates |= 1<<8;
						}
						if (wcschr(pszStr, '9'))
						{
							pQ->ulTriggerStates |= 1<<9;
						}
					}
					free(pszUpper);
					pszUpper = NULL;
				}
				delete [] pszTemp;
				pszTemp = NULL;
			}

			if (pQ->szUserConfigPath)
			{
				delete [] pQ->szUserConfigPath;
				pQ->szUserConfigPath = NULL;
			}

			if (pQ->szChildPath)
			{
				delete [] pQ->szChildPath;
				pQ->szChildPath = NULL;
			}

			if (pQ->pTempSink && !bSameQuery)
			{
				g_pIWbemServices->CancelAsyncCall((IWbemObjectSink*)pQ->pTempSink);
				pQ->pTempSink->Release();
				pQ->pTempSink = NULL;
			}

			//
			// Next, setup the temporary consumer for the event(s) that the actions is
			// triggered by, so we can do throttling.
			//
//			hRetRes = GetUint32Property(pObj, L"ThrottleTime", &pQ->lThrottleTime);
//			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
			pQ->lThrottleTime = 0;

			hRetRes = GetUint32Property(pObj, L"ReminderTime", &pQ->lReminderTime);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

			hRetRes = GetStrProperty(pObj, L"ParentPath", &pQ->szUserConfigPath);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

			hRetRes = GetStrProperty(pObj, L"ChildPath", &pQ->szChildPath);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

			//
			// Setup the event query. We need to be able to identify each individual
			// use of this Action! Pass in this unique property.
			//
			bTimeOK = checkTime();
			if (!bSameQuery && m_bEnabled==TRUE && bTimeOK==TRUE)
			{
				pQ->pTempSink = new CTempConsumer(pQ->szConfigActionAssocPath);
				MY_ASSERT(pQ->pTempSink); if (!pQ->pTempSink) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

				Language = SysAllocString(L"WQL");
				MY_ASSERT(Language); if (!Language) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
				Query = SysAllocString(pQ->szQuery);
				MY_ASSERT(Query); if (!Query) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
				hRes = 1;
				pQ->hRes = 0;
				if (g_pIWbemServices != NULL)
				{
					hRes = g_pIWbemServices->ExecNotificationQueryAsync(
												Language,
												Query,
												0,
												NULL,
												//XXXm_pContext,
												pQ->pTempSink);
					pQ->hRes = hRes;
				}

				SysFreeString(Query);
				Query = NULL;
				SysFreeString(Language);
				Language = NULL;

				if (hRes != 0)
				{
					MY_HRESASSERT(hRes);
				}
				else
				{
					pQ->startTick = 0;
					pQ->reminderTimeTick = 0;
					pQ->bThrottleOn = FALSE;
				}
			}
		}

		return TRUE;
	}
	else
	{
		return FALSE;
	}

error:
	MY_ASSERT(FALSE);
	if (pszTemp)
		free(pszTemp);
	if (Query)
		SysFreeString(Query);
	if (Language)
		SysFreeString(Language);
	Cleanup(TRUE);
	m_bValidLoad = FALSE;
	return TRUE;
}

BOOL CAction::DeleteConfigActionAssoc(LPTSTR pszConfigGUID, LPTSTR pszActionGUID)
{
	TCHAR *pszEventFilter = NULL;
	BSTR instName = NULL;
	BOOL bFound = FALSE;
	TCHAR szGUID[1024];
	LPTSTR pStr;
	LPTSTR pStr2;
	int i, iSize;
	QSTRUCT *pQ;
	QLIST::iterator iaQ;
	IWbemClassObject* pInst = NULL;
	HRESULT hRes;
	HRESULT hRetRes = S_OK;

	if (m_bValidLoad == FALSE)
		return FALSE;

	//
	// First verify that we have the proper action identified.
	//
	if (_wcsicmp(m_szGUID, pszActionGUID))
	{
		return FALSE;
	}

	TCHAR msgbuf[1024];
	wsprintf(msgbuf, L"DELETECONFIGACTIONASSOCIATION: CONFIGGUID=%s AGUID=%s", pszConfigGUID, pszActionGUID);
	MY_OUTPUT(msgbuf, 4);

	//
	// Verify the Configuration instance associated to this Action
	// We are not deleting the ActionConfig, or the __EventConsumer,
	//__EventFilter, __FilterToConsumerBinding Just usage of it - ConfigActionAssoc
	//
	iSize = m_qList.size();
	iaQ = m_qList.begin();
	for (i=0; i<iSize; i++, iaQ++)
	{
		MY_ASSERT(i<m_qList.size());
		pQ = &m_qList[i];
		wcscpy(szGUID, pQ->szUserConfigPath);
		pStr = wcschr(szGUID, '\"');
		if (pStr)
		{
			pStr++;
			pStr2 = wcschr(pStr, '\"');
			if (pStr2)
			{
				*pStr2 = '\0';
			}
		}
		else
		{
			pStr = wcschr(szGUID, '=');
			if (pStr)
			{
				pStr++;
				if (*pStr == '@')
				{
					pStr2 = pStr;
					pStr2++;
					*pStr2 = '\0';
				}
			}
		}

		if (pStr)
		{
			if (!_wcsicmp(pStr, pszConfigGUID))
			{
				hRetRes = GetWbemObjectInst(&g_pIWbemServices, pQ->szConfigActionAssocPath, NULL, &pInst);
				if (!pInst)
				{
					MY_HRESASSERT(hRetRes);
					return FALSE;
				}
				hRetRes = GetStrProperty(pInst, L"EventFilter", &pszEventFilter);
				MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
				pInst->Release();
				pInst = NULL;

				delete [] pszEventFilter;
				pszEventFilter = NULL;


				//
				// Delete MicrosoftHM_ConfigurationActionAssociation.
				//
				instName = SysAllocString(pQ->szConfigActionAssocPath);
				MY_ASSERT(instName); if (!instName) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
				if ((hRes = g_pIWbemServices->DeleteInstance(instName, 0L,	NULL, NULL)) != S_OK)
				{
 					MY_OUTPUT(L"ENTER ***** ConfigurationActionAssoc Delete failed...", 4);
				}
				SysFreeString(instName);
				instName = NULL;

				if (pQ->szQuery)
				{
					delete [] pQ->szQuery;
					pQ->szQuery = NULL;
				}

				if (pQ->szConfigActionAssocPath)
				{
					delete [] pQ->szConfigActionAssocPath;
					pQ->szConfigActionAssocPath = NULL;
				}

				if (pQ->szUserConfigPath)
				{
					delete [] pQ->szUserConfigPath;
					pQ->szUserConfigPath = NULL;
				}

				if (pQ->szChildPath)
				{
					delete [] pQ->szChildPath;
					pQ->szChildPath = NULL;
				}

				if (pQ->pTempSink)
				{
					g_pIWbemServices->CancelAsyncCall((IWbemObjectSink*)pQ->pTempSink);
					pQ->pTempSink->Release();
					pQ->pTempSink = NULL;
				}

				m_qList.erase(iaQ);
				bFound = TRUE;
				break;
			}
		}
	}

	if (bFound == TRUE)
	{
	}

	return bFound;

error:
	MY_ASSERT(FALSE);
	if (pszEventFilter)
		delete [] pszEventFilter;
	if (instName)
		SysFreeString(instName);
	if (pInst)
		pInst->Release();
	return TRUE;
}

BOOL CAction::DeleteEFAndFTCB(void)
{
	TCHAR szTemp[1024];
	TCHAR *pszEventConsumer = NULL;
	BSTR instName = NULL;
	LPTSTR pStr;
	LPTSTR pStr2;
	IWbemClassObject* pInst = NULL;
	HRESULT hRes;
	HRESULT hRetRes = S_OK;

	//
	// Delete the __EventFilter
	//
	wcscpy(szTemp, L"__EventFilter.Name=\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\"");
	instName = SysAllocString(szTemp);
	MY_ASSERT(instName); if (!instName) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
	if ((hRes = g_pIWbemServices->DeleteInstance(instName, 0L,	NULL, NULL)) != S_OK)
	{
		MY_OUTPUT(L"ENTER ***** __EventFilter Delete failed...", 4);
	}
	SysFreeString(instName);
	instName = NULL;

	//
	// Delete the __FilterToConsumerBinding. Looks as follows -
	//__FilterToConsumerBinding.
	//Consumer="CommandLineEventConsumer.Name=\"{944E9251-6C58-11d3-90E9-006097919914}\"",
	//Filter="__EventFilter.Name=\"{944E9251-6C58-11d3-90E9-006097919914}\""
	//
	wcscpy(szTemp, L"MicrosoftHM_ActionConfiguration.GUID=\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\"");
	hRetRes = GetWbemObjectInst(&g_pIWbemServices, szTemp, NULL, &pInst);
	if (!pInst)
	{
		return FALSE;
	}
	hRetRes = GetStrProperty(pInst, L"EventConsumer", &pszEventConsumer);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pInst->Release();
	pInst = NULL;

	// We need to format the strings a bit, need extra backslashes in there
	pStr = wcschr(pszEventConsumer, ':');
	MY_ASSERT(pStr); if (!pStr) goto Badstring;
	pStr++;
	pStr2 = wcschr(pStr, '\"');
	MY_ASSERT(pStr2); if (!pStr2) goto Badstring;
	*pStr2 = '\0';
	wcscpy(szTemp, L"__FilterToConsumerBinding.Consumer=\"");
	lstrcat(szTemp, L"\\\\\\\\.\\\\root\\\\cimv2\\\\MicrosoftHealthMonitor:");
	lstrcat(szTemp, pStr);
	lstrcat(szTemp, L"\\\"");
	pStr = pStr2;
	pStr++;
	pStr2 = wcschr(pStr, '\"');
	MY_ASSERT(pStr2); if (!pStr2) goto Badstring;
	*pStr2 = '\0';
	lstrcat(szTemp, pStr);
	lstrcat(szTemp, L"\\\"\",Filter=\"\\\\\\\\.\\\\root\\\\cimv2\\\\MicrosoftHealthMonitor:");

	lstrcat(szTemp, L"__EventFilter.Name=\\\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\\\"\"");

	instName = SysAllocString(szTemp);
	MY_ASSERT(instName); if (!instName) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
	if ((hRes = g_pIWbemServices->DeleteInstance(instName, 0L,	NULL, NULL)) != S_OK)
	{
		MY_OUTPUT(L"ENTER ***** __FilterToConsumerBinding Delete failed...", 4);
	}
	SysFreeString(instName);
	instName = NULL;
	goto Goodstring;

Badstring:
	MY_OUTPUT(L"ENTER ***** Bad FilterToConsumer string", 4);
Goodstring:
	delete [] pszEventConsumer;
	pszEventConsumer = NULL;

	return TRUE;

error:
	MY_ASSERT(FALSE);
	if (pszEventConsumer)
		delete [] pszEventConsumer;
	if (instName)
		SysFreeString(instName);
	if (pInst)
		pInst->Release();
	return FALSE;
}

//
// Delete the Action Configuration instance and everything that goes with it.
// Need to delete the ActionConfiguration, __EventConsumer, all ConfigActionAssoc's,
// __EventFilter's, __FilterToConsumerBinding's
//
BOOL CAction::DeleteAConfig(void)
{
	HRESULT hRes;
	QLIST qList;
	QSTRUCT Q;
	QSTRUCT *pQ;
	int i, iSize;
	TCHAR szTemp[1024];
	TCHAR *pszEventConsumer = NULL;
	BSTR instName = NULL;
	LPTSTR pStr1;
	LPTSTR pStr2;
	LPTSTR pStr3;
	LPTSTR pStr4;
	BSTR Language = NULL;
	BSTR Query = NULL;
	IEnumWbemClassObject *pEnum;
	IWbemClassObject *pAssocObj = NULL;
	ULONG uReturned;
	IWbemClassObject* pInst = NULL;
	HRESULT hRetRes = S_OK;

	MY_OUTPUT(L"ENTER ***** CAction::DeleteAConfig...", 1);

	TCHAR msgbuf[1024];
	wsprintf(msgbuf, L"DELETE: AGUID=%s", m_szGUID);
	MY_OUTPUT(msgbuf, 4);
	//
	// Get rid of all associations to the action.
	// Make use of other function, loop through all associations
	//
	Language = SysAllocString(L"WQL");
	MY_ASSERT(Language); if (!Language) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
	wcscpy(szTemp, L"REFERENCES OF {MicrosoftHM_ActionConfiguration.GUID=\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\"} WHERE ResultClass=MicrosoftHM_ConfigurationActionAssociation");
	Query = SysAllocString(szTemp);
	MY_ASSERT(Query); if (!Query) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

	// Initialize IEnumWbemClassObject pointer
	pEnum = 0;

	// Issue query
   	hRes = g_pIWbemServices->ExecQuery(Language, Query, WBEM_FLAG_FORWARD_ONLY, 0, &pEnum);

	SysFreeString(Query);
	Query = NULL;
	SysFreeString(Language);
	Language = NULL;

	if (hRes != 0)
	{
		MY_OUTPUT(L"ENTER ***** DeleteAConfig failed...", 4);
		return FALSE;
	}

	// Retrieve objects in result set
	while (TRUE)
	{
		pAssocObj = NULL;
		uReturned = 0;

		hRes = pEnum->Next(0, 1, &pAssocObj, &uReturned);

		if (uReturned == 0)
		{
			break;
		}

		hRetRes = GetStrProperty(pAssocObj, L"ParentPath", &Q.szUserConfigPath);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

		hRetRes = GetStrProperty(pAssocObj, L"ChildPath", &Q.szChildPath);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;

		qList.push_back(Q);

		// Release it.
		pAssocObj->Release();
		pAssocObj = NULL;
	}
	// All done
	pEnum->Release();
	pEnum = NULL;

	iSize = qList.size();
	for (i=0; i<iSize; i++)
	{
		MY_ASSERT(i<qList.size());
		pQ = &qList[i];

		pStr1 = wcschr(pQ->szUserConfigPath, '\"');
		if (pStr1)
		{
			pStr1++;
			pStr2 = wcschr(pStr1, '\"');
			if (pStr2)
			{
				*pStr2 = '\0';
			}
		}
		pStr3 = wcschr(pQ->szChildPath, '\"');
		if (pStr3)
		{
			pStr3++;
			pStr4 = wcschr(pStr3, '\"');
			if (pStr4)
			{
				*pStr4 = '\0';
			}
		}

		DeleteConfigActionAssoc(pStr1, pStr3);

		if (pQ->szUserConfigPath)
			delete [] pQ->szUserConfigPath;

		if (pQ->szChildPath)
			delete [] pQ->szChildPath;
	}
	qList.clear();

	//
	// Finally we can get rid of the __EventFilter and __FilterToConsumerBinding.
	//
	DeleteEFAndFTCB();

	//
	// Finally we can get rid of the actual ActionConfiguration instance, and the __EventConsumer.
	//
	wcscpy(szTemp, L"MicrosoftHM_ActionConfiguration.GUID=\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\"");
	hRetRes = GetWbemObjectInst(&g_pIWbemServices, szTemp, NULL, &pInst);
	if (!pInst)
	{
		MY_HRESASSERT(hRetRes);
		return FALSE;
	}
	hRetRes = GetStrProperty(pInst, L"EventConsumer", &pszEventConsumer);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pInst->Release();
	pInst = NULL;

	instName = SysAllocString(szTemp);
	MY_ASSERT(instName); if (!instName) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
	if ((hRes = g_pIWbemServices->DeleteInstance(instName, 0L,	NULL, NULL)) != S_OK)
	{
		MY_OUTPUT(L"ENTER ***** ActionConfiguration Delete failed...", 4);
	}
	SysFreeString(instName);
	instName = NULL;

	instName = SysAllocString(pszEventConsumer);
	MY_ASSERT(instName); if (!instName) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
	if ((hRes = g_pIWbemServices->DeleteInstance(instName, 0L,	NULL, NULL)) != S_OK)
	{
		MY_OUTPUT(L"ENTER ***** EventConsumer Delete failed...", 4);
	}
	SysFreeString(instName);
	instName = NULL;
	delete [] pszEventConsumer;
	pszEventConsumer = NULL;

	MY_OUTPUT(L"EXIT ***** CAction::DeleteAConfig...", 1);
	return TRUE;

error:
	MY_ASSERT(FALSE);
	if (pszEventConsumer)
		delete [] pszEventConsumer;
	if (instName)
		SysFreeString(instName);
	if (Query)
		SysFreeString(Query);
	if (Language)
		SysFreeString(Language);
	if (pAssocObj)
		pAssocObj->Release();
	if (pEnum)
		pEnum->Release();
	if (pInst)
		pInst->Release();
	return FALSE;
}

// This is only for event sending related to going into a scheduled outage time,
// or the disabling of the action.
BOOL CAction::FireEvent(long lErrorCode, LPTSTR pszErrorDescription, int iResString)
{
	HRESULT hRetRes = S_OK;
	GUID guid;
	HRESULT hRes;
	BOOL bRetValue = TRUE;
	IWbemClassObject* pInstance = NULL;
	LPVOID lpMsgBuf = NULL;
	TCHAR szTemp[1024] = L"";
	TCHAR buf[256] = L"";

	MY_OUTPUT(L"ENTER ***** CAction::FireEvent...", 2);

	// Don't send if no-one is listening!
	if (g_pActionEventSink == NULL)
	{
		return bRetValue;
	}

	if (iResString != HMRES_ACTION_LOADFAIL)
	{
		if (m_pszStatusGUID)
		{
			delete [] m_pszStatusGUID;
			m_pszStatusGUID = NULL;
		}
		hRetRes = CoCreateGuid(&guid);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
		m_pszStatusGUID = new TCHAR[100];
		MY_ASSERT(m_pszStatusGUID); if (!m_pszStatusGUID) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
		StringFromGUID2(guid, m_pszStatusGUID, 100);

		wcscpy(m_szDTTime, m_szDTCurrTime);
		wcscpy(m_szTime, m_szCurrTime);
	}

	hRes = GetHMActionStatus(&pInstance, NULL, L"MicrosoftHM_ActionStatusEvent", iResString);
	if (SUCCEEDED(hRes))
	{
		// Add in the extra error info if available
		if (lErrorCode != -1)
		{
			if (g_hResLib == NULL || !LoadString(g_hResLib, iResString, szTemp, 1024))
			{
				MY_ASSERT(FALSE);
				wcscpy(szTemp, L"Could not locate resource string.");
			}
			wsprintf(buf, L" 0x%08x : ", lErrorCode);
			wcsncat(szTemp, buf, 1023-wcslen(szTemp));
			szTemp[1023] = '\0';
			if (g_hWbemComnModule)
			{
				FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_HMODULE,
								g_hWbemComnModule, lErrorCode, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
				if (lpMsgBuf)
				{
					wcsncat(szTemp, (LPCTSTR)lpMsgBuf, 1023-wcslen(szTemp));
					szTemp[1023] = '\0';
					LocalFree(lpMsgBuf);
					wcsncat(szTemp, L". ", 1023-wcslen(szTemp));
					szTemp[1023] = '\0';
				}
			}
			if (pszErrorDescription)
			{
				wcsncat(szTemp, pszErrorDescription, 1023-wcslen(szTemp));
				szTemp[1023] = '\0';
			}
			PutStrProperty(pInstance, L"Message", szTemp);
		}

		MY_OUTPUT2(L"EVENT: Action State Change=%d", m_lCurrState, 4);
		if (g_pActionEventSink) 
		{
			hRes = g_pActionEventSink->Indicate(1, &pInstance);
			//WBEM_E_SERVER_TOO_BUSY is Ok. Wbem will deliver.
			if (FAILED(hRes) && hRes != WBEM_E_SERVER_TOO_BUSY)
			{
				MY_HRESASSERT(hRes);
				bRetValue = FALSE;
				MY_OUTPUT(L"Failed on Indicate!", 4);
			}
		}

		pInstance->Release();
		pInstance = NULL;
	}
	else
	{
		MY_HRESASSERT(hRes);
		MY_OUTPUT(L"failed to get instance!", 1);
	}

	MY_OUTPUT(L"EXIT ***** CAction::FireEvent...", 2);
	return bRetValue;

error:
	MY_ASSERT(FALSE);
	if (pInstance)
		pInstance->Release();
	return FALSE;
}

HRESULT CAction::GetHMActionStatus(IWbemClassObject** ppInstance, IWbemClassObject* pObj, LPTSTR pszClass, int iResString)
{
	BOOL bRetValue = TRUE;
	IWbemClassObject* pClass = NULL;
	TCHAR szTemp[1024];
	BSTR bsString = NULL;
	HRESULT hRes;
	HRESULT hRetRes;
	VARIANT v;
	long lState;
	DWORD dwNameLen = MAX_COMPUTERNAME_LENGTH + 2;
	TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 2];
	VariantInit(&v);

	MY_OUTPUT(L"ENTER ***** CAction::GetHMActionStatus...", 1);

	bsString = SysAllocString(pszClass);
	MY_ASSERT(bsString); if (!bsString) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}
	hRes = g_pIWbemServices->GetObject(bsString, 0L, NULL, &pClass, NULL);
	SysFreeString(bsString);
	bsString = NULL;

	if (FAILED(hRes))
	{
		MY_HRESASSERT(hRes);
		return hRes;
	}

	hRes = pClass->SpawnInstance(0, ppInstance);
	pClass->Release();
	pClass = NULL;

	if (FAILED(hRes))
	{
		MY_HRESASSERT(hRes);
		return hRes;
	}

	if (iResString != HMRES_ACTION_LOADFAIL)
	{
		PutStrProperty(*ppInstance, L"GUID", m_szGUID);
		PutStrProperty(*ppInstance, L"Name", m_szName);

		if (GetComputerName(szComputerName, &dwNameLen))
		{
			PutStrProperty(*ppInstance, L"SystemName", szComputerName);
		}
		else
		{
			PutStrProperty(*ppInstance, L"SystemName", L"LocalMachine");
		}

		if (pObj)
		{
			hRetRes = GetUint32Property(pObj, L"State", &lState);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
			PutUint32Property(*ppInstance, L"State", lState);
		}
		else
		{
			PutUint32Property(*ppInstance, L"State", m_lCurrState);
		}

		if (pObj)
		{
			VariantInit(&v);
			V_VT(&v) = VT_UNKNOWN;
			V_UNKNOWN(&v) = (IUnknown*)pObj;
			(V_UNKNOWN(&v))->AddRef();
			hRes = (*ppInstance)->Put(L"EmbeddedStatusEvent", 0L, &v, 0L);
			VariantClear(&v);
			MY_HRESASSERT(hRes);
		}

		PutStrProperty(*ppInstance, L"TimeGeneratedGMT", m_szDTCurrTime);
		PutStrProperty(*ppInstance, L"LocalTimeFormatted", m_szCurrTime);
		PutStrProperty(*ppInstance, L"StatusGUID", m_pszStatusGUID);
	}
	else
	{
		PutUint32Property(*ppInstance, L"State", HM_CRITICAL);
		PutStrProperty(*ppInstance, L"Name", L"...");
	}

	if (g_hResLib == NULL || !LoadString(g_hResLib, iResString, szTemp, 1024))
	{
		MY_ASSERT(FALSE);
		wcscpy(szTemp, L"Could not locate resource string.");
	}
	PutStrProperty(*ppInstance, L"Message", szTemp);

	MY_OUTPUT(L"EXIT ***** CAction::GetHMActionStatus...", 1);
	return hRes;

error:
	MY_ASSERT(FALSE);
	if (bsString)
		SysFreeString(bsString);
	if (pClass)
		pClass->Release();
	return hRetRes;
}

LPTSTR CAction::GetGUID(void)
{
	return m_szGUID;
}

HRESULT CAction::SendHMActionStatusInstances(IWbemObjectSink* pSink)
{
	HRESULT hRes = S_OK;
	IWbemClassObject* pObj = NULL;
	int iResString;

	MY_OUTPUT(L"ENTER ***** CAction::SendHMActionStatusInstances...", 2);

	if (m_bValidLoad == FALSE)
		return WBEM_E_INVALID_OBJECT;

	if (pSink == NULL)
	{
		MY_OUTPUT(L"Instances-Invalid Sink", 1);
		return WBEM_E_INVALID_PARAMETER;
	}

	if (m_lCurrState==HM_SCHEDULEDOUT)
	{
		iResString = HMRES_ACTION_OUTAGE;
	}
	else if (m_lCurrState==HM_DISABLED)
	{
		iResString = HMRES_ACTION_DISABLE;
	}
	else if (m_lCurrState==HM_CRITICAL)
	{
		iResString = HMRES_ACTION_FAILED;
	}
	else
	{
		iResString = HMRES_ACTION_ENABLE;
	}
	// Provide Instance
	hRes = GetHMActionStatus(&pObj, NULL, L"MicrosoftHM_ActionStatus", iResString);
	if (SUCCEEDED(hRes))
	{
		hRes = pSink->Indicate(1, &pObj);

		if (FAILED(hRes) && hRes!=WBEM_E_SERVER_TOO_BUSY && hRes!=WBEM_E_CALL_CANCELLED && hRes!=WBEM_E_TRANSPORT_FAILURE)
		{
			MY_HRESASSERT(hRes);
			MY_OUTPUT(L"SendHMSystemStatusInstances-failed to send status!", 1);
		}

		pObj->Release();
		pObj = NULL;
	}
	else
	{
		MY_HRESASSERT(hRes);
		MY_OUTPUT(L":SendHMSystemStatusInstances-failed to get instance!", 1);
	}

	MY_OUTPUT(L"EXIT ***** CAction::SendHMSystemStatusInstances...", 2);
	return hRes;
}

// For a single GetObject
HRESULT CAction::SendHMActionStatusInstance(IWbemObjectSink* pSink, LPTSTR pszGUID)
{
	MY_OUTPUT(L"ENTER ***** CAction::SendHMActionStatusInstance...", 1);

	//
	// Is this the one we are looking for?
	//
	if (!_wcsicmp(m_szGUID, pszGUID))
	{
		if (m_bValidLoad == FALSE)
			return WBEM_E_INVALID_OBJECT;

		SendHMActionStatusInstances(pSink);
		return S_OK;
	}

	MY_OUTPUT(L"EXIT ***** CAction::SendHMActionStatusInstance...", 1);
	return WBEM_S_DIFFERENT;
}

BOOL CAction::checkTime(void)
{
	BOOL bTimeOK;
	SYSTEMTIME st;	// system time

	//
	// Make sure that we are in a valid time to run.
	// NULL (-1) means run all the time.
	//
	bTimeOK = FALSE;
	if (m_bEnabled==TRUE)
	{
		GetLocalTime(&st);

		bTimeOK = FALSE;
		// Check the Day of the Week
		if (!(m_iActiveDays&(1<<st.wDayOfWeek)))
		{
		}
		else if (m_lBeginHourTime<0 || m_lEndHourTime<0)
		{
			bTimeOK = TRUE;
		}
		else if (m_lBeginHourTime==m_lEndHourTime && m_lBeginMinuteTime==m_lEndMinuteTime)
		{
			// Check the Hours of operation
			// First see if we are doing an inclusive time tests, or an exclusive time test
			// Case where the time is exactly equal, and that means run this once per day
			if (st.wHour==m_lBeginHourTime && st.wMinute==m_lBeginMinuteTime)
			{
				if (st.wSecond <= HM_POLLING_INTERVAL)
				{
					bTimeOK = TRUE;
				}
			}
		}
		else if ((m_lBeginHourTime < m_lEndHourTime) ||
			((m_lBeginHourTime==m_lEndHourTime) && m_lBeginMinuteTime < m_lEndMinuteTime))
		{
			// Inclusive case
			if ((m_lBeginHourTime < st.wHour) ||
				((m_lBeginHourTime == st.wHour) && m_lBeginMinuteTime <= st.wMinute))
			{
				if ((st.wHour < m_lEndHourTime) ||
					((st.wHour == m_lEndHourTime) && st.wMinute < m_lEndMinuteTime))
				{
					bTimeOK = TRUE;
				}
			}
		}
		else
		{
			// Exclusive case
			if ((m_lEndHourTime > st.wHour) ||
				((m_lEndHourTime == st.wHour) && m_lEndMinuteTime > st.wMinute))
			{
				bTimeOK = TRUE;
			}
			else if ((st.wHour > m_lBeginHourTime) ||
				((st.wHour == m_lBeginHourTime) && st.wMinute >= m_lBeginMinuteTime))
			{
				bTimeOK = TRUE;
			}
		}
	}

	return bTimeOK;
}

CBase *CAction::FindImediateChildByName(LPTSTR pszName)
{
	MY_ASSERT(FALSE);

	return NULL;
}

BOOL CAction::GetNextChildName(LPTSTR pszChildName, LPTSTR pszOutName)
{
	MY_ASSERT(FALSE);

	return NULL;
}

CBase *CAction::FindPointerFromName(LPTSTR pszName)
{
	MY_ASSERT(FALSE);

	return NULL;
}

#ifdef SAVE
BOOL CAction::ModifyAssocForMove(CBase *pNewParentBase)
{
	MY_ASSERT(FALSE);

	return TRUE;
}
#endif

BOOL CAction::ReceiveNewChildForMove(CBase *pBase)
{
	MY_ASSERT(FALSE);

	return FALSE;
}

BOOL CAction::DeleteChildFromList(LPTSTR pszGUID)
{
	MY_ASSERT(FALSE);

	return FALSE;
}

BOOL CAction::SendReminderActionIfStateIsSame(IWbemObjectSink* pActionEventSink, IWbemObjectSink* pActionTriggerEventSink, IWbemClassObject* pActionInstance, IWbemClassObject* pActionTriggerInstance, unsigned long ulTriggerStates)
{
	MY_ASSERT(FALSE);

	return FALSE;
}

BOOL CAction::HandleTempErrorEvent(BSTR szGUID, long lErrorCode, LPTSTR pszErrorDescription)
{
	if (m_bValidLoad == FALSE)
		return FALSE;

	//
	// Is this us we are looking for?
	//
	if (!_wcsicmp(m_szGUID, szGUID))
	{
		m_lCurrState = HM_CRITICAL;
		FireEvent(lErrorCode, pszErrorDescription, HMRES_ACTION_FAILED);
		return TRUE;
	}

	return FALSE;
}

BOOL CAction::Cleanup(BOOL bClearAll)
{
	int i, iSize;
	QSTRUCT *pQ;

	if (m_szName)
	{
		delete [] m_szName;
		m_szName = NULL;
	}
	if (m_szDescription)
	{
		delete [] m_szDescription;
		m_szDescription = NULL;
	}
	if (m_szTypeGUID)
	{
		delete [] m_szTypeGUID;
		m_szTypeGUID = NULL;
	}

	if (bClearAll)
	{
		if (m_pszStatusGUID)
		{
			delete [] m_pszStatusGUID;
			m_pszStatusGUID = NULL;
		}

		iSize = m_qList.size();
		for (i=0; i<iSize; i++)
		{
			MY_ASSERT(i<m_qList.size());
			pQ = &m_qList[i];
			if (pQ->szQuery)
				delete [] pQ->szQuery;

			if (pQ->szConfigActionAssocPath)
				delete [] pQ->szConfigActionAssocPath;

			if (pQ->szUserConfigPath)
				delete [] pQ->szUserConfigPath;

			if (pQ->szChildPath)
				delete [] pQ->szChildPath;

			if (pQ->pTempSink)
			{
				g_pIWbemServices->CancelAsyncCall((IWbemObjectSink*)pQ->pTempSink);
				pQ->pTempSink->Release();
				pQ->pTempSink = NULL;
			}
		}
		m_qList.clear();
	}

	return TRUE;
}

HRESULT CAction::RemapAction(void)
{
	HRESULT hRetRes = S_OK;
	QLIST qList;
	TCHAR szTemp[1024];
	LPTSTR pStr2;
	TCHAR *pszEventConsumer = NULL;
	LPTSTR pStr;
	IWbemClassObject* pInst = NULL;

	MY_OUTPUT(L"ENTER ***** CAction::RemapAction...", 1);

	wcscpy(szTemp, L"MicrosoftHM_ActionConfiguration.GUID=\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\"");
	hRetRes = GetWbemObjectInst(&g_pIWbemServices, szTemp, NULL, &pInst);
	if (!pInst)
	{
		MY_HRESASSERT(hRetRes);
		return FALSE;
	}
	hRetRes = GetStrProperty(pInst, L"EventConsumer", &pszEventConsumer);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pInst->Release();
	pInst = NULL;

	//
	// __EventConsumer.
	//
	hRetRes = GetWbemObjectInst(&g_pIWbemServices, pszEventConsumer, NULL, &pInst);
	if (!pInst)
	{
		MY_HRESASSERT(hRetRes);
		return FALSE;
	}
	hRetRes = RemapOneAction(pInst);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pInst->Release();
	pInst = NULL;

	//
	// __EventFilter.
	//
	wcscpy(szTemp, L"__EventFilter.Name=\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\"");
	hRetRes = GetWbemObjectInst(&g_pIWbemServices, szTemp, NULL, &pInst);
	if (!pInst)
	{
		MY_HRESASSERT(hRetRes);
		return FALSE;
	}
	hRetRes = RemapOneAction(pInst);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pInst->Release();
	pInst = NULL;

	//
	// __FilterToConsumerBinding
	//
	// We need to format the strings a bit, need extra backslashes in there
	pStr = wcschr(pszEventConsumer, ':');
	MY_ASSERT(pStr); if (!pStr) goto Badstring;
	pStr++;
	pStr2 = wcschr(pStr, '\"');
	MY_ASSERT(pStr2); if (!pStr2) goto Badstring;
	*pStr2 = '\0';
	wcscpy(szTemp, L"__FilterToConsumerBinding.Consumer=\"");
	lstrcat(szTemp, L"\\\\\\\\.\\\\root\\\\cimv2\\\\MicrosoftHealthMonitor:");
	lstrcat(szTemp, pStr);
	lstrcat(szTemp, L"\\\"");
	pStr = pStr2;
	pStr++;
	pStr2 = wcschr(pStr, '\"');
	MY_ASSERT(pStr2); if (!pStr2) goto Badstring;
	*pStr2 = '\0';
	lstrcat(szTemp, pStr);
	lstrcat(szTemp, L"\\\"\",Filter=\"\\\\\\\\.\\\\root\\\\cimv2\\\\MicrosoftHealthMonitor:");

	lstrcat(szTemp, L"__EventFilter.Name=\\\"");
	lstrcat(szTemp, m_szGUID);
	lstrcat(szTemp, L"\\\"\"");

	hRetRes = GetWbemObjectInst(&g_pIWbemServices, szTemp, NULL, &pInst);
	if (!pInst)
	{
		MY_HRESASSERT(hRetRes);
		return FALSE;
	}
	hRetRes = RemapOneAction(pInst);
	MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
	pInst->Release();
	pInst = NULL;
	goto Goodstring;

Badstring:
	MY_OUTPUT(L"ENTER ***** Bad FilterToConsumer string", 4);
Goodstring:
	delete [] pszEventConsumer;
	pszEventConsumer = NULL;

	MY_OUTPUT(L"EXIT ***** CAction::RemapAction...", 1);
	return TRUE;

error:
	MY_ASSERT(FALSE);
	if (pszEventConsumer)
		delete [] pszEventConsumer;
	if (pInst)
		pInst->Release();
	return FALSE;
}

HRESULT CAction::RemapOneAction(IWbemClassObject* pObj)
{
	HANDLE hToken = NULL;
	HRESULT hRetRes = S_OK;
	IWbemCallResult *pResult = 0;
	SAFEARRAY* psa = NULL;
	VARIANT	var;
	CIMTYPE vtType;
	long lBound, uBound;
	BOOL bObjIsLocalSystem;
	BOOL bSuccess;
	SID_IDENTIFIER_AUTHORITY ntauth = SECURITY_NT_AUTHORITY;
	DWORD dwLengthNeeded;
	void* psid = 0;
	PTOKEN_USER pUserInfo = NULL;

	VariantInit(&var);

	// Need to verify that we're in fact running under the LocalSystem SID!
	// otherwise we'd end up in an infinite loop!  Need an assert to check that
	// the current thread is running as LocalSystem.
	bSuccess = OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken);
	MY_ASSERT(bSuccess); if (!bSuccess) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

	// Call GetTokenInformation to get the buffer size.
	dwLengthNeeded = 0;
	bSuccess = GetTokenInformation(hToken, TokenUser, NULL, dwLengthNeeded, &dwLengthNeeded);

	// Allocate the buffer.
	pUserInfo = (PTOKEN_USER) GlobalAlloc(GPTR, dwLengthNeeded);
	MY_ASSERT(pUserInfo); if (!pUserInfo) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

	// Call GetTokenInformation again to get the group information.
	bSuccess = GetTokenInformation(hToken, TokenUser, pUserInfo, dwLengthNeeded, &dwLengthNeeded);
	MY_ASSERT(bSuccess); if (!bSuccess) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

	bSuccess = AllocateAndInitializeSid(&ntauth, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psid);
	MY_ASSERT(bSuccess); if (!bSuccess) {hRetRes = WBEM_E_OUT_OF_MEMORY; goto error;}

	// Verify that winmgmt is running under LocalSystem. If not we don't do anything.
	if (EqualSid(pUserInfo->User.Sid, psid))
	{
		// get the CreatorSID of this instance
		hRetRes = pObj->Get(L"CreatorSID", 0, &var, &vtType, NULL);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
		if (vtType != (CIM_UINT8 | CIM_FLAG_ARRAY))
		{
			hRetRes = WBEM_E_FAILED;
			goto error;
		}

		// make sure it's the right size
		psa = var.parray;
		if (::SafeArrayGetElemsize(psa) != 1)
		{
			hRetRes = WBEM_E_FAILED;
			goto error;
		}

		hRetRes = ::SafeArrayGetLBound(psa, 1, &lBound);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
		hRetRes = ::SafeArrayGetUBound(psa, 1, &uBound);
		MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
		if (lBound !=0)
		{
			hRetRes = WBEM_E_FAILED;
			goto error;
		}

		// now see if this is LocalSystem by comparing to 
		// the hardcoded LocalSystem SID
		bObjIsLocalSystem = false;
		if (uBound == (sizeof LocalSystemSID)-1 )
		{
			LPVOID lpCreatorSID = NULL;
			hRetRes = ::SafeArrayAccessData(psa, &lpCreatorSID);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
			if (memcmp (lpCreatorSID, LocalSystemSID, sizeof LocalSystemSID) == 0)
			{
				bObjIsLocalSystem = true;
			}
			hRetRes = ::SafeArrayUnaccessData(psa);
			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
		}

		// If it's not a LocalSystem SID, to replace it with a LocalSytem SID,
		// we need to just store the instance in WMI-- WMI will automatically
		// replace the SID that's there with our SID-- LocalSystem.
		//
		if (bObjIsLocalSystem == FALSE)
		{
			hRetRes = g_pIWbemServices->PutInstance(pObj, WBEM_FLAG_UPDATE_ONLY, NULL, &pResult);
//			MY_HRESASSERT(hRetRes); if (hRetRes!=S_OK) goto error;
		}
		VariantClear(&var);
	}

	CloseHandle(hToken);
	FreeSid(psid);
	if (pUserInfo)
		GlobalFree(pUserInfo);
	return S_OK;

error:
MY_OUTPUT2(L"11 0x%08x\n",hRetRes,5);
	MY_ASSERT(FALSE);
	VariantClear(&var);
	CloseHandle(hToken);
	FreeSid(psid);
	if (pUserInfo)
		GlobalFree(pUserInfo);
	return hRetRes;
}

HRESULT CAction::CheckForBadLoad(void)
{
	HRESULT hRetRes = S_OK;
	IWbemClassObject* pObj = NULL;
	TCHAR szTemp[1024];

	if (m_bValidLoad == FALSE)
	{
		wcscpy(szTemp, L"MicrosoftHM_ActionConfiguration.GUID=\"");
		lstrcat(szTemp, m_szGUID);
		lstrcat(szTemp, L"\"");
		hRetRes = GetWbemObjectInst(&g_pIWbemServices, szTemp, NULL, &pObj);
		if (!pObj)
		{
			MY_HRESASSERT(hRetRes);
			return S_FALSE;
		}
		hRetRes = LoadInstanceFromMOF(pObj, FALSE);
		// Here is where we can try and send out a generic SOS if the load failed each time!!!
		if (hRetRes != S_OK)
		{
			FireEvent(-1, NULL, HMRES_ACTION_LOADFAIL);
		}
		MY_HRESASSERT(hRetRes);
		pObj->Release();
		pObj = NULL;
		return hRetRes;
	}

	return S_OK;
}