Query User Sessions in Windows - c#

There is a built-in tool: query.exe that allows you to view all active users and sessions in Windows.
It's output looks like:
PS C:\> query.exe user
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
>testusr console 1 Active none 12/16/2014 9:29 AM
Right now I have code that just parses the query.exe output, but some of us are concerned that future OS upgrades might change query's output so we're looking for an API call that can be used instead.
Is there anyway .Net way to gather the same information?
For alternatives, we don't necessary need all the fields, but we do need: Username, State, and Logon Time. We also need to run this code from the System context so any code or WMI classes that requires the user context won't work for us.

Well what I found and I think could be useful for you is Cassia project.
There is almost ready for you example in main page:
ITerminalServicesManager manager = new TerminalServicesManager();
using (ITerminalServer server = manager.GetLocalServer())
{
server.Open();
foreach (ITerminalServicesSession session in server.GetSessions())
{
Console.WriteLine("Hi there, " + session.UserAccount + " on session " + session.SessionId);
Console.WriteLine("It looks like you logged on at " + session.LoginTime +
" and are now " + session.ConnectionState);
}
}
As you can see when you get a TerminalServicesSession you will find UserName, LoginTime and ConnectionState.
Hope this will help you.

You can simply get the last logon time using WMI:
using System;
using System.Management;
using System.Windows.Forms;
namespace WMISample
{
public class MyWMIQuery
{
public static void Main()
{
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_LogonSession");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("Win32_LogonSession instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("Last Logon: {0}", queryObj["StartTime"]);
}
}
catch (ManagementException e)
{
MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
}
}
}
}

Sounds like you want NetWkstaUserEnum. I couldn't find a native .NET version of this but it seems like a fairly light interop call.

Related

Simple C# WMI Get & Put

I am trying to Read & Put values from and to WMI using C#.
The current example uses ccm namespace, for configmgr client.
The read functions works correctly, able to read ADV_RepeatRunBehavior value.
Though the Put(); doesn't work as expected, the values are not stored back and Invalid Class exception is thrown.
Some advice would be nice as I am new to this, many thanks.
static void Main(string[] args)
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher(
"root\\ccm\\Policy\\Machine",
"SELECT * FROM CCM_SoftwareDistribution WHERE PKG_PackageID='XXXXXXXX'");
foreach (ManagementObject queryObj in searcher.Get())
{
//Read works
//Console.WriteLine(queryObj["ADV_RepeatRunBehavior"].ToString());
//Console.ReadLine();
//Put doesn't
queryObj["ADV_RepeatRunBehavior"] = "RerunNever";
queryObj.Put();
}
}
catch (ManagementException z)
{
Console.WriteLine("An error occurred: " + z.Message);
Console.ReadLine();
}
}
Found the resolution for this.
You have to run Visual Studio as Administrator if testing on localhost
The connection to WMI has to be \\root\\ccm\\Policy\\Machine\\ActualConfig then its possible to Put() values.

How to check in C# if user account is active

How can I check from C# if a local user account (namely the local Administrator account) is active?
What I actually want is a C# replacement for the "Account Active" = "Yes" (or "No") output from the "net user Administrator" command.
I'm afraid this question looks like a duplicate to this one, but I don't know what to pass in for the parameter for the root DirectoryEntry object. Tried different things like "ldap://" + Environment.MachineName, "ldap://127.0.0.1", "WinNT://" + Environment.MachineName, but none of them worked. I get an exception thrown by the searcher.FindAll() call in all three cases.
class Program
{
static void Main(string[] args)
{
// Create the context for the principal object.
PrincipalContext ctx = new PrincipalContext(ContextType.Machine);
UserPrincipal u = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "Administrator");
Console.WriteLine(String.Format("Administrator is enable: {0}", u.Enabled));
}
}
You can query WMI's Win32_UserAccount
This is boilerplate what MS's wmi code creator spits out as a reference;
using System;
using System.Management;
using System.Windows.Forms;
namespace WMISample
{
public class MyWMIQuery
{
public static void Main()
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT Disabled FROM Win32_UserAccount WHERE name = 'alexk'");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("Win32_UserAccount instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("Disabled: {0}", queryObj["Disabled"]);
Console.ReadKey();
}
}
catch (ManagementException e)
{
MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
}
}
}
}
(I'd link the tool but as usual the msdn links are dead)
Try this.
var server = "YOURMACHINENAME";
var username = "Guest";
var de = new DirectoryEntry {Path = "WinNT://" + server + ",computer"};
var result = de.Children
.Cast<DirectoryEntry>()
.First<DirectoryEntry>(d => d.SchemaClassName == "User" && d.Properties["Name"].Value.ToString() == username);
var flags = (int)result.Properties["UserFlags"].Value;
var disabled = (flags & 2) == 2;
This isn't quite the same but they use DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("WinNT://{0}/{1}", computerName, username)); Would that help?
Considering it's a local user, you need to call the win32 api funcion NetGetUserInfo to get what you need.
The example in pinvoke.net is almost what you need, however you need to change the level parameter to 2 to get the neccesary info

finding the actual executable and path associated to a windows service using c#

I am working on an installation program for one of my company's product. The product can be installed multiple times and each installation represents a separate windows service. When users upgrade or reinstall the program, I would like to look up the services running, find the services that belong to the product, and then find the executable file and its path for that service. Then use that information to find which one of the services the user wishes to upgrade/replace/install/etc. In my code example below, I see the service name, description, etc, but don't see the actual filename or path. Could someone please tell me what I'm missing? Thank you in advance!
The code I have is as follows:
ServiceController[] scServices;
scServices = ServiceController.GetServices();
foreach (ServiceController scTemp in scServices)
{
if (scTemp.ServiceName == "ExampleServiceName")
{
Console.WriteLine();
Console.WriteLine(" Service : {0}", scTemp.ServiceName);
Console.WriteLine(" Display name: {0}", scTemp.DisplayName);
ManagementObject wmiService;
wmiService = new ManagementObject("Win32_Service.Name='" + scTemp.ServiceName + "'");
wmiService.Get();
Console.WriteLine(" Start name: {0}", wmiService["StartName"]);
Console.WriteLine(" Description: {0}", wmiService["Description"]);
}
}
I might be wrong but the ServiceController class doesn't provide that information directly.
So as suggested by Gene - you will have to use the registry or WMI.
For an example of how to use the registry, refer to http://www.codeproject.com/Articles/26533/A-ServiceController-Class-that-Contains-the-Path-t
If you decide to use WMI (which I would prefer),
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service");
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject obj in collection)
{
string name = obj["Name"] as string;
string pathName = obj["PathName"] as string;
...
}
You can decide to wrap the properties you need in a class.
the interface has changed since #sidprasher answered, try:
var collection = searcher.Get().Cast<ManagementBaseObject>()
.Where(mbo => mbo.GetPropertyValue("StartMode")!=null)
.Select(mbo => Tuple.Create((string)mbo.GetPropertyValue("Name"), (string)mbo.GetPropertyValue("PathName")));

Can't get names of running applications

I am trying to get name of running applications (mozila, visual studio etc.) I try code bellow but my I get "Access Denied" exception. I don't know how to solve this?
StringBuilder sb = new StringBuilder();
foreach (Process p in Process.GetProcesses("."))
{
try
{
foreach (ProcessModule pm in p.Modules)
{
sb.Append("Image Name:\t" +
pm.ModuleName.ToString()
+ Environment.NewLine);
sb.Append("File Path:\t\t" +
pm.FileName.ToString() +
Environment.NewLine);
sb.Append("Memory Size:\t" +
pm.ModuleMemorySize.ToString() +
Environment.NewLine);
sb.Append("Version:\t\t" +
pm.FileVersionInfo.FileVersion.ToString() +
Environment.NewLine);
sb.Append(Environment.NewLine);
}
}
catch { }
}
Your code is ok, this is privilege problem.
Perhaps you are trying to get this under user account that has no privileges to access this API.
There are group called something like performance counter user, add your application account to admin or performance counter user group.
If you are using the ASP.NET, ASPNET account (configured by default) has no privileges to access this API.
Ah I solved. I changed account type from 'local service' to 'local system'.

How do I retrieve the username that a Windows service is running under?

Given a service name, I would like to retrieve the username that it runs under (i.e. the username shown in the 'Log On' tab of a service's properties window).
There doesn't appear to be anything in the ServiceController class to retrieve this basic information. Nothing else in System.ServiceProcess looks like it exposes this information either.
Is there a managed solution to this, or am I going to have to drop down into something lower-level?
Using WMI, with the System.Management you can try the following code:
using System;
namespace WindowsServiceTest
{
class Program
{
static void Main(string[] args)
{
System.Management.SelectQuery sQuery = new System.Management.SelectQuery(string.Format("select name, startname from Win32_Service")); // where name = '{0}'", "MCShield.exe"));
using (System.Management.ManagementObjectSearcher mgmtSearcher = new System.Management.ManagementObjectSearcher(sQuery))
{
foreach (System.Management.ManagementObject service in mgmtSearcher.Get())
{
string servicelogondetails =
string.Format("Name: {0} , Logon : {1} ", service["Name"].ToString(), service["startname"]).ToString();
Console.WriteLine(servicelogondetails);
}
}
Console.ReadLine();
}
}
}
You can then later substitute the commented code with your service name, and it should only return the instances of your service process that is running.
WMI is your friend. Look at Win32_Service, specifically the StartName property. You can access WMI from C# via the System.Management.ManagementClass.
If you've not used WMI before, this article seems to be quite a good tutorial.
You can find this using the Windows Registry, reading the following string value, replacing [SERVICE_NAME] with the name of the Windows Service:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\[SERVICE_NAME]\ObjectName
Try this:
System.Security.Principal.WindowsIdentity.GetCurrent();
but the most obvious you will get LOCAL SYSTEM or NETWORK. The reason that you cannot show this user - that service can manage multiple users (shared by desktop, attached to current windows session, using shared resource ...)
System starts service, but any user can use it.
This solution works fine for me:
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + this.ServiceName + "'");
wmiService.Get();
string user = wmiService["startname"].ToString();
public String getUsername() {
string username = null;
try {
ManagementScope ms = new ManagementScope("\\\\.\\root\\cimv2");
ms.Connect();
ObjectQuery query = new ObjectQuery
("SELECT * FROM Win32_ComputerSystem");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(ms, query);
foreach (ManagementObject mo in searcher.Get()) {
username = mo["UserName"].ToString();
}
string[] usernameParts = username.Split('\\');
username = usernameParts[usernameParts.Length - 1];
} catch (Exception) {
username = "SYSTEM";
}
return username;
}

Categories