Is it possible, using System.DirectoryServices.AccountManagement, to find who the user is who is currently logged into a specific pc?
Using the PrincipalSearcher it is easy to find all the computers on a domain, and even when they were last logged into. However, I can’t find an easy way to find the usernames of users logged into what pcs.
Any ideas on how to do this?
To find currently logged in users you need to know which processes are running from the user accounts. So for each PCs you need to get list of processes and find under which account the process executed. To do this you can use WMI.
const string ns = #"root\cimv2";
var host = "your host";
var scope = new ManagementScope(string.Format(#"\\{0}\{1}", host, ns));
scope.Connect();
//List of logged in users
using (var searcher = new ManagementObjectSearcher(scope,
new ObjectQuery("select * from Win32_LoggedOnUser")))
{
foreach (var logonUser in searcher.Get())
{
//see MSDN for available properties of Win32_LoggedOnUser,
//take into consideration "Dependent", "Antecedent" properties
}
}
//list of processes
using (var searcher = new ManagementObjectSearcher(scope,
new ObjectQuery( "select * from Win32_SessionProcess")))
{
foreach (var sessProc in searcher.Get())
{
//see "Antecedent" property
}
}
Related
I have a sonicwall on a network of around 100 machines.
I have tried and tried to find a way of creating 2 combobox's that will contain both the IP addresses linked to the sonicwall, as well as the currently logged in user of the machine as well.
What i am trying to create is a employee monitoring software (like Interguard/ActivTrak/etc), but am unable at present to even get near to finding this information?
I've been researching A LOT of stuff (~25 pages of google, with not a single link not clicked), and have met no conclusion as to get this information.
I'm not exactly a great programmer, but I would LOVE to be able to get this sort of information into two combobox's/arrays/other suitable object/control.
If anyone knows a way of even creating an array of IP addresses, along with corresponding Logins, that would be of great help to this project!
(PLEASE NOTE: I know there is a Networking exchange site, but I have already looked! Also, since i'm designing a piece of software for this, I thought i'd ask it here!)
Thanks for any advice/suggestions much appreciated!
What you looking for is similar to a packet sniffer application like wireshark or ethereal. But if you want to design it yourself, I think your best solution is a collection/list to store the data that is binded to a datagrid.
If you're in an Active Directory domain, you could use the code from my S/O question. Instead of using the CheckConnectionAsync() method to check for connectivity, you could just re-write it to get the IP address using System.Net.Dns.
To retrieve the user, you can use WMI. For this, you need to make a reference to the System.Management namespace.
public Task<string> GetUserName(string ComputerName)
{
return Task.Run(() =>
{
ConnectionOptions conn = ConnectionOptions()
{
EnablePrivileges = true,
Username = // string in format of #"DomainName\UserName",
Password = password,
Authentication = AuthenticationLevel.PacketPrivacy,
Impersonation = ImpersonationLevel.Impersonate
};
ManagementScope scope = new ManagementScope(#"\\" + ComputerName + #"\root\cimv2", conn);
try
{
scope.Connect();
ObjectQuery user = new ObjectQuery("Select UserName From Win32_ComputerSystem");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, user);
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject m in collection)
{
string username = m["UserName"].ToString().Trim();
if(!String.IsNullOrEmpty(username))
{
return username;
}
}
return null; // no current logged in user
}
catch (Exception) // error handling...
});
}
Then, just iterate through your collection of computers and retrieve the username like so:
private async Task RetrieveUsers()
{
Parallel.ForEach(Computers, c =>
{
string user = await GetUserName(c.Name); // or c.IP. both work.
});
}
I want to find out if the remote host has r/w access to a network share. To start out I wanted to see if I could query the target host's ability to query the UNC path for info, ala
var query = string.Format("select * from CIM_Directory where name = '{0}'", path);
This works fine for local files, e.g.
var path = #"c:\\Windows";
However, I can't figure out an appropriate way of querying a UNC path (e.g. \\foo\bar). The query always returns a blank set. I saw a related question about executing remote files and the solution for that one ended up being PsExec. I was hoping to ideally solve this problem entirely using WMI without having to rely on 3rd party execs, or uploading my own tool to the remote host.
Cheers
Here's a little usage sample of what I am trying to do right now (var values taken out):
using System;
using System.Linq;
using System.Management;
namespace netie
{
class Program
{
static void Main()
{
var connection = new ConnectionOptions
{
Username = "user",
Password = "pass",
Authority = "domain",
Impersonation = ImpersonationLevel.Impersonate,
EnablePrivileges = true
};
var scope = new ManagementScope("\\\\remote\\root\\CIMV2", connection);
scope.Connect();
var path = #"\\\\foo\\bar\\";
var queryString = string.Format("select * from CIM_Directory where name = '{0}'", path);
try
{
var query = new ObjectQuery(queryString);
var searcher = new ManagementObjectSearcher(scope, query);
foreach (var queryObj in searcher.Get().Cast<ManagementObject>())
{
Console.WriteLine("Number of properties: {0}", queryObj.Properties.Count);
foreach (var prop in queryObj.Properties)
{
Console.WriteLine("{0}: {1}", prop.Name, prop.Value);
}
Console.WriteLine();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadLine();
}
}
}
So it looks like this is basically impossible as WMI locks you out of network access for security reasons. Looks like your best bet is WinRM or PsExec for one-offs. You can potentially enable WinRM through WMI if that's your only path of access, but I imagine that ability can be blocked by group policies. The third option is to write your own Windows Service that will respond to requests and installing that through WMI if you have the access.
In short: the answer to my question is a No. Use WinRm, PsExec, or a custom win-service solution.
I know this is an old question, but for anyone looking to do this, the following code works. (I know that it's not WMI. Given the OP's answer I didn't even try it with WMI, but I shudder to think that people may write a service for something like this.)
if (System.IO.Directory.Exists(#"[SOME UNC PATH]"))
{
System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(#"[SOME UNC PATH]");
var securityInfo = info.GetAccessControl();
var rules = securityInfo.GetAccessRules(
true,
true,
typeof(System.Security.Principal.SecurityIdentifier));
foreach (var rule in rules)
{
var fileSystemRule = rule as System.Security.AccessControl.FileSystemAccessRule;
if (ruleastype != null)
{
string user = fileSystemRule.IdentityReference.Translate(
typeof(System.Security.Principal.NTAccount)).Value;
System.Diagnostics.Debug.Print("{0} User: {1} Permissions: {2}",
fileSystemRule.AccessControlType.ToString(),
user,
fileSystemRule.FileSystemRights.ToString());
}
}
}
When run it produces the following output:
Allow User: Everyone Permissions: ReadAndExecute, Synchronize
Allow User: CREATOR OWNER Permissions: FullControl
Allow User: NT AUTHORITY\SYSTEM Permissions: FullControl
Allow User: BUILTIN\Administrators Permissions: FullControl
Allow User: BUILTIN\Users Permissions: ReadAndExecute, Synchronize
I need to enumerate all user profiles on a local computer and list them in a combo box. Any special accounts need to be filtered out. I'm only concerned about actual user profiles on the computer where the app is running. I have done some searching but I haven't found a clear answer posted anywhere. I did find some code that might work but SelectQuery and ManagementObjectSearcher are displaying errors in VS and I'm not sure what I need to do to make this work.
using System.Management;
SelectQuery query = new SelectQuery("Win32_UserAccount");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject envVar in searcher.Get())
{
Console.WriteLine("Username : {0}", envVar["Name"]);
}
By saying "SelectQuery and ManagementObjectSearcher are displaying errors" I guess you didn't reference the System.Management dll.
You should right click References in your solution and add System.Management.
Then, with your using statement, the errors should disappear.
Anyway, including the error next time will assist everyone to help you :)
The mentioned code is great but when I tried on a machine connected to a Active Directory Domain all the usernames where returned for the domain. I was able to tweak the code a bit to only return the users that actually have a local directory on the current machine. If a better C# developer can refactor the code to make it cleaner - please help!
var localDrives = Environment.GetLogicalDrives();
var localUsers = new List<string>();
var query = new SelectQuery("Win32_UserAccount") { Condition = "SIDType = 1 AND AccountType = 512" };
var searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject envVar in searcher.Get())
{
foreach (string drive in localDrives)
{
var dir = Path.Combine(String.Format("{0}Users", drive), envVar["name"].ToString());
if (Directory.Exists(dir))
{
localUsers.Add(envVar["name"].ToString());
}
}
}
Once you have the localUsers variable you can set this as the data source to your ComboBox control of our choice.
In a C# application I use the following code to determine existing local windows accounts (inkl. filtering build-in security principals for some reasons):
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * from Win32_Account Where LocalAccount = True AND Status = 'OK' AND (SidType = 1 OR SidType = 5)" +
" AND (SID <> 'S-1-3-3' AND SID <> 'S-1-3-2' AND SID <> 'S-1-5-9' " +
" AND SID <> 'S-1-5-8' AND SID <> 'S-1-5-10' AND SID <> 'S-1-5-12' " +
" AND SID <> 'S-1-2-0')");
ManagementObjectCollection objects = searcher.Get();
foreach (ManagementBaseObject obj in objects)
{
....
}
Now I am looking for an alternative method/way to determine existing local windows accounts like above because this method is not very stable --> sometimes an COMException is thrown ( when executing searcher.Get() ):
System.Runtime.InteropServices.COMException
(0x800706BA) at
System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32
errorCode, IntPtr errorInfo) at
System.Management.ManagementObjectCollection.ManagementObjectEnumerator.MoveNext()
The exception occurs non-determinstic in my opinion.
I do not exactly understand what u need but here is a good example of getting all windows accounts on the system
http://csharptuning.blogspot.com/2007/09/how-to-get-list-of-windows-user-in-c.html
and to get current system user u simply write
System.Security.Principal.WindowsIdentity.GetCurrent()
you can also do something like this
static void Main(string[] args)
{
SelectQuery query = new SelectQuery("Win32_UserAccount");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject envVar in searcher.Get())
{
Console.WriteLine("Username : {0}", envVar["Name"]);
}
Console.ReadLine();
}
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a context (including a "machine" context for the local accounts), and then easily find users and/or groups:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Machine);
// find all users - define a "UserPrincipal" as your "QBE" principal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// enumerate all users
PrincipalSearcher searcher = new PrincipalSearcher(qbeUser);
foreach(Principal p in searcher.FindAll())
{
// do something here
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:
I am trying to get the list of local users of a computer using the following code.
internal void GetUsers()
{
try
{
List<string> adUsers = new List<string>();
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
foreach (DirectoryEntry child in directoryEntry.Children)
{
if (child.SchemaClassName.Equals("User", StringComparison.OrdinalIgnoreCase))
{
adUsers.Add(child.Name);
}
}
}
catch (Exception ex)
{
//Exception
}
}
This code works fine in my computer. However, when I tested it on a few other computers, the following system users were included in the list:
ASPNET,
HelpAssistant
Could some one throw some light on how I can get rid of these system users and get only users who actually log in, ie, normal users.
Thanks,
Ram
Not an answer as such, but some suggestions that might help.
I think the problem is that those accounts aren't real system accounts, so might not be so easy to distinguish.
You could look at the WMI classes Win32_UserAccount and Win32_UserProfile and see if there are any properties in there that might indicate which user accounts are normal ones and which ones are the ones you mention. Specifically, maybe the 'SIDType' or 'AccountType' properties of Win32_UserAccount or maybe the Special property of the Win32_UserProfile class.
Might be other WMI classes that might be worth looking at as well.
Or there might be some way that you can query if a user account has the interactive logon right (which I assume those two accounts might not have normally).
Have you tried enumerating the Properties collection on DirectoryEntry?
using (DirectoryEntry dirEntry = new DirectoryEntry(strchild))
{
foreach (string strPropertyName in dirEntry.Properties.PropertyNames)
{
Console.WriteLine(strPropertyName + " " + dirEntry.Properties[strPropertyName].Value.ToString());
}
}
Other than that, you may have to do an LDAP search on Active Directory to match the UserName you have found to an ActiveDirectory user.
Have a look at this article.
http://www.codeproject.com/KB/system/everythingInAD.aspx
Have fun.
The following code will get you the local users that actually have local accessible folders.
var localDrives = Environment.GetLogicalDrives();
var localUsers = new List<string>();
var query = new SelectQuery("Win32_UserAccount") { Condition = "SIDType = 1 AND AccountType = 512" };
var searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject envVar in searcher.Get())
{
foreach (string drive in localDrives)
{
var dir = Path.Combine(String.Format("{0}Users", drive), envVar["name"].ToString());
if (Directory.Exists(dir))
{
localUsers.Add(envVar["name"].ToString());
}
}
}