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"
Related
In order to get information about Serial Port devices, with System.Management, we can do as described in Getting Serial Port Information:
using System;
using System.Management;
using System.Collections.Generic;
using System.Linq;
using System.IO.Ports;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (var searcher = new ManagementObjectSearcher
("SELECT * FROM WIN32_SerialPort"))
{
string[] portnames = SerialPort.GetPortNames();
var ports = searcher.Get().Cast<ManagementBaseObject>().ToList();
var tList = (from n in portnames
join p in ports on n equals p["DeviceID"].ToString()
select n + " - " + p["Caption"]).ToList();
tList.ForEach(Console.WriteLine);
}
// pause program execution to review results...
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
}
}
How can this be achieved using Microsoft.Management.Infrastructure, I haven't managed to find examples and the documentation isn't detailed enough.
It's quite similar:
Create a CimSession, where you specify the ComputerName (null for LocalHost) and a CimCredential object (passing the usual UserName and Password for authentication, is necessary). This reflects System.Management's ConnectionOption.
Use the CimSession's QueryInstances method to build the query, passing the namespace and the class. This reflects System.Management's SelectQuery and ManagementObjectSearcher
QueryInstances() returns an IEnumerble<CimInstance> objects (the good news is that no COM objects are returned here).
Get the Value of the CimInstanceProperties you care about.
As a note, you're skipping the ConnectionOption and EnumerationOptions in your WMI Query, which is not really good PERF-wise.
Your query can then be translated to:
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Options;
using (var session = CimSession.Create(null) {
var ports = session.QueryInstances(#"root\cimv2", "WQL", "SELECT * FROM WIN32_SerialPort");
string[] portnames = SerialPort.GetPortNames();
var tList = (from n in portnames
join p in ports on n equals p.CimInstanceProperties["DeviceID"].Value
select n + " - " + p.CimInstanceProperties["Caption"].Value);
}
I'm not sure why you use string[] portnames = SerialPort.GetPortNames(); here.
You can just use the CimProperties:
using (var session = CimSession.Create(null)) {
var ports = session.QueryInstances(#"root\cimv2", "WQL", "SELECT * FROM WIN32_SerialPort");
var portsDescriptions = ports.Select(p =>
$"{p.CimInstanceProperties["DeviceID"].Value} - {p.CimInstanceProperties["Caption"].Value}");
// If you actually need to materialize a List<T>...
portsDescriptions.ToList().ForEach(Console.WriteLine);
}
Unrelated, but could be useful: I suggest to build some methods to create a CimSession with more options. For example:
public static CimSession CreateSession(string computerName)
=> CreateSession(computerName, string.Empty, string.Empty, null);
public static CimSession CreateSession(string computerName, string domain, string userName, SecureString password)
{
if (string.IsNullOrEmpty(computerName) ||
computerName.Equals("localhost", StringComparison.InvariantCultureIgnoreCase)) {
computerName = null;
}
var option = new CimSessionOptions();
if (password != null && password.Length > 0) {
option.AddDestinationCredentials(
new CimCredential(PasswordAuthenticationMechanism.Default, domain, userName, password));
}
return CimSession.Create(computerName, option);
}
So, instead of:
var session = CimSession.Create(null);
You can call it as:
// LocalHost, default permissions
var session = CreateSession(null);
Or pass domain, Username and Password (as Char*), if needed.
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
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 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.