none
Getting the wrong process output - System.Diagnostics.Process RRS feed

  • Question

  • Hi, I'm implementing a Windows service using C# which launch the whoami command to retrieve the user logged on the machine. 

    Process process = new Process();
    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = @"/C whoami";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.CreateNoWindow = true;
    process.Start();
    string hostAndUser = process.StandardOutput.ReadToEnd();
    process.WaitForExit();
    process.Close();
    //do something with the string hostAndUser
    //...
    //...


    The process starts with a specific user which is the admin of the machine in which the service is executing (in the myservice Log-On section of services.msc there is .\myuser).

    But the output is: mymachine\mymachine instead of mymachine\myuser (myuser is the currently logged user on mymachine). 

     I want something that if user1 is logged into  machineX, I want machineX\user1. If another one is logged, I want machineX\anotheruser. How I can get this? If whoami is wrong, which command I can use? The service run under a particular account which is the admin of the machineX

    Thank you.


    Monday, January 20, 2020 10:25 AM

All replies

  • Sorry, I can't tell you how to get the logged-in user(s), but at least I can explain why what you are doing doesn't work as you expect.

    Whoami does not return the user who is logged into the machine. There could be multiple users logged-in in Terminal Services mode, or with "switch user" -- also there could be zero users logged in and the Service would still run.

    Instead, whoami shows the credentials under which the calling code is running. You can verify this by invoking whoami from inside a RUNAS command.

    In the case of a Service, these are the credentials that are written into the Service Properties during installation of the service. They can be changed from the Services applet in Control Panel. If said credentials happen to be for a service account such as "network service", then they are seen as the machine account by any program that executes remotely. That's why you get mymachine\mymachine.

    Monday, January 20, 2020 3:42 PM
    Moderator
  • "whoami command to retrieve the user logged on the machine. "

    You cannot get the correct information from that. There can be multiple people logged into a machine at once. Running that command will only ever return 1 person and that is who ran the command, nothing more. In the case of a service you'd get the account under which the service is running.

    To get the list of logged in users there are a couple of approaches.

    NetUserEnum gets you the list of all users and when called with the correct info level you can get the last login/logout time. This API works across all versions of Windows. If you are comforable using WMI then you can use Win32_NetworkLoginProfile to get the same information more easily.

    However if you are comfortable with WMI then look at the Win32_LoggedOnUser class as this only contains the logged on users. MS has a sample of getting all logged on users here

    Note that for all of these it would include user accounts that are being used to run services, IIS processes and scheduled tasks. To filter these out you'd need to take a closer look at the user information. For example the logon type would specify whether this is a service or interactive account. 


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, January 20, 2020 4:41 PM
    Moderator
  • Sorry, I can't tell you how to get the logged-in user(s), but at least I can explain why what you are doing doesn't work as you expect.

    Whoami does not return the user who is logged into the machine. There could be multiple users logged-in in Terminal Services mode, or with "switch user" -- also there could be zero users logged in and the Service would still run.

    Instead, whoami shows the credentials under which the calling code is running. You can verify this by invoking whoami from inside a RUNAS command.

    In the case of a Service, these are the credentials that are written into the Service Properties during installation of the service. They can be changed from the Services applet in Control Panel. If said credentials happen to be for a service account such as "network service", then they are seen as the machine account by any program that executes remotely. That's why you get mymachine\mymachine.

    So, which is the syntax of this command to insert in 
    process.StartInfo.Arguments
    ?
    Tuesday, January 21, 2020 8:50 AM
  • Hello,

    >Why I'm getting the wrong whoami output?

    Think a bit - service supposed to run even you are not logged in.

    Your process in the service run under different accaunt and by running whoiam from service you getting accaunt from which it running. 



    Sincerely, Highly skilled coding monkey.

    Tuesday, January 21, 2020 9:08 AM
  • Hello,

    >Why I'm getting the wrong whoami output?

    Think a bit - service supposed to run even you are not logged in.

    Your process in the service run under different accaunt and by running whoiam from service you getting accaunt from which it running. 



    Sincerely, Highly skilled coding monkey.

    This is what I want. I want something that if user1 is logged into  machineX, I want machineX\user1. If another one is logged, I want machineX\anotheruser. How I can get this? If whoami is wrong, which command I can use? The service run under a particular account which is the admin of the machineX
    Tuesday, January 21, 2020 9:25 AM
  • >How I can get this?

    Your initial question was - Why I'm getting the wrong whoami output?

    Explanation was provided.

    What you want looks slightly different and need time to look how to get user cardentials for running apps. Most likely - yes, it possible - take a Task Manager for example. How - it's a bit different question. I think you need to qualify what you want to do and WHERE you want to do before start doing this.

    Sincerely, Highly skilled coding monkey.

    Tuesday, January 21, 2020 10:01 AM
  • >How I can get this?

    Your initial question was - Why I'm getting the wrong whoami output?

    Explanation was provided.

    What you want looks slightly different and need time to look how to get user cardentials for running apps. Most likely - yes, it possible - take a Task Manager for example. How - it's a bit different question. I think you need to qualify what you want to do and WHERE you want to do before start doing this.

    Sincerely, Highly skilled coding monkey.

    QUESTION MODIFIED
    Tuesday, January 21, 2020 11:05 AM
  • The following sample console application will enumerate logged-on interactive users.  The same code can be run from within a windows service.  Of course, you would not attempt to write console output from within a service.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Diagnostics;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                IntPtr sessioninfo = IntPtr.Zero;
                int count = 0;
    
                int ret = WTS.WTSEnumerateSessions(WTS.WTS_CURRENT_SERVER, 0, 1, ref sessioninfo, ref count);
                if(ret != 0)
                {
                    for(int i = 0; i < count; i++)
                    {
                        IntPtr p = sessioninfo + i * Marshal.SizeOf<WTS.WTS_SESSION_INFO>();
    
                        WTS.WTS_SESSION_INFO si = Marshal.PtrToStructure<WTS.WTS_SESSION_INFO>(p);
                        if(si.SessionId != 0 && (si.State == WTS.WTS_CONNECTSTATE_CLASS.WTSActive || si.State == WTS.WTS_CONNECTSTATE_CLASS.WTSDisconnected))
                        {
                            int cBytes = 0;
                            IntPtr isess = IntPtr.Zero;
                            ret = WTS.WTSQuerySessionInformation(WTS.WTS_CURRENT_SERVER, si.SessionId, WTS.WTS_INFO_CLASS.WTSSessionInfo, ref isess, ref cBytes);
                            if (ret != 0)
                            {
                                WTS.WTSINFO sinfo = Marshal.PtrToStructure<WTS.WTSINFO>(isess);
                                Console.WriteLine("Interactive session id {0}, Session user is {1}, domain is {2}", si.SessionId, sinfo.UserName, sinfo.Domain);
                                WTS.WTSFreeMemory(isess);
                            }
                            else
                            {
                                Console.WriteLine("WTSQuerySessionInformation error was {0}", new Win32Exception(Marshal.GetLastWin32Error()).Message);
                            }
                        }
                    }
    
                    WTS.WTSFreeMemory(sessioninfo);
                }
                else
                {
                    Console.WriteLine("WTSEnumerateSessions error was {0}", new Win32Exception(Marshal.GetLastWin32Error()).Message);
                }
            }
        }
    
        public class WTS
        {
            public static IntPtr WTS_CURRENT_SERVER = IntPtr.Zero;
    
            public enum WTS_CONNECTSTATE_CLASS
            {
                WTSActive,              // User logged on to WinStation
                WTSConnected,           // WinStation connected to client
                WTSConnectQuery,        // In the process of connecting to client
                WTSShadow,              // Shadowing another WinStation
                WTSDisconnected,        // WinStation logged on without client
                WTSIdle,                // Waiting for client to connect
                WTSListen,              // WinStation is listening for connection
                WTSReset,               // WinStation is being reset
                WTSDown,                // WinStation is down due to error
                WTSInit                 // WinStation in initialization
            }
    
            public enum WTS_INFO_CLASS
            {
                WTSInitialProgram,
                WTSApplicationName,
                WTSWorkingDirectory,
                WTSOEMId,
                WTSSessionId,
                WTSUserName,
                WTSWinStationName,
                WTSDomainName,
                WTSConnectState,
                WTSClientBuildNumber,
                WTSClientName,
                WTSClientDirectory,
                WTSClientProductId,
                WTSClientHardwareId,
                WTSClientAddress,
                WTSClientDisplay,
                WTSClientProtocolType,
                WTSIdleTime,
                WTSLogonTime,
                WTSIncomingBytes,
                WTSOutgoingBytes,
                WTSIncomingFrames,
                WTSOutgoingFrames,
                WTSClientInfo,
                WTSSessionInfo,
                WTSSessionInfoEx,
                WTSConfigInfo,
                WTSValidationInfo,   // Info Class value used to fetch Validation Information through the WTSQuerySessionInformation
                WTSSessionAddressV4,
                WTSIsRemoteSession
            }
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            public struct WTS_SESSION_INFO
            {
                public int SessionId;
                [MarshalAs(UnmanagedType.LPWStr)]
                public string pWinStationName;
                public WTS_CONNECTSTATE_CLASS State;
            }
    
            [StructLayout(LayoutKind.Explicit)]
            public struct LARGE_INTEGER
            {
                [FieldOffset(0)]
                public int Low;
                [FieldOffset(4)]
                public int High;
                [FieldOffset(0)]
                public long QuadPart;
            }
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            public struct WTSINFO
            {
                public WTS_CONNECTSTATE_CLASS State; // connection state (see enum)
                public int  SessionId;             // session id
                public int  IncomingBytes;
                public int  OutgoingBytes;
                public int  IncomingFrames;
                public int  OutgoingFrames;
                public int  IncomingCompressedBytes;
                public int  OutgoingCompressedBytes;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
                public string WinStationName;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
                public string Domain;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
                public string UserName;
                public LARGE_INTEGER ConnectTime;
                public LARGE_INTEGER DisconnectTime;
                public LARGE_INTEGER LastInputTime;
                public LARGE_INTEGER LogonTime;
                public LARGE_INTEGER CurrentTime;
            }
    
    
            [DllImport("wtsapi32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern int WTSEnumerateSessions(IntPtr handle, int Reserved, int Version, ref IntPtr ppSessionInfo, ref int pCount);
    
            [DllImport("wtsapi32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
            public static extern void WTSFreeMemory(IntPtr pMemory);
    
            [DllImport("wtsapi32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern int WTSQuerySessionInformation(IntPtr handle, int SessionId, WTS_INFO_CLASS WTSInfoClass, ref IntPtr buffer, ref int BytesReturned);
    
    
        }
    }
    

    Tuesday, January 21, 2020 3:48 PM