2

Short: How do I, in a service, identify whether a pc is currently not showing an active desktop (i.e. it's either locked or not logged in)?

Long: I'm trying to build a Win 7-10 solution consisting of a DB, a service and a WPF app. When a flag is set in the DB, the service sends a custom message to the user - whether or not one is logged in.
If one is logged in, the service launches the app, which displays the message along with reboot/log off options. This works fine - so far so good.

The problem is displaying a message on the lock screen, if the screen is locked or no user is logged in. I can show the message using "msg.exe * message", but I don't want both msg.exe and the app launching if a user is logged in. Therefore I need to be able to determine which executable is appropriate at the time the flag is read from the DB.
To be clear: I'm not interested in events firing. I'm interested in reading "current state" at a specific time.

NB: There have been many questions sort of similar to this one, but I haven't been able to find one which covers my predicament exactly. Most have to do with lock events, or doesn't work in a service/with no user logged on.

Nathan
  • 59
  • 6
  • I once worked on something were I had to launch apps on the desktop from a service. To do that I had to create an icon in the system tray to get permissions to the desktop. Maybe if you setup a system try icon app to run at start up you can communicate with that to determine if someone is logged it (I assume this is a single sign in situation). – juharr Jul 17 '17 at 12:40
  • It's a single sign in situation, yes. I could probably determine whether someone is signed in that way, but it wouldn't tell me whether they are locked or not. I'll keep it in mind as a possible part solution, but I'd really prefer not to have to add an always-running app on top of the service. I was hoping to keep it as simple as possible, as stability and low footprint is key. It'll be running on 10-30k pc's and will be serving critical info to the users. – Nathan Jul 17 '17 at 12:57
  • If the console session shows as "disconnected" when locked you could enumerate all terminal services sessions and check that they are all in the disconnected state. I don't know if it does or not though. – Scott Chamberlain Jul 17 '17 at 12:58
  • Yeah as far as I can see it remains Active even when locked, unfortunately (on Win 10). I tested this with powershell before posting so I'm fairly certain. – Nathan Jul 18 '17 at 06:10

2 Answers2

0

Call RegisterServiceCtrlHandlerEx in your service, your HandlerEx callback will then receive SERVICE_CONTROL_SESSIONCHANGE notifications and you can track the WTS_SESSION_LOCK and WTS_SESSION_UNLOCK events.

WTSQuerySessionInformation(.., WTSSessionInfoEx, ...) can give you the state for a specific session (WTSINFOEX_LEVEL1.SessionFlags).

Anders
  • 97,548
  • 12
  • 110
  • 164
  • As I mentioned in the OP I'd rather not look at events, only current states. I'll look into WTSQuerySessionInformation and get back to you though, that sounds promising :) – Nathan Jul 18 '17 at 06:15
  • Well the event was designed for use in a service and is easy to implement so I don't see why you can't use it. – Anders Jul 18 '17 at 11:28
  • I can get WTSQuerySessionInformation working fine, but it doesn't give me what I need without WTSSessionInfoEx - and that's more troublesome. I can only find examples in C++ which I have no experience with. I found a post where someone had tried to convert it to C#: https://stackoverflow.com/a/36596656/5992820 ...but I can't get his code to work. I'm not a very strong programmer, so I'm probably in over my head here. I may have to fall back on using the events after all. – Nathan Jul 19 '17 at 06:56
0

I didn't manage to implement WTSSessionInfoEx in C# (I could only find C++ examples and was not able to successfully convert), so I accepted defeat and will monitor events instead.
This answer from Michael Piendl worked: https://stackoverflow.com/a/734037/5992820
Thanks Michael!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
using System.Diagnostics;

namespace MyCode
{
    class MyService : ServiceBase
    {
        public MyService()
        {
            this.CanHandleSessionChangeEvent = true;
        }

        protected override void OnSessionChange(SessionChangeDescription changeDescription)
        {
            switch (changeDescription.Reason)
            {
                case SessionChangeReason.SessionLogon:
                    Debug.WriteLine(changeDescription.SessionId + " logon");
                    break;
                case SessionChangeReason.SessionLogoff:
                    Debug.WriteLine(changeDescription.SessionId + " logoff");
                    break;
                case SessionChangeReason.SessionLock:
                    Debug.WriteLine(changeDescription.SessionId + " lock");
                    break;
                case SessionChangeReason.SessionUnlock:
                    Debug.WriteLine(changeDescription.SessionId + " unlock");
                    break;
            }

            base.OnSessionChange(changeDescription);
        }
    }
}
Nathan
  • 59
  • 6