C# GUI starting service not working when "Switch User" is selected from Lock Screen

369 views Asked by At

I'm currently writing a Helpdesk for my work-ed Company. One of the many functions it provides is an easy way to reset a Windows Password using a GUI which is "always available" - when a user has locked their account (win+l), logged off, or simply before any user has logged in (right after Windows boots to the logon Screen).

Most of the functionality works as expected thanks to the ISenseLogon Interface and some code I obtained from the LogonLauncher Open Source project. The GUI is a simple Windows Form launched by a service registered to the ISenseLogon Events (Logon, Logoff, DisplayLock, DisplayUnlock, ShellStart).

The Problem I face is the following: after an account is locked and another user presses the Switch User button on the lock screen, the GUI is closed and the Press ctrl + alt + delete to continue prompt appears, but the GUI does not reopen.

As the code works under all other circumstances (Boot, Logoff, Display Locked all generate the GUI), some other event must be causing the GUI to close (programmatically it is only being killed by a user logging on, unlocking the display or the service shutting down).

Service Class:

public partial class Service1:ServiceBase {
    private const string LpDesktop = @"WinSta0\Winlogon";
    private const string appName = "C:\\LoginAgent.exe";

    public Service1() {
        InitializeComponent();
        Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
        SensLogon.DisplayLock += SensLogon_DisplayLock;
        SensLogon.DisplayUnlock += SensLogon_DisplayUnlock;
        SensLogon.Logon += SensLogon_Logon;
        SensLogon.Logoff += SensLogon_Logoff;
        SensLogon.ShellStart += SensLogon_ShellStart;
    }

    #region Cases
    protected override void OnStart(string[] args) {
        var processEventThread = new Thread(ProcessSensEvent);
        processEventThread.Start("ServiceStart");
    }

    private void SensLogon_Logoff(string userName) {
        var processEventThread = new Thread(ProcessSensEvent);
        processEventThread.Start("Logoff");
    }

    private void SensLogon_Logon(string userName) {
        var processEventThread = new Thread(ProcessSensEvent);
        processEventThread.Start("Logon");
    }

    private void SensLogon_DisplayUnlock(string userName) {
        var processEventThread = new Thread(ProcessSensEvent);
        processEventThread.Start("DisplayUnlock");
    }

    private void SensLogon_DisplayLock(string userName) {
        var processEventThread = new Thread(ProcessSensEvent);
        processEventThread.Start("DisplayLock");
    }

    protected void SensLogon_ShellStart(string userName) {
        var processEventThread = new Thread(ProcessSensEvent);
        processEventThread.Start("ShellStart");
    }

    protected override void OnStop() {
        var processEventThread = new Thread(ProcessSensEvent);
        processEventThread.Start("ServiceStop");
    }
    #endregion

    //Starts instance of the InjectProcess class, which starts or kills the LoginAgent process.
    private void ProcessSensEvent(object e) {
        var ip = new InjectProcess();

        if(e.ToString().Equals("Logoff") || e.ToString().Equals("ServiceStart") || e.ToString().Equals("DisplayLock") || e.ToString().Equals("ShellStart")) {
            ip.Inject(LpDesktop, appName);
            
        }
        else if(e.ToString().Equals("Logon") || e.ToString().Equals("DisplayUnlock") || e.ToString().Equals("ServiceStop"))
            ip.TerminateSystemProcess(appName);
    }
}

SensLogon:

class SensLogon {
    private static SensLogonInterop _eventCatcher;

    static SensLogon() {}

    #region Event Registers
    private static int _registerCount = 0;
    private static bool IsRegistered {
        get {
            return (_registerCount > 0);
        }
    }

    private static SensLogonEventHandler RegisterEvent(SensLogonEventHandler original, SensLogonEventHandler newDel) {
        bool shouldRegister = (original == null);
        original = original + newDel;

        if(shouldRegister) {
            if(_registerCount <= 0) {
                if(_eventCatcher == null)
                    _eventCatcher = new SensLogonInterop();
                _registerCount = 1;
            }
            else
                _registerCount++;
        }
        return original;
    }

    private static SensLogonEventHandler UnregisterEvent(SensLogonEventHandler original, SensLogonEventHandler oldDel) {
        original = original - oldDel;

        if(original == null) {
            _registerCount--;

            if(_registerCount == 0) {
                _eventCatcher.Dispose();
                _eventCatcher = null;
            }
        }
        return original;
    }
    #endregion

    #region ISensLogon Event creation
    public static void OnDisplayLock(string bstrUserName) {
        if(displayLock != null)
            displayLock(bstrUserName);
    }
    public static void OnDisplayUnlock(string bstrUserName) {
        if(displayUnlock != null)
            displayUnlock(bstrUserName);
    }
    public static void OnLogon(string bstrUserName) {
        if(logon != null)
            logon(bstrUserName);
    }
    public static void OnLogoff(string bstrUserName) {
        if(logoff != null)
            logoff(bstrUserName);
    }
    public static void OnShellStart(string bstrUserName) {
        if(shellStart != null)
            OnShellStart(bstrUserName);
    }
    #endregion

    #region Event declarations
    private static SensLogonEventHandler displayLock = null;
    private static SensLogonEventHandler displayUnlock = null;
    private static SensLogonEventHandler logon = null;
    private static SensLogonEventHandler logoff = null;
    private static SensLogonEventHandler shellStart = null;

    public static event SensLogonEventHandler DisplayLock {
        add {
            displayLock = RegisterEvent(displayLock, value);
        }
        remove {
            displayLock = UnregisterEvent(displayLock, value);
        }
    }

    public static event SensLogonEventHandler DisplayUnlock {
        add {
            displayUnlock = RegisterEvent(displayUnlock, value);
        }
        remove {
            displayUnlock = UnregisterEvent(displayUnlock, value);
        }
    }

    public static event SensLogonEventHandler Logon {
        add {
            logon = RegisterEvent(logon, value);
        }
        remove {
            logon = UnregisterEvent(logon, value);
        }
    }

    public static event SensLogonEventHandler Logoff {
        add {
            logoff = RegisterEvent(logoff, value);
        }
        remove {
            logoff = UnregisterEvent(logoff, value);
        }
    }

    public static event SensLogonEventHandler ShellStart {
        add {
            shellStart = RegisterEvent(logoff, value);
        }
        remove {
            shellStart = UnregisterEvent(logoff, value);
        }
    }
    #endregion
}

Any ideas how I can prevent the GUI from closing, or reopen it after the Switch User Button is clicked?

If further code is required (EventSystemRegistrar, SensLogonInterop classes) I will gladly attach it, but as everything is working up to the switch user case, I doubt it is needed. Cheers; and sorry for the long post, the issue is somewhat out of my league!

1

There are 1 answers

0
Praetorian On BEST ANSWER

Found a Workaround, using the Standard

CanHandleSessionChangeEvent = true

and overriding the default Session Events handling function. I recommend using these instead of the ISensLogon Interface as they require a great deal less code. A good example can be found in the official Microsoft documentation: SessionChangeDescription Structure.

I hope someone finds this helpful, I spent hours trying to get the above code working!