Renaming Printer using C# and WMI - c#

I have created a C# application to rename printers on a Citrix server (Server 2008 R2).
The reason for this is because every time a user logs on the printer gets forwarded to the server and gets a unique name(For example Microsoft XPS Document Writer (from WI_UFivcBY4-wgoYOdlQ) in session 3) and from within some applications thats an issue since the printer is pointed to the name and by that you need to change the printer setting everytime you logon a session.
The program itself works like a charm and the printer gets the names I desire.
However the issue is after that the printers have been renamed Windows does not seem to be able to identify them anymore. For example if I try to change default printer i get an error saying "Error 0x00000709 Double check the printer name and make sure that the printer is connected to the network."
var query = new ManagementObjectSearcher("SELECT * FROM Win32_Printer where name like '%(%'");
ManagementObjectCollection result = query.Get();
foreach (ManagementObject printer in result)
{
string printerName = printer["name"].ToString();
if (printerName.IndexOf('(') > 0)
{
printer.InvokeMethod("RenamePrinter", new object[] { printerName.Substring(0, printerName.IndexOf('(')).Trim() + " " + userName }); //userName is provided as an inputparameter when running the application
}
}
Am I missing anything? Are there anything else i need to do when renaming?
I cant seem to find any info regarding this case at all.

i thing this codeproject is what your looking for. But after some own experiences with the printers in C# i can only say it does not make fun and it can be really frustrating
Code with small modifications:
//Renames the printer
public static void RenamePrinter(string sPrinterName, string newName)
{
ManagementScope oManagementScope = new ManagementScope(ManagementPath.DefaultPath);
oManagementScope.Connect();
SelectQuery oSelectQuery = new SelectQuery();
oSelectQuery.QueryString = #"SELECT * FROM Win32_Printer WHERE Name = '" + sPrinterName.Replace("\\", "\\\\") + "'";
ManagementObjectSearcher oObjectSearcher =
new ManagementObjectSearcher(oManagementScope, oSelectQuery);
ManagementObjectCollection oObjectCollection = oObjectSearcher.Get();
if (oObjectCollection.Count == 0)
return;
foreach (ManagementObject oItem in oObjectCollection)
{
int state = (int)oItem.InvokeMethod("RenamePrinter", new object[] { newName });
switch (state)
{
case 0:
//Success do noting else
return;
case 1:
throw new AccessViolationException("Access Denied");
case 1801:
throw new ArgumentException("Invalid Printer Name");
default:
break;
}
}
}

Still works great in 2022, thank you. Just had to change type
int
to
UInt32
to avoid new Exception:
UInt32 state = (UInt32)oItem.InvokeMethod("RenamePrinter", new object[] { newName });
switch (state)
{...

Related

C# WMI SetMTU System.ManagementException

We are working on a programm to configure some NICs.
We have to change IP Adresses, Subnetmask and the MTU.
Everything went well except the MTU Statement:
public void SetMTU()
{
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC)
{
if (networkadapterID == (String)objMO["SettingID"])
{
ManagementBaseObject setMTU;
ManagementBaseObject newMTU = objMO.GetMethodParameters("SetMTU");
Int32 test = 9216;
newMTU["MTU"] = test;
setMTU = objMO.InvokeMethod("SetMTU", newMTU, null);
}
}
}
The correct NIC ID is given. Other WMI Operations succeed but we stuck on that one with error Message:
System.Management.ManagementException: "Die Methode ist ungültig. "
(System.Management.ManagementException: "The Method is invalid.")
We have also tried to use "test" as string or uint32 (because the microsoft docs says it's an uint32),also:
newMTU["MTU"] = new (u)int[] { MTU };
but it doesnt work either.
Meanwhile we don't have any ideas how to fix the problem.
I am grateful for every idea.
Thanks for your help and have a good day,
Alex
Edit:
Code to read the MTU should be (you have to tell this part a NetworkID so you don't read the Value of every NIC, you find this in your registry, but you should be able to delete the if part):
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC)
{
if (networkadapterID == (String)objMO["SettingID"])
{
MessageBox.Show(Convert.ToString(objMO["MTU"]) + ": " + Convert.ToString(objMO["SettingID"]));
}
}
So far we didn't get the problem solved but we now check if the OS is Windows 10 and we will start a PowerShell task with this command:
Get-NetAdapterAdvancedProperty -Name 'NICName' -DisplayName 'Jumbo-Rahmen' | Set-NetAdapterAdvancedProperty -RegistryValue 'MTUsize';
Jumbo-Rahmen should be JumboPacket on an English OS
If the OS is not Windows 10 the User will be asked to change the MTU manually

Printer status return wrong value

I am trying to get the status of a network printer. I tried WMI code as this link says. But even if I removed the network cable, it always goes to the else part.
Here is the code I tried :
ManagementObjectSearcher searcher = new
ManagementObjectSearcher("SELECT * FROM Win32_Printer");
string printerName = "";
foreach (ManagementObject printer in searcher.Get())
{
printerName = printer["Name"].ToString().ToLower();
if (printer["WorkOffline"].ToString().ToLower().Equals("true") && printer["Default"].ToString().ToLower().Equals("true"))
{
string s = "Printer offline" + printerName;
listBox1.Items.Add(s);
}
else
{
// printer is not offline
// Console.WriteLine("Your Plug-N-Play printer is connected.");
string s = "Printer found and is online " + printerName;
listBox1.Items.Add(s);
}
Check out the condition
printer["PrinterState"].ToString().ToLower().Equals("Offline")
// this would mean offline == Offline which would be false
you would need the comparison in lower case, you have a typo Offline has a Caps Lock O
the comparison could be not right... 'cause the statement
ConsoleWriteLine(printer["PrinterState"].ToString());
returns "0" when the printer is on line, and "128" when this fault due any reason (power off, uninstalled, cable conection, network troubles, etc)
the right comparisons must be:
printer["PrinterState"].ToString().ToLower().Equals("128") for off line
printer["PrinterState"].ToString().ToLower().Equals("0") for on line

Detect Server Display Resolution

On windows server 2008 can I have a web service or something I can query from a C# application as to the display properties (resolution (height & width)). The C# application does not run on the server so I cannot just detect it from the application itself.
Addition to help explain why:
I will have a user named "display" and that will be logged on displaying a website (on the server) and I want to be able to check the display from the desktop application so the user knows what resolution to design a template for. The resolution will change from different displays so it can't be a set value.
I'd recommend just querying the server using WMI. Check the third example here:
http://msdn.microsoft.com/en-us/library/aa394591%28v=vs.85%29.aspx
My Code
This is the code that I used to solve the problem:
System.Management.ConnectionOptions oConnectionOptions = new System.Management.ConnectionOptions();
{
oConnectionOptions.Username = ServerManagement.GetServerUser();
oConnectionOptions.Password = ServerManagement.GetServerPassword();
}
ManagementPath oPath = new ManagementPath("\\\\" + ServerManagement.GetServerAddress() + "\\root\\cimv2");
ManagementScope oScope = new ManagementScope(oPath, oConnectionOptions);
try
{
oScope.Connect();
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DesktopMonitor");
ManagementObjectCollection obj = searcher.Get();
foreach (ManagementObject service in obj)
{
this.DisplayHeight = Convert.ToInt16(service["ScreenHeight"]);
this.DisplayWidth = Convert.ToInt16(service["ScreenWidth"]);
}
}
catch (Exception)
{
MessageBox.Show("Cannot connect to server, please try again later.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}

C# List currently open files and programs

Is there a way I can get a list of all open applications and a list of all open files? For the files I only need the files that I opened (documents etc) not OS's open system files. The same for the applications (only browsers, document processors etc).
I already tried various functions from the Windows API like EnumWindows but I couldn't get what I wanted.
An example of what my ultimate goal would be, is to have lists like this:
Applications
Microsoft Word,
Notepad,
Mozilla Firefox
Files
foo.txt,
foo.mp3,
foo.doc
What I need is just the names, I don't need handles etc (even though I'm sure I'll have to use them to get what I want)
You can get a list of running processes with their information
public static string ListAllProcesses()
{
StringBuilder sb = new StringBuilder();
// list out all processes and write them into a stringbuilder
ManagementClass MgmtClass = new ManagementClass("Win32_Process");
foreach (ManagementObject mo in MgmtClass.GetInstances())
{
sb.Append("Name:\t" + mo["Name"] + Environment.NewLine);
sb.Append("ID:\t" + mo["ProcessId"] + Environment.NewLine);
sb.Append(Environment.NewLine);
}
return sb.ToString();
}
The only method (That I know) to see if the process is opened by user or system is to check it's owner. If it's system, then it's not run by user:
//You will need to reference System.Management.Dll and use System.Management namespace
public string GetProcessOwner(string processName)
{
string query = "Select * from Win32_Process Where Name = \"" + processName + "\"";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection processList = searcher.Get();
foreach (ManagementObject obj in processList)
{
string[] argList = new string[] { string.Empty, string.Empty };
int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnVal == 0)
{
// return DOMAIN\user
string owner = argList[1] + "\\" + argList[0];
return owner;
}
}
return "NO OWNER";
}
For the list of opened files, It is possible to do using Managed Code which is somehow hard. Here is an example on codeproject demonstrating the same matter
You can have a list of running processes with Process.GetProcesses(): http://msdn.microsoft.com/en-us/library/1f3ys1f9.aspx
But you can have the file they have open if they exposes some automation interface you know.

How do I retrieve the username that a Windows service is running under?

Given a service name, I would like to retrieve the username that it runs under (i.e. the username shown in the 'Log On' tab of a service's properties window).
There doesn't appear to be anything in the ServiceController class to retrieve this basic information. Nothing else in System.ServiceProcess looks like it exposes this information either.
Is there a managed solution to this, or am I going to have to drop down into something lower-level?
Using WMI, with the System.Management you can try the following code:
using System;
namespace WindowsServiceTest
{
class Program
{
static void Main(string[] args)
{
System.Management.SelectQuery sQuery = new System.Management.SelectQuery(string.Format("select name, startname from Win32_Service")); // where name = '{0}'", "MCShield.exe"));
using (System.Management.ManagementObjectSearcher mgmtSearcher = new System.Management.ManagementObjectSearcher(sQuery))
{
foreach (System.Management.ManagementObject service in mgmtSearcher.Get())
{
string servicelogondetails =
string.Format("Name: {0} , Logon : {1} ", service["Name"].ToString(), service["startname"]).ToString();
Console.WriteLine(servicelogondetails);
}
}
Console.ReadLine();
}
}
}
You can then later substitute the commented code with your service name, and it should only return the instances of your service process that is running.
WMI is your friend. Look at Win32_Service, specifically the StartName property. You can access WMI from C# via the System.Management.ManagementClass.
If you've not used WMI before, this article seems to be quite a good tutorial.
You can find this using the Windows Registry, reading the following string value, replacing [SERVICE_NAME] with the name of the Windows Service:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\[SERVICE_NAME]\ObjectName
Try this:
System.Security.Principal.WindowsIdentity.GetCurrent();
but the most obvious you will get LOCAL SYSTEM or NETWORK. The reason that you cannot show this user - that service can manage multiple users (shared by desktop, attached to current windows session, using shared resource ...)
System starts service, but any user can use it.
This solution works fine for me:
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + this.ServiceName + "'");
wmiService.Get();
string user = wmiService["startname"].ToString();
public String getUsername() {
string username = null;
try {
ManagementScope ms = new ManagementScope("\\\\.\\root\\cimv2");
ms.Connect();
ObjectQuery query = new ObjectQuery
("SELECT * FROM Win32_ComputerSystem");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(ms, query);
foreach (ManagementObject mo in searcher.Get()) {
username = mo["UserName"].ToString();
}
string[] usernameParts = username.Split('\\');
username = usernameParts[usernameParts.Length - 1];
} catch (Exception) {
username = "SYSTEM";
}
return username;
}

Categories