|
|
/*--------------------------------------------------------------------------*
* * Microsoft Windows * Copyright (C) Microsoft Corporation, 1992 - 000 * * File: power.cpp * * Contents: Implementation file for CConsolePower * * History: 25-Feb-2000 jeffro Created * *--------------------------------------------------------------------------*/
#include <stdafx.h>
#include "power.h"
/*
* allocate a TLS index for CConsolePower objects */ const DWORD CConsolePower::s_dwTlsIndex = TlsAlloc();
const DWORD CConsolePower::s_rgExecStateFlag[CConsolePower::eIndex_Count] = { ES_SYSTEM_REQUIRED, // eIndex_System
ES_DISPLAY_REQUIRED, // eIndex_Display
};
const CConsolePower::ExecutionStateFunc CConsolePower::s_FuncUninitialized = (ExecutionStateFunc)(LONG_PTR) -1;
CConsolePower::ExecutionStateFunc CConsolePower::SetThreadExecutionState_ = s_FuncUninitialized;
/*+-------------------------------------------------------------------------*
* ScPrepExecutionStateFlag * * This function increments or decrements the count for dwTestBit for this * object, and for the thread. On exit dwSTESArg contains the appropriate * flag to pass to SetThreadExecutionState for dwTestBit. That is, if the * thread count for dwTestBit is non-zero, dwSTESArg will contain dwTestBit * on return; if the thread count is zero, dwSTESArg will not contain * dwTestBit. *--------------------------------------------------------------------------*/
static SC ScPrepExecutionStateFlag ( DWORD dwTestBit, /* I:single ES_* flag to test */ DWORD dwAdd, /* I:flags to add */ DWORD dwRemove, /* I:flags to remove */ DWORD * pdwSTESArg, /* I/O:arg to SetThreadExecutionState */ LONG * pcObjectRequests, /* I/O:request count for this object */ LONG * pcThreadRequests, /* I/O:request count for this thread */ UINT cIterations = 1) /* I:times to add/remove dwTestBit */ { DECLARE_SC (sc, _T("ScPrepExecutionStateFlag"));
/*
* validate inputs -- DO NOT clear these output variables, the * existing values are modified here */ sc = ScCheckPointers (pdwSTESArg, pcObjectRequests, pcThreadRequests); if (sc) return (sc);
/*
* make sure the bit isn't to be both removed and added */ if ((dwAdd & dwTestBit) && (dwRemove & dwTestBit)) return (sc = E_INVALIDARG);
/*
* We should always have a non-negative number of requests for the bit * under test for this object, and at least as many requests for the * thread as we do for this object. */ ASSERT (*pcObjectRequests >= 0); ASSERT (*pcThreadRequests >= *pcObjectRequests); if ((*pcObjectRequests < 0) || (*pcThreadRequests < *pcObjectRequests)) return (sc = E_UNEXPECTED);
/*
* If we're adding the test bit, bump up the request count for this * object and this thread. */ if (dwAdd & dwTestBit) { *pcObjectRequests += cIterations; *pcThreadRequests += cIterations; }
/*
* Otherwise, if we're removing the test bit, bump down the request counts * for this object and this thread. */ else if (dwRemove & dwTestBit) { /*
* Can't remove the bit under test if we don't have an outstanding * request for it on this object. */ if (*pcObjectRequests < cIterations) return (sc = E_INVALIDARG);
*pcObjectRequests -= cIterations; *pcThreadRequests -= cIterations; }
/*
* If the net count for this thread is non-zero, the bit under * test needs to be in the argument for SetThreadExecutionState; * if not, the bit under test needs to be removed. */ if (*pcThreadRequests != 0) *pdwSTESArg |= dwTestBit; else *pdwSTESArg &= ~dwTestBit;
return (sc); }
/*+-------------------------------------------------------------------------*
* CConsolePower::CConsolePower * * Constructs a CConsolePower object. *--------------------------------------------------------------------------*/
CConsolePower::CConsolePower () : m_wndPower (this) { DEBUG_INCREMENT_INSTANCE_COUNTER(CConsolePower);
/*
* If any of these fail, s_rgExecStateFlag is out of order. It would * be better to use COMPILETIME_ASSERT here, but using that gives us * * error C2051: case expression not constant * * Bummer. */ ASSERT (s_rgExecStateFlag[eIndex_System] == ES_SYSTEM_REQUIRED); ASSERT (s_rgExecStateFlag[eIndex_Display] == ES_DISPLAY_REQUIRED); }
/*+-------------------------------------------------------------------------*
* CConsolePower::~CConsolePower * * Destroys a CConsolePower object. If this object holds references to * ES_SYSTEM_REQUIRED or ES_DISPLAY_REQUIRED settings, they will be cleared. *--------------------------------------------------------------------------*/
CConsolePower::~CConsolePower () { DECLARE_SC (sc, _T("CConsolePower::~CConsolePower"));
DEBUG_DECREMENT_INSTANCE_COUNTER(CConsolePower);
/*
* clean up outstanding references, if any */ if (!IsBadCodePtr ((FARPROC) SetThreadExecutionState_) && ((m_Counts.m_rgCount[eIndex_System] != 0) || (m_Counts.m_rgCount[eIndex_Display] != 0))) { try {
/*
* get the thread counts */ sc = ScCheckPointers (m_spThreadCounts, E_UNEXPECTED); if (sc) sc.Throw();
DWORD dwFlags = ES_CONTINUOUS;
/*
* clean up each individual count */ for (int i = 0; i < eIndex_Count; i++) { /*
* prevent underflow */ if (m_Counts.m_rgCount[i] > m_spThreadCounts->m_rgCount[i]) (sc = E_UNEXPECTED).Throw();
sc = ScPrepExecutionStateFlag (s_rgExecStateFlag[i], // dwTestBit
0, // dwAdd
s_rgExecStateFlag[i], // dwRemove
&dwFlags, &m_Counts.m_rgCount[i], &m_spThreadCounts->m_rgCount[i], m_Counts.m_rgCount[i]); if (sc) sc.Throw(); }
/*
* clean up the execution state for this thread */ if (!SetThreadExecutionState_(dwFlags)) { sc.FromLastError(); sc.Throw(); } } catch (SC& scCaught) { sc = scCaught; } } }
/*+-------------------------------------------------------------------------*
* CConsolePower::FinalConstruct * * This isn't the typical use of FinalConstruct in ATL objects. It is * typically used for creating aggregated objects, but using it in this * way allows us to prevent the creation of CConsolePower objects without * throwing an exception from the ctor, which ATL can't handle. *--------------------------------------------------------------------------*/
HRESULT CConsolePower::FinalConstruct () { DECLARE_SC (sc, _T("CConsolePower::FinalConstruct"));
/*
* if this is the first time a CConsolePower has been created, try to * dynaload SetThreadExecutionState (it's not supported on WinNT and * Win95) */ if (SetThreadExecutionState_ == s_FuncUninitialized) { SetThreadExecutionState_ = (ExecutionStateFunc) GetProcAddress ( GetModuleHandle (_T("kernel32.dll")), "SetThreadExecutionState"); }
/*
* if SetThreadExecutionState is supported on this platform, do the * other initialization we'll need */ if (!IsBadCodePtr ((FARPROC) SetThreadExecutionState_)) { /*
* if we couldn't get the thread-local CRefCountedTlsExecutionCounts * object for this thread, CConsolePower is useless, so fail creation */ sc = ScGetThreadCounts (&m_spThreadCounts); if (sc) return (sc.ToHr());
sc = ScCheckPointers (m_spThreadCounts, E_UNEXPECTED); if (sc) return (sc.ToHr());
/*
* create the window to handle WM_POWERBROADCAST */ sc = m_wndPower.ScCreate (); if (sc) return (sc.ToHr()); }
return (sc.ToHr()); }
/*+-------------------------------------------------------------------------*
* CConsolePower::ScGetThreadCounts * * Returns a pointer to the thread-local CRefCountedTlsExecutionCounts object * for this thread, allocating one if necessary. * * NOTE: The returned pointer has a reference added. It is the client's * responsibility to release the reference. *--------------------------------------------------------------------------*/
SC CConsolePower::ScGetThreadCounts (CRefCountedTlsExecutionCounts** ppThreadCounts) { DECLARE_SC (sc, _T("CConsolePower::ScGetThreadCounts"));
/*
* we shouldn't get here if we're on a platform that doesn't support * SetThreadExecutionState */ ASSERT (!IsBadCodePtr ((FARPROC) SetThreadExecutionState_)); if (IsBadCodePtr ((FARPROC) SetThreadExecutionState_)) return (sc = E_UNEXPECTED);
sc = ScCheckPointers (ppThreadCounts); if (sc) return (sc);
/*
* init output */ (*ppThreadCounts) = NULL;
/*
* couldn't allocate a TLS index? fail */ if (s_dwTlsIndex == TLS_OUT_OF_INDEXES) return (sc = E_OUTOFMEMORY);
/*
* Get the existing thread counts structure. If this is the first * time through (i.e. the first CConsolePower created on this thread), * we won't have a thread counts structure, so we'll allocate one now. */ CTlsExecutionCounts* pTEC = CTlsExecutionCounts::GetThreadInstance(s_dwTlsIndex);
if (pTEC == NULL) { /*
* allocate the struct for this thread */ (*ppThreadCounts) = CRefCountedTlsExecutionCounts::CreateInstance(); if ((*ppThreadCounts) == NULL) return (sc = E_OUTOFMEMORY);
/*
* put it in our TLS slot */ sc = (*ppThreadCounts)->ScSetThreadInstance (s_dwTlsIndex); if (sc) return (sc); } else (*ppThreadCounts) = static_cast<CRefCountedTlsExecutionCounts*>(pTEC);
/*
* put a reference on for the caller */ (*ppThreadCounts)->AddRef();
return (sc); }
/*+-------------------------------------------------------------------------*
* CConsolePower::SetExecutionState * * This method wraps the ::SetThreadExecutionState API in a manner that is * safe in the presence of multiple COM servers (i.e. snap-ins) that might * need to call ::SetThreadExecutionState. * * The problem is that ::SetThreadExecutionState doesn't maintain reference * counts on the flags that it is passed. For instance: * * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED); * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED); * SetThreadExecutionState (ES_CONTINUOUS); * * will result in the ES_SYSTEM_REQUIRED bit being off, even though it was * set twice and only cleared once. This can lead to conflicts between * snap-ins, like in this scenario: * * SnapinA: * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED); * * SnapinB: * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED); * SetThreadExecutionState (ES_CONTINUOUS); * * (a Long Time passes) * * SnapinA: * SetThreadExecutionState (ES_CONTINUOUS); * * Because of the nature of SetThreadExecutionState, during * the Long Time, SnapinA thinks the ES_SYSTEM_REQUIRED bit is set, even * though SnapinB has turned it off. * * The CConsolePower object maintains a per-snap-in count of the execution * state bits, so they can all happily coexist. *--------------------------------------------------------------------------*/
STDMETHODIMP CConsolePower::SetExecutionState ( DWORD dwAdd, /* I:flags to add */ DWORD dwRemove) /* I:flags to remove */ { DECLARE_SC (sc, _T("CConsolePower::SetExecutionState")); #ifdef DBG
/*
* this object is CoCreated so we can't tell what the snap-in name is */ sc.SetSnapinName (_T("<unknown>")); #endif
/*
* if SetExecutionState isn't supported on this platform, don't do * anything, but still "succeed" */ if (IsBadCodePtr ((FARPROC) SetThreadExecutionState_)) return ((sc = S_FALSE).ToHr());
const DWORD dwValidFlags = ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED; DWORD dwFlags = 0;
/*
* if either dwAdd or dwRemove contain flags we don't recognize * (including ES_CONTINUOUS, which we expect to get in fContinuous) * fail */ if (((dwAdd | dwRemove) & ~dwValidFlags) != 0) return ((sc = E_INVALIDARG).ToHr());
/*
* if we didn't get any flags, fail */ if ((dwAdd == 0) && (dwRemove == 0)) return ((sc = E_INVALIDARG).ToHr());
/*
* make sure we've got our thread counts */ sc = ScCheckPointers (m_spThreadCounts, E_UNEXPECTED); if (sc) return (sc.ToHr());
dwFlags = ES_CONTINUOUS;
/*
* add/remove each individual flag */ for (int i = 0; i < eIndex_Count; i++) { sc = ScPrepExecutionStateFlag (s_rgExecStateFlag[i], // dwTestBit
dwAdd, dwRemove, &dwFlags, &m_Counts.m_rgCount[i], &m_spThreadCounts->m_rgCount[i]); if (sc) return (sc.ToHr()); }
/*
* set the execution state for this thread */ if (!SetThreadExecutionState_(dwFlags)) sc.FromLastError().ToHr();
return (sc.ToHr()); }
/*+-------------------------------------------------------------------------*
* CConsolePower::ResetIdleTimer * * Simple wrapper for SetThreadExecutionState (without ES_CONTINUOUS). *--------------------------------------------------------------------------*/
STDMETHODIMP CConsolePower::ResetIdleTimer (DWORD dwFlags) { DECLARE_SC (sc, _T("CConsolePower::ResetIdleTimer")); #ifdef DBG
/*
* this object is CoCreated so we can't tell what the snap-in name is */ sc.SetSnapinName (_T("<unknown>")); #endif
/*
* if SetExecutionState isn't supported on this platform, don't do * anything, but still "succeed" */ if (IsBadCodePtr ((FARPROC) SetThreadExecutionState_)) return ((sc = S_FALSE).ToHr());
/*
* Set the execution state for this thread. SetThreadExecutionState * will do all parameter validation. */ if (!SetThreadExecutionState_(dwFlags)) sc.FromLastError().ToHr();
return (sc.ToHr()); }
/*+-------------------------------------------------------------------------*
* CConsolePower::OnPowerBroadcast * * WM_POWERBROADCAST handler for CConsolePower. *--------------------------------------------------------------------------*/
LRESULT CConsolePower::OnPowerBroadcast (WPARAM wParam, LPARAM lParam) { /*
* PBT_APMQUERYSUSPEND is the only event that the recipient can * deny. If a snap-in denies (by returning BROADCAST_QUERY_DENY), * there's no need to continue to fire the event to other snap-ins, * so we can break out and return the denial. */ bool fBreakIfDenied = (wParam == PBT_APMQUERYSUSPEND);
int cConnections = m_vec.GetSize();
for (int i = 0; i < cConnections; i++) { CComQIPtr<IConsolePowerSink> spPowerSink = m_vec.GetAt(i);
if (spPowerSink != NULL) { LRESULT lResult = TRUE; HRESULT hr = spPowerSink->OnPowerBroadcast (wParam, lParam, &lResult);
/*
* if the snap-in denied a PBT_APMQUERYSUSPEND, short out here */ if (SUCCEEDED(hr) && fBreakIfDenied && (lResult == BROADCAST_QUERY_DENY)) return (lResult); } }
return (TRUE); }
/*+-------------------------------------------------------------------------*
* CConsolePower::CExecutionCounts::CExecutionCounts * * Constructs a CConsolePower::CExecutionCounts object. *--------------------------------------------------------------------------*/
CConsolePower::CExecutionCounts::CExecutionCounts () { for (int i = 0; i < countof (m_rgCount); i++) m_rgCount[i] = 0; }
/*+-------------------------------------------------------------------------*
* CConsolePower::CTlsExecutionCounts::CTlsExecutionCounts * * Constructs a CConsolePower::CTlsExecutionCounts object. *--------------------------------------------------------------------------*/
CConsolePower::CTlsExecutionCounts::CTlsExecutionCounts () : m_dwTlsIndex (Uninitialized) { }
/*+-------------------------------------------------------------------------*
* CConsolePower::CTlsExecutionCounts::~CTlsExecutionCounts * * Destroys a CConsolePower::CTlsExecutionCounts object. *--------------------------------------------------------------------------*/
CConsolePower::CTlsExecutionCounts::~CTlsExecutionCounts () { if (m_dwTlsIndex != Uninitialized) TlsSetValue (m_dwTlsIndex, NULL); }
/*+-------------------------------------------------------------------------*
* CConsolePower::CTlsExecutionCounts::ScSetThreadInstance * * Accepts a valid TLS index and stores a pointer to this object in the * TLS slot identified by dwTlsIndex. *--------------------------------------------------------------------------*/
SC CConsolePower::CTlsExecutionCounts::ScSetThreadInstance (DWORD dwTlsIndex) { DECLARE_SC (sc, _T("CConsolePower:CTlsExecutionCounts::ScSetThreadInstance"));
/*
* this can only be called once */ ASSERT (m_dwTlsIndex == Uninitialized); if (m_dwTlsIndex != Uninitialized) return (sc = E_UNEXPECTED);
/*
* there shouldn't already be something in this slot */ if (TlsGetValue (dwTlsIndex) != NULL) return (sc = E_UNEXPECTED);
/*
* save a pointer to ourselves in the TLS slot */ if (!TlsSetValue (dwTlsIndex, this)) return (sc.FromLastError());
/*
* save the TLS index */ m_dwTlsIndex = dwTlsIndex;
return (sc); }
/*+-------------------------------------------------------------------------*
* CConsolePower::CTlsExecutionCounts::GetThreadInstance * * *--------------------------------------------------------------------------*/
CConsolePower::CTlsExecutionCounts* CConsolePower::CTlsExecutionCounts::GetThreadInstance (DWORD dwTlsIndex) { return ((CTlsExecutionCounts*) TlsGetValue (dwTlsIndex)); }
/*+-------------------------------------------------------------------------*
* CConsolePowerWnd::CConsolePowerWnd * * Constructs a CConsolePowerWnd object. *--------------------------------------------------------------------------*/
CConsolePowerWnd::CConsolePowerWnd (CConsolePower* pConsolePower) : m_pConsolePower(pConsolePower) { }
/*+-------------------------------------------------------------------------*
* CConsolePowerWnd::~CConsolePowerWnd * * Destroys a CConsolePowerWnd object. *--------------------------------------------------------------------------*/
CConsolePowerWnd::~CConsolePowerWnd () { /*
* the Windows window for this class should never outlive the * C++ class that wraps it. */ if (IsWindow ()) DestroyWindow(); }
/*+-------------------------------------------------------------------------*
* CConsolePowerWnd::ScCreate * * Creates the window for a CConsolePowerWnd object. This window will * handle WM_POWERBROADCAST. *--------------------------------------------------------------------------*/
SC CConsolePowerWnd::ScCreate () { DECLARE_SC (sc, _T("CConsolePowerWnd::ScCreate"));
/*
* create an invisible top-level window (only top-level windows receive * WM_POWERBROADCAST). */ RECT rectEmpty = { 0, 0, 0, 0 };
if (!Create (GetDesktopWindow(), rectEmpty)) return (sc.FromLastError());
return (sc); }
/*+-------------------------------------------------------------------------*
* CConsolePowerWnd::OnPowerBroadcast * * WM_POWERBROADCAST handler for CConsolePowerWnd. *--------------------------------------------------------------------------*/
LRESULT CConsolePowerWnd::OnPowerBroadcast ( UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { /*
* if we aren't connected to a CConsolePower (shouldn't happen), * we can't handle the message */ ASSERT (m_pConsolePower != NULL); if (m_pConsolePower == NULL) { bHandled = false; return (0); }
return (m_pConsolePower->OnPowerBroadcast (wParam, lParam)); }
|