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?
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 write a small command line application with C# that will prompt for a username and a password that will be used to login to several remote computers that are sitting on the same network/domain and start a local session.
I've tried connecting to a remote computer and to query the remote PC's operating system info with the following code:
ConnectionOptions options = new ConnectionOptions();
ManagementScope scope = new ManagementScope(#"\\REMOTE_COMPUTER_NAME");
scope.Connect();
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
// Display the remote computer information
Console.WriteLine("Computer Name : {0}", m["csname"]);
Console.WriteLine("Windows Directory : {0}", m["WindowsDirectory"]);
Console.WriteLine("Operating System: {0}", m["Caption"]);
Console.WriteLine("Version: {0}", m["Version"]);
Console.WriteLine("Manufacturer : {0}", m["Manufacturer"]);
}
However, this only returns information on the local PC that I'm working on and not on the remote PC.
Am I overlooking something with this code? And is there an appropriate approach to accomplish what I am trying to do?
I don't have remote machine right now to give you the working example, but you can try this. advapi32.logonuser
Example:
[DllImport("advapi32.dll")]
public static extern bool LogonUser(string name, string domain, string pass,
int logType, int logpv, out IntPtr pht);
IntPtr ptr;
// LogonUser("username", "remotemachine", "password", 2, 0, out ptr);
LogonUser("username", "remotemachine", "password", 9, 0, out ptr);
WindowsIdentity windowsIdentity = new WindowsIdentity(ptr);
var impersonationContext = windowsIdentity.Impersonate();
// your code goes here...
impersonationContext.Undo();
This logon type allows the caller to clone its current token and
specify new credentials for outbound connections. The new logon
session has the same local identifier but uses different credentials
for other network connections. NOTE: This logon type is supported
only by the LOGON32_PROVIDER_WINNT50 logon provider. NOTE: Windows NT:
This value is not supported.
http://www.pinvoke.net/default.aspx/advapi32.logonuser
Edit
Give it a try cassia
ITerminalServicesManager manager = new TerminalServicesManager();
using (ITerminalServer server = manager.GetRemoteServer("servername"))
{
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);
}
}
You have to use ConnectionOptions and pass it to the ManagementScope
public void GetSystemInformation(string _yourDomain, string _hostName, string _userName, SecureString _password)
{
ManagementScope Scope = null;
string computerName = _hostName;
string userName = _userName;
SecureString password = _password;
ManagementObjectCollection collection = null;
try
{
SelectQuery query = new SelectQuery("SELECT * FROM Win32_OperatingSystem");
//string query = "SELECT * FROM Win32_NetworkAdapterConfiguration" + " WHERE IPEnabled = 'TRUE'";
var options = new ConnectionOptions
{
EnablePrivileges = false,
Impersonation = ImpersonationLevel.Impersonate,
Username = _userName,
SecurePassword = _password,
Authority = "ntlmdomain:" + _yourDomain
};
Scope.Options = options;
Scope.Connect();
ManagementObjectSearcher searcher = new ManagementObjectSearcher(Scope, query);
collection = searcher.Get();
//Do something with the collection
}
catch (ManagementException ex)
{
Console.WriteLine(ex.Message);
}
catch (UnauthorizedAccessException ex)
{
throw new ArgumentException(ex.Message);
}
}
private static SecureString CreateSecuredString(string pw)
{
var secureString = new SecureString();
foreach (var c in pw)
{
secureString.AppendChar(c);
}
return secureString;
}
You might have to try some different stats with the variables EnablePrivileges and Impersonation
edit:
If you want to get your information from your pc (local) than you dont have to pass the options to the scope.
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();
}
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;
}
I am trying to make a comparison between a machine name i have retrieved from AD, and the DNS Host Name i want to get using WMI from the machine.
I currently have:
foreach (SearchResult oneMachine in allMachinesCollected)
{
pcName = oneMachine.Properties["name"][0].ToString();
ConnectionOptions setupConnection = new ConnectionOptions();
setupConnection.Username = USERNAME;
setupConnection.Password = PASSWORD;
setupConnection.Authority = "ntlmdomain:DOMAIN";
ManagementScope setupScope = new ManagementScope("\\\\" + pcName + "\\root\\cimv2", setupConnection);
setupScope.Connect();
ObjectQuery dnsNameQuery = new ObjectQuery("SELECT * FROM Win32_ComputerSystem");
ManagementObjectSearcher dnsNameSearch = new ManagementObjectSearcher(setupScope, dnsNameQuery);
ManagementObjectCollection allDNSNames = dnsNameSearch.Get();
string dnsHostName;
foreach (ManagementObject oneName in allDNSNames)
{
dnsHostName = oneName.Properties["DNSHostName"].ToString();
if (dnsHostName == pcName)
{
shutdownMethods.ShutdownMachine(pcName, USERNAME, PASSWORD);
MessageBox.Show(pcName + " has been sent the reboot command");
}
}
}
}
But i get a ManagementException >> dnsHostName = oneName.Properties["DNSHostName"].ToString(); << here saying not found.
Any ideas?
Depending on the operating system you are connecting to this property will not be available. You can see from the documentation that it is not available on Windows 2000 and XP. However, it is available on the Win32_NetworkAdapterConfiguration class, but you will receive more than one object, which you will have to loop over to get the name as most of them will be null.
Also, dnsHostName = oneName.Properties["DNSHostName"].ToString(); is not correct. It should be dnsHostName = oneName.Properties["DNSHostName"].Value.ToString(). Again, if you decide to use Win32_NetworkAdapterConfiguration keep in mind that it can be null.