I am trying to find a way to get a list of Windows sessions? I need the same information as the one displayed in the Task Manager on the User tab. I need to know if the user is active or not and if s/he is logged on in the Remote Desktop session.
Any idea on how to do that with C# / Windows XP Pro?
As a starting point you can get a list of users logged on by running the command
qwinsta
From the command prompt.
This will give output like
C:\WINDOWS\system32>qwinsta
SESSIONNAME USERNAME ID STATE TYPE DEVICE
>console me 0 Active wdcon
rdp-tcp 65536 Listen rdpwd
and will list any remote sessions.
Use LsaEnumerateLogonSessions via P/Invoke. You'll also need LsaFreeReturnBuffer to cleanup after enumerating.
I believe you'll need to use P/Invoke to retrieve this information.
The relevant APIs are documented in this MSDN page.
Another way is to use the Logonsessions utility from Sysinternals:
http://technet.microsoft.com/en-us/sysinternals/bb896769.aspx
You do not need to use Pinvoke. WMI does it, and well: "select Name, SessionId from Win32_Process" in the root\cimv2 namespace. And, it can be called from a remote machine. Simpler. Add in a where clause in the select to fine tune what you get back.
Related
I have a messy collection of Windows platforms in various labs. Everything from XP to the latest. I have a simple C#/.NET app that runs at user login and logout. I need to differentiate between true Console logins and RDP sessions. I am using
System.Diagnostics.Process.GetCurrentProcess().SessionId
to get a sessionId but I don't see anything that says "you are on the Console" or "you are in an RDP session". Is there such a thing?
For my needs it turns out that the windows environment variable %sessionname% holds the info I am looking for. In my C# code I can say:
string sessionName = Environment.GetEnvironmentVariable("SESSIONNAME")
?? "CONSOLE";
%sessionname% seems to hold either the string "console" or "rdp-(something)#(number)", ex "rdt-tcp#1234". I don't know the whole range of possible values but it looks like it will work for my needs.
You can determine which session ID (if any) is currently attached to the physical console by calling the WTSGetActiveConsoleSessionId() function.
I am currently writing a C# service to basically make sure a group of exes are running and if they aren't then relaunch them.
The service starts up automatically so I run in to the problem that it attempts to launch the exe when no one is logged in. I have a solution that works for non-VM systems as shown below:
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem");
ManagementObjectCollection collection = searcher.Get();
string loggedIn = (string)collection.Cast<ManagementBaseObject>().First()["UserName"];
I then check this against null or whitespace and then just don't check the process list if no user is logged in.
However, while running on a VM this section of code returns null when a user is logged in. So I need a way of checking for a logged in user on both a standard machine and VM. I also need it to work for XP and windows 7, and both 32bit and 64bit. I am using .Net 4.
Official documentation to list sessions prescribes to use LsaLogOnSessions to enumerate them, refer to MSDN or to this CodeProject article for more examples.
That said if you just need to be sure there is at least one logged in user then you can simply check for default shell process. Default shell will always be loaded for each session:
bool loggedInUserExist = Process.GetProcessesByName("explorer").Any();
In this example I hard-coded value but you must read it from string value Shell in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon\, in case default shell has been overridden (or for Server Core installation when GUI isn't present).
If you don't need anything else then you're done. If you have to filter specific users then you have to get process' owner. It's not such easy in C# unless you use WMI to query Win32_Process, search for explorer in Name property and then call GetOwner() method of the ManagementObject (see this post here on SO). Note that you may use same query both to determine if there is any logged in user and who he is. As alternative you may P/Invoke for Windows API functions as described in this post (but in this case I'd go back to LsaLogOnSessions).
I've written a win forms application that uses Attachmate EXTRA. The application itself works great but I was just notified that there is a need to be able to switch to different device names for the sessions. The names would be set before the sessions are launched and then the session would go through the connection process. The only way I can think to do this is create and save a separate session with each device name needed and go that route but I would like to be able to do this dynamically instead if possible.
I've tried doing this during debug to look at the object but have been unable to find a method or property to set.
After having a support ticket entered and getting confirmation, the only way to change the device name programatically is to open the .EDP file, replace the string with the new device name, save the file and then run that .EDP file.
Apparently their new application, Reflection, can handle these types of things with calls without making changes to the files.
I am trying to achive a situation:
I am logged as a user on my account.
I've created a two more accounts which I will try to log on in the
"background"
And now I want to execute my code to log on different user in the background,
so that if i can click ctrl+alt+del and go to the switch user screen
i can see that another account was logged on the system.
During investigation I've read that this was possbile with some undocumented winapi methods before Microsoft implemented Fast User Switch(FUS) which replaced GINA.
I don't want to Impersonate code as user. I want to start up the whole windows session for a user from code.
I've tried to do sth with CreateProcessWithLogonW() but this was unsuccessful.
Big virtual beer for anybody who can give me a hand with this! :)
Okay had to do a bit of digging to figure this out my self.
Essentially you would need to run a command in command prompt.
This should load explorer.exe as a second user.
runas /user:*computer name\*account name explorer.exe
Follow this to get an idea on running a command line command via c#:
How To: Execute command line in C#, get STD OUT results
Other sources:
http://www.computerhope.com/runas.htm
http://lifehacker.com/290435/switch-user-accounts-from-the-command-prompt
My code needs to determine how long a particular process has been running. But it continues to fail with an access denied error message on the Process.StartTime request. This is a process running with a User's credentials (ie, not a high-privilege process). There's clearly a security setting or a policy setting, or something that I need to twiddle with to fix this, as I can't believe the StartTime property is in the Framework just so that it can fail 100% of the time.
A Google search indicated that I could resolve this by adding the user whose credentials the querying code is running under to the "Performance Log Users" group. However, no such user group exists on this machine.
I've read something similar to what you said in the past, Lars. Unfortunately, I'm somewhat restricted with what I can do with the machine in question (in other words, I can't go creating user groups willy-nilly: it's a server, not just some random PC).
Thanks for the answers, Will and Lars. Unfortunately, they didn't solve my problem.
Ultimate solution to this is to use WMI:
using System.Management;
String queryString = "select CreationDate from Win32_Process where ProcessId='" + ProcessId + "'";
SelectQuery query = new SelectQuery(queryString);
ManagementScope scope = new System.Management.ManagementScope(#"\\.\root\CIMV2");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection processes = searcher.Get();
//... snip ... logic to figure out which of the processes in the collection is the right one goes here
DateTime startTime = ManagementDateTimeConverter.ToDateTime(processes[0]["CreationDate"].ToString());
TimeSpan uptime = DateTime.Now.Subtract(startTime);
Parts of this were scraped from Code Project:
http://www.codeproject.com/KB/system/win32processusingwmi.aspx
And "Hey, Scripting Guy!":
http://www.microsoft.com/technet/scriptcenter/resources/qanda/jul05/hey0720.mspx
Process of .Net 1.1 uses the Performance Counters to get the information. Either they are disabled or the user does not have administrative rights. Making sure the Performance Counters are enabled and the user is an administrator should make your code work.
Actually the "Performance Counter Users Group" should enough. The group doesn't exist by default. So you should create it yourself.
Process of .Net 2.0 is not depended on the Performance Counters.
See http://weblogs.asp.net/nunitaddin/archive/2004/11/21/267559.aspx
The underlying code needs to be able to call OpenProcess, for which you may require SeDebugPrivilege.
Is the process you're doing the StartTime request on running as a different user to your own process?
OK, sorry that didn't work... I am no expert on ASP.NET impersonation, I tend to use app pools which I don't think you can do on W2K Have you tried writing a tiny little test app which does the same query, and then running that as various users?
I am reluctant to post a chunk of MS framework code here, but you could use either Reflector or this: http://www.codeplex.com/NetMassDownloader to get the source code for the relevant bits of the framework so that you could try implementing various bits to see where it fails.
Can you get any other info about the process without getting Access Denied?
I can enumerate the process (ie, the GetProcessById function works), and we have other code that gets the EXE name and other bits of information.
I will give the test app a try. I'm also going to attempt to use WMI to get this information if I can't get the C# implementation working properly in short order (this is not critical functionality, so I can't spend days on it).