Is there a straightforward way to enumerate all visible network printers in .NET? Currently, I'm showing the PrintDialog to allow the user to select a printer. The problem with that is, local printers are displayed as well (along with XPS Document Writer and the like). If I can enumerate network printers myself, I can show a custom dialog with just those printers.
Thanks!!
Get the default printer from LocalPrintServer.DefaultPrintQueue
Get the installed printers (from user's perspective) from PrinterSettings.InstalledPrinters
Enumerate through the list:
Any printer beginning with \\ is a network printer - so get the queue with new PrintServer("\\UNCPATH").GetPrintQueue("QueueName")
Any printer not beginning with \\ is a local printer so get it with LocalPrintServer.GetQueue("Name")
You can see which is default by comparing FullName property.
Note: a network printer can be the default printer from LocalPrintServer.DefaultPrintQueue, but not appear in LocalPrintServer.GetPrintQueues()
// get available printers
LocalPrintServer printServer = new LocalPrintServer();
PrintQueue defaultPrintQueue = printServer.DefaultPrintQueue;
// get all printers installed (from the users perspective)he t
var printerNames = PrinterSettings.InstalledPrinters;
var availablePrinters = printerNames.Cast<string>().Select(printerName =>
{
var match = Regex.Match(printerName, #"(?<machine>\\\\.*?)\\(?<queue>.*)");
PrintQueue queue;
if (match.Success)
{
queue = new PrintServer(match.Groups["machine"].Value).GetPrintQueue(match.Groups["queue"].Value);
}
else
{
queue = printServer.GetPrintQueue(printerName);
}
var capabilities = queue.GetPrintCapabilities();
return new AvailablePrinterInfo()
{
Name = printerName,
Default = queue.FullName == defaultPrintQueue.FullName,
Duplex = capabilities.DuplexingCapability.Contains(Duplexing.TwoSidedLongEdge),
Color = capabilities.OutputColorCapability.Contains(OutputColor.Color)
};
}).ToArray();
DefaultPrinter = AvailablePrinters.SingleOrDefault(x => x.Default);
using the new System.Printing API
using (var printServer = new PrintServer(string.Format(#"\\{0}", PrinterServerName)))
{
foreach (var queue in printServer.GetPrintQueues())
{
if (!queue.IsShared)
{
continue;
}
Debug.WriteLine(queue.Name);
}
}
found this code here
private void btnGetPrinters_Click(object sender, EventArgs e)
{
// Use the ObjectQuery to get the list of configured printers
System.Management.ObjectQuery oquery =
new System.Management.ObjectQuery("SELECT * FROM Win32_Printer");
System.Management.ManagementObjectSearcher mosearcher =
new System.Management.ManagementObjectSearcher(oquery);
System.Management.ManagementObjectCollection moc = mosearcher.Get();
foreach (ManagementObject mo in moc)
{
System.Management.PropertyDataCollection pdc = mo.Properties;
foreach (System.Management.PropertyData pd in pdc)
{
if ((bool)mo["Network"])
{
cmbPrinters.Items.Add(mo[pd.Name]);
}
}
}
}
Update:
"This API function can enumerate all network resources, including servers, workstations, printers, shares, remote directories etc."
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=741&lngWId=10
PrinterSettiings.InstalledPrinters should give you the collection you want
In another post(https://stackoverflow.com/a/30758129/6513653) relationed to this one, Scott Chamberlain said "I do not believe there is anything in .NET that can do this, you will need to make a native call". After to try all the possible .NET resource, I think he is right.
So, I started to investigate how ADD PRINTER dialog does its search. Using Wireshark, I found out that ADD PRINTER send at least two types of packages to all hosts in local network: two http/xml request to 3911 port and three SNMP requests.
The first SNMP request is a get-next 1.3.6.1.2.1.43, which is Printer-MIB. The second one, is a get 1.3.6.1.4.1.2699.1.2.1.2.1.1.3 which is pmPrinterIEEE1284DeviceId of PRINTER-PORT-MONITOR-MIB. This is the most interesting because is where ADD PRINTER takes printer name. The third is a get 1.3.6.1.2.1.1.1.0, which is sysDescr of SNMP MIB-2 System.
I do believe that the second SNMP request is enough to find most of network printers in local network, so I did this code. It works for Windows Form Application and it depends on SnmpSharpNet.
Edit: I'm using ARP Ping instead normal Ping to search active hosts in network. Link for an example project: ListNetworks C# Project
Note that if you're working over RDP it seems to complicate this because it looks like it just exports everything on the host as a local printer.
Which is then a problem if you're expecting it to work the same way when not on RDP.
Related
I am printing using PrintDocument class.
I have a DB table having doc info to be printed. Instead of having its PrintName I just have IP address of the printer. All printers are installed locally. And I'm working on a windows service that will print those documents.
There is another app, out of my scope, where the user chose one printer, but just its IP is stored at DB... so
How can I set PrinterSettings.PrinterName having just its IP address??
By printername I assume you mean the name the printer is set up with in Windows and not the printer model, or sharename.
I don't really understand what you mean by the printers are installed locally. Is your computer acting as a printserver for the printers, since you have their IP address, or are they installed and shared from another printserver?
What you are actually looking for, when you only have the IP address is the printers TCPIPPrinterPort, which then is related to a printer. Unfortunatly the PrintServer class in C# doesn't return the hostaddress of the associated port (which is why we always name our ports "IP_10.200.49.230" or the like, because then you can find the port by name instead of hostaddress, which IS included in the printserver class.
In your situation I would do something like this:
static void Main(string[] args)
{
String serverName = "Print-Server"; //set servername (your own computername if you truly are hosting the printers locally)
String ipToSearchFor = "10.91.40.75";//ip to search for in this example
//this loads all TCPPrinterPorts into a Dictionary indexed by the ports Hostaddress (IP)
//I'm loading all because I assume you are going to iterate over them at some point, since It seems you have a list
Dictionary<string, ManagementObject> printerPorts = LoadScope(serverName, "select * from Win32_TCPIPPrinterPort");
//after we've got the ports, open the printserver
using (PrintServer ps = new PrintServer("\\\\" + serverName))
{
//find the queue where queueport.name equals name of port we look up from IP
var queue = ps.GetPrintQueues().Where(p => p.QueuePort.Name == printerPorts[ipToSearchFor]["Name"].ToString()).FirstOrDefault();
//print sharename
Console.WriteLine(queue.ShareName);
}
}
//Loads everything in scope into a dictionary, in this case indexed by hostaddress
private static Dictionary<string, ManagementObject> LoadScope(string server, string query)
{
ManagementScope scope = new ManagementScope("\\\\" + server + "\\root\\cimv2");
scope.Connect();
SelectQuery q = new SelectQuery(query);
ManagementObjectSearcher search = new ManagementObjectSearcher(scope, q);
ManagementObjectCollection pp = search.Get();
Dictionary<string, ManagementObject> objects = new Dictionary<string, ManagementObject>();
foreach (ManagementObject p in pp)
{
string name = p["HostAddress"].ToString().ToLower();
if (!objects.ContainsKey(name))
objects.Add(name, p);
}
return objects;
}
I would advise for you to loop through your list and from here on, also save the sharename and servername of the printer.
I need to find the IP address of the installed printers on my laptop. I move my laptop between different locations and networks. Each network has its own set of ip addresses. The laptop has different printers installed for each location with all connections being made wirelessly.
In using the below code (.net 4.0), the QueuePort.Name returns:
WSD-27e3f972-cdc7-459d-b0c1-20e8410fb1db.0032 and
192.168.1.12_1
Since these are network printers, I assume these have to resolve to a real IP Address??
Where am I going wrong? Or is there a better way? Any help is really appreciated.
IEnumerable<Printer> GetLocalPrinters()
{
EnumeratedPrintQueueTypes[] enumerationFlags = { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections };
LocalPrintServer printServer = new LocalPrintServer();
var x = printServer.GetPrintQueues(enumerationFlags).Select(y =>
new Printer
{
Fullname = y.FullName,
QueuePortName = y.QueuePort.Name,
Location = y.Location
})
.OrderBy( z => z.QueuePortName);
return x;
}
The portname is NOT the IP address. Sometimes they are the same text.
They answer appears to be here:
Determine the IP Address of a Printer in C#
Edited 31-Oct-2011:
Query the WMI for the printer port IP address.
using System;
using System.Management;
namespace WMI_example_01
{
class Program
{
static void Main(string[] args)
{
var scope = new ManagementScope(#"\\.\root\cimv2");
var query = new ObjectQuery("SELECT * FROM win32_tcpipprinterport");
var searcher = new ManagementObjectSearcher(scope, query);
var collection = searcher.Get();
foreach(var col in collection)
{
Console.WriteLine("Port name: {0}\tHostAddress: {1}", col["Name"], col"HostAddress"]);
}
}
}
}
The printing queue has a corresponding port that is handled by the port monitor.
There are different port monitors (not only standard monitors like TCPMON and WSD but also custom and vendor-specific), as far as I know, there is no universal way to deal with all kinds of them.
From the provided port name, I assume you are dealing with the WSD port. Here things become a bit tricky, I suggest you read my answer https://stackoverflow.com/a/63705944/4700228 for the solution.
I am retrieving the default print queues thanks to the help of this question. I am also able to determine the DefaultPrintQueue
But how does one properly determine what print queue in the list of print queues is equal to the DefaultPrintQueue?
I've tried:
var dq = LocalPrintServer.GetDefaultPrintQueue();
foreach(PrintQueue pq in pqcOnLocalServer)
{
if(pq.Equals(dq))
{
System.Console.WriteLine("Found default");
}
}
but the two objects obviously won't be the same. I would then assume I could compare properties of each PrintQueue with the default, but what properties should be used to determine, 100%, that the two PrintQueues are referring to the same PrintQueue?
Try and use the LocalPrintServer.DefaultPrintQueue property to get the default print queue and compare the PrintQueue.FullName. This negates the need to iterate through the LocalPrintServer PrintQueueCollection.
LocalPrintServer printServer = new LocalPrintServer(PrintSystemDesiredAccess.AdministrateServer);
PrintQueue pq = printServer.DefaultPrintQueue;
PrintQueue dq = LocalPrintServer.GetDefaultPrintQueue();
if (dq != null && pq.FullName.Equals(dq.FullName))
{
Console.WriteLine("Found default print Queue: {0}", dq.FullName);
}
If you still need to iterate through the LocalPrintServer PrintQueueCollection you can try the implementation below.
LocalPrintServer printServer = new LocalPrintServer(PrintSystemDesiredAccess.AdministrateServer);
PrintQueue dq = LocalPrintServer.GetDefaultPrintQueue();
foreach (PrintQueue pq in printServer.GetPrintQueues())
{
if (dq != null && pq.FullName.Equals(dq.FullName))
{
Console.WriteLine("Found default print Queue: {0}", dq.FullName);
}
}
This question might have done well on Expert Exchange, or Server Exchange. What I've found is that a print server will not allows printers on the server which have existing names already on the printer server. With that being said, a printer must have a unique name per server.
With that being said, a user must be careful to not only compare printer names to ensure that they are unique, but they must also compare the printer server that they are on. For example, when enumerating connected a printers. A computer could be connected to two print servers where there is a \\PRNTSRVR1\HQ_LaserJet01 and \\PRNTSRVR2\HQ_LaserJet01; so checking the connected server is important too.
There are a lot of questions about getting the name and IP addresses of the local machine and several about getting IP addresses of other machines on the LAN (not all answered correctly). This is different.
In windows explorer if I select Network on the side bar I get a view of local machines on my LAN listed by machine name (in a windows workgroup, anyway). How do I get that same information programatically in C#?
You can try using the System.DirectoryServices namespace.
var root = new DirectoryEntry("WinNT:");
foreach (var dom in root.Children) {
foreach (var entry in dom.Children) {
if (entry.Name != "Schema") {
Console.WriteLine(entry.Name);
}
}
}
You need to broadcast an ARP request for all IPs within a given range. Start by defining the base IP on your network and then setting an upper identifier.
I was going to write up some code examples etc but it looks like someone has covered this comprehensively here;
Stackoverflow ARP question
This seems to be what you are after: How get list of local network computers?
In C#: you can use Gong Solutions
Shell Library
(https://sourceforge.net/projects/gong-shell/)
public List<String> ListNetworkComputers()
{
List<String> _ComputerNames = new List<String>();
String _ComputerSchema = "Computer";
System.DirectoryServices.DirectoryEntry _WinNTDirectoryEntries = new System.DirectoryServices.DirectoryEntry("WinNT:");
foreach (System.DirectoryServices.DirectoryEntry _AvailDomains in _WinNTDirectoryEntries.Children)
{
foreach (System.DirectoryServices.DirectoryEntry _PCNameEntry in _AvailDomains.Children)
{
if (_PCNameEntry.SchemaClassName.ToLower().Contains(_ComputerSchema.ToLower()))
{
_ComputerNames.Add(_PCNameEntry.Name);
}
}
}
return _ComputerNames;
}
I have an application where I need to print a ticket. Each ticket must be unique. The application is windows forms and written entirely in c#. For our application we're using Samsung ML- 2525 laser monochromatic printers.
The flow is basically the following, the operator picks a product/ticket (which is unique) and then it presses a button that does 2 things:
Connects to a database and updates the product as used
Prints the ticket (this is done using System.Drawing and GDI+)
For some reason, every once in a while, the image that needs to be printed is not sent to the printer. It's a rare case, but it happens.
I tried to connect to the printer using Win32_Printer ( http://msdn.microsoft.com/en-us/library/Aa394363 ) but I can't get to get the current printer's state (online, offline, low toner, paper jam, etc). I can only check if the printer exists and that the paper size is installed correctly. I tried code similar to the following but it didn't work
private string MonitorPrintJobWmi()
{
var jobMessage = String.Empty;
var scope = new ManagementScope(ManagementPath.DefaultPath);
scope.Connect();
var selectQuery = new SelectQuery { QueryString = #"select * from Win32_PrintJob" };
var objSearcher = new ManagementObjectSearcher(scope, selectQuery);
var objCollection = objSearcher.Get();
foreach (var job in objCollection)
{
if (job != null)
{
jobMessage += String.Format("{0} \r\n", job["Name"].ToString());
jobMessage += String.Format("{0} \r\n", job["JobId"].ToString());
_jobId = Convert.ToInt32(job["JobId"]);
jobMessage += String.Format("{0} \r\n", job["JobStatus"].ToString());
jobMessage += String.Format("{0} \r\n", job["Status"].ToString());
}
}
return jobMessage;
}
I tried to get an API for the printer but I couldn't get a hold of it. By the way, the printer's software do indicate different errors in the windows toolbar.
My question is if anyone can lead me in the right direction as to how to connect to a printer and check if printing was successful.
Also, it would be helpful if someone know of some other specific printer in which I may accomplish this ie, changing hardware.
Thanks,
To get a list of print queues on the local machine, try PrintServer's GetPrintQueues method.
Once you have an instance of the PrintQueue object associated with the relevant printer, you can use it to access the printer's status (IsOffline, IsPaperOut, etc.). Also, you can use it to get a list of the jobs in the given queue (GetPrintJobInfoCollection) which then will allow you to get job-specific status information (IsInError, IsCompleted, IsBlocked, etc.).
Hope this helps!
After try to print your PrintDocument (System.Drawing.Printing), try to check status of printjobs.
First step: Initialize your printDocument.
Second step: Get your printer Name From System.Drawing.Printing.PrinterSettings.InstalledPrinters.Cast<string>();
And copy it into your printerDocument.PrinterSettings.PrinterName
Third step: Try to print and dispose.
printerDocument.Print();
printerDocument.Dispose();
Last step: Run the check in a Task (do NOT block UI thread).
Task.Run(()=>{
if (!IsPrinterOk(printerDocument.PrinterSettings.PrinterName,checkTimeInMillisec))
{
// failed printing, do something...
}
});
Here is the implementation:
private bool IsPrinterOk(string name,int checkTimeInMillisec)
{
System.Collections.IList value = null;
do
{
//checkTimeInMillisec should be between 2000 and 5000
System.Threading.Thread.Sleep(checkTimeInMillisec);
// or use Timer with Threading.Monitor instead of thread sleep
using (System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_PrintJob WHERE Name like '%" + name + "%'"))
{
value = null;
if (searcher.Get().Count == 0) // Number of pending document.
return true; // return because we haven't got any pending document.
else
{
foreach (System.Management.ManagementObject printer in searcher.Get())
{
value = printer.Properties.Cast<System.Management.PropertyData>().Where(p => p.Name.Equals("Status")).Select(p => p.Value).ToList();
break;
}
}
}
}
while (value.Contains("Printing") || value.Contains("UNKNOWN") || value.Contains("OK"));
return value.Contains("Error") ? false : true;
}
Good luck.