{"id":53733,"date":"2009-05-18T15:23:00","date_gmt":"2009-05-18T15:23:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/05\/18\/hey-scripting-guy-how-can-i-document-printer-usage-on-client-workstations\/"},"modified":"2009-05-18T15:23:00","modified_gmt":"2009-05-18T15:23:00","slug":"hey-scripting-guy-how-can-i-document-printer-usage-on-client-workstations","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-document-printer-usage-on-client-workstations\/","title":{"rendered":"Hey, Scripting Guy! How Can I Document Printer Usage on Client Workstations?"},"content":{"rendered":"<h2><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" width=\"34\" height=\"34\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\"> <\/h2>\n<p>Hey, Scripting Guy! I am tasked with the problem of documenting printer use on our client workstations. I must be able to determine whether someone prints directly to a printer, or if they are printing through our print servers. This is important because we have purchased a print monitoring software package that is used to back-charge the departments for their printer usage. This software runs on the printer servers. If someone prints directly to a network printer through the IP address, the print job does not go through the print server, and therefore we lose the billing. I would love to spend a week going around to each client in our office building, chatting with the users, and looking at their printer settings. Unfortunately, the boss nixed that idea. I told him the revenue we regained would pay my salary for the week, but he was not swayed. He suggested I contact you and ask for help with script ideas.<\/p>\n<p>&#8211; LB<\/p>\n<p><img decoding=\"async\" border=\"0\" alt=\"Spacer\" width=\"5\" height=\"5\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\"><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" width=\"34\" height=\"34\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\"> <\/p>\n<p>Hi LB,<\/p>\n<p>What a refreshing e-mail message. It sounds as if you are serious that you would enjoy a week spent talking to users, you would be surprised how many e-mail messages we receive from IT pros who seek to avoid users. Even your boss seems to be a wise and wonderful person. Last week was a rather eventful week at Tech\u00b7Ed. Ed gave a Windows PowerShell presentation on Friday. Both Ed and Craig talked to thousands of adoring fans and were hounded by autograph seekers all week (some of this sentence uses exaggeration). Needless to say, we are beat. So we are catching up on e-mail, and Ed is mellowing out sipping an excellent cup of <a target=\"_blank\" href=\"http:\/\/en.wikipedia.org\/wiki\/Darjeeling_tea\">Darjeeling tea<\/a> perfectly brewed in my little tea pot with freshly drawn spring water. Two spoons of fresh leaves, seasoned with a stick of <a target=\"_blank\" href=\"http:\/\/en.wikipedia.org\/wiki\/Cinnamon\">Ceylon cinnamon<\/a>. Ed is also listening to <a target=\"_blank\" href=\"http:\/\/en.wikipedia.org\/wiki\/Earth%2C_Wind_%26_Fire\">Earth, Wind &amp; Fire<\/a> on his Zune. Life is good.<\/p>\n<table id=\"EED\" class=\"dataTable\" cellspacing=\"0\" cellpadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" valign=\"top\">\n<td>\n<p class=\"lastInCell\">This week we will examine printing. There are lots of resources related to printing on the TechNet Script Center. There is a collection of <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/printing.mspx\">Hey, Scripting Guy! articles<\/a> that lists 24 articles relates to both server-side and client-side printing. We have almost 50 scripts in the <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/scripts\/printing\/default.mspx\">printer section<\/a> of the <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/scripts\/default.mspx\">Script Center Script Repository<\/a>. There are also almost 50 scripts in the <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/csc\/scripts\/print\/default.mspx\">printer section<\/a> of the Community-Submitted Scripts Center. We have some good technical information about printing in the <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/guide\/sas_prn_overview.mspx\">Scripting Guide<\/a>. All those resources are in VBScript. To use them in Windows PowerShell, you would have to translate the scripts into PowerShell. The Microsoft Press book, <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/MSPress\/books\/authors\/auth9541.aspx\">Windows PowerShell Scripting Guide<\/a>, has a whole chapter devoted to printing.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>You can use WMI to get the printer information that you must have. We wrote a script named <b>GetLocalAndNetworkPrinters.ps1<\/b> in Windows PowerShell for you. A VBScript that provides a similar capability is developed in the <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/aug06\/hey0814.mspx\">\u201cHow Can I List All the Printers on a Remote Computer?\u201d<\/a> Hey, Scripting Guy! article. The complete <b>GetLocalAndNetworkPrinters.ps1<\/b> script is seen here:<\/p>\n<pre class=\"codeSample\">$computer = \"localhost\"Write-Host -foregroundcolor Yellow \"Printer report for $computer...\"Get-WmiObject -class Win32_Printer -computername $computer|Foreach-Object -Begin {$NetworkPrn=$localPrn = @{\"Name\"=\"PortName\"} } ` -Process {  if($_.Local)    {       $localPrn += @{ $_.name = $_.portname }    } #end if if(-not $_.Local)   {   $NetworkPrn += @{ $_.name = $_.portname } } #end if} #end foreach-object process# *** Generate Report ***if($localPrn.count -gt 1){ Write-Host -foreground cyan \"There are $($localPrn.count -1) Local printers on $computer\" $localPrn | Format-Table -auto -Hide -wrap}Else { \"No local printers detected on $computer\" }if($networkPrn.count -gt 1) {  Write-Host -foregroundcolor green \"There are $($networkPrn.count -1) Network printers on $computer \"  $networkPrn | Format-Table -auto -Hide -wrap }Else { Write-Host -foregroundcolor red \"No network printers detected on $computer\"} #end else<\/pre>\n<p>Are you still here? Good, let&#8217;s dig into the <b>GetLocalAndNetworkPrinters.ps1<\/b> script and see whether we can determine what makes it tick. The first thing we have to do is create a variable <b>$computer<\/b> to hold the name of the computer. We set the default value to <b>localhost<\/b>. This is a better name to use for the local computer than the period (&#8220;.&#8221;) that is frequently seen in WMI scripts. This is because, when the report is displayed, a printer report for <b>localhost<\/b> reads better than a printer report for a &#8220;period.&#8221; We use the <b>Write-Host<\/b> cmdlet to print the text in yellow:<\/p>\n<pre class=\"codeSample\">$computer = \"localhost\"Write-Host -foregroundcolor Yellow \"Printer report for $computer...\"<\/pre>\n<p>Now we use WMI to obtain the printer information. The <b>Win32_Printer<\/b> WMI class provides this information. To query from WMI, use the <b>Get-WmiObject<\/b> cmdlet. More information about WMI can be found in our new <a target=\"_blank\" href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/wmi.mspx\">WMI Scripting Hub<\/a>. The resulting management objects are pipelined to the <b>ForEach-Object<\/b> cmdlet:<\/p>\n<pre class=\"codeSample\">Get-WmiObject -class Win32_Printer -computername $computer|<\/pre>\n<p>Next we use the <b>ForEach-Object<\/b> cmdlet to work our way through the detailed printer information that was returned from the <b>Win32_Printer<\/b> WMI class. We use the <b>\u2013Begin<\/b> parameter from the <b>ForEach-Object<\/b> cmdlet to perform an action once for the entire pipeline. We create two hashtables. One is stored in the <b>$networkPrn<\/b> variable and the other in the <b>$localPrn<\/b> variable. The hashtables contain one key and one value: <b>Name<\/b> and <b>PortName<\/b>. We will use these for column headers in our report:<\/p>\n<pre class=\"codeSample\">ForEach-Object -Begin {$networkPrn=$localPrn = @{\"Name\"=\"PortName\"} } `<\/pre>\n<p>The tasks that will be performed once for each item on the pipeline go into the <b>\u2013process<\/b> parameter of the <b>ForEach-Object<\/b> cmdlet. Inside the <b>\u2013process<\/b> parameter, we examine the value of the Boolean property <b>Local<\/b>. If the <b>Local<\/b> property is set to true, it exists. This is shown here:<\/p>\n<pre class=\"codeSample\"> -Process {  if($_.Local)    { <\/pre>\n<p>We add the name of the printer and the port name to the hashtable stored in the <b>$localPrn<\/b> variable if the <b>Local<\/b> property is set to true. To add items to the end of the hashtable, we use the <b>+=<\/b> operator. This is seen here:<\/p>\n<pre class=\"codeSample\">      $localPrn += @{ $_.name = $_.portname }    } #end if<\/pre>\n<p>If the <b>Local<\/b> property does not exist, it means the printer is a network printer: <\/p>\n<pre class=\"codeSample\"> if(-not $_.Local)   {<\/pre>\n<p>Again, we have to add the name of the printer and the port name to the hashtable. This time, the hashtable is stored in the <b>$networkPrn<\/b> variable. This is seen here:<\/p>\n<pre class=\"codeSample\">   $networkPrn += @{ $_.name = $_.portname } } #end if} #end foreach-object process<\/pre>\n<p>After you create the two hashtables, it is time to produce the report. Each hashtable is seeded with the <b>name<\/b> and the <b>portname<\/b> strings in the first row of the hashtable. This means that if no printers are found, the hashtable will have a count of 1. If the hashtable has a count greater than 1, a printer is detected and we want to display the report. This is seen here:<\/p>\n<pre class=\"codeSample\">if($localPrn.count -gt 1){<\/pre>\n<p>Then we use the <b>count<\/b> property from the <b>hashtable<\/b> object to provide information about how many printers are defined on the computer. We must use <b>$localPrn.count-1<\/b> because of using the first row of the hashtable as a header for our report. We have to use a subexpression, which is a <b>$()<\/b> around the <b>$localPrn.count<\/b> to force the evaluation of the <b>count<\/b> property before trying to display it as a string. If we did not do this, we would be greeted with something that looks like this:<\/p>\n<pre class=\"codeSample\">There are System.Collections.Hashtable.count -1 Local printers on localhost<\/pre>\n<p>This is also known as <i>unraveling<\/i> because the name of the object that is displayed in the variable is displayed instead of the value that is contained in the variable. The subexpression is one way to deal with the problem. This is seen here:<\/p>\n<pre class=\"codeSample\"> Write-Host -foreground cyan \"There are $($localPrn.count -1) Local printers on $computer\"<\/pre>\n<p>The other way to deal with the problem of unraveling is to avoid it completely and use concatenation on a literal string instead of using the expanding string. The confusing aspect is that, because you are using the <b>Write-Host<\/b> cmdlet, you do not have to use the concatenation operator (<b>+<\/b>). This is seen here:<\/p>\n<pre class=\"codeSample\">Write-Host -foreground cyan 'There are ' ($localPrn.count -1) ' Local printers on ' $computer<\/pre>\n<p>To display the printer information, we use a really cool trick. We pipeline the <b>hashtable<\/b> object to the <b>Format-Table<\/b> cmdlet. This will enable us a measure of control over the display. In particular, we want to automatically size it, hide the table headers, and wrap the long field printer port names to avoid cutting them off on the display. This is shown here:<\/p>\n<pre class=\"codeSample\"> $localPrn | Format-Table -auto -Hide -wrap}<\/pre>\n<p>If no local printers were detected, we display a message telling the user that there are no local printers:<\/p>\n<pre class=\"codeSample\">Else { \"No local printers detected on $computer\" }<\/pre>\n<p>Now we perform the identical steps for the network printers:<\/p>\n<pre class=\"codeSample\">if($networkPrn.count -gt 1) {Write-Host -foregroundcolor green \"There are $($networkPrn.count -1) Network printers on $computer \" $networkPrn | Format-Table -auto -Hide -wrap }Else {Write-Host -foregroundcolor red \"No network printers detected on $computer\"} #end else<\/pre>\n<p>That is all the moving parts involved in the script. When you run it, you are greeted with a display that resembles the one shown here (of course, the display that is seen after you run the script depends on your printer configuration):<\/p>\n<p><img decoding=\"async\" border=\"0\" alt=\"Image of the information displayed after running the script\" width=\"500\" height=\"248\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/may\/hey0518\/hsg-05-18-09-01.jpg\"> <\/p>\n<p>&nbsp;<\/p>\n<p>Well, LB, that is all there is to producing a listing of local and network printers from your workstation. It should work on a remote computer as well as on a local computer. You just have to change the name of the destination computer. I have to go unpack, and then I plan to hit the treadmill. Too much good Tech\u00b7Ed food. See you tomorrow when Printer Week continues. Until then, peace.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! I am tasked with the problem of documenting printer use on our client workstations. I must be able to determine whether someone prints directly to a printer, or if they are printing through our print servers. This is important because we have purchased a print monitoring software package that is used to [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[445,404,3,5,45],"class_list":["post-53733","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-client-side-printing","tag-printing","tag-scripting-guy","tag-vbscript","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! I am tasked with the problem of documenting printer use on our client workstations. I must be able to determine whether someone prints directly to a printer, or if they are printing through our print servers. This is important because we have purchased a print monitoring software package that is used to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/53733","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=53733"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/53733\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=53733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=53733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=53733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}