I have been trying to create a line item within a .bat or .ps script that toggles the "Show hidden files/folders" option ON. I am having no luck so far. I have tried using the get-childitem command in various flavors and it's not working for me. What am I missing? Is this even something that can be done?
There are two things you want to achieve here.
Set the value of a registry key which can be done with Set-ItemProperty.
Refresh any open Explorer windows. We can use a ComObject called Shell.Application to do this.
N.B. This will not take effect until you press F5 in an Explorer Window if you do not have any Explorer Windows open.
That being said, you can write a small function much like the one below that will toggle the Hidden Files value in Explorer.
function Show-HiddenFiles {
[CmdletBinding(DefaultParameterSetName = "On")]
Param (
[Parameter(Mandatory = $true, ParameterSetName = "On")]
[System.Management.Automation.SwitchParameter]
$On,
[Parameter(Mandatory = $true, ParameterSetName = "Off")]
[System.Management.Automation.SwitchParameter]
$Off
)
Process {
# Set a variable with the value we want to set on the registry value/subkey.
if ($PSCmdlet.ParameterSetName -eq "On") { $Value = 1 }
if ($PSCmdlet.ParameterSetName -eq "Off") { $Value = 2 }
# Define the path to the registry key that contains the registry value/subkey
$Path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
# Set the registry value/subkey.
Set-ItemProperty -Path $Path -Name Hidden -Value $Value
# Refresh open Explorer windows.
# You will need to refresh the window if you have none currently open.
# Create the Shell.Application ComObject
$Shell = New-Object -ComObject Shell.Application
# For each one of the open windows, refresh it.
$Shell.Windows() | ForEach-Object { $_.Refresh() }
}
}
Usage is simple.
Show-HiddenFiles -On
Show-HiddenFiles -Off
If you just want to display hidden files in PowerShell you can use either the -Hidden or -Force parameter of Get-ChildItem
-Hidden returns only the Hidden files.
-Force returns Hidden and non-Hidden files.
Working from Ash's fine script,here's a slight enhancement.
This allows a third option of no parameter which will just toggles the current setting.
function Show-HiddenFiles {
Param (
[Parameter(Mandatory = $False,Position=0)]
[String] $Setting
)
# Define the path to the registry key that contains the
# registry value/subkey
$Path = "HKCU:\Software\Microsoft\Windows\CurrentVersion" +
"\Explorer\Advanced"
#No argument toggle setting..
If ($Args.Count -eq 0) {
$GIPArgs = #{Path = $Path
Name = "Hidden"}
If ((Get-ItemProperty #GIPArgs ).Hidden -eq 1) {
$Value = 2
}
Else {$Value = 1}
}
Else {
# Set a variable with the value we want to set on the
# registry value/subkey.
If ($Setting -eq "On" ) { $Value = 1 }
Else { $Value = 2 }
}
# Set the registry value/subkey.
Set-ItemProperty -Path $Path -Name Hidden -Value $Value
# Refresh open Explorer windows.
# You will need to refresh the window if you have none
# currently open.
# Create the Shell.Application ComObject
$Shell = New-Object -ComObject Shell.Application
# For each one of the open windows, refresh it.
$Shell.Windows() | ForEach-Object { $_.Refresh() }
}
Registry Path: HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
Name: Hidden
Type: DWord
Settings:
1 = Show
2 = Don't Show
Related
The following shortcut is created in SendTo and calls a PowerShell script. I want this script to be invisible (-WindowStyle Hidden) as the script uses Add-Type -AssemblyName System.Windows.Forms; $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{ InitialDirectory = $parent } and processes results based on the item selected in the OpenFileDialog.
$oShell = New-Object -ComObject Shell.Application
$lnk = $WScriptShell.CreateShortcut("$Env:AppData\Microsoft\Windows\SendTo\Get Info.lnk")
$lnk.TargetPath = "%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe"
$lnk.Arguments = "-WindowStyle Hidden -NoProfile -File `"C:\Scripts\Get Info.ps1`""
$lnk.Save()
However, the script is not silent, and throws up a blue PowerShell window briefly before the OpenFileDialog. How can I make the script completely silent when called by the shortcut?
Only way I know about to ensure this is using vbscript to launch your .ps1 script. It goes as follows:
Launcher.vbs
0 as argument in objShell.Run command,0 stands for Hide the window (and activate another window.)
Set objShell = CreateObject("WScript.Shell")
path = WScript.Arguments(0)
command = "powershell -NoProfile -WindowStyle Hidden -ExecutionPolicy ByPass -File """ & path & """"
objShell.Run command,0
myScript.ps1
Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = $parent
}
$FileBrowser.ShowDialog()
Now in your shortcut's Target field you can use use:
wscript path\to\launcher.vbs path\to\myScript.ps1
You can read more about this method in this site.
To expand upon Santiago's great answer, and with a real world example, my goal was to take an input from SendTo and then pass that input to PowerShell for additional processing. I don't need to see anything on the console so I wanted this to be hidden.
I want this for Beyond Compare, as I like the app a lot, but I really dislike how applications pollute our right-click context menu's with endless commands. I might only use Beyond Compare a couple of times per week, so I don't need it polluting my context menu for the 1,000+ other times per week that I right-click on something. I do this for all of my tools so that I have a very minimal and clean right-click context menu. The below would also apply to almost any other app that you would want a customised tooling in SendTo, and also almost unchanged for WinMerge if you prefer that tool (but Beyond Compare can also compare folders which can be very useful). For WinMerge, just break out of the script if a folder is selected at the first step as it cannot handle folders.
As I required two inputs, one from the initial SendTo, and the second from the OpenFileDialog or FolderBrowerDialog, this meant that I had to also pass the Argument given to the VBScript part of the solution. The syntax for that was a bit tricky to work out (it's 10+ years since I've had to use VBScript!), but is:
""" & path & """ """ & arg & """"
The solution then requires a .vbs launcher plus the .ps1 script and finally the shortcut in shell:sendto to call the scripts:
D:\MyPortableApps\ShortcutLauncher.vbs
Set oShell = CreateObject("WScript.Shell")
path = WScript.Arguments(0)
arg = WScript.Arguments(1)
PSCommand = "powershell -NoProfile -ExecutionPolicy ByPass -File """ & path & """ """ & arg & """"
oShell.run PScommand,0
D:\MyPortableApps\Compare with (Files or Folders, Beyond Compare).ps1
# Selected item in SendTo is the left side, then use OpenFileDialog or FolderBrowserDialog to pick the right side
$MyPrograms = "D:\MyPortableApps" # Location of my portable apps
$left_side = (Get-Item $args[0]).FullName # In case path contains '.' or '..'
$parent = Split-Path $left_side # Use this as InitialDirectory
$IsFolder = $false; if ((Get-Item $left_side) -is [System.IO.DirectoryInfo]) { $IsFolder = $true }
Add-Type -AssemblyName System.Windows.Forms # Required to access the OpenFileDialog object
if ($IsFolder) {
$FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog # -Property #{ InitialDirectory = $parent }
$FolderBrowser.RootFolder = $parent
$FolderBrowser.Description = "Select Folder to compare to '$left_side':"
$Show = $FolderBrowser.ShowDialog()
if ($Show -eq "OK") {
$right_side = $FolderBrowser.SelectedPath
} else {
break
}
} else {
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{ InitialDirectory = $parent } # [Environment]::GetFolderPath('Desktop')
$FileBrowser.Title = "Select File to compare to '$left_side':"
$null = $FileBrowser.ShowDialog() # Assign to null as $FileBrowser does not return useful information by itself
$right_side = $FileBrowser.FileName
}
# $ButtonClicked = [System.Windows.Forms.MessageBox]::Show("Beyond Compare will be opened with the following panes:`n`nLeft side: '$left_side'`n`nRight side: '$right_side'", 'Open Beyond Compare?', 'OKCancel')
$appexe_pf = "C:\Program Files\Beyond Compare 4\BCompare.exe"
$appexe_sb = "$MyPrograms\Beyond Compare 4\BCompare.exe"
if ( (!(Test-Path $appexe_sb)) -and (!(Test-Path $appexe_pf))) { choco install beyondcompare -y }
if (Test-Path $appexe_pf) {
& $appexe_pf "$left_side" "$right_side"
} else {
& $appexe_sb "$left_side" "$right_side"
}
Snippet to create a shortcut in SendTo
function New-Shortcut ($mylnk, $mytgt, $myarg, $mywrk, $myico) {
$lnk = $WScriptShell.CreateShortcut($mylnk)
$lnk.TargetPath = $mytgt
if ($myarg -ne "") { $lnk.Arguments = $myarg }
if ($mywrk -ne "") { $lnk.WorkingDirectory = $mywrk }
if ($myico -ne "") { $lnk.IconLocation = $myico }
$lnk.Save()
}
$SendTo = "$Env:AppData\Microsoft\Windows\SendTo"
$lnkName = "Compare with (Files or Folders, Beyond Compare)"
$SendToLnk = "$SendTo\$lnkName.lnk"
$wscript = "C:\Windows\system32\wscript.exe"
New-Shortcut $SendToLnk $wscript "`"D:\MyPortableApps\ShortcutLauncher.vbs`" `"D:\MyPortableApps\$lnkName.ps1`"" "" ""
There is a script for users to log in, it calls other scripts in turn, depending on the conditions.
In order to call scripts separately manually, the [switch]$Silent parameter has been added. Question - how to pass this parameter inside Start-Job? I tried to add to the list of arguments in different ways - the value always falls into the neighboring parameter, regardless of the order.
Main script example
Param(
[string]$location = 'C:\Users',
[switch]$Silent
)
Start-Job -FilePath ".\Fonts_Install.ps1" -ArgumentList ($Silent,$location) | Wait-Job
Fonts_Install.ps1
Param(
[switch]$Silent = $false,
[string]$location = '.'
)
$path_fonts = "$env:LOCALAPPDATA\Microsoft\Windows\Fonts"
$Registry = "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
function WriteLog {
Param ([string]$LogString)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$LogMessage = "$Stamp $LogString"
Add-content $LogFile -value $LogMessage
}
$Logfile = "$env:LOCALAPPDATA\Temp\fonts_install.log"
WriteLog "Silent $Silent"
WriteLog "location $location"
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName PresentationCore
$SourceFolder = "$location\Fonts_Install"
$WindowsFonts = [System.Drawing.Text.PrivateFontCollection]::new()
$Fonts = Get-ChildItem -Path $SourceFolder -Include *.ttf, *.otf -Recurse -File
ForEach ($Font in $Fonts) {
$Font_Name = $Font.Name
$font_fullname = $Font.fullname
if (Test-Path -PathType Leaf -Path "$path_fonts\$Font_Name") {
WriteLog "Previously installed $Font_Name"
}
else {
Copy-Item $Font -Destination "$path_fonts" -Force -Confirm:$false -PassThru
$WindowsFonts.AddFontFile("$font_fullname")
$ValueFont = "$path_fonts" + "\" + "$Font_Name"
$Typeface = New-Object -TypeName Windows.Media.GlyphTypeface -ArgumentList "$font_fullname"
[string]$FamilyFaceNames = $Typeface.FamilyNames.Values + $Typeface.FaceNames.Values
$RegistryValue = #{
Path = $Registry
Name = $FamilyFaceNames
Value = $ValueFont
}
if (Test-Path $Registry\$FamilyFaceNames) {
Remove-ItemProperty -name $FamilyFaceNames -path $Registry
}
New-ItemProperty #RegistryValue
WriteLog "New fonts installed $Font_Name"
}
}
switch ($Silent) {
$false {
if ($Error.Count -gt 0) {
for ($i = 0; $i -le ($Error.Items.Count + 1); $i++) {
$errMSG = "$Error"
}
[System.Windows.Forms.MessageBox]::Show("$errMSG", "Error", "OK", "Error")
}
else {
[System.Windows.Forms.MessageBox]::Show("ок", "Fonts", "OK", "Asterisk") | out-null
}
}
}
Unfortunately, specifying pass-through arguments via Start-Job's -ArgumentList (-Args) is limited to positional arguments, which prevents binding [switch] parameters, whose arguments must by definition be named.
As a workaround, instead of using -FilePath, invoke your script via the -ScriptBlock parameter. Inside of a script block ({ ... }, named arguments may be used in script calls, as usual:
Start-Job -ScriptBlock {
# Set the current location to the same location as the caller.
# Note: Only needed in *Windows PowerShell*.
Set-Location -LiteralPath ($using:PWD).ProviderPath
.\Fonts_Install.ps1 -Silent:$using:Silent $using:Location
} | Receive-Job -Wait -AutoRemoveJob
Note the use of the $using: scope in order to embed variable values from the caller's scope in the script block that will execute in the background.
You still need to refer to the -Silent parameter by name, and the whether the switch is on or off can be communicated by appending :$true or :$false to it, which is what :$using:Silent does.
In Windows PowerShell, background jobs execute in a fixed location (working directory), namely the user's Documents folder, hence the Set-Location call to explicitly use the same location as the caller, so that the script file can be referenced by a relative path (.\). This is no longer necessary in PowerShell (Core) 7+, which now thankfully uses the same location as the calller.
Here is a different alternative to mklement0's helpful answer, this answer does not use Start-Job and uses a PowerShell instance instead, using this method we can leverage the automatic variable $PSBoundParameters.
Do note, that for this to work properly, both .ps1 scripts must share the same parameter names or Alias Attribute Declarations that matches the same parameter from the caller. See this answer for more details.
You can use these snippets below as a example for you to test how it works.
caller.ps1
param(
[string] $Path = 'C:\Users',
[switch] $Silent
)
try {
if(-not $PSBoundParameters.ContainsKey('Path')) {
$PSBoundParameters['Path'] = $Path
}
$ps = [powershell]::Create().
AddCommand('path\to\myScript.ps1').
AddParameters($PSBoundParameters)
$iasync = $ps.BeginInvoke()
# Do something else here while the .ps1 runs
# ...
# Get async result from the PS Instance
$ps.EndInvoke($iasync)
}
finally {
if($ps -is [IDisposable]) {
$ps.Dispose()
}
}
myScript.ps1
# Note, since we're bounding this parameters from the caller.ps1,
# We don't want to assign Default Values here!
param(
[string] $Path,
[switch] $Silent
)
foreach($param in $MyInvocation.MyCommand.Parameters.Keys) {
[pscustomobject]#{
Parameter = $param
Value = Get-Variable $param -ValueOnly
}
}
A few examples:
PS /> .\caller.ps1
Parameter Value
--------- -----
Path C:\Users
Silent False
PS /> .\caller.ps1 -Path hello
Parameter Value
--------- -----
Path hello
Silent False
PS /> .\caller.ps1 -Path world -Silent
Parameter Value
--------- -----
Path world
Silent True
I have very basic knowledge in creating scripts and self taught.
I need to build a powershell that creates a url shortcut and opens to google chrome
The Website has some features that currently only work in chrome
I have created a foundation for creating the icon but need help telling it to open only through Chrome and not the default browser
$new_object = New-Object -ComObject WScript.Shell
$destination = $new_object.SpecialFolders.Item("AllUsersDesktop")
$source_path = Join-Path -Path $destination -ChildPath "\\Procore Login.url"
$source = $new_object.CreateShortcut($source_path)
$source.TargetPath = "https://login.procore.com/"
$source.Save()
As commented, if you create a .url internet shortcut, this wil open in whatever the user has set to be the default browser.
If you want to always have that url open in chrome, you need to create a .lnk shortcut where you set it to start chrome.exe with the wanted url as target to open.
For that you can use below helper function:
function New-Shortcut {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$TargetPath, # the path to the executable
# the rest is all optional
[string]$ShortcutPath = (Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath 'New Shortcut.lnk'),
[string[]]$Arguments = $null, # a string or string array holding the optional arguments.
[string[]]$HotKey = $null, # a string like "CTRL+SHIFT+F" or an array like 'CTRL','SHIFT','F'
[string]$WorkingDirectory = $null,
[string]$Description = $null,
[string]$IconLocation = $null, # a string like "notepad.exe, 0"
[ValidateSet('Default','Maximized','Minimized')]
[string]$WindowStyle = 'Default',
[switch]$RunAsAdmin
)
switch ($WindowStyle) {
'Default' { $style = 1; break }
'Maximized' { $style = 3; break }
'Minimized' { $style = 7 }
}
$WshShell = New-Object -ComObject WScript.Shell
# create a new shortcut
$shortcut = $WshShell.CreateShortcut($ShortcutPath)
$shortcut.TargetPath = $TargetPath
$shortcut.WindowStyle = $style
if ($Arguments) { $shortcut.Arguments = $Arguments -join ' ' }
if ($HotKey) { $shortcut.Hotkey = ($HotKey -join '+').ToUpperInvariant() }
if ($IconLocation) { $shortcut.IconLocation = $IconLocation }
if ($Description) { $shortcut.Description = $Description }
if ($WorkingDirectory) { $shortcut.WorkingDirectory = $WorkingDirectory }
# save the link file
$shortcut.Save()
if ($RunAsAdmin) {
# read the shortcut file we have just created as [byte[]]
[byte[]]$bytes = [System.IO.File]::ReadAllBytes($ShortcutPath)
# set bit 6 of byte 21 ON
# ([math]::Pow(2,5) or 1 -shl 5 --> 32)
# see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/16cb4ca1-9339-4d0c-a68d-bf1d6cc0f943
# page 13
$bytes[21] = $bytes[21] -bor 32
[System.IO.File]::WriteAllBytes($ShortcutPath, $bytes)
}
# clean up the COM objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($WshShell) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
And use it like this:
$chromePath = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe').'(Default)'
$props = #{
'ShortcutPath' = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath 'Procore Login.lnk'
'TargetPath' = $chromePath
'Arguments' = 'https://login.procore.com/'
}
New-Shortcut #props
I'm writing PS Script and following block of code shows dialogbox below windows forms gui.
$btn1 = New-Object Windows.Forms.Button
$btn1.Text = "Wybierz folder projektowy"
$btn1.Location = New-Object System.Drawing.Point(170,140)
$btn1.Size = New-Object System.Drawing.Size(160,20)
$btn1.add_Click({
function Select-Folder($message='Select a folder', $path = 0) {
$object = New-Object -comObject Shell.Application
$object.topmost=$true
$folder = $object.BrowseForFolder(0, $message, 0, $path)
if ($folder -ne $null) {
$folder.self.Path
}
}
$folderPath = Select-Folder 'Select the folder where the move scripts reside'
If ($folderPath) {
Set-Content -Path "C:\Projekty\logs\temp_path.txt" -Value $folderPath.ToString() -Encoding Unicode
write-host $folderPath
get-content -Path "C:\Projekty\logs\temp_path.txt"
}
Else { Write-Host 'I do not have a folder path' }
})
$form_acl.Controls.Add($btn1)
Is there any way to make it display on top?
Here's screenshot of a problem:
You can try this alternative instead:
function Select-Folder {
[CmdletBinding()]
param (
# sets the descriptive text displayed above the tree view control in the dialog box
[Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
[string]$Message = "Please select a directory.",
# sets the (pre)selected path
[Parameter(Mandatory=$false, Position=1)]
[string]$InitialDirectory,
# sets the root folder where the browsing starts from
[Parameter(Mandatory=$false)]
[System.Environment+SpecialFolder]$RootFolder = [System.Environment+SpecialFolder]::Desktop,
# sets a value indicating whether the 'New Folder' button appears in the folder browser dialog box
[switch]$ShowNewFolderButton
)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
$dialog.Description = $Message
$dialog.SelectedPath = $InitialDirectory
$dialog.RootFolder = $RootFolder
$dialog.ShowNewFolderButton = $ShowNewFolderButton.IsPresent
$selected = $null
# force the dialog TopMost:
# because the owning window will not be used after the dialog has been closed,
# you can simply create a new form inside the method call.
$result = $dialog.ShowDialog((New-Object System.Windows.Forms.Form -Property #{TopMost = $true; TopLevel = $true}))
if ($result -eq [Windows.Forms.DialogResult]::OK){
$selected = $dialog.SelectedPath
}
# clear the FolderBrowserDialog from memory
$dialog.Dispose()
# return the selected folder
$selected
}
Select-Folder -Message 'Select the folder where the move scripts reside' -ShowNewFolderButton
Theo's helpful answer shows an alternative, WinForms-based way to invoke a folder-browsing dialog, via the System.Windows.Forms.FolderBrowserDialog class.
However, it seems that all that is missing from your original approach is to pass your form's window handle (hWND, .Handle) as the first argument to the Shell.Application COM object's .BrowseForFolder() method, which makes it the dialog's owner window and therefore shows the dialog on top of it - even if the form itself has the .TopMost property set:
$folder = (New-Object -ComObject Shell.Application).BrowseForFolder(
$form_acl.Handle, # Pass your form's window handle to make it the owner window
$message,
0,
$path)
Here's a simplified, self-contained example (requires PSv5+, but can be adapted to earlier versions):
using namespace System.Windows.Forms
using namespace System.Drawing
Add-Type -AssemblyName System.Windows.Forms
# Create a sample topmost form with a single
# button that invokes a folder-browsing dialog.
($form = [Form] #{
Text = "Topmost Form"
Size = [Size]::new(300, 100)
TopMost = $true # Make the form topmost.
StartPosition = 'CenterScreen'
}).Controls.AddRange(#(
($folderBrowseButton = [Button] #{
Location = [Point]::new(70, 20)
Size = [Size]::new(160,30)
Text = 'Browse for Folder'
})
))
$folderBrowseButton.add_Click({
$path = 'C:\'
# IMPORTANT: Pass $form.Handle, the form's window handle (HWND) as the first argument, which
# makes the form the owning window, ensuring that the dialog is displayed on
# top - even if the form itself is set to topmost.
$folder = (New-Object -ComObject Shell.Application).BrowseForFolder(
$form.Handle,
'Pick a target folder:',
0, # Options
$path # Starting directory path.
)
if ($null -ne $folder) {
Write-Verbose -vb ('Folder picked: ' + $folder.self.Path)
} else {
Write-Verbose -vb 'Folder browsing canceled.'
}
})
$null = $form.ShowDialog()
This question already has an answer here:
Issues with Target Path in powershell in creating a short cut
(1 answer)
Closed 3 years ago.
thanks for trying to help me^^
And sorry for my bad English(maybe?)
I'm trying to create a shortcut to a PowerShell-Script to execute it.
I'm using:
$WshShell = New-Object -comObject WScript.Shell
$link = $wshshell.CreateShortcut(”$home\Desktop\TimeStamp.lnk”)
$link.targetpath = ("%windir%\System32\WindowsPowerShell\v1.0\powershell.exe -noexit -File " + '"' + ($SaveDir + "\TimeStamp.ps1") + '"')
$link.save()
$SaveDir is the Path where the Script is.
It creates the shortcut, but then there is a error: (In German sorry) Der Wert liegt außerhalb des erwarteten Bereichs.
I tried to find a way to solve the problem. I now know whats the problem: It puts a " at the start and the end of the Target Location in the Properties of the Shortcut. Is there any way to prevent that?
To create a shortcut where you have arguments for the executable lik in your question, you will need to at least also use the Arguments property.
You can use this function which allows you to set all kinds of properties for a new shortcut, including the option to have the executable run as Administrator.
function New-Shortcut {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$TargetPath, # the path to the executable
# the rest is all optional
[string]$ShortcutPath = (Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath 'New Shortcut.lnk'),
[string[]]$Arguments = $null, # a string or string array holding the optional arguments.
[string[]]$HotKey = $null, # a string like "CTRL+SHIFT+F" or an array like 'CTRL','SHIFT','F'
[string]$WorkingDirectory = $null,
[string]$Description = $null,
[string]$IconLocation = $null, # a string like "notepad.exe, 0"
[ValidateSet('Default','Maximized','Minimized')]
[string]$WindowStyle = 'Default',
[switch]$RunAsAdmin
)
switch ($WindowStyle) {
'Default' { $style = 1; break }
'Maximized' { $style = 3; break }
'Minimized' { $style = 7 }
}
$WshShell = New-Object -ComObject WScript.Shell
# create a new shortcut
$shortcut = $WshShell.CreateShortcut($ShortcutPath)
$shortcut.TargetPath = $TargetPath
$shortcut.WindowStyle = $style
if ($Arguments) { $shortcut.Arguments = $Arguments -join ' ' }
if ($HotKey) { $shortcut.Hotkey = ($HotKey -join '+').ToUpperInvariant() }
if ($IconLocation) { $shortcut.IconLocation = $IconLocation }
if ($Description) { $shortcut.Description = $Description }
if ($WorkingDirectory) { $shortcut.WorkingDirectory = $WorkingDirectory }
# save the link file
$shortcut.Save()
if ($RunAsAdmin) {
# read the shortcut file we have just created as [byte[]]
[byte[]]$bytes = [System.IO.File]::ReadAllBytes($ShortcutPath)
$bytes[21] = 0x22 # set byte no. 21 to ASCII value 34
[System.IO.File]::WriteAllBytes($ShortcutPath, $bytes)
}
# clean up the COM objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($WshShell) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Using your example, use it like this:
$SaveDir = 'D:\' #'# the path to the .ps1 file
$props = #{
'ShortcutPath' = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath 'TimeStamp.lnk'
'TargetPath' = '%windir%\System32\WindowsPowerShell\v1.0\powershell.exe'
'Arguments' = '-NoExit -NoLogo -File "{0}"' -f (Join-Path -Path $SaveDir -ChildPath 'TimeStamp.ps1')
# add more parameters here when needed
}
New-Shortcut #props