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:
Related
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
}
}
So, I'm kind of stuck here...
I'm writing a program that should be able to list all users in the local administrator group on a MS Windows Server 2008 R2.
The problem here is that I'm only allowed to use .NET 2.0 - so I'm not able to use the GroupPrincipal Class... Which would have made this a really easy task.
Any pointers would be appriciated!
Cheers!
Jeez!
Don't know what I was thinking really - it's so simple!
All creds to Masoud Tabatabaei - found the following codesnippet on:
http://csharptuning.blogspot.se/2007/09/how-to-get-list-of-windows-user-in-c.html
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName);
DirectoryEntry admGroup = localMachine.Children.Find("administrators","group");
object members = admGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
lstUsers.Items.Add(member.Name);
}
Did you try WMI ?
for example
ManagementObjectSearcher search = new ManagementObjectSearcher(#"SELECT * FROM Win32_UserAccount where LocalAccount = true");
ManagementObjectCollection userList = search.Get();
foreach (ManagementObject user in userList)
{
Console.WriteLine("User name: {0}, Full Name: {1}",
user["Name"].ToString(), user["FullName"].ToString());
}
Will give you a list of users in local SAM. You can add other attributes to the query and refine your list.
Do not forget to add a reference to System.Management.dll
If your are still looking for an answer, here:
If you'd like to get the administrator group, you can use this code:
public static DirectoryEntry GetLocalAdminstratorGroup()
{
using (var WindowsActiveDirectory = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer"))
{
return WindowsActiveDirectory.Children.Find(GetLocalizedAdministratorGroupName(), "group");
}
}
//Localized == Language Independent
public static string GetLocalizedAdministratorGroupName()
{
//For English Windows version, this equals "BUILTIN\Administrators".
var adminGroupName = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount)).Value;
//Remove the "BUILTIN\" part, get the local name of the group
return adminGroupName.Split('\\')[1];
}
If you'd also like to enumerate it (like you need a username), you can do this, using the methods before:
object members = AdminGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
Console.WriteLine(member.Name);
}
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
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());
}
}
}
I have the following method used for searching for a User Group either on the local computer (done first) or in the Current Forest.
public string FindUserGroup(string group)
{
//Search local computer
using (DirectorySearcher searcher = new DirectorySearcher(new DirectoryEntry()))
{
searcher.Filter = "(&(objectClass=group)(|(cn=" + group + ")(dn=" + group + ")))";
SearchResult result = searcher.FindOne();
if (result != null)
return TranslateDirectoryEntryPath(result.GetDirectoryEntry().Path);
}
//Search current forest
Forest forest = Forest.GetCurrentForest();
foreach (Domain domain1 in forest.Domains)
{
using (DirectorySearcher searcher = new DirectorySearcher(domain1.GetDirectoryEntry()))
{
searcher.Filter = "(&(objectClass=group)(|(cn=" + group + ")(dn=" + group + ")))";
SearchResult result = searcher.FindOne();
if (result != null)
return TranslateDirectoryEntryPath(result.GetDirectoryEntry().Path);
}
}
return string.Empty;
}
My problem is that we as an example have say "domain.local" and "mydomain.local", and my current login is bound to "domain.local", then using below won't be able to find anything in "mydomain.local", even if I through the Windows User Interface is able to.
How can I search all viewable providers from my computers perspective when I don't nessesarily know them all? Do I REALLY have to do the Registry Work my self?
Edit:
One difference in the 2 domains is the "level" they are on when I in an object browser dialog chooses "Locations", it layouts as:
Computer
Entire Direction
domain.local
mydomain.local
So "mydomain.local" excists outside what is referred to as "Entire Directory", yet my computer can locate it, if that makes any difference?
I don't see a problem as this code here would have already be binded to the other domains.
foreach (Domain domain1 in forest.Domains)
{
using (DirectorySearcher searcher = new DirectorySearcher(domain1.GetDirectoryEntry()))
{
Are you trying to say that later on you're binding a DirectoryEntry on your own, and you can't find objects from other domain?