Expiring Today—Celebrate National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Getting a List of NT Printers

Posted on 1999-12-12
15
Medium Priority
?
481 Views
Last Modified: 2012-06-27
How do I get a list of printers that are installed on an NT Server using Perl code?
0
Comment
Question by:paulca
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 6
15 Comments
 
LVL 2

Expert Comment

by:cadabra
ID: 2275730
Check out the following package:

Win32::NetResource - manage network resources in perl

This module offers control over the network resources of Win32.Disks, printers etc can be shared over a network. The documentation (NetResource.html) shows how to print a list of shared disks and printers.


The package can be found at http://www.activestate.com/packages/zips/libwin32.zip


Another possible alternative to this would be to try to use Win32-API package to access the enumprinters win32 API calls
0
 

Author Comment

by:paulca
ID: 2277943
I am only looking for the printers that are installed on the actual server, not the entire list of printers on the network.  Some of the printers that were listed, were not installed on my server but were shared printers on the network.  Also, I am getting an Access Denied error.  Any insight on that?
0
 
LVL 2

Expert Comment

by:cadabra
ID: 2278183
My best bet would be to use the Win32-API package and call the printer enumeration functions, which return structures called PRINTER_INFOn if I remember correctly (I have no access to the documentation right now).
I hope someone else has an easier solution.

What say you ?
Cadabra.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 2

Accepted Solution

by:
cadabra earned 1000 total points
ID: 2282795
Hi paulca,

The following code works on my system (windows NT4 server) with activestate perl 522. It relies on the Win32-API package.

Code starts bellow:
================================================================================

#!perl -w
use Win32::API;

# BOOL EnumPrinters(
#   DWORD Flags,         // printer object types
#   LPTSTR Name,         // name of printer object
#   DWORD Level,         // information level
#   LPBYTE pPrinterEnum, // printer information buffer
#   DWORD cbBuf,         // size of printer information buffer
#   LPDWORD pcbNeeded,   // bytes received or required
#   LPDWORD pcReturned   // number of printers enumerated
# );

my $EnumPrinters = new Win32::API("winspool.drv", "EnumPrinters",
['N','P','N','P','N','P','P'], 'I') || die;

my $Flags = 2;  # request local printers.
my $Level = 4; # _PRINTER_INFO_4
my $BuffSize = 0; # initial setting.
my $PinfoBuffer = "\0"; # initial setting
my $PrinterName = "\0";
my $pack_pcbNeeded= pack("L", 0);
my $pack_pcReturned = pack("L", 0);

# First find out how many bytes are going to be returned.
if (defined $EnumPrinters->Call($Flags,$PrinterName,$Level,$PinfoBuffer,
                              $BuffSize,$pack_pcbNeeded,$pack_pcReturned))
{
   $BuffSize = unpack("L", $pack_pcbNeeded);
   $PinfoBuffer = "\0" x $BuffSize;
}
else {
   die $^E;
}

# Now get our printer information.
if ($EnumPrinters->Call($Flags,$PrinterName,$Level,$PinfoBuffer,
                              $BuffSize,$pack_pcbNeeded,$pack_pcReturned))
{

   my $pcReturned = unpack("L", $pack_pcReturned);
   print "NumPrinters=$pcReturned\n";
#   print "RawBuffer=[[$PinfoBuffer]]\n";

   #typedef struct _PRINTER_INFO_4 {
   #    LPTSTR  pPrinterName;
   #    LPTSTR  pServerName;
   #    DWORD  Attributes;
   #} PRINTER_INFO_4;

   # get the length of a /strpointer, strpointer, dword/
   my $atom = length(pack("PPL", 0, 0, 0));

   my $offset = 0;
   for (1..$pcReturned) {
      my $info = substr($PinfoBuffer, $offset, $atom);
      my ($name, $server, $attr) = unpack("P128P128L", $info);
      $offset = $_ * $atom;

        $name =~ s/\0.*$//;
      print "$name";
        $_ = $name;
        if ( /^\\/ )  {  print "\n"; }
        else { print "   <-- local\n"; }
   }
}
else {
   die $^E;
}




0
 
LVL 2

Expert Comment

by:cadabra
ID: 2283317
Code fixed up a bit:
===================================================================================

#!perl -w
use Win32::API;

# BOOL EnumPrinters(
#   DWORD Flags,         // printer object types
#   LPTSTR Name,         // name of printer object
#   DWORD Level,         // information level
#   LPBYTE pPrinterEnum, // printer information buffer
#   DWORD cbBuf,         // size of printer information buffer
#   LPDWORD pcbNeeded,   // bytes received or required
#   LPDWORD pcReturned   // number of printers enumerated
# );

my $EnumPrinters = new Win32::API("winspool.drv", "EnumPrinters",
['N','P','N','P','N','P','P'], 'I') || die;

my $Flags = 2;  # request local printers.
my $Level = 4; # _PRINTER_INFO_4
my $BuffSize = 0; # initial setting.
my $PinfoBuffer = "\0"; # initial setting
my $PrinterName = "\0";
my $pack_pcbNeeded= pack("L", 0);
my $pack_pcReturned = pack("L", 0);

# First find out how many bytes are going to be returned.
if (defined $EnumPrinters->Call($Flags,$PrinterName,$Level,$PinfoBuffer,
                              $BuffSize,$pack_pcbNeeded,$pack_pcReturned))
{
   $BuffSize = unpack("L", $pack_pcbNeeded);
   $PinfoBuffer = "\0" x $BuffSize;
}
else {
   die $^E;
}

# Now get our printer information.
if ($EnumPrinters->Call($Flags,$PrinterName,$Level,$PinfoBuffer,
                              $BuffSize,$pack_pcbNeeded,$pack_pcReturned))
{

   my $pcReturned = unpack("L", $pack_pcReturned);
   print "NumPrinters=$pcReturned\n";
#   print "RawBuffer=[[$PinfoBuffer]]\n";

   #typedef struct _PRINTER_INFO_4 {
   #    LPTSTR  pPrinterName;
   #    LPTSTR  pServerName;
   #    DWORD  Attributes;
   #} PRINTER_INFO_4;

   # get the length of a /strpointer, strpointer, dword/
   my $atom = length(pack("PPL", 0, 0, 0));

   my $offset = 0;
   for (1..$pcReturned) {
      my $info = substr($PinfoBuffer, $offset, $atom);
      my($name, $server, $attr) = unpack("ppL", $info);
      $offset = $_ * $atom;

      print "$name";
      $_ = $name;
      if ( /^\\/ ) { print "\n"; } else { print "  <-- local\n"; }
   }
}
else {
   die $^E;
}



0
 

Author Comment

by:paulca
ID: 2283893
I need to get info from PRINTER_INFO_2.  I completed the revision but I would like to double check.  Do you have the revised code for PRINTER_INFO_2.
0
 
LVL 2

Expert Comment

by:cadabra
ID: 2284015
I don't have code for PRINTER_INFO_2. (I didn't have code for PRINTER_INFO_4 before either).

I just had a look at the PRINTER_INFO_2 structure, and PHEW thats one long structure.

If you like, you can post the code you wrote and I will double check it for you.
0
 

Author Comment

by:paulca
ID: 2284039
  #typedef struct _PRINTER_INFO_2 {
   # LPTSTR    pServerName;
   # LPTSTR    pPrinterName;
   # LPTSTR    pShareName;
   # LPTSTR    pPortName;
   # LPTSTR    pDriverName;
   # LPTSTR    pComment;
   # LPTSTR    pLocation;
   # LPDEVMODE pDevMode;
   # LPTSTR    pSepFile;
   # LPTSTR    pPrintProcessor;
   # LPTSTR    pDatatype;
   # LPTSTR    pParameters;
   # PSECURITY_DESCRIPTOR   pSecurityDescriptor;
   # DWORD     Attributes;
   # DWORD     Priority;
   # DWORD     DefaultPriority;
   # DWORD     StartTime;
   # DWORD     UntilTime;
   # DWORD     Status;
   # DWORD     cJobs;
   # DWORD     AveragePPM;
   #} PRINTER_INFO_2;

   # get the length of a /strpointer, strpointer, strpointer, dword/
   my $atom = length(pack("PPPPPPPPPPPPPLLLLLLLL", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));

   my $offset = 0;
   for (1..$pcReturned) {
      my $info = substr($PinfoBuffer, $offset, $atom);
      my($server, $name, $share, $port, $driver, $comment, $location, $devstruct, $sepfile, $printproc, $data, $parameters, $descript, $attr, $priority, $default, $start, $until, $status, $cjobs, $average) = unpack("pppppppppppppLLLLLLL", $info);
      $offset = $_ * $atom;

      print "\nName: $name\nServer: $server\nDriver: $driver\nAttributes: $attr\n";
      $_ = $name;
      if ( /^\\/ ) { print "\n"; } else { print "  <-- local\n"; }
   }
}
else {
   die $^E;
}
0
 
LVL 2

Expert Comment

by:cadabra
ID: 2284256
Hi paulca

There was a problem on the line with the unpack command:

Bad:
  unpack("pppppppppppppLLLLLLL", $info)
Good:
  unpack("pppppppppppppLLLLLLLL", $info)


Here is the full code. I also added a check for $server being defined.

I sure am glad you don't have to analyze the devmode structure ...


Cadabra.
=============================================================================================
#!perl -w
use Win32::API;

# BOOL EnumPrinters(
#   DWORD Flags,         // printer object types
#   LPTSTR Name,         // name of printer object
#   DWORD Level,         // information level
#   LPBYTE pPrinterEnum, // printer information buffer
#   DWORD cbBuf,         // size of printer information buffer
#   LPDWORD pcbNeeded,   // bytes received or required
#   LPDWORD pcReturned   // number of printers enumerated
# );

my $EnumPrinters = new Win32::API("winspool.drv", "EnumPrinters",
['N','P','N','P','N','P','P'], 'I') || die;

my $Flags = 2;  # request local printers.
my $Level = 2; # _PRINTER_INFO_2
my $BuffSize = 0; # initial setting.
my $PinfoBuffer = "\0"; # initial setting
my $PrinterName = "\0";
my $pack_pcbNeeded= pack("L", 0);
my $pack_pcReturned = pack("L", 0);

# First find out how many bytes are going to be returned.
if (defined $EnumPrinters->Call($Flags,$PrinterName,$Level,$PinfoBuffer,
                              $BuffSize,$pack_pcbNeeded,$pack_pcReturned))
{
   $BuffSize = unpack("L", $pack_pcbNeeded);
   $PinfoBuffer = "\0" x $BuffSize;
}
else {
   die $^E;
}


# Now get our printer information.
if ($EnumPrinters->Call($Flags,$PrinterName,$Level,$PinfoBuffer,
                              $BuffSize,$pack_pcbNeeded,$pack_pcReturned))
{

   my $pcReturned = unpack("L", $pack_pcReturned);
   print "NumPrinters=$pcReturned\n";
#   print "RawBuffer=[[$PinfoBuffer]]\n";

   #typedef struct _PRINTER_INFO_4 {
   #    LPTSTR  pPrinterName;
   #    LPTSTR  pServerName;
   #    DWORD  Attributes;
   #} PRINTER_INFO_4;


   #typedef struct _PRINTER_INFO_2 {
   # LPTSTR    pServerName;
   # LPTSTR    pPrinterName;
   # LPTSTR    pShareName;
   # LPTSTR    pPortName;
   # LPTSTR    pDriverName;
   # LPTSTR    pComment;
   # LPTSTR    pLocation;
   # LPDEVMODE pDevMode;
   # LPTSTR    pSepFile;
   # LPTSTR    pPrintProcessor;
   # LPTSTR    pDatatype;
   # LPTSTR    pParameters;
   # PSECURITY_DESCRIPTOR   pSecurityDescriptor;
   # DWORD     Attributes;
   # DWORD     Priority;
   # DWORD     DefaultPriority;
   # DWORD     StartTime;
   # DWORD     UntilTime;
   # DWORD     Status;
   # DWORD     cJobs;
   # DWORD     AveragePPM;
   #} PRINTER_INFO_2;

   # get the length of a /13 strpointers, 8 dwords/
   my $atom = length(pack("PPPPPPPPPPPPPLLLLLLLL", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));

   my $offset = 0;
   for (1..$pcReturned) {
      my $info = substr($PinfoBuffer, $offset, $atom);
      my($server, $name, $share, $port, $driver, $comment, $location, $devstruct, $sepfile, $printproc, $data, $parameters, $descript, $attr, $priority, $default, $start, $until, $status, $cjobs, $average) = unpack("pppppppppppppLLLLLLLL", $info);
      $offset = $_ * $atom;
      if (!defined($server)) { $server = ""; }
      print "\nName: $name\nServer: $server\nDriver: $driver\nAttributes: $attr\n";
      $_ = $name;
      if ( /^\\/ ) { print "\n"; } else { print "  <-- local\n"; }
   }
}
else {
   die $^E;
}  

0
 

Author Comment

by:paulca
ID: 2284428
Actually I have may have to analyze the DEVMODE struct.  I might have to pass it over a C++ program.  I'm really looking forward to that.
0
 
LVL 2

Expert Comment

by:cadabra
ID: 2284544
I am interested in what context you are using this code, and why you chose perl specifically to this task, if you intend to pass the info to a C++ program.

Care to share ?
Cadabra.
0
 

Author Comment

by:paulca
ID: 2284582
I have a web based app in which a client can choose a printer from the server.  I want to pass it the printer properties so that that it can check to make sure that the printer driver supports the selections made in the browser. If it does, it will go ahead and print the document according to the users specifications.
0
 
LVL 2

Expert Comment

by:cadabra
ID: 2285055
Does your app currently support printing from the server ?

I once wrote and maintained an app that did batch printing from an NT server (in Delphi / C++) using the win32 api, where users submitted requests for server generated reports through Oracle database pipes. Things to be aware of are (forgive inaccuracies, I am doing this from memory):

1. When printers are offline, especially remote printers, and you try to query them with API (especially queries trying to get the printer state) you may sometimes get hangs (for minutes).
2. Processes trying to print to a problematic / not responding printer could accumulate and burden the system. Some mechanism could be written to queue print requests, and dequeue / print them in backgound so that the process requesting the print does not stall while waiting for acknowledge from printer.
4. Problematic print queues could impact other applications running on the same server, I have seen renegade print queues gobbling up 100% of a servers CPU, bringing all activity to a standstill.
5. If possible, I recommend having a different server handling the printing issues, so if things get stuck there, at least the online application is not stalled.
6. If you are getting all sorts of problems with certain print queues, try to define them in a different manner, TCP/IP printing etc. The technet has a lot of articles about these problems .

I have a bunch of technet articles I used when faced with these problems lying about somewhere. If you like, I can look for them, and send you the article numbers or documents.

Other nice features you can have in a server generated print system, is defining multiple NT queues on the same physical port, with each queue representing a different paper tray, different document defaults etc. Your application can define printer groups, and control printers; pause them, resume them via win32 api etc.
One of my clients requested this feature, and it works well.

Cadabra.
0
 

Author Comment

by:paulca
ID: 2286713
Those are some very good points you made and I'll definately look into them.  I would appreciate it if you could send me the article number.  Thanks.
0
 
LVL 2

Expert Comment

by:cadabra
ID: 2289556
Hi,

Here are the technet articles which helped solve severe printing
problems at one of my clients. There were around 300 printer queues
setup on the printing machine (NT server), most were remote printers
(over WAN). The most problematic queues were shared printers on user's
win95 workstations (steer clear of this if possible),
next came print-server based printers, and the most reliable printers
were those with network cards installed in them.

Hope this helps, and that you won't need to use the information
bellow :)

Cadabra.






SESSTIMEOUT Information
PSS ID Number: Q102067


HP JetDirect Firmware Versions and Windows NT Protocol Support
PSS ID Number: Q124293


Text of RFC1179 Standard for Windows NT TCP/IP Printing
PSS ID Number: Q124734


Print Job to LPR Printer Does Not Print
PSS ID Number: Q128352


Windows NT LPR Does Not Support Job Removal
PSS ID Number: Q134854


Printing to HP Laserjet Printer Results in Event 2004
PSS ID Number: Q142370


TCP/IP Printing Causes File Cache to Grow
PSS ID Number: Q149658


LPR Printers Show Status Unknown
PSS ID Number: Q163241


Troubleshooting Printing Problems in Windows NT 4.0
PSS ID Number: Q163551


Print Queue May Stop Responding When Running LPR on Server
PSS ID Number: Q167035


Configuring Individual Printers to Passthrough LPR Print Jobs
PSS ID Number: Q168457


How to Modify the TCP/IP Maximum Retransmission Timeout
PSS ID Number: Q170359


Updated TCP/IP Printing Options for Windows NT 4.0 SP3 and Later
PSS ID Number: Q179156

0

Featured Post

On Demand Webinar: Networking for the Cloud Era

Ready to improve network connectivity? Watch this webinar to learn how SD-WANs and a one-click instant connect tool can boost provisions, deployment, and management of your cloud connection.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

On Microsoft Windows, if  when you click or type the name of a .pl file, you get an error "is not recognized as an internal or external command, operable program or batch file", then this means you do not have the .pl file extension associated with …
There are many situations when we need to display the data in sorted order. For example: Student details by name or by rank or by total marks etc. If you are working on data driven based projects then you will use sorting techniques very frequently.…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
Six Sigma Control Plans

718 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question