i try to display all groups a special User is in.
I also know, that i could do it like this:
public static List<Principal> getUsers(){
PrincipalContext context = new PrincipalContext(ContextType.Machine, "computername");
PrincipalSearcher search = new PrincipalSearcher(new UserPrincipal(context));
return search.FindAll().ToList();
}
But i want to work arount PrincipalContext because i need to Use this remotely on a PC, wich is in no Domain. So i tried this:
public static void findUsers()
{
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Group WHERE LocalAccount.Name =\'Test'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var result = searcher.Get();
foreach (var envVar in result)
{
Console.WriteLine("GroupName: {0}", envVar["Name"]);
}
Console.ReadLine();
}
It gives me an Exception because the query isnĀ“t correct.
Thanks alot for any kind of help.
#Edper your tips were very nice but i used another way to solve my problem.
the mission was to just enter a username and an IP of a remote-Server and u get all Groups this local user is in.
class Program
{
static ManagementScope scope =
new ManagementScope(
"\\\\ServerIP\\root\\cimv2");
static string username = "Test";
static void Main(string[] args)
{
string partComponent = "Win32_UserAccount.Domain='Domain',Name='"+username+"'";
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_GroupUser WHERE PartComponent = \"" + partComponent + "\"");
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
var result = searcher.Get();
foreach (var envVar in result)
{
ManagementObject groupComponent = new ManagementObject("\\\\ServerIP\\root\\cimv2", envVar["GroupComponent"].ToString(), null);
Console.WriteLine(groupComponent["Name"]);
}
}
Console.ReadLine();
}
}
of course this is not done jet(GUI in progress) but it does all i want for now.
if you want to test it you need to make a local user on the remote PC that has got the same username and Password as the User u run the Code with.(and this user needs admin rights)
There is no LocalAccount.Name field instead just use simply Name and remove also \, so that it would look like: (I used 'Guests' as my example not 'Test'
public static void findUsers()
{
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Group WHERE Name = 'Guests'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var result = searcher.Get();
foreach (var envVar in result)
{
Console.WriteLine("GroupName: {0}", envVar["Name"]);
}
Console.ReadLine();
}
Related
I'm building a C# application to monitor server and workstation workloads with WMI and WQL queries. I'm using WMI because it seems to be faster in comparison to powershell queries. My hardship starts when I try to retrieve logged on users on a remote machine. I figured I need to use the Win32_LoggedOnUser class. I have tried the following queries:
#"SELECT * FROM Win32_LoggedOnUser"
#"SELECT Antecedent FROM Win32_LoggedOnUser"
What I'm used to is to retrieve the desired value like this:
var cims = connection.getCimInstances(this, queryUser);
if (cims != null)
{
foreach (CimInstance cim in cims)
{
Komponenten.User user = new Komponenten.User();
user.Name = Convert.ToString(cim.CimInstanceProperties["Name"].Value);
users.Add(user);
}
}
where queryUser is one of the strings from above.
In both cases, I get a Win32_Account object in return, which seems to suggest - and the debugger seems to confirm - that I should use CimInstanceProperties["Name"].Value on the returned Win32_Account class again. But that's not working at all. Any ideas on how to get access to the CimInstanceProperties of a Win32_Account stored in a CimInstanceProperity ? I can't find anything on the respective Windows reference page (https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-loggedonuser) nor during my extensive google-search.
Thanks!
I ended up using the ManagementObject-Class and Regex to find the usernames after converting the Antecedent - Object to a string:
var users = new List<Komponenten.User>();
var searcher = this.connection.makeQuery(this, "SELECT * FROM Win32_LoggedOnUser");
if (searcher != null)
{
foreach (ManagementObject queryObj in searcher.Get())
{
Komponenten.User user = new Komponenten.User();
var win32_account = queryObj["Antecedent"].ToString();
string stripped = Regex.Replace(win32_account, "[^a-zA-Z=]+", "", RegexOptions.Compiled);
int end = stripped.LastIndexOf("=");
user.Name = stripped.Substring(end+1);
users.Add(user);
}
this.users = users;
An alternative which takes the LogonSession into account is:
var users = new List<Komponenten.User>();
var searcher = this.connection.makeQuery(this, "SELECT LogonId FROM Win32_LogonSession Where LogonType=2");
var Scope = this.connection.getScope(this, this.connection.getConnection());
if (searcher != null)
{
foreach (ManagementObject queryObj in searcher.Get())
{
ObjectQuery LQuery = new ObjectQuery("Associators of {Win32_LogonSession.LogonId=" + queryObj["LogonId"] + "} Where AssocClass=Win32_LoggedOnUser Role=Dependent");
ManagementObjectSearcher LSearcher = new ManagementObjectSearcher(Scope, LQuery);
foreach (ManagementObject LWmiObject in LSearcher.Get())
{
Komponenten.User user = new Komponenten.User();
user.Name = Convert.ToString(LWmiObject["Name"]);
users.Add(user);
}
}
this.users = users;
}
where this.connection.getConnection is a ConnectionsOption object depending on your respective domain and account data
I'm trying to identify if a certain service on a remote PC is running or not and identify its start up type.
Using ServiceController I can successfully see the expected service on the remote machine but when switching to use WMI to drill deeper this service no longer appears.
Heres my code:
public static void Main()
{
var ctl = ServiceController.GetServices("[Name]");
List<string> namelist = new List<string>();
foreach (var x in ctl)
{
if (x.DisplayName == "NHS Card Checker")
{
Console.WriteLine(string.Format("NHS Card checker found on MPC - Status: {0}", x.Status));
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope scope = new ManagementScope(#"[Name]\root\cimv2");
scope.Connect();
string wmiQuery = string.Format("Select * from Win32_Service", x.DisplayName);
ManagementObjectSearcher wmi = new ManagementObjectSearcher(wmiQuery);
ManagementObjectCollection coll = wmi.Get();
foreach (var service in coll)
{
Console.WriteLine(string.Format("{0} - {1}", service["Name"].ToString(), service["StartMode"].ToString()) );
}
}
}
Console.ReadKey();
}
Looks good for the most part. I would lead the scope with "\\" before your machine name. Also, if you are just looking for one specific service, add a WHERE clause to your query.
public static void Main()
{
string MachineName = "[Name]";
var ctl = ServiceController.GetServices(MachineName);
List<string> namelist = new List<string>();
foreach (var x in ctl)
{
if (x.DisplayName == "NHS Card Checker")
{
Console.WriteLine(string.Format("NHS Card checker found on MPC - Status: {0}", x.Status));
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope scope = new ManagementScope(String.Format(#"\\{0}\root\cimv2", MachineName));
scope.Connect();
string wmiQuery = string.Format("Select * from Win32_Service WHERE DisplayName='{0}'" , x.DisplayName);
ManagementObjectSearcher wmi = new ManagementObjectSearcher(wmiQuery);
ManagementObjectCollection coll = wmi.Get();
foreach (var service in coll)
{
Console.WriteLine(string.Format("{0} - {1}", service["Name"].ToString(), service["StartMode"].ToString()));
}
}
}
Console.ReadKey();
}
or maybe simplify it to only use WMI like...
string MachineName = "[Name]";
string TargetService = "NHS Card Checker";
{
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope scope = new ManagementScope(String.Format(#"\\{0}\root\cimv2", MachineName));
scope.Connect();
string wmiQuery = String.Format("Select * from Win32_Service WHERE DisplayName='{0}'", TargetService);
ManagementObjectSearcher wmi = new ManagementObjectSearcher(wmiQuery);
ManagementObjectCollection coll = wmi.Get();
if (coll.Count > 0)
{
foreach (var service in coll)
{
Console.WriteLine(string.Format("NHS Card checker found on MPC - Status: {0}", service["Status"].ToString()));
Console.WriteLine(string.Format("{0} - {1}", service["Name"].ToString(), service["StartMode"].ToString()));
}
}
else
{
Console.WriteLine(string.Format("{0} Service was not found", TargetService));
}
}
}
Also, in newer implementations of C# you can use an easier variance of string interpolation. Instead of...
string.format("{0} is your value", VariableName");
you can use
$"{VariableName} is your value";
I want to enter the logon ID and to receive the current PC that the account is logged.
I found a code with WMI, but running on 10,000+ computers takes a very long while (~ I gave up after 10 minutes.)
What I do is checking who is logged in every computer until there is a match between logged account and searched account.
private void getCurrentUser()
{
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + DOMAIN);
DirectorySearcher dSearch = new DirectorySearcher(entry);
dSearch.Filter = ("objectCategory=computer");
foreach (SearchResult result in dSearch.FindAll())
{
ManagementScope theScope = new ManagementScope("\\\\" + result.Properties["cn"][0].ToString() + "\\root\\cimv2");
ObjectQuery theQuery = new ObjectQuery("SELECT username FROM Win32_ComputerSystem");
ManagementObjectSearcher theSearcher = new ManagementObjectSearcher(theScope, theQuery);
ManagementObjectCollection theCollection = theSearcher.Get();
foreach (ManagementObject theCurObject in theCollection)
{
if (theCurObject["username"].ToString() == "LAS\\" + USERNAME)
Computer1.Text = result.Properties["cn"][0].ToString();
}
}
}
catch (Exception)
{
MessageBox.Show("Error");
}
}
this is the code, it works but I would like to know if there is another way to do that without waiting so long or how can I do it quickly?
Is there a way in C# to get the time when the current user logged in?
A command called quser in command prompt will list some basic information about current users, including LOGON TIME.
Is there a System property or something I can access in c# which I can get the user's login time from?
I am getting username by Environment.UserName property. Need the login time.
I've tried this:
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
Console.WriteLine("Login Time: {0}",GetLastLoginToMachine(Environment .MachineName , Environment.UserName));
public static DateTime? GetLastLoginToMachine(string machineName, string userName)
{
PrincipalContext c = new PrincipalContext(ContextType.Machine, machineName);
UserPrincipal uc = UserPrincipal.FindByIdentity(c, userName);
return uc.LastLogon;
}
Got the following errors:
You can get the LastUserLogon time from the following namespace.
using System.DirectoryServices.AccountManagement;
Try
DateTime? CurrentUserLoggedInTime = UserPrincipal.Current.LastLogon;
You can get the account information as well :
string userName = WindowsIdentity.GetCurrent().Name.Split('\\')[1];
string machineName = WindowsIdentity.GetCurrent().Name.Split('\\')[0];
Make sure you include the reference to System.DirectoryServices.AccountManagement:
Then you can do this to get the last logon time:
using System.DirectoryServices.AccountManagement;
public static DateTime? GetLastLoginToMachine(string machineName, string userName)
{
PrincipalContext c = new PrincipalContext(ContextType.Machine, machineName);
UserPrincipal uc = UserPrincipal.FindByIdentity(c, userName);
return uc.LastLogon;
}
You can query WMI:
// using System.Management;
private static Dictionary<string, DateTime> getMachineLogonName(string machine)
{
var loggedOnUsers = new Dictionary<string, DateTime>();
ManagementScope scope = new ManagementScope(String.Format(#"\\{0}\root\cimv2", machine));
SelectQuery sessionQuery = new SelectQuery("Win32_LogonSession");
using (ManagementObjectSearcher sessionSearcher = new ManagementObjectSearcher(scope, sessionQuery))
using (ManagementObjectCollection sessionMOs = sessionSearcher.Get())
{
foreach (var sessionMO in sessionMOs)
{
// Interactive sessions
if ((UInt32)sessionMO.Properties["LogonType"].Value == 2)
{
var logonId = (string)sessionMO.Properties["LogonId"].Value;
var startTimeString = (string)sessionMO.Properties["StartTime"].Value;
var startTime = DateTime.ParseExact(startTimeString.Substring(0, 21), "yyyyMMddHHmmss.ffffff", System.Globalization.CultureInfo.InvariantCulture);
WqlObjectQuery userQuery = new WqlObjectQuery(#"ASSOCIATORS OF {Win32_LogonSession.LogonId='" + logonId + #"'} WHERE AssocClass=Win32_LoggedOnUser");
using (var userSearcher = new ManagementObjectSearcher(scope, userQuery))
using (var userMOs = userSearcher.Get())
{
var username = userMOs.OfType<ManagementObject>().Select(u => (string)u.Properties["Name"].Value).FirstOrDefault();
if (!loggedOnUsers.ContainsKey(username))
{
loggedOnUsers.Add(username, startTime);
}
else if(loggedOnUsers[username]> startTime)
{
loggedOnUsers[username] = startTime;
}
}
}
}
}
return loggedOnUsers;
}
Then just call the method with target machine name:
var logins = getMachineLogonName(".");
I'm looking for a way to get the users that are logged in on a remote machine. I would love to know if they are logged on localy or remotely, but most of all I MUST know their status.
I saw some answers on the net that are written in VB, but I need it in c#.
the solution given in markdmak answer here is looking like a good start, but it's in VB and it looks for remote sessions only.
I have this piece of code, which can be a start, but I would like to couple the LogonId to a username and to see its status:
string fqdn = ""; // set!!!
ConnectionOptions options = new ConnectionOptions();
options.EnablePrivileges = true;
// To connect to the remote computer using a different account, specify these values:
// these are needed in dev environment
options.Username = ConfigurationManager.AppSettings["KerberosImpersonationUser"];
options.Password = ConfigurationManager.AppSettings["KerberosImpersonationPassword"];
options.Authority = "ntlmdomain:" + ConfigurationManager.AppSettings["KerberosImpersonationDomain"];
ManagementScope scope = new ManagementScope("\\\\" + fqdn + "\\root\\CIMV2", options);
try
{
scope.Connect();
}
catch (Exception ex)
{
if (ex.Message.StartsWith("The RPC server is unavailable"))
{
// The Remote Procedure Call server is unavailable
// cannot check for logged on users
return false;
}
else
{
throw ex;
}
}
SelectQuery query = new SelectQuery("Select * from Win32_LogonSession");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection results = searcher.Get();
bool returnVal = false;
foreach (ManagementObject os in results)
{
try
{
if (os.GetPropertyValue("LogonId").ToString() != null && os.GetPropertyValue("LogonId").ToString() != "")
{
returnVal = true;
}
}
catch (NullReferenceException)
{
continue;
}
}
return returnVal;
}
What I really need and can't find, is a way of getting ALL users on a remote machine AND their status, meaning: Active, Disconnected, Logged-off, etc.
You can use the Win32_LogonSession WMI class filtering for the LogonType property with the value 2 (Interactive)
Try this sample
using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
namespace GetWMI_Info
{
class Program
{
static void Main(string[] args)
{
try
{
string ComputerName = "remote-machine";
ManagementScope Scope;
if (!ComputerName.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{
ConnectionOptions Conn = new ConnectionOptions();
Conn.Username = "username";
Conn.Password = "password";
Conn.Authority = "ntlmdomain:DOMAIN";
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), Conn);
}
else
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), null);
Scope.Connect();
ObjectQuery Query = new ObjectQuery("SELECT LogonId FROM Win32_LogonSession Where LogonType=2");
ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query);
foreach (ManagementObject WmiObject in Searcher.Get())
{
Console.WriteLine("{0,-35} {1,-40}", "LogonId", WmiObject["LogonId"]);// String
ObjectQuery LQuery = new ObjectQuery("Associators of {Win32_LogonSession.LogonId=" + WmiObject["LogonId"] + "} Where AssocClass=Win32_LoggedOnUser Role=Dependent");
ManagementObjectSearcher LSearcher = new ManagementObjectSearcher(Scope, LQuery);
foreach (ManagementObject LWmiObject in LSearcher.Get())
{
Console.WriteLine("{0,-35} {1,-40}", "Name", LWmiObject["Name"]);
}
}
}
catch (Exception e)
{
Console.WriteLine(String.Format("Exception {0} Trace {1}", e.Message, e.StackTrace));
}
Console.WriteLine("Press Enter to exit");
Console.Read();
}
}
}
#RRUZ got me started but the Associators query did not work on remote machine with a lot of Win32_LoggedOnUser objects (don't know why). No results were returned.
I also needed remote Desktop sessions so I used LogonType "10" sessions and my ConnectionOptions were differents
I replaced the Associators query with WmiObject.GetRelationships("Win32_LoggedOnUser") and the speed increases by a lot and results were there.
private void btnUnleash_Click(object sender, EventArgs e)
{
string serverName = "serverName";
foreach (var user in GetLoggedUser(serverName))
{
dataGridView1.Rows.Add(serverName, user);
}
}
private List<string> GetLoggedUser(string machineName)
{
List<string> users = new List<string>();
try
{
var scope = GetManagementScope(machineName);
scope.Connect();
var Query = new SelectQuery("SELECT LogonId FROM Win32_LogonSession Where LogonType=10");
var Searcher = new ManagementObjectSearcher(scope, Query);
var regName = new Regex(#"(?<=Name="").*(?="")");
foreach (ManagementObject WmiObject in Searcher.Get())
{
foreach (ManagementObject LWmiObject in WmiObject.GetRelationships("Win32_LoggedOnUser"))
{
users.Add(regName.Match(LWmiObject["Antecedent"].ToString()).Value);
}
}
}
catch (Exception ex)
{
users.Add(ex.Message);
}
return users;
}
private static ManagementScope GetManagementScope(string machineName)
{
ManagementScope Scope;
if (machineName.Equals("localhost", StringComparison.OrdinalIgnoreCase))
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", "."), GetConnectionOptions());
else
{
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", machineName), GetConnectionOptions());
}
return Scope;
}
private static ConnectionOptions GetConnectionOptions()
{
var connection = new ConnectionOptions
{
EnablePrivileges = true,
Authentication = AuthenticationLevel.PacketPrivacy,
Impersonation = ImpersonationLevel.Impersonate,
};
return connection;
}