My program needs to do X when someone 'starts using' their Windows user
account, and it should do Y when they 'stop using' their Windows user
account. By 'starts using' I mean they log on, unlock the desktop, resume
from hibernate/sleep, or resume a session that was paused via Switch User. By
'stop using' I mean they lock the desktop, initiate a hibernate/sleep
, or choose Switch User while logged on. For context, the program is a
parental control application that helps parents limit the total time their
kids use the computer. There is a Windows service that does the actual time
keeping. The program I'm struggling with is the reminder program that gives
the child audible reminders like "5 minutes left". It should tell them how
much time is left when they start using Windows, and it should be quiet when
they take a break.
This sounds like a simple task, but I can't get it to work reliably.
My current approach is to put the program in the child's StartUp folder.
When the application starts, it does X. It then listens to the events from
SystemEvents.SessionSwitch and SystemEvents.PowerModeChanged and does X if it
detects a start event like Unlock and does Y if it detects a stop event like
Suspend.
The first problem is that if you log on and then immediately Lock the
computer or do a Switch User, then by the time my application has fully
loaded it may be running in the context of a locked workstation. So it
shouldn't automatically do X (tell the child how much time is left) when the
application starts. Somehow my program needs to know if it is running in the
context of a locked or switched out Windows session. I don't know how to
check for that.
The second problem is that interpreting the SystemEvents.SessionSwitch and
SystemEvents.PowerModeChanged events is tricky. For example, if Windows is
configured to require a password after resuming from sleep, then doing a
hibernate followed by resume leads to these events:
Suspend->Lock->Resume->Unlock. But if Windows is not configured to require a
password after resuming from sleep, then these events are generated:
Suspend->Resume. Therefore I can't simply look to the Resume event to tell me
if the user has started using Windows again. Likewise if you do a Switch user
and then resume, these events are generated: Disconnect->Connect. But if you
do a lock, switch out, switch in, these events are generated:
Lock->Disconnect->Connect->Unlock. Therefore I can't simply look to the
Connect event to tell me if the user has started using Windows again.
Here's my algorithm:
I initialize a variable called "Depth" to 0.
Lock, Disconnect, Suspend all decrement "Depth" by 1.
Unlock, Connect, Resume all increment "Depth" by 1.
When "Depth" changes to 0, that means the user has started using Windows.
When "Depth" changes to -1, that means the user has stopped using Windows.
This works most of the time but if the users session is switched out soon
after they log on and before my application has fully loaded and subscribed
to the events, then "Depth" should not really be initialized to 0. It should
start at -1 or -2. And since I can't properly detect the initial state, I
can't properly interpret all future SessionSwitch and PowerModeChanged events.
How can I reliably do X when someone starts using Windows, and do Y when
they stop?
How can I know whether my program is running in the context of a locked or
switched out user session?