Collecting user input with pop up window in Powershell - powershell

so I am trying to prepare a simple robocopy script. the script will be used by almost 500 users, so I am trying to keep it as simple and as user-friendly as possible.
to collect the information such as source and destination, i wanted to have a pop up window asking users to enter the information. I checked on the other forums and here as well, so far i found several alternatives, unfortunately none of them "does the trick"
Option 1 (my favorite except PowerShell hangs when I use it):
Add-Type -AssemblyName Microsoft.VisualBasic
$title = 'Your Current File Shares (Source)'
$msg = 'Please enter the EEX file share you want to copy from ( please make sure the format is \\server\share\...) :'
$source = [Microsoft.VisualBasic.Interaction]::InputBox($msg, $title)
Add-Type -AssemblyName Microsoft.VisualBasic
$title = 'Your new drive (Destination)'
$msg = 'Please enter to where you want to copy your files (please make sure you choose the full destination) :'
$destination = [Microsoft.VisualBasic.Interaction]::InputBox($msg, $title)
Robocopy $source $Destination /log:N:\logfile.txt
Option 2 (similar to option just a different way to call VB it seems): replying the first line Add-Type -AssemblyName Microsoft.VisualBasic with [void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') , doesn't make it any better .
with both 2 options above PowerShell hangs after the 3rd run. SOmetimes after the first run it hangs. Odd thing is it runs succesfully and runs the robocopy command , but then after it's done it stops responding after a few minutes. Not sure if VB is looping in the background and has to be stopped?
Option 3:
$source = Read-Host 'Enter Your current file share:' -AsSecureString
$destination = Read-Host 'Enter Your new file share:' -AsSecureString
This is simple enough, however you cannot control anything in the box, which is something we can live with, but the main issue is the secure string, so it doesn't allow the user to see what he or she is typing, which would lead to a lot of human errors.
Option 4:
function copy_files {
param (
[string]$Copy_from,
[string]$Copy_to
)
[pscustomobject]#{
copy_from = $Copy_from
copy_to = $copy_to
}
}
$result = Invoke-Expression (Show-Command Copy_files -PassThru )
$result
has a form that is really not desirable , with the "copy" in the middle on the bottom (on the bottom of the form one sees ok / copy / cancel ) which would confuse users causing to hit copy and wait for something to happen (as the purpose is to transfer files…)
another negative thing is that it is very limited in terms what text and title you can use (or at least what I can use as I tried to have spaces but it wouldn't recognize it no matter if put them in quotations or double quotations). But such cosmetic con i can live with.
I cannot add any additional modules so showui for example is unfortunately not an option.
Any ideas on how this can be done?
Thanks in advance

There are pre-built scripts for this sort of thing that you can use as-is or tweak as needed.
The AutoCopier - PowerShell File Copy Utility w/GUI
A PowerShell GUI utility to copy files to computers based on a supplied text file of Hostnames/IP addresses. Please read below for more details and feedback would be appreciated!
Download: AutoCopier.ps1

Related

Can i use existing variables in powershell read-host

i am writing a simple script in powershell using "read-host -prompt" so that i can input some text that will then be used to create some environment variables.
Is there a way that the subsequent times the script is run, the read-host message will show an existing value for the variable if it exists ? and if not what i want then accept my new input to change that variable ?
for example ...
$myvar = read-host -prompt "whats your value" and i enter 10 to set $myvar value to 10
next time the script is run, "whats your value" will show 10, and if i press enter without changing the value it will use the value 10 again .. if i enter a new value it will update $myvar to the new value
Thanks for any help
If I understand correctly, you're looking for two features that are not implemented in Read-Host as of PowerShell 7.1:
(a) Pre-filling the edit buffer with a default value, which the user can either accept as-is or modify.
The alternative is just to inform the user, via the -Prompt string, what value will be used if they don't enter a new one, but that way you won't be able to distinguish between the user choosing the default value or simply wanting to abort the prompt (which they could do with Ctrl-C, however).
(b) Keeping a persistent history of user-entered values that remembers (at least) the most recently entered value, across PowerShell sessions.
Note: Read-Host is currently bare-bones. The module that provides the rich interactive command-line editing experience for PowerShell itself is PSReadLine, and would be great if its features, which include a persistent history and modifying the edit buffer, could be made available to user code for general-purpose prompting - see GitHub proposal #881.
Surfacing such enhancements via Read-Host is probably the best option, or at least the ability to prefill the edit buffer could be implemented there: see GitHub proposal #14013.
See - limited - custom implementations of (a) and (b) below.
(a) is currently only possibly via a workaround, and only on Windows, both in regular console windows and Windows Terminal (it does not work in the obsolescent PowerShell ISE Thanks, CFou., and in Visual Studio Code's integrated terminal it only works if you place the focus on it by clicking immediately after launching a debug session):
# The (default) value to pre-fill the Read-Host buffer with.
$myVar = 'This is a default value.'
# Workaround: Send the edit-buffer contents as *keystrokes*
# !! This is not 100% reliable as characters may get dropped, so we send
# !! multiple no-op keys first (ESC), which usually works.
(New-Object -ComObject WScript.Shell).SendKeys(
'{ESC}' * 10 + ($myVar -replace '[+%^(){}]', '{$&}')
)
$myVar = Read-Host 'Enter a value' # Should display prompt with value of $myVar
Note: The -replace operation is necessary to escape characters in the default value that would otherwise have special meaning to .SendKeys().
(b) requires you to implement your own persistence mechanism, and the obvious choice is to use a file:
Here's a simplistic approach that only stores the most recent value entered.
Supporting multiple historic values per prompt would also support for recall in Read-Host, such as as using up-arrow and down-arrow to cycle through the history, which is not supported as of PowerShell 7.1.
# Choose a location for the history file.
$historyFile = "$HOME/.rhhistory"
# Read the history file (which uses JSON), if it exists yet.
$history = Get-Content -Raw -ErrorAction Ignore $historyFile | ConvertFrom-Json
$defaultValue = 'This is a default value.'
# Get the 'myVar' entry, if it exists, otherwise create it and use the default value.
$myVar =
if (-not $history) { # no file yet; create the object to serialize to JSON later
$history = [pscustomobject] #{ myVar = '' }
$defaultValue
} elseif (-not $history.myVar) { # file exists, but has no 'myVar' entry; add it.
$history | Add-Member -Force myVar ''
$defaultValue
} else { # return the most recently entered value.
$history.myVar
}
# Prompt the user.
(New-Object -ComObject WScript.Shell).SendKeys(
'{ESC}' * 10 + ($myVar -replace '[+%^(){}]', '{$&}')
)
$myVar = Read-Host 'Enter a value'
# Validate the value...
# Update the history file with the value just entered.
$history.myVar = $myVar
$history | ConvertTo-Json > $historyFile
You cannot do this straight :
####[int]$myVar = 10 Read too fast the question :)
#$myVar = Get-ItemProperty ...
#or
#myVar = Get-Content ...
$tempVar = Read-Host "Enter the value ($myVar is default)"
if ($tempVar)
{
$myVar = $tempVar
}
$myVar
# Store $myVar in the registry or in the file for next run
My bad, I answered without a good reading :) As #postanote said,
If you need to keep a track of the value from each run, you can store it in a file (IMHO, I would avoid the profile.ps1) or in a personal registry key.
Then read the file or the registry value to initialize your variable.

Powershell GUI Drag-and-Drop empty arg from email attachments

I have a GUI with a read-only textbox that I drag files to and a function then runs with the file. This works on my machine for files in local download folders, all file extensions, outlook email attachments, etc. I have no issues.
A colleague has begun using it and he is unable to successfully drag email attachments from outlook into the box. The arg is empty. It all works on my machine, but not his. I had one other person test, and they had the same issue.
$filebox.Add_DragEnter({
if ($_.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) {
$_.Effect = [Windows.Forms.DragDropEffects]::Copy
}
})
$filebox.Add_DragDrop({
foreach ($file in $_.Data.GetData([Windows.Forms.DataFormats]::FileDrop)) {
create_dir $file # Function doesn't even trigger here with outlook email attachments because no arg
}
})
We are all pulling from a shared inbox. Nothing is hard-coded to identify the user, or define a set path, it should just grab the path of the arg dropped in the box, right?
I'm just confused how the drag/drop is working for some files but not email attachments, and then also working fully on my machine.
I would consider myself a pretty novice coder, and first time ever messing with Powershell. (I do not have access to anything else in this situation. No ability to compile c#, powershell is it.). Maybe I'm missing something fundemental here. Anyone have any thoughts on this?
File format wont be present since there is no physical file on the file system. see Upload fails when user drags and drops attachment from email client
$filebox.Add_DragDrop({
if ($_.Data.GetDataPresent([System.Windows.Forms.DataFormats]::FileDrop)) {
foreach ($file in $_.Data.GetData([System.Windows.Forms.DataFormats]::FileDrop)) {
create_dir $file
}
}
else {
$outlook = New-Object -ComObject Outlook.Application
$s = $outlook.ActiveExplorer().Selection
foreach ($item in $s){
foreach($a in $item.Attachments){
$name = Join-Path -Path $dlpath -ChildPath $a.filename
$a.SaveAsFile($name)
create_dir $name
}
}
}
})
As Dmitry noted, the files aren't actually saved on the machine so I can't just Copy-Item. Here, the file is saved, and then the create_dir function is called and it can copy the item from the new download path.

How can I set the save location of a file using powershell?

I am trying to print a PDF as XPS, the script opens the PDF Print Output As screen and enters the correct name before sending the ENTER command to the window to save the file.
How can I select the address bar to enter the desired path? Or how can I change the default save path?
EDIT: Thank you for the feedback. Here is the script:
function print_files($secure_pdf_dir){
#Retrieves the name for the .xps files
Get-ChildItem $secure_pdf_dir -Filter *.pdf -Recurse | Foreach-Object {
#For each .pdf file in that directory, continue
same_time $_.FullName
}
}
## The following function keeps checking for a new window called "Save Print Output As"
## When the window shows up, it enters the name of the file and press ENTER
function enter_my_names($xps_dir, $fullname){
$wshell = New-Object -ComObject wscript.shell;
while($wshell.AppActivate('Save Print Output As') -ne $true){
$wshell.AppActivate('Save Print Output As')
}
$basename = [io.path]::GetFileNameWithoutExtension($fullname)
#This is where the name is actually entered
$wshell.SendKeys($xps_dir\$basename)
$wshell.SendKeys("{ENTER}")
}
## The following function launches simultaneously a print job on the input file
## and a function waiting for the print job to show up to name the file
workflow same_time{
Param(
$fullname
)
parallel{
Start-Process -FilePath $fullname –Verb Print -PassThru
enter_my_names $xps_dir $fullname
}
}
#MAIN PROGRAM
#Here the script saves your current printer as default
$defprinter = Get-WmiObject -Query "Select * from Win32_Printer Where Default=$true"
#Queries for a XPS printer
$printer = Get-WmiObject -Query "Select * from Win32_Printer Where Name='Microsoft XPS Document Writer'"
#Sets the XPS printer as Default
$printer.SetDefaultPrinter()
#Starts the main job
print_files($secure_pdf_dir)
#Sets the old default printer back as default again
$defprinter.SetDefaultPrinter()
#This is a small delay to be sure everything is completed before closing Adobe Reader. You can probably shorten it a bit
sleep 2
#Finally, close Adobe Reader
Get-Process "acrord32" | Stop-Process
It seems that the $xps_dir variable is not being passed into the function properly.
Edit: Error I get when trying to add $xps_dir to the enter_my_names function:
Microsoft.PowerShell.Utility\Write-Error : Cannot validate argument on parameter
'FilePath'. The argument is null or empty. Provide an argument that is not null or
empty, and then try the command again.
At same_time:115 char:115
+
+ CategoryInfo : NotSpecified: (:) [Write-Error], ParameterBindingValidati
onException
+ FullyQualifiedErrorId : System.Management.Automation.ParameterBindingValidationEx
ception,Microsoft.PowerShell.Commands.WriteErrorCommand
+ PSComputerName : [localhost]
I'm not sure if you got this fully worked out or I misread your post, but I was able to implement some code from your initial post to finish my project [been beating my head against a wall as a non-traditional comp sci person]; so I wanted to share some things I did in hopes of helping you (in the event you're still at that crossroads).
Some points of interest:
-My overall process is a bulk report generator. A variable amount of .XML stylesheets are sent to IE because a javascript subroutine numbers the reports; to accomplish this I have visual basic doing some database parsing, passing values into custom scripts needed to form the .XML stylesheets, then VB creates PS .ps1 files with the various PS commands needed to create the IE com object. Finally, VB calls the .ps1 scripts, and the reports are generated by sending the stylesheet to IE, waiting for HTML render, send print command, and close the object.
-While your endgoal is to generate an XPS, mine was to fully render an HTML, then print it with a PDF printer; I assume we have a similar process in that we must engage a dialog box in the intermediate stage (in this case, using sendkeys to interact with it); this is specifically what I'm going to discuss below in hopes of helping out!
So, two points of discussion:
First, more of a general observation/query: I don't see where your $xps_dir variable is actually being defined; I mention this because there could be a string-related issue with the values being passed by the $xps_dir. One way to check your string to make sure its 'pretty' is to pass it to a .txt or something using the OUT-FILE command:
"$xps_dir" | Out-File -FilePath "C:\somefolder\sometext.txt" -Append
The text file itself doesn't need to exist, but the file does...the above command will create the text file.
Also, if you're ok with viewing the powershell prompt, a WRITE-HOST can be used.
Either way, I'm wondering if the final compiled string isn't compiled correctly, so its not recognizing it as a directory.
I think what more likely is happening is the SENDKEYS aren't being sent to the correct fields. For example, by using your code I was [FINALLY] able to set my 'Save As' dialog box as an object and interact with it. (When using my PDF printer, I get the 'Save As' dialog box).
At first, I tried to send the file name, then send enter. I assumed this would be fine, because during manual interaction the focus for the 'Save As' dialog box goes to the 'File Name' field; however, after trying:
$wshell.SendKeys($myfilename)
$wshell.SendKeys("{ENTER}")
I realized the SendKeys command was sending the keys to the 'Save in' drop down (or probably the first element of the Save As dialog box). So, I manually click inside of the 'Save in' field, then tabbed around until I got to 1. 'File Name' (wrote the # of tabs down), and 2. the 'Save' button (wrote # of tabs down). By adding SendKeys("{TAB}") to correspond with the number of tabs I observed, I was able to successfully enter my name string and close out the print dialog box:
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys($myfilename)
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{ENTER}")
NOTE: the number of tabs might be different for your dialog box, so I strongly recommend doing a manual run through to count how many tabs get you to the field you want to interact with.
In conclusion, I have one reflection for consideration:
I've used (to much success) VB and PS to use html elements to dynamically enhance my routines. Since we're able to set the dialog box as a Com object, my next goal will be to try to tap into the respective 'elements', similar to how html elements can be accessed (or more generally, object-oriented language allows). This way, I might be able to better interact with the dialog box without suffering the drawbacks of SendKeys:
Has to run as active process; user cannot engage any other windows else keys might be sent to them;
Keys are based on a count; any variation to that count will require the subroutine to be updated/edited, making it not elastic;
Also, I have not done any work regarding error windows, but one I've already noticed is the "your document has the same name blah blah", so just a heads up.
Even though we're doing two different things, here's my final code for this part of my routine in case it helps provide context:
#send command to internet explorer application with execWB; 6 for print, 2 for no user prompt on window
while ( $ie.busy ) { Start-Sleep -second 3 }
$ie.execWB(6,2)
#create dialog box object
$wshell = New-Object -ComObject wscript.shell;
while($wshell.AppActivate('Save As') -ne $true){ Start-Sleep -second 3 }{
$wshell.AppActivate('Save As')
}
#create file string from 3 strings: directory,basename returned, file extension
#the return from my output log is: "c:\test_folder\mybasenamestring.PDF"
$mydirectory = "c:\test_folder\"
$mybasename = [io.path]::GetFileNameWithoutExtension($fullname)
$myextension = ".PDF"
$myfilename = "$mydirectory$mybasename$myextension"
#Using tabs to navigate around the dialog window; send my string to the 'File Name' field, then tab to 'Save' and send enter
#This is where the name is actually entered
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys($myfilename)
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{ENTER}")
#going to add a command to report on the file size of the pdf; the pdf will stay 0kb until its fully generated. Once fully generated will add the $ie.quit or the kill command here for the $ie object
#this sends a stop process command to kill powershell since I'm running it as a ps1 file and it will remain open otherwise
stop-process -Id $PID
Last thing: I observed some weirdness with the capitalization, so I updated the final SendKeys to capitalize the string since I want all caps anyhow:
$wshell.SendKeys($myfilename.toupper())
Hope this helps! Thanks for posting; I was finally able to complete my entire process. As I clean and improve, if I find improvements for this area I'll try to remember to share. Thanks for your help!
Pass the variable containing the desired directory ($xps_dir in this case) to each function in the process until it gets to the enter_my_names function where it can then be sent to the window with the $wshell.SendKeys("$xps_dir\$basename").
function print_files($xps_dir, $secure_pdf_dir){
#Retrieves the name for the .xps files
Get-ChildItem $secure_pdf_dir -Filter *.pdf -Recurse | Foreach-Object {
#For each .pdf file in that directory, continue
same_time $xps_dir $_.FullName
}
}
## The following function keeps checking for a new window called "Save Print Output As"
## When the window shows up, it enters the name of the file and press ENTER
function enter_my_names{
param ($xps_dir, $fullname)
$wshell = New-Object -ComObject wscript.shell;
while($wshell.AppActivate('Save Print Output As') -ne $true){
$wshell.AppActivate('Save Print Output As')
}
$basename = [io.path]::GetFileNameWithoutExtension($fullname)
#This is where the name is actually entered
$wshell.SendKeys("$xps_dir\$basename")
$wshell.SendKeys("{ENTER}")
}
## The following function launches simultaneously a print job on the input file
## and a function waiting for the print job to show up to name the file
workflow same_time{
Param(
$xps_dir, $fullname
)
parallel{
Start-Process -FilePath $fullname –Verb Print -PassThru
enter_my_names $xps_dir $fullname
}
}
#MAIN PROGRAM
#Here the script saves your current printer as default
$defprinter = Get-WmiObject -Query "Select * from Win32_Printer Where Default=$true"
#Queries for a XPS printer
$printer = Get-WmiObject -Query "Select * from Win32_Printer Where Name='Microsoft XPS Document Writer'"
#Sets the XPS printer as Default
$printer.SetDefaultPrinter()
#Starts the main job
print_files $xps_dir $secure_pdf_dir
#Sets the old default printer back as default again
$defprinter.SetDefaultPrinter()
#This is a small delay to be sure everything is completed before closing Adobe Reader. You can probably shorten it a bit
sleep 2
#Finally, close Adobe Reader
Get-Process "acrord32" | Stop-Process
The file dialogue will accept a path in the file name field, so instead of sending the filename file.pdf send the full path to the file C:\folder\folder\file.pdf
EDIT:
You can send the folder path like so:
$wshell.SendKeys($xps_dir)
$wshell.SendKeys("{ENTER}")
$wshell.SendKeys($basename)
$wshell.SendKeys("{ENTER}")

Working with Word templates with Powershell

I am writing a function that is part of a much larger script that will take input from a web form, check to see if that user exists in either our AD or Linux systems, create the account if it doesn't, email the user when it's done, then create a Word document that we can print out and give them with their credentials (sans temp password), email address, and basic information about our IT services. I have been beating my head against the wall with the Word integration. There is almost ZERO Powershell documentation online for Word integration. I've been having to translate what I can from C# and VB and even half of that isn't even translateable. I've got it mostly working now but I'm having problems getting PS to put my text in the correct location in the Word template. I have a Word Template with 4 bookmarks where I am inserting the user's name, username, email address, and account expiration. The problem is, PS is placing all of the text at the same bookmark. I've found that if I put info in the script statically it will work (ie. $FillName.Text = 'John Doe') but if I use a variable it will just stick all of them at the first bookmark. Here is my code:
Function createWordDocument($fullname,$sam,$mailaddress,$Expiration)
{
$word = New-Object -ComObject "Word.application"
$doc = $word.Documents.add("C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\welcome2.dotx")
$FillName=$doc.Bookmarks.Item("Name").Range
$FillName.Text="$fullname "
$FillUser=$doc.Bookmarks.Item("Username").Range
$FillUser.Text="$sam"
$FillMail=$doc.Bookmarks.Item("Email").Range
$FillMail.Text="$mailaddress"
$FillExpiration=$doc.Bookmarks.Item("Expiration").Range
$FillExpiration.Text="$Expiration"
$file = "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\test1.docx"
$doc.SaveAs([ref]$file)
$Word.Quit()
}
The function is receiving parameters that originated from a import-csv. $fullname, $sam and potentially $mailaddress have all been modified from their original inputs. #Expiration comes from the import-csv raw. Any help would be appreciated. This seems to be the most relevant info I could find and as far as I can tell I've got the same code, but It won't work for multiple bookmarks.
Ok, like I suggested you can setup a Mail Merge base that you can use to create docs for people. It does mean that you would need to output your data to a CSV file, but that is pretty trivial.
Start by setting up a test CSV with the data that you want to include. For simplicity you may want to place it with the word doc that references it. We'll call it mailmerge.csv for now, but you can name it whatever you want. Looks like Name, UserName, Email, and Expiration are the fields you would want. You can use dummy data in those fields for the time being.
Then setup your mail merge in Word, and save it someplace. We'll call it Welcome3.docx, and stash it in the same place as your last doc. Then, once it's setup to reference your CSV file, and saved, you can launch Word, open the master document, and perform the merge, then just save the file, and away you go.
I'll just use a modified version of your function which will create the CSV from the parameters provided, open the merge doc, execute the merge, save the new file, and close word. Then it'll pass a FileInfo object back so you can use that to send the email, or whatever.
Function createWordDocument($fullname,$sam,$mailaddress,$Expiration)
{
[PSCustomObject]#{Name=$fullname;Username=$sam;Email=$mailaddress;Expiration=$Expiration}|Export-Csv "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\mailmerge.csv" -NoTypeInformation -Force
$word = New-Object -ComObject "Word.application"
$doc = $word.Documents.Open("C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\welcome3.dotx")
$doc.MailMerge.Execute()
$file = "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\$fullname.docx"
($word.documents | ?{$_.Name -Match "Letters1"}).SaveAs([ref]$file)
$Word.Quit()
[System.IO.FileInfo]$file
}
TheMadTechnician put me on the right track, but I had to do some tweaking. Here is what I wound up with:
Function createWordDocument($fullname)
{
$word = New-Object -ComObject "Word.application"
$doc = $word.Documents.Add("C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\welcome_letter.docx")
$doc.MailMerge.Execute()
$file = "C:\Users\smiths\Documents\Powershell Scripts\webformCreateUsers\$fullname.docx"
($word.documents | ?{$_.Name -Match "Letters1"}).SaveAs([ref]$file)
$quitFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveOptions],"wdDoNotSaveChanges")
$Word.Quit([ref]$quitformat)
}
Instead of passing the arguments to the function, I had the main function create the mailmerge.csv file for me and just have the Word template connect to it. I'm still passing $fullname since that's what I'm naming the file in the end. The two major hiccups in the end were that everytime a mailmerge document file is opened, Word asks if you want to conect back to the source data. This means that when Powershell was trying to open it, Word was waiting for interaction and then PS would close it when it thought it was done. Of course, this meant that nothing got done. I found that there is a registry key that you must create to enable Word to skip the SQL Security check. for posterity's sake you must create a key here:
HKCU\Software\Microsoft\Office\14.0\Word\Options\ called SQLSecurityCheck with a DWORD value of 0. That allowed Word to properly open the template and manipulate the files. The last bit of trouble that I had was that Word was wanting to re-save the original file each time it ran and would leave a dialogue box open which would leave Word open and in memory. The last 2 lines force word to close without saving.

Generating printer shortcuts from list

I am trying to generate a shortcut for every printer I have on a print server. The idea is to be able to email these shortcuts to people and when they click on them, it automatically installs that printer for them.
I've populated an array from a list of printer names exported from the print server:
$list = #((get-contnet $home\dekstop\plist.txt))
I then created a method to create a shortcut:
function Make-Shortcut
{
param ([string]$dest, [string]$source)
$WshShell = New-Object -comObject Wscript.Shell
$Shortcut = $WshShell.CreateShortcut($dest)
$Shortcut.TargetPath = $Source
$Shortcut.Save()
}
The function works fine. I was able to create standard shortcuts with no problem.
This next part is where I am getting stuck:
foreach ($i in $list)
{
Make-Shortcut "C:\pshort\$i.lnk" "C:\Windows\System32\rundll32.exe
printui.dll,PrintUIEntry /in /q /n\\printserver\$i"
}
When this runs, it does generate a shortcut with the same name as the printer for each printer on the list. However, the problem comes in at the target path. Instead of
C:\Windows\System32\rundll32.exe printui.dll,PrintUIEntry /in /q /n\\printserver\printername
it changes it to:
C:\Windows\System32\rundll32.exe printui.dll,PrintUIEntry \in \q \n\printserver\printername
The three problems with this are:
It is reversing the forward slash for the parameters
It is removing one of the backslashes preceding the server name
It is adding quotes to both sides. I need the quotes to come off for the shortcut to work properly.
I assume this is happening because Powershell thinks I am trying to make a standard shortcut and thinks I made mistakes while typing out the path.
I have tried putting a ` in front of each forward slash hoping the escape character would prevent it from reversing it, but no luck. I also tried using a hyphen for each parameter but that did not work either.
Is there anyway to stop this from happening? Or is there perhaps a better way to try to accomplish what I am trying to do?
You need to add arguments to the com object
Try adding a new param $arguments to your Make-Shortcut function and do:
Make-Shortcut "C:\pshort\$i.lnk" "C:\Windows\System32\rundll32.exe"
"printui.dll,PrintUIEntry /in /q /n\\printserver\$i"
add this in your function:
$Shortcut.Arguments = $arguments
So the link is created successfully ... but I have no idea if it works :)
Completely different answer but in a standard windows environment simply clicking a hyperlink to \printserver\printer will add a shared printer to someone's system?
So an email that simply lists :
\\PrintServer\Printer01
\\PrintServer\Printer02
\\PrintServer\Printer03
Would probably do the job just as well.