Print PDFs to specific printers based on filename - powershell

I would just like to preface this by saying I am brand new to Powershell and have been trying to learn by picking things up here and there. I'm currently trying to automate a process within my company using strictly powershell and Adobe reader.
Our company currently is manually printing individual sets of records and a separate cover page, binding them, and sending them off. An idea to automate this process was to fill a folder with a zipped set of .pdfs for the day. This zip file would then be extracted and it's contents moved to another folder. PDFs with the normal set of records listed as "WO-xxxxxx Set" and the cover page as "WO-xxxxxx Cover". All I would need to do is create a simple script that prints these out in order, so that "WO-000001 Cover" is on top of "WO-000001 Set" and then print the next set in the order.
The complication I've run into is that Start-Process -FilePath $File.Fullname -Verb Print only allows me to target a default printer. Our Covers will need to be printed on thicker paper, and as such I thought the best course of action would be to create two printers on the network with the required printer settings. If I could have the script swap between the two printers based on file name then it would solve my issue.
This script is sending the documents to the printer in order but not actually swapping the default printer. I'm sure this is something I've done wrong in my IfElse cmdlet and would appreciate an experts eye in this.
Function UnZipEverything($src, $dest)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
$zps = Get-ChildItem $src -Filter *.zip
foreach ($zp IN $zps)
{
$all = $src + $zp
[System.IO.Compression.ZipFile]::ExtractToDirectory($all, $dest)
}
}
UnZipEverything -src "C:\Users\admin\Desktop\Zip Test\" -dest'C:\Users\admin\Desktop\UnZip Test\'
Remove-Item "C:\Users\admin\Desktop\Zip Test\*.zip"
$files = Get-ChildItem “C:\Users\admin\Desktop\UnZip Test\*.*” -Recurse
ForEach ($file in $files){
If ($files -eq '*Cover*') {
(New-Object -ComObject WScript.Network).SetDefaultPrinter('Test')
Start-Process -FilePath $File.FullName -Verb Print -PassThru | %{ sleep 10;$_ } | kill
(New-Object -ComObject WScript.Network).SetDefaultPrinter('\\RFC-Print01\Collections Tray 6')
}
Else {Start-Process -FilePath $File.FullName -Verb Print -PassThru | %{ sleep 10;$_ } | kill
}
}
Any help would be greatly appreciated.

If you use the verb PrintTo instead of Print, you can specify the printer:
Start-Process -FilePath $File.FullName -Verb PrintTo '\\RFC-Print01\Collections Tray 6' -PassThru
This would allow you to remove the SetDefaultPrinter calls from the script.

Related

Powershell Form watching wsusutil.exe until finished

This is my first form I'm making to accomplish a task, but also to learn how to do it. I'm making a simple front end for exporting and then importing WSUS data from a connected server to a disconnected server. I'm working on the export part now and I need to figure out how to start the export process, then once it is done then make the iso file.
Here is the button code I have so far, but not sure how to watch the WsusUtil.exe until it's done then proceed to the next task. I thought I could watch the process and when it's over move to the next step. But have not been able to make that work. I tried a do until, but it kept running the start-process over and over. Also when it starts it make a large black box with some message on it. I tried to use the -NoNewWindow but it did launched. WsusUtil.exe running
$btnStartExport.Add_Click({
$nicedate = get-date -UFormat %m-%d-%y #put in MM-DD-YY format
#progress bar for overall progress of steps 1 and 2 and individual progress bar for each steps
$WSUSUtilPath = "c:\Program Files\Update Services\Tools\"
$WSUSMetaDataPath = "c:\tools\wsusexport\"
$isotitle = "WSUS Offline Server update $nicedate"
$ProcessName = "WsusUtil" #process to watch until it's done
$isofilename = "WSUSSvrOffline-$nicedate.iso" #creates WSUS Offline Server update-11-14-2021.iso
#Step 1 Check if directory exists
Check-Path $WSUSMetaDataPath
#Step 1 - export the WSUS Metadata
Start-process -FilePath $WSUSUtilPath\WsusUtil.exe -ArgumentList #("export","$WSUSMetaDataPath\$nicedate-export.xml.gz","$WSUSMetaDataPath\$nicedate-export.log") -NoNewWindow -Wait
$wsusProcess = get-process WsusUtil -ErrorAction SilentyContinue
# Step 2 - create ISO
get-childitem "$WSUSMetaDataPath","$txtWSUSContentPath.text" | New-IsoFile -path $txtISOLocation.text+$isofilename -Force -Title $isotitle
#clean up medatadata directory
get-childitem -path $WSUSMetaDataPath -Include *.* -File -Recurse | foreach {$_.Delete()}
})
$frmMain.controls.add($btnStartExport)

Executing a file that may reside at two different locations using PowerShell

I have a cute little script in my $PROFILE helping me start Notepad++ from the script showing a file of my choosing.
function Edit{
param([string]$file = " ")
Start-Process "C:\Program Files\Notepad++\notepad++.exe" -ArgumentList $file
}
It's worked great until recently, where I jump between different systems. I discovered that NPP is installed in C:\Program Files on some systems but in C:\Program Files (x86) on others. I can edit the script adapting it but having done so a gazillion times (i.e. 5 to this point), I got sick and tired of it, realizing that I have to automate this insanity.
Knowing little about scripting, I wonder what I should Google for. Does best practice dictate using exception handling in such a case or is it more appropriate to go for conditional expressions?
According to Get-Host | Select-Object Version I'm running version 5.1, if it's of any significance. Perhaps there's an even neater method I'm unaware of? Relying on an environment variable? I'd also prefer to not use a method valid in an older version of PS, although working, if there's a more convenient approach in a later one. (And given my experience on the subject, I can't tell a duck from a goose.)
I would use conditionals for this one.
One option is to test the path directly if you know for certain it is in a particular location.
Hard coded paths:
function Edit{
param([string]$file = " ")
$32bit = "C:\Program Files (x86)\Notepad++\notepad++.exe"
$64bit = "C:\Program Files\Notepad++\notepad++.exe"
if (Test-Path $32bit) {Start-Process -FilePath $32bit -ArgumentList $file}
elseif (Test-Path $64bit) {Start-Process -FilePath $64bit -ArgumentList $file}
else {Write-Error -Exception "NotePad++ not found."}
}
Another option is pulling path information from registry keys, if they're available:
function Edit{
param([string]$file = " ")
$32bit = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Notepad++\' -ErrorAction SilentlyContinue).("(default)")
$64bit = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Notepad++\' -ErrorAction SilentlyContinue).("(default)")
if ($32bit) {Start-Process -FilePath "$32bit\notepad++.exe" -ArgumentList $file}
elseif ($64bit) {Start-Process -FilePath "$64bit\notepad++.exe" -ArgumentList $file}
else {Write-Error -Exception "NotePad++ not found."}
}
Based on the great help from #BoogaRoo (who should get some +1 for effort) and asked by the same to post my own version of the answer, I go against my reluctance to post asnwers to own questions due to strong sensation of tackiness.
My final version, taking into account systems that lack NP++ but still want to show the editor of some kind.
function Edit{
param([string]$file = " ")
$executable = "Notepad++\notepad++.exe"
$32bit = "C:\Program Files (x86)\" + $executable
$64bit = "C:\Program Files\" + $executable
$target = "notepad"
if(Test-Path $32bit) { $target = $32bit }
if(Test-Path $64bit) { $target = $64bit }
Start-Process $target -ArgumentList $file
}
Let me offer a streamlined version that also supports passing multiple files:
function Edit {
param(
# Allow passing multiple files, both with explicit array syntax (`,`-separated)
# or as indiv. arguments.
[Parameter(ValueFromRemainingArguments)]
[string[]] $File
)
# Construct the potential Notepad++ paths.
# Note: `-replace '$'` is a trick to append a string to each element
# of an array.
$exePaths = $env:ProgramFiles, ${env:ProgramFiles(x86)} -replace '$', '\Notepad++\notepad++.exe'
# See which one, if any, exists, using Get-Command.
$exeToUse = Get-Command -ErrorAction Ignore $exePaths | Select-Object -First 1
# Fall back to Notepad.
if (-not $exeToUse) { $exeToUse = 'notepad.exe' }
# Invoke whatever editor was found with the optional file(s).
# Note that both Notepad++ and NotePad can be invoked directly
# without blocking subsequent commands, so there is no need for `Start-Process`,
# whose argument processing is buggy.
& $exeToUse $File
}
An array of potential executable paths is passed to Get-Command, which returns a command-info object for each actual executable found, if any.
-ErrorAction Ignore quietly ignores any errors.
Select-Object -First 1 extracts the first command-info object, if present, from the Get-Command output; this is necessary to guard against the (perhaps unlikely) case where the executable exists in both locations.
$exeToUse receives $null (effectively) if Get-Command produces no output, in which case Boolean expression -not $exeToUse evaluates to $true, causing the fallback to notepad.exe to take effect.
Both command names (strings) and command-info objects (instances of System.Management.Automation.CommandInfo or derived classes, as returned by Get-Command) can be executed via &, the call operator.

Printing with Powershell and files in folders

I have a script which does some onsite printing. It doesnt work too well at the moment as the below runs for various file types which are sent to a folder to print, but the problem is it will only print 1 document at a time.
Start-Process –FilePath “c:\tests\*.docx” –Verb Print
I had the idea of doing this to get around it:
get-ChildItem "C:\Tests\*.docx" | `
foreach-object {
start-process -verb Print
}
This doesnt seem to work though. So then i tried this:
get-childitem "C:\Tests\*.xlsx" | `
foreach-object {
Start-Process -Filepath "C:\Program Files\Microsoft Office\Office14\EXCEL.exe" –Verb Print }
Also no luck,
Returns this error:
Start-Process : This command cannot be run due to the error: No application is associated with the specified file for this operation.
I think i am maybe not visualing the process here. Any ideas at all anyone on how to achieve printing of every file in a folder via powershell?
Windows 7 64 bit and $PSVersion = 5.0
Thanks in advance
You are very close, start-process needs the full path and name of the file:
Get-ChildItem "c:\tests\*.docx" | ForEach-Object {start-process $_.FullName –Verb Print}
Using a foreach loop should help you too:
$files = Get-ChildItem "c:\tests\*.docx"
foreach ($file in $files){
start-process -FilePath $file.fullName -Verb Print
}

Closing a file powershell

I am trying to write a script that opens all pdf files in a folder, prints them, and then closes the files. I have the first 2 parts working, however I can not find a way to close the files after. I've done some google searching but found nothing. I'm new to powershell so if there is a better way to doing this please let me know:
Get-ChildItem -Filter *.pdf |
Foreach-Object {
# File item variable is $_
Write-Host "Printing: $($_.Name)"
#Open the file with a print command.
Start-Process -FilePath $_.FullName -Verb Print
}
Any tips would be appreciated!
I don't have experience with this but was curious about the Start-Process cmdlet with print verb. I Googled that and came up with this that looks to be a good solution to your problem:
Start-Process -FilePath $.FullName -Verb Print -PassThru | %{sleep 10;$_} | kill
Source: http://gregcaporale.wordpress.com/2012/01/18/powershell-to-print-files-automatically/

Report generation using For loop powershell

I have a program which generate some reports by reading .XML file and I have to generate reports for multiple files.
But the problem which I am facing is for doing this I need to run it multiple times for each files as program reads only 1 file in 1 click.
Is there any way by which I can generate reports for multiple files in one click ?
So far i have tried below codes
$a = Get-ChildItem "D:\Directory1\Files\*.xml"
foreach ($i in $a)
{
Move-Item $i "D:\Directory1\"
if ($a) {
D:\Directory1\Program1.exe /run /exit /SilentMode
}
}
As per the above code I am trying to Read files from "D:\Directory1\Files\" Then move any 1 file (Not all Files) to the directory "D:\Directory1\" and then start the Program "Program1.exe" and generate the reports and repeat it till the .xml files exist in "D:\Directory1\Files\"
Is your goal to copy all files from D:\Directory1\Files\ to D:\Directory1\ in one step and then run D:\Directory1\Program1.exe /run /exit /SilentMode?
EDIT:
This work for you?
0. Set location that your program work
1. Get all files
2. For each file
3. Move file to new location
4. Start you program
5. Remove the moved file
Set-Location -Path "D:\Directory1\"
$arrFiles = Get-ChildItem -Path "D:\Directory1\Files\*.xml"
Foreach ($objFile in $arrFiles) {
Move-Item -Path $objFile.FullName -Destination "D:\Directory1\$($objFile.Name)"
Start-Process -FilePath "D:\Directory1\Program1.exe" -ArgumentList "/run /exit /SilentMode" -Wait
Remove-Item -Path "D:\Directory1\$($objFile.Name)"
}
Your logic here was sound, however, one issue you would have is the script will continue processing even while Program1.exe is running. Thereby making it possible for it to seeminly skip files. Also your If statement is just check if $a contains data which it always will in you example. Makes the condition check mute.
What you can do is something like this.
$moveLocation = "D:\Directory1\"
Get-ChildItem "D:\Directory1\Files\*.xml" | ForEach-Object{
# Move the file to its new location
Move-Item -Path $_.FullName -Destination $moveLocation
Start-Process -FilePath "D:\Directory1\Program1.exe" -ArgumentList "/run /exit /SilentMode" -Wait
}