tags:

views:

124

answers:

3

In a LAN normally printers are shared, and we can add these shared computers in the LAN to our machine through Windows "Add Remote Printer". I want to get the list of added printer like this, and there online status and printer settings through C#. The list of added printers could be obtained by

System.Drawing.Printing.PrinterSettings.InstalledPrinters

through this code I can get the list of added printers to a combobox. The problem is how can I get the online status of each printer, and there other possible settings through the c# code. Please help.

+2  A: 

Using WMI is the common option for this kind of task...

    ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer");

                foreach (ManagementObject printer in searcher.Get())
                {
                    string printerName = printer["Name"].ToString().ToLower();
                    Console.WriteLine("Printer :" + printerName);
                    PrintProps(printer, "Status");
                    PrintProps(printer, "PrinterState");
                    PrintProps(printer, "PrinterStatus");


                }

}

static void PrintProps(ManagementObject o, string prop)     
{         
        try { Console.WriteLine(prop + "|" + o[prop]); 
        }         
        catch (Exception e) 
        { 
            Console.Write(e.ToString()); 
        }     
}   
Jonathan
Thank you very much for the answer. this method worked successfully but sometimes it doesn't give the most accurate status since the status it determined by the print job information. MSDN says so.. but I wonder how Windows 7 finds very accurately the status of a printer whether its online or offline and is there a way that we can get that status which Windows displays; through c# code?
Zerone
@Zerone: Did you forget to upvote this answer?
pipitas
@pipitas: not that i forgot I did not notice the up vote; joined very recently. and thanks for pointing that out.
Zerone
+2  A: 

Online Status

Consider this CodeProject article entitled "How To Check If Your Printer Is Connected" The sample code uses WMI and the System.Management namespace.

Copy/pasted:

   ManagementScope scope = new ManagementScope(@"\root\cimv2");
   scope.Connect();

   // Select Printers from WMI Object Collections

   ManagementObjectSearcher searcher = new 
    ManagementObjectSearcher("SELECT * FROM Win32_Printer");

   string printerName = "";
   foreach (ManagementObject printer in searcher.Get()) 
   {
    printerName = printer["Name"].ToString().ToLower();
    if (printerName.Equals(@"hp deskjet 930c"))
    {
     Console.WriteLine("Printer = " + printer["Name"]); 
     if (printer["WorkOffline"].ToString().ToLower().Equals("true"))
     {
      // printer is offline by user

      Console.WriteLine("Your Plug-N-Play printer is not connected.");
     }
     else
     {
      // printer is not offline

       Console.WriteLine("Your Plug-N-Play printer is connected.");
     }
    }
   }
  }
p.campbell
Thank you very much for the answer. this method also worked successfully but sometimes it doesn't give the most accurate status since the status it determined by the print job information. MSDN says so.. but I wonder how Windows 7 finds very accurately the statu of a printer whether its online or offline and is there a way that we can get that status which windows displayes; through c# code?
Zerone
@Zerone: even if you have some questions left open, I think this answer deserves an upvote as well.
pipitas
@pipitas: thanks for pointing out the up vote.
Zerone
+1  A: 

As an alternative to WMI, you can use the Print Spooler API to gather detailed information about the printers. Most of the P/Invoke signatures for the API are available on http://www.pinvoke.net.

Open a handle to a printer: http://www.pinvoke.net/default.aspx/winspool.OpenPrinter

Gather printer information: http://www.pinvoke.net/default.aspx/winspool.GetPrinterData

Edit:

Quickly smashed together an example of what you can gather from the PrintSpoolerApi as per request.

PrintSpoolerAPIExample:

Create new console project and replace all code in Program.cs with this (.NET 3.5 and above, due to type inference), the printer details of each printer available on the machine (local or networked) will be printed to the console. The status value is what you are interested in. The status code for "Ready" is 0, mess around by disabling printers and disabling your network connection to networked printers to see the status change.

using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

namespace PrintSpoolerAPIExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var printers = System.Drawing.Printing.PrinterSettings.InstalledPrinters as IEnumerable;

            foreach (string printer in printers)
            {
                var printerInfo = PrintSpoolerApi.GetPrinterProperty(printer);
                StringBuilder sb = new StringBuilder();
                sb.AppendLine(string.Format("ServerName:{0}", printerInfo.ServerName));
                sb.AppendLine(string.Format("PrinterName:{0}", printerInfo.PrinterName));
                sb.AppendLine(string.Format("ShareName:{0}", printerInfo.ShareName));
                sb.AppendLine(string.Format("PortName:{0}", printerInfo.PortName));
                sb.AppendLine(string.Format("DriverName:{0}", printerInfo.DriverName));
                sb.AppendLine(string.Format("Comment:{0}", printerInfo.Comment));
                sb.AppendLine(string.Format("Location:{0}", printerInfo.Location));
                sb.AppendLine(string.Format("DevMode:{0}", printerInfo.DevMode));
                sb.AppendLine(string.Format("SepFile:{0}", printerInfo.SepFile));
                sb.AppendLine(string.Format("PrintProcessor:{0}", printerInfo.PrintProcessor));
                sb.AppendLine(string.Format("Datatype:{0}", printerInfo.Datatype));
                sb.AppendLine(string.Format("Parameters:{0}", printerInfo.Parameters));
                sb.AppendLine(string.Format("Attributes:{0}", printerInfo.Attributes));
                sb.AppendLine(string.Format("Priority:{0}", printerInfo.Priority));
                sb.AppendLine(string.Format("DefaultPriority:{0}", printerInfo.DefaultPriority));
                sb.AppendLine(string.Format("StartTime:{0}", printerInfo.StartTime));
                sb.AppendLine(string.Format("UntilTime:{0}", printerInfo.UntilTime));
                sb.AppendLine(string.Format("Status:{0}", printerInfo.Status));
                sb.AppendLine(string.Format("Jobs:{0}", printerInfo.Jobs));
                sb.AppendLine(string.Format("AveragePpm:{0}", printerInfo.AveragePpm));
                Console.WriteLine(sb.ToString());
            }

            Console.ReadLine();
        }
    }

    class PrintSpoolerApi
    {
        [DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool OpenPrinter(
            [MarshalAs(UnmanagedType.LPTStr)]
            string printerName,
            out IntPtr printerHandle,
            PrinterDefaults printerDefaults);

        [DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool GetPrinter(
            IntPtr printerHandle,
            int level,
            IntPtr printerData,
            int bufferSize,
            ref int printerDataSize);

        [DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool ClosePrinter(
            IntPtr printerHandle);

        [StructLayout(LayoutKind.Sequential)]
        public struct PrinterDefaults
        {
            public IntPtr pDatatype;
            public IntPtr pDevMode;
            public int DesiredAccess;
        }

        public enum PrinterProperty
        {
            ServerName,
            PrinterName,
            ShareName,
            PortName,
            DriverName,
            Comment,
            Location,
            PrintProcessor,
            Datatype,
            Parameters,
            Attributes,
            Priority,
            DefaultPriority,
            StartTime,
            UntilTime,
            Status,
            Jobs,
            AveragePpm
        };

        public struct PrinterInfo2
        {
            [MarshalAs(UnmanagedType.LPTStr)]
            public string ServerName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string PrinterName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string ShareName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string PortName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string DriverName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string Comment;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string Location;
            public IntPtr DevMode;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string SepFile;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string PrintProcessor;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string Datatype;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string Parameters;
            public IntPtr SecurityDescriptor;
            public uint Attributes;
            public uint Priority;
            public uint DefaultPriority;
            public uint StartTime;
            public uint UntilTime;
            public uint Status;
            public uint Jobs;
            public uint AveragePpm;
        }

        public static PrinterInfo2 GetPrinterProperty(string printerUncName)
        {
            var printerInfo2 = new PrinterInfo2();

            var pHandle = new IntPtr();
            var defaults = new PrinterDefaults();
            try
            {
                //Open a handle to the printer
                bool ok = OpenPrinter(printerUncName, out pHandle, defaults);

                if (!ok)
                {
                    //OpenPrinter failed, get the last known error and thrown it
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                //Here we determine the size of the data we to be returned
                //Passing in 0 for the size will force the function to return the size of the data requested
                int actualDataSize = 0;
                GetPrinter(pHandle, 2, IntPtr.Zero, 0, ref actualDataSize);

                int err = Marshal.GetLastWin32Error();

                if (err == 122)
                {
                    if (actualDataSize > 0)
                    {
                        //Allocate memory to the size of the data requested
                        IntPtr printerData = Marshal.AllocHGlobal(actualDataSize);
                        //Retrieve the actual information this time
                        GetPrinter(pHandle, 2, printerData, actualDataSize, ref actualDataSize);

                        //Marshal to our structure
                        printerInfo2 = (PrinterInfo2)Marshal.PtrToStructure(printerData, typeof(PrinterInfo2));
                        //We've made the conversion, now free up that memory
                        Marshal.FreeHGlobal(printerData);
                    }
                }
                else
                {
                    throw new Win32Exception(err);
                }

                return printerInfo2;
            }
            finally
            {
                //Always close the handle to the printer
                ClosePrinter(pHandle);
            }
        }
    }
}

You can retrieve even more details from the API, if required.

fletcher
I used the above 2 methods which queries the WMI but sometimes it doesn't give accurate information. MSDN says since the status of a printer is determined by the print job status it cant display most accurate information until it tries to print. But all I need is to find out whether printer is online or offline, and I see windows very accurately show the status of its added printer locally or remotely under "Devices and Printers". Is there anyway to access that windows status through c# code? also I'm still learning and I don't understand print Spooler API do u have more resources on that?
Zerone
Edited the main post to include a quick example
fletcher
@Zerone - Did this help, or did you find another way of doing this?
fletcher
@Zerone: I think you should upvote this answer.
pipitas
@fletcher: yes this helped a lot thank you very much for your very clear example and instructions, very helpful.
Zerone
@pipitas: Thank you for pointing out the up vote, It says that up vote is blocked for this answer until its edited again..
Zerone