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
Related
How can we check (return true or false) if a folder or file is activated for "Always available offline"? I am using Microsoft Sync Center.
I was able to get the need informations by using the WMI provider:
https://learn.microsoft.com/de-de/previous-versions/windows/desktop/offlinefiles/about-offline-files-wmi-provider
EDIT:
Don't forget to add a reference to System.Management.
I came up with following snippet:
ManagementScope scope = new ManagementScope("\\\\.\\ROOT\\cimv2");
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_OfflineFilesItem");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
var pinInfo = (ManagementBaseObject)m.GetPropertyValue("PinInfo");
if (pinInfo != null)
{
if ((bool)pinInfo.GetPropertyValue("Pinned"))
{
//the file or folder is set to "always available offline"
var itemPath = m["ItemPath"]
}
}
}
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?
I want to remove a Printer from a Windows account. This will be used via Citrix.
First I want to retrieve all printers that are installed for the user and then I want to remove a printer.
I am using the following code to do this.
This works on a normal PC. But when I use this via Citrix then it does not work.
Not all Printers are retrieved via this method. Also I cannot remove the Printer.
Does somebody know why?
What can I do to use this via Citrix?
What is different when using this via Citrix?
using System.Collections.Generic;
using System.Linq;
using System.Management;
namespace RemovePrinter
{
public class PrinterManager
{
public List<string> GetInstalledPrinters()
{
var managementScope = new ManagementScope(ManagementPath.DefaultPath);
managementScope.Connect();
var selectQuery = new SelectQuery {QueryString = #"SELECT * FROM Win32_Printer"};
var objectSearcher = new ManagementObjectSearcher(managementScope, selectQuery);
var ojectCollection = objectSearcher.Get();
return (from ManagementBaseObject item in ojectCollection select item["Name"].ToString()).ToList();
}
public bool DeletePrinter(string printerName)
{
var managementScope = new ManagementScope(ManagementPath.DefaultPath);
managementScope.Connect();
var selectQuery = new SelectQuery
{
QueryString = #"SELECT * FROM Win32_Printer WHERE Name = '" +
printerName.Replace("\\", "\\\\") + "'"
};
var ojectSearcher = new ManagementObjectSearcher(managementScope, selectQuery);
var ojectCollection = ojectSearcher.Get();
if (ojectCollection.Count == 0) return false;
foreach (var item in ojectCollection.Cast<ManagementObject>())
{
item.Delete();
return true;
}
return false;
}
}
}
ManagementObjectSearcher is a part of WMI API classes. By default these services are not enabled on Citrix and that is the reason why it does not work.
You need to have the right services installed as well have license to use those.
Check this out "http://support.citrix.com/article/ctx116423"
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 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.