ZerOne
ZerOne

Reputation: 1326

Windows service: Get username when user log on

my windows service should save the name of the user, which logon/logoff at the moment. The following code works for me but didn't save the username:

protected override void OnSessionChange(SessionChangeDescription changeDescription)
    {
        try
        {
            string user = "";

            foreach (ManagementObject currentObject in _wmiComputerSystem.GetInstances())
            {
                user += currentObject.Properties["UserName"].Value.ToString().Trim();
            }

            switch (changeDescription.Reason)
            {
                case SessionChangeReason.SessionLogon:
                    WriteLog(Constants.LogType.CONTINUE, "Logon - Program continues: " + user);
                    OnContinue();
                    break;
                case SessionChangeReason.SessionLogoff:
                    WriteLog(Constants.LogType.PAUSE, "Logoff - Program is paused: " + user);
                    OnPause();
                    break;
            }
            base.OnSessionChange(changeDescription);
        }
        catch (Exception exp)
        {
            WriteLog(Constants.LogType.ERROR, "Error");
        }
    }

edit: The foreach loop gives me an error:

Message: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) Type: System.UnauthorizedAccessException

But in my opinion, this code is not the solution, because it saves all users, which are logged onto the server.

Upvotes: 1

Views: 4209

Answers (3)

Soma Mbadiwe
Soma Mbadiwe

Reputation: 1674

I ran into a similar problem while building a Windows Service. Just like you, I had the Session ID and needed to get the corresponding username. After several unsuccessful solution hereon SO, I ran into this particular answer and it inspired my solution:

Here's my code (all of them residing inside a class; in my case, the class inheriting ServiceBase).

    [DllImport("Wtsapi32.dll")]
    private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WtsInfoClass wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
    [DllImport("Wtsapi32.dll")]
    private static extern void WTSFreeMemory(IntPtr pointer);

    private enum WtsInfoClass
    {
        WTSUserName = 5, 
        WTSDomainName = 7,
    }

    private static string GetUsername(int sessionId, bool prependDomain = true)
    {
        IntPtr buffer;
        int strLen;
        string username = "SYSTEM";
        if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSUserName, out buffer, out strLen) && strLen > 1)
        {
            username = Marshal.PtrToStringAnsi(buffer);
            WTSFreeMemory(buffer);
            if (prependDomain)
            {
                if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
                {
                    username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
                    WTSFreeMemory(buffer);
                }
            }
        }
        return username;
    }

With the above code in your class, you can simply get the username in the method you're overriding by calling

string username = GetUsername(changeDescription.SessionId);

Upvotes: 5

ZerOne
ZerOne

Reputation: 1326

Finally I got a solution. In the windows service method, there is the session id provided. So with this session id we can execute a powershell command 'quser' and get the current user, who login/logoff on the server. Seen here: How to get current windows username from windows service in multiuser environment using .NET

So this is the function, which we need to create:

private string GetUsername(int sessionID)
        {
            try
            {
                Runspace runspace = RunspaceFactory.CreateRunspace();
                runspace.Open();

                Pipeline pipeline = runspace.CreatePipeline();
                pipeline.Commands.AddScript("Quser");
                pipeline.Commands.Add("Out-String");

                Collection<PSObject> results = pipeline.Invoke();

                runspace.Close();

                StringBuilder stringBuilder = new StringBuilder();
                foreach (PSObject obj in results)
                {
                    stringBuilder.AppendLine(obj.ToString());
                }

                foreach (string User in stringBuilder.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Skip(1))
                {
                    string[] UserAttributes = User.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                    if (UserAttributes.Length == 6)
                    {
                        if (int.Parse(UserAttributes[1].Trim()) == sessionID)
                        {
                            return UserAttributes[0].Replace(">", string.Empty).Trim();
                        }
                    }
                    else
                    {
                        if (int.Parse(UserAttributes[2].Trim()) == sessionID)
                        {
                            return UserAttributes[0].Replace(">", string.Empty).Trim();
                        }
                    }
                }

            }
            catch (Exception exp)
            {
                // Error handling
            }

            return "Undefined";
        } 

And this is the windows service function:

protected override void OnSessionChange(SessionChangeDescription changeDescription)
        {
            try
            {
                switch (changeDescription.Reason)
                {
                    case SessionChangeReason.SessionLogon:
                        string user = GetUsername(changeDescription.SessionId);

                        WriteLog("Logon - Program continue" + Environment.NewLine + 
                            "User: " + user + Environment.NewLine + "Sessionid: " + changeDescription.SessionId);

                        //.....

Upvotes: 0

SteveFerg
SteveFerg

Reputation: 3572

You could try:

System.Security.Principal.WindowsIdentity.GetCurrent();

another option, see: Getting logged-on username from a service

Upvotes: -2

Related Questions