How to get path of shortcut's icon file? - powershell

Is there a way to get the path of an .ico of a short cut?
I know how to change the shortcuts icon, but how do I find the path of the shortcut's icon file?

You could use below function.
It handles both 'regular' shrotcut files (.lnk) as well as Internet shortcut files (.url)
function Get-ShortcutIcon {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[Alias('FullName')]
[string]$Path # needs to be an absulute path
)
switch ([System.IO.Path]::GetExtension($Path)) {
'.lnk' {
$WshShell = New-Object -ComObject WScript.Shell
$shortcut = $WshShell.CreateShortcut($Path)
$iconPath = $shortcut.IconLocation
$iconInfo = if ($iconPath -match '^,(\d+)') {
[PsCustomObject]#{ IconPath = $shortcut.TargetPath; IconIndex = [int]$matches[1] }
}
else {
[PsCustomObject]#{ IconPath = $iconPath; IconIndex = 0 }
}
# clean up
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WshShell)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
# return the icon information
$iconInfo
}
'.url' {
$content = Get-Content -Path $Path -Raw
$iconPath = [regex]::Match($content, '(?im)^\s*IconFile\s*=\s*(.*)').Groups[1].Value
$iconIndex = [regex]::Match($content, '(?im)^\s*IconIndex\s*=\s*(\d+)').Groups[1].Value
[PsCustomObject]#{ IconPath = $iconPath; IconIndex = [int]$iconIndex }
}
default { Write-Warning "'$Path' does not point to a '.lnk' or '.url' shortcut file.." }
}
}

Related

How to programatically set Shortcuts TargetPath to a website?

I want to use powershell to modify the TargetPath of a shortcut to open a website
I found the below script that almost works
function Set-Shortcut {
param(
[Parameter(ValueFromPipelineByPropertyName=$true)]
$LinkPath,
$Hotkey,
$IconLocation,
$Arguments,
$TargetPath
)
begin {
$shell = New-Object -ComObject WScript.Shell
}
process {
$link = $shell.CreateShortcut($LinkPath)
$PSCmdlet.MyInvocation.BoundParameters.GetEnumerator() |
Where-Object { $_.key -ne 'LinkPath' } |
ForEach-Object { $link.$($_.key) = $_.value }
$link.Save()
}
}
However
Set-Shortcut -LinkPath "C:\Users\user\Desktop\test.lnk" -TargetPath powershell start process "www.youtube.com"
Will default out to attaching a default path if you do not define one to look like:
"C:\Users\micha\Desktop\powershell start process "www.youtube.com""
how do I get rid of that default file path?
BONUS:
I'd be appreciative if someone broke down this line of code:
ForEach-Object { $link.$($_.key) = $_.value }
have you tried to change the properties of the shortcut object directly?
Try this and let me know:
function Set-Shortcut {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[System.String]$FilePath,
[Parameter(Mandatory, Position = 1)]
[System.String]$TargetPath
)
Begin {
$shell = new-object -ComObject WScript.Shell
}
Process {
try {
$file = Get-ChildItem -Path $FilePath -ErrorAction Stop
$shortcut = $shell.CreateShortcut($file.FullName)
$shortcut.TargetPath = $TargetPath
$shortcut.Save()
}
catch {
throw $PSItem
}
}
End {
while ($result -ne -1) {
$result = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($shell)
}
}

I need to build a powershell that creates a url shortcut and opens to google chrome

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

OpenFileDialog in PowerShell - Can the icon of the dialog be changed?

I have this code that will invoke the OpenFileDialog class.
There doesn't seem to be a property to change the icon of the dialog that's shown at the top left corner of the dialog.
Is there a way to hack a change this?
Here's the code:
Function Invoke-OpenFileDialog {
Param (
[Parameter(Mandatory=$true, Position=0)] [string] $Title,
[Parameter(Mandatory=$true, Position=1)] [string] $InitialDirectory,
[Parameter(Mandatory=$true, Position=2)] [string] $Filter
)
Add-Type -AssemblyName System.Windows.Forms
if ($InitialDirectory -ne "") {
if (!(Test-Path -LiteralPath $InitialDirectory -PathType Container)) {
$InitialDirectory = ([System.IO.FileInfo]$InitialDirectory).DirectoryName
}
}
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.Filter = $Filter
$OpenFileDialog.InitialDirectory = $InitialDirectory
$OpenFileDialog.Title = $Title
$result = $OpenFileDialog.ShowDialog()
$OpenFileDialog.Dispose()
if ($result -eq 'OK') {
return $OpenFileDialog.FileName
}
else { return "" }
}
$title = 'OpenFileDialog Title'
$initialDirectory = ([System.IO.FileInfo]"C:\Users\").DirectoryName
$filter = 'All files (*.*)| *.*'
$filePicked = Invoke-OpenFileDialog -Title $title -InitialDirectory $initialDirectory -Filter $filter
# Then use this to check if the picker has done its job.
if ($filePicked -ne "") {
$filePicked
Write-Host 'File Picked...'
}
else {
Write-Host 'File Not Picked...'
}
The icon in question:
Found it!
Create your form and use my code from here to give that form your own icon.
Then, from a button for example on the form, you open the OpenFile dialog, using your function. The dialog will then have the icon used in your form.
# start the code with your own function to show the OpenFile dialog
function Invoke-OpenFileDialog {
Param (
[Parameter(Mandatory=$true, Position=0)] [string] $Title,
[Parameter(Mandatory=$true, Position=1)] [string] $InitialDirectory,
[Parameter(Mandatory=$true, Position=2)] [string] $Filter
)
if ($InitialDirectory -ne "") {
if (!(Test-Path -LiteralPath $InitialDirectory -PathType Container)) {
$InitialDirectory = ([System.IO.FileInfo]$InitialDirectory).DirectoryName
}
}
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.Filter = $Filter
$OpenFileDialog.InitialDirectory = $InitialDirectory
$OpenFileDialog.Title = $Title
$result = $OpenFileDialog.ShowDialog()
$OpenFileDialog.Dispose()
if ($result -eq 'OK') {
return $OpenFileDialog.FileName
}
else { return "" }
}
# next, create your form (just a simple demo below) and from there show the dialog
# (I'm using a button on the form to do this)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$form = New-Object system.Windows.Forms.Form
$form.ClientSize = '400,230'
$form.text = "Test"
$form.TopMost = $false
# This base64 string holds the bytes that make up the 16x16 StackOverflow icon
$iconBase64 = #'
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB
tUlEQVQ4y5WSTUtUYRzFf/d6r00FMQPCFG3yUsyYH6BCooUv5ZeoIImEghauMojqAwQu3QSSZEEt
KmozgwVGYC+EFGTGtcHc1DgSjI339bQwJsaZUeesHv7PeQ7nf85jSBLbQJXfgDB2J+vuTHYA78Ew
/pNRiILWBPRnFYD206OEC9MEsxMNSM0QVLR256S857ekOJL/5q7Kt7OKlj7U0Jo7sBK0D1wj/PSU
9alhrO5BrOwA3uMRVC5uvUL84yPxyiLW0TMkzt0j/jnP+sRZ7BMXkGL8Z9erXKNRC96jq4SfX2Bl
+7F7LmIkD+I9vEz86xt2zxBmx2HaMr3NBUBEX6cJZsaJlt7TdugY9vHzhF9yWNn+6uOGIUbLc4qW
56SgIkkKC29VmRxS+eYR+a/H67Kuc+BNXSKcz4NpYXZ0Yqa7MPd3gWFidQ9i7DtQ49WQpEXXxXVd
evv60FqRuOiiUoG4VEAr34lXC6hUYM/ILPlXMziOQ6fjbJS1efvw3f3/6tYujHQGM53ZGNiJ+rY3
D/yXY02/hn3qyvYCe28s0ApqBPK5HK3CAkimUjj/QtkJkqlU9fwXBBkCP6jNZvAAAAAASUVORK5C
YII=
'#
$iconBytes = [Convert]::FromBase64String($iconBase64)
$stream = New-Object IO.MemoryStream($iconBytes, 0, $iconBytes.Length)
$stream.Write($iconBytes, 0, $iconBytes.Length)
$form.Icon = [System.Drawing.Icon]::FromHandle((New-Object System.Drawing.Bitmap -Argument $stream).GetHIcon())
$button = New-Object System.Windows.Forms.Button
$button.Size = New-Object System.Drawing.Size(90,24)
$button.Left = $form.Width - $button.Width - 40
$button.Top = $form.Height - $button.Height * 3
$button.Text = 'Open File'
$button.Add_Click({
# in here, we use your function
$title = 'OpenFileDialog Title'
$initialDirectory = ([System.IO.FileInfo]"C:\Users\").DirectoryName
$filter = 'All files (*.*)| *.*'
$filePicked = Invoke-OpenFileDialog -Title $title -InitialDirectory $initialDirectory -Filter $filter
# Then use this to check if the picker has done its job.
if ($filePicked -ne "") {
$filePicked
Write-Host 'File Picked...'
}
else {
Write-Host 'File Not Picked...'
}
})
$form.Controls.Add($button)
[void]$form.ShowDialog()
# when done, dispose of the form
$form.Dispose()
Now, if you run this, you'll see the form:
and if you click the button, the dialog shows up, including your forms icon:
You can do this for a WPF UI aswell, although setting a WPF form with an embedded icon is slightly different:
# start the code with your own function to show the OpenFile dialog
function Invoke-OpenFileDialog {
Param (
[Parameter(Mandatory=$true, Position=0)] [string] $Title,
[Parameter(Mandatory=$true, Position=1)] [string] $InitialDirectory,
[Parameter(Mandatory=$true, Position=2)] [string] $Filter
)
if ($InitialDirectory -ne "") {
if (!(Test-Path -LiteralPath $InitialDirectory -PathType Container)) {
$InitialDirectory = ([System.IO.FileInfo]$InitialDirectory).DirectoryName
}
}
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.Filter = $Filter
$OpenFileDialog.InitialDirectory = $InitialDirectory
$OpenFileDialog.Title = $Title
$result = $OpenFileDialog.ShowDialog()
$OpenFileDialog.Dispose()
if ($result -eq 'OK') {
return $OpenFileDialog.FileName
}
else { return "" }
}
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen"
Width = "400" Height = "230" ShowInTaskbar = "True">
<Button x:Name = "Button" Height = "24" Width = "90" Content = 'Open File' />
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )
# This base64 string holds the bytes that make up the 16x16 StackOverflow icon
$iconBase64 = #'
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB
tUlEQVQ4y5WSTUtUYRzFf/d6r00FMQPCFG3yUsyYH6BCooUv5ZeoIImEghauMojqAwQu3QSSZEEt
KmozgwVGYC+EFGTGtcHc1DgSjI339bQwJsaZUeesHv7PeQ7nf85jSBLbQJXfgDB2J+vuTHYA78Ew
/pNRiILWBPRnFYD206OEC9MEsxMNSM0QVLR256S857ekOJL/5q7Kt7OKlj7U0Jo7sBK0D1wj/PSU
9alhrO5BrOwA3uMRVC5uvUL84yPxyiLW0TMkzt0j/jnP+sRZ7BMXkGL8Z9erXKNRC96jq4SfX2Bl
+7F7LmIkD+I9vEz86xt2zxBmx2HaMr3NBUBEX6cJZsaJlt7TdugY9vHzhF9yWNn+6uOGIUbLc4qW
56SgIkkKC29VmRxS+eYR+a/H67Kuc+BNXSKcz4NpYXZ0Yqa7MPd3gWFidQ9i7DtQ49WQpEXXxXVd
evv60FqRuOiiUoG4VEAr34lXC6hUYM/ILPlXMziOQ6fjbJS1efvw3f3/6tYujHQGM53ZGNiJ+rY3
D/yXY02/hn3qyvYCe28s0ApqBPK5HK3CAkimUjj/QtkJkqlU9fwXBBkCP6jNZvAAAAAASUVORK5C
YII=
'#
$iconBytes = [Convert]::FromBase64String($iconBase64)
$stream = New-Object IO.MemoryStream($iconBytes, 0, $iconBytes.Length)
$stream.Write($iconBytes, 0, $iconBytes.Length)
# set the form's icon
$Window.Icon = [System.Windows.Media.Imaging.BitmapFrame]::Create($stream)
# connect to the button
$button = $Window.FindName("Button")
$button.Add_Click({
# in here, we use your function
$title = 'OpenFileDialog Title'
$initialDirectory = ([System.IO.FileInfo]"C:\Users\").DirectoryName
$filter = 'All files (*.*)| *.*'
$filePicked = Invoke-OpenFileDialog -Title $title -InitialDirectory $initialDirectory -Filter $filter
# Then use this to check if the picker has done its job.
if ($filePicked -ne "") {
$filePicked
Write-Host 'File Picked...'
}
else {
Write-Host 'File Not Picked...'
}
})
$null = $Window.ShowDialog()
I found a way to set the icon independently from any UI by setting up:
$OpenFileDialogForm = New-Object System.Windows.Forms.Form
and passing a base64 string to the icon property of the $OpenFileDialogForm.
Then pass the $OpenFileDialogForm to:
$userClicked = $OpenFileDialog.ShowDialog($OpenFileDialogForm),
Some comments in the Invoke-OpenFileDialog function explaining this.
The IconBase64 parameter in the function is where this can be set.
Here's the code for this:
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
Function Get-LastValidDirectory {
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'The literal path where the folder you want to check is, which will return the last valid folder.')]
[Array]$LiteralPath
)
if (Test-Path -LiteralPath $LiteralPath -PathType Container) {
# Returns the path passed to the function if it has been found.'
return $LiteralPath
}
# Increment the input folder
$lastDir = Split-Path -LiteralPath $LiteralPath
# Test that folder for it not existing and check if it's doesn't exist and in the
# while loop increment the $lastDir variable.
while (!(Test-Path -LiteralPath $lastDir -PathType Container)) {
$lastDir = Split-Path -LiteralPath $lastDir
}
return $lastDir
}
function Invoke-OpenFileDialog {
Param (
[Parameter(Mandatory=$true, Position=0)] [string] $Title,
[Parameter(Mandatory=$true, Position=1)] [string] $InitialDirectory,
[Parameter(Mandatory=$true, Position=2)] [string] $Filter,
[Parameter(Mandatory=$true, Position=3)] [string] $IconBase64,
[Parameter(Mandatory=$true, Position=4)] [boolean] $TopMost
)
if ($InitialDirectory -ne "") {
if (!(Test-Path -LiteralPath $InitialDirectory -PathType Container)) {
$InitialDirectory = ([System.IO.FileInfo]$InitialDirectory).DirectoryName
}
}
# Create a streaming image by streaming the base64 string to a bitmap stream source.
$IconBase64 = $IconBase64
$iconBytes = [Convert]::FromBase64String($IconBase64)
$stream = New-Object IO.MemoryStream($iconBytes, 0, $iconBytes.Length)
$stream.Write($iconBytes, 0, $iconBytes.Length)
# Create a new form to hold the object and set it properties for the main
# OpenFileDialog form.
$OpenFileDialogForm = New-Object System.Windows.Forms.Form
$OpenFileDialogForm.Icon = [System.Drawing.Icon]::FromHandle((New-Object System.Drawing.Bitmap -Argument $stream).GetHIcon())
$OpenFileDialogForm.TopMost = $TopMost
# Set the properties of the main OpenFileDialog along with using the
# OpenFileDialogForm properties from above.
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.Filter = $Filter
$OpenFileDialog.InitialDirectory = $InitialDirectory
$OpenFileDialog.Title = $Title
$userClicked = $OpenFileDialog.ShowDialog($OpenFileDialogForm)
$OpenFileDialog.Dispose()
if ($userClicked -eq 'OK') { return $OpenFileDialog.FileName } else { return "" }
}
$title = 'OpenFileDialog Title'
$initialDirectory = ([System.IO.FileInfo]"C:\Users\").DirectoryName + '\no folder here'
$filter = 'Application (*.exe)|*.exe|SpreadSheet (*.xlsx)|*.xlsx'
$filter = 'All files (*.*)| *.*'
$iconBase64OpenFileDialog = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGkmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTEyLTI2VDIxOjQxOjA0WiIgeG1wOk1vZGlmeURhdGU9IjIwMjAtMTItMjZUMjE6NDc6MjZaIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTEyLTI2VDIxOjQ3OjI2WiIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpkODAwODQwYy0yN2JiLTI3NDItYjVjOS0yNjY5NjI4ZjE1NGEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6N2Y4MTY1YTYtZmVkYi02MDRkLThkNjgtYjM3YjBiN2RmMWRlIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6N2Y4MTY1YTYtZmVkYi02MDRkLThkNjgtYjM3YjBiN2RmMWRlIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ZjgxNjVhNi1mZWRiLTYwNGQtOGQ2OC1iMzdiMGI3ZGYxZGUiIHN0RXZ0OndoZW49IjIwMjAtMTItMjZUMjE6NDE6MDRaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhODk5YWVjLTA4ZDAtYjQ0Yi1hYzU3LWM0NGFmZGU1MDY1MiIgc3RFdnQ6d2hlbj0iMjAyMC0xMi0yNlQyMTo0Njo0MloiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZDgwMDg0MGMtMjdiYi0yNzQyLWI1YzktMjY2OTYyOGYxNTRhIiBzdEV2dDp3aGVuPSIyMDIwLTEyLTI2VDIxOjQ3OjI2WiIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PjkLPFQAABG3SURBVHic5Zt7rGxXXcc/a7/fM3NrkVeqlaZiMZYmJQKlBQ3gjaEGFLC1EQxIytwSjETTVGnQopRgRbFwjmkMpErFSI1EoAETCC3lYgivNAgoFFOQV3pve87M7DOzZz+Wf8z+bdbMmTnn3NtLSOSXrJyTM2vWWr/v+r1/6yitNT/O5MgvSqkf5Rl+HrgSeALgAwXwJeB+4JvnekPz0p0D5p0RBUGAUgrbtrEsC601ZVli2zZVVaGU6obWmul0+gTgMuDDlmVh2zZKqe67dV3TNA1N01wex/HnXNdFKcVsNmM6nZ6rY587AA6j2Wz2S8DVwEXAxUqpnxWwhHEhkcYWvM/meX4PsD0YDD50rs+lRBweqwqsk4DxeOwALweutCzrtY7jILctVNc1WmuqqkJr3YmnZVk4joPjODRNQ1EUNE3zpjAMb3msEmCqwDkHYDqdvgQYApcCj3McB9u2MfcRZpum+SrwX8DXgE+1P78PKODngGcrpW51XZcgCJjNZszn8xuBbWB8tmf9oQAAvAIYOo7zTM/zuluuqkpu73ZgBnwHeBB4KE3TB+q67hYQdRBJ8DyPRx999HJgaNv2q3zfpyxLyrK8EXjb2R70hwHAllJqGIYhjuNQliUAZVlSVdV/ANtBEPy9bds4jsN8PgfAcRw2AaCUwvO8DsDZbHYD8M4gCKiqiqqqrndd9w7Z60zoXAOwBQyzLENrzWQyQWv9IPB14G7f9//ONGqHASDqUhRFN78sS5RS7O3tXQoMXde9vrUbzwPuPdMDL8U+puE5C7oR0FmW6SiKNAvf/cowDImiiDAM8X2fIAgIgoA4jun1eoRhSBiGpGlKFEVEUUSSJGRZRhRFOI7TAZJlGWmaEscxcRyTpinA7VEUacuyNIsLOGMAZFiHT99ILwSGvV4Px3HY29sD2E7T9M4zkSZxgXVds7e3x97eHlVVAXSuUWvd2ZRW5CdVVQkYQ+B3z5aJs1WB84E7wjB8sed5jEYjtNbbwAkxgKLLTdOwSQVc16UsS+bz+ZIqGPSTwK8BFwJz4EOu6362LMsLgIeyLKNpGiaTyeeBN3BEdTgXKrDlOI4eDAbacZwlMbRtm6OoQBzHZFmGAOY4Dq7rdgO4TimlPc/Tnudp3/dF5N8HXAVsWZal+/2+9jxPA//EIqQ+EgAd32cBwJbrurrX62nbtjULD7A0wfM84jgmDEM8z8P3fcIwJEkS+v0+cRwvMet53r4B3B9Fkc6yTKdpqsMw1HEca9/3NYsc4Trgg3EcmzZoC7A5hB4LAFuWZeljx46ZGy7ZEblxMXS+7xNFEZ7nEYZhd+uH7aOU6hhrb/79gI6iSIdhKCBco5TSApRI4zpADWDPGoC3KKV0mqY6CAINfAx4qnzoui5pmtLv9zvxFwufpmlnxA6yNe1nN7HsWTRwA4vI8G/kDHEcaxY6f43jODoIAp1lmVZKaSA+duwY68bZAnA98Gi/39f9fl+zSFFfCgtL7Xlex3yapt3tCxBJknSu7RDmrwd0kiQi6h8GXtTahC3P83SSJDpNU51lmXZdVwP3imoEQSB/+wPf91k3VgE46FTnAQmLmH4YBEG/aRpGoxEsYvG7HcfB9/0ufX0s0aTW+qXA3yZJQl3XFEXxz8B2GIafmE6nFymlhkEQLDEQRRF5nl+V5zlRFAHQNA3Ak4667xIAcRwDkOf5eUqpU+1mHwnD8FLf99nZ2RHmb3VdlyiKqKqKuq6XMryzoMcDwzAMqeua6XT6buC2KIq+4jgO0+n0EjOhEmqahiAIyPOcoihIkkTmPO2odm0JgBY9gBiQdPS467qMx2NYMH9CLLvWmjzPY+BXgZ9m4bcrFpndx6Io2lvdUDI7Yy/yPB96nvfLbbj7LWBbKfUVY86D5nzzvPP5nF6vx87OziTP8ySKIqbT6Qvm8/njsyz73up3JAbpyLQBkroCA1pd7PV6nXVVSuE4Dr1eDxY+912Atm1bu66rgyDQvu+LexQvcYlpA2zbXjVKLq3Ra/e5STxJFEVdCEzrgXzf12EY6iiKxBh/0fd9sRnfTZJE7MAtUk8wh8nzPiMoAQtwreM4utfriVXtfH1rkN4O6CAIdBiG2vd97XleB4L4bt/35ftvM42gGfQAzxRXBnxdDKjpQpMkER1/erv3R4FPA38BXK+U6s5qWZas9TCL6tM+2giA7/v0+32A++I41kmSaED7vm8auL+2bVsnSaKDIBAf/S7gj4BbWNT4dJIkOssyHcex3MimpOU5tP4d+B+5BBkmAOJS4zgmSZLO8wAn0zSVfR4aDAbdnqvrHQgAcAxIAZ2mqU7TVETZbl3IX0mA0orcfcC1WZZ1mVx7U7fKvDRNTUn6FdnI8zySJIFFFVgbontjv99HhkSUqwCYGSLwatd1JUA67TiOTtNU9vzN888/HxmrAJhR3BOB08BIqret4flamqZ1G0UNkyTpanQs0t8HJpMJRVF0hQzf92/SWr9lPB6jlKIoCsIwRCn1EeBq27ZpmgbLsuj3+wXwJ1VViY4Od3Z2+pPJhDzPu4py0zTLhQylqOtajNonq6oSlTom32kvYzibzZCxjwwJeLNt250REx0G7mhF57ccx+lu1QhElhKhJEnMCu+W67paVKZNWu4RdZIgqpW8hwwp2EqShMFg0IXVQRCQJMmSNIgaAMeUUjpJElHJTwdBYEretet4XpUAT0RTkG3z8s+3knCtaci01sRxjGVZQ+C453lYlkVZlt1N2bZ9oizLVzRNY6a7tVn5aW/wEWA7z/NuzclksiV1AbnxDRcH8Hiz5wB8WvoKrRT8I3DVukDNBCA3awNS2gY+2N7ohWbtXg7R0pNlQwmKpCbged4/aK3vhy7O+IxkgK3ICr1Va709Ho9J0xTP84bj8fi+2Wx27XQ6PSx7GgZBIOB/FDhpltpbCR6uS8L2hcISXbVdmXcnSfLt9iYqc57oYAvC3dK5qeu6u+32ht+olHqO7/uig9sSNUrN37jpE3VdM5lMhm3scGVRFFe20eH7gFMsKsp5e/YnAc91XfdK27a7qhTwoaqqkCpyexnXFEXxehbu8QdkiNIbxH0lSSLBzG84jiNidKf4eCPp0MDrRRdX9B/gYuDbURSJPbm91+vR6/XIsox+v78qBUJvl2KIGWv4vq8dx+mG53k6jmMdx7Ho+s2ABHMfD4KgK6i0HuKNJs+rbvDVlmV1vrtd8FiapmKdr1BKSRp6CngdECRJ0vnl1q2ZtOV5nu71eppFT+CSddyuUutynwq8A7jfsiztOE6X8QkYxkV8CbhB7EAL6sssy+qKKO28u1YBMFWgEcPUjpNZlj0i7se27U/Vdf3uqqpeZVnWeU3T3BcEwayqqqVESAxlVVVblmUNoyhid3cXFqL55aMAADAYDL46m81+D2A6nV7WNM0zqqq6kIWxViwqPxGL1Hw7DMNT0+kUQ73eX1XVN7TWPyPl97Is9yUUJgAdF60OPzidTlFKdVniaDSqDUv7/Pl8/oBEV7CwH67rMp1Ot2zbHqZpahZMf/+ozK9SGIZfUEp9wbQzEnOYXgfoCrEt3VOW5euiKJI5+9Jka/V3QyX+W2J2KWICP+X7vvjuYdM0T3Ndd+kAwnybldE0zTZw4myZPyoJ+L7vm3nG+0ywWLw/WCJTApQ0LlsEH3QcB6WU3CLA9mg0emEcxyilLprNZrft7u6eBL7CIhV+mW3bz43jmKIoKIpiG/izx8LYUcv2SimCIOg8C4Dv+yd3d3cbwGrXefJBAMikDgBZuO3xAXxAa72V5/mJJElQSh2v6/q41P5d18W2benibgO327b9nQ01/0OZlv2NG1wLhNa6a6EVRbE65/6yLK9qbdM+K20CMJGFWil4koARhqH0/ABu0Fp/djweD13XfYaZZ9d1zWw2E7G/Ocuy03meL20oMbplWV3TRAKW7lDtLZrryjyRytWKj/QPVwEEPlGW5VWmrdoEQCEHbH3585qm+Vf4QczeJkD4vv+eIAjes7u7e11ZlseBC1gUNr4JbGdZdu+mkpRIismEMCUknkdAEpLozpQGUzrkbyv0RfleSxbQLbrWC7SLWOZinucxn8+X0E3T9C7Lsu6SGyrLclNgs0Ry84fV7VY/X73hdSCtoYe01hhtdI9FTAIsA6Bl0XYjzywh2bYtVdhD2Dv3tLe39zjgkTiOl8JxSallrKOiKPZgSTKWwn/zW7VMbIOJZ06nU6bTKbPZjDzPO6QPQbyjc/H0bjwe/7FS6vtAmef5dZvmmdHdUqjb5jASJbIIojralw4DgugvFEXxYhNhpRRhGLLq+9eRZVmmSz2UDmBgKFUhYGg+tzPHAeSZ6wNLlWoTgEfNKKuNx4erAIjuHtQIEeb39vbOpO+4iXrQGcorJpNJugmEDcD0hC/grbSSvg6Az0tOb1DSvsfpxpo5S2TbNnVdk+f5WuabpklGo9HgII5Xvve/K8HQ4zZJywYJ8o01T6/uZQLwvaZp3qWUYj6fC5NfPKjLugqEWjyTkybKOkqAsVLqkd3d3Vs2Mb5i0PIVQNQZqoBveJx9N7dqOidtIUQs7InxePwUqfCYw4i3lw4u6mPW92UAx23bpg2lbx6NRheYL8OE5NVIWy4bma9MgHrDTW+iAroawRVAdhAA3xCXB10T5PnrOizSRZKfYhOCIFhKSFZefVjCsCRUEmabPt5xHIIgkO9MBSCx6mdoCB+o65ogCLBt+9dZ9C42AvClpmm6el676FXiDteN9hFkd2sHzQVOCrMtc6/J8/y5qwZ1pcD5xBWVmK8yLqCuoyRJHpEUuc1oh+bnq4+kIiBP07R7pzebzdBaP8VxnG9sghjo1OIwmkwmW0EQDCW0bsPrbRatrm8DJYtHWBcDL/c877IwDDtwAddxnGp13YPUoK7rr4VheJFt20wmk12tdX8TAAB/6TjOG8wqT1EUt8Vx/IeHMTedTo8S3j4NuM33/eNBEEilprM7chaxNXL7rVf5HeDOdevKM5x1Hmo0Gn0qDMNntwA0WuuOuXUPJN5aVVVpWdaNUmoGfnFTqCkk+t9WZg+i/wS227T1uHgV0xCav1dVJVJ44ybmga4nsUrtOk+VLBf4yL4Ja27t6bQt6yRJpEA6MHt2MqQvmGUZg8HgKA+ghJ5I+xjKcRztum7X+pZKbluZvge4Wspyq2TbNkEQdH2GNYb3zZZl6TRNZb0Xmd5jSQXMnHk2m52MouhZjuMwHo/RWl/u+/7nTOCkViC9PjFKeZ6vvY3Vg/f7fU6fPn0ei3+XuRz4CRaBy4hF/f7eNE0/KZHlutqCgL6p6PLwww+f6vV657XfPwWcv1R7WF3QoO2iKJ4lVZ6qql5dluXnBLkgCLpU1MznlVIkScJkMjkUBIDBYHC6qqoPlGX5AdH7+Xx+YIYnJO+CNqndaDT6c9d1z2uapmvKrM5ZkgBzw6ZpOo8ASEXoTUqpWzzPW/L/q8mRSMJ4PDYLEUskEiCdIengrAIg51qVAMuyDk3K5vP5I1EUDRzHYTQaEcexWg3RlyA2gxzP8/aA94qfz7IM4E+11m9po7qNOYFISZqmR3KNZ0MCyqYxn88vVUoNHMfpXO2hvcGWyY5OnTr1XcdxmM1m3eOm2Wx2087OTh3H8c0HHVBQlt7AJkk4G5In9QclZZPJ5DXiFlsAbt33QIoVCZD424jDv1qWJZZlMZvNvmxZFr1eD6XUG/M8P/SdvhjKcy0J7TtC+feZtQP4bSMu+FgQBN9aZyiXAFgTuv6b8fLikr29vW9KSOm67nAymfx7nucv2dnZ2RiIiyQc5bXoQSTryPO8g0ZRFJcppbL2d1i0ziQhW6K1DyUNOrWzs/NJpdSVbSBx53g8fqXv+xe0ycULyrJ8QV3X7O7uWm0TdB+Ji0zT9MjeYR3Jc9fDos35fD6UZ7F1XU/iOP6XTXsuScBkMtk3gG3j//Q+A7ywKIp3tJ+ZjdEDfZYUWzfV5w/7rlh9KZkfUgT5uESQtO8RNrrVI+TTAM8HXrTytyuA9wKv5Qhv9H8EdA3wknUfrI0Ef1zpsfzT1P8L+j+jBDv6UEdeKQAAAABJRU5ErkJggg=="
$filePicked = Invoke-OpenFileDialog -Title $title -InitialDirectory $initialDirectory -Filter $filter -IconBase64 $iconBase64OpenFileDialog -TopMost $true
# Then use this to check if the picker has done its job.
if ($filePicked -ne "") {
$filePicked
Write-Host 'File Picked...'
}
else {
Write-Host 'File Not Picked...'
}

Ask user for file path to save

This is what I was going for
$x = Get-Process
$y = Get-Date -Format yyyy-MM-dd_hh.mmtt
$SelPath = Read-Host -Prompt "Choose a location to save the file?"
$Path = $SelPath + 'Running Process' + ' ' + $FixedDate + '.txt'
$x | Out-File $Path
"Read-Host". For example:
$folder = Read-Host "Folder location"
While the links provided in the comments show the use of the FolderBrowserDialog, all of these fail to show it as a Topmost form and also do not dispose of the form when done.
Here's two functions that ensure the dialog gets displayed on top.
The first one uses the Shell.Application Com object:
# Show an Open Folder Dialog and return the directory selected by the user.
function Get-FolderName {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
[string]$Message = "Select a directory.",
[string]$InitialDirectory = [System.Environment+SpecialFolder]::MyDocuments,
[switch]$ShowNewFolderButton
)
$browserForFolderOptions = 0x00000041 # BIF_RETURNONLYFSDIRS -bor BIF_NEWDIALOGSTYLE
if (!$ShowNewFolderButton) { $browserForFolderOptions += 0x00000200 } # BIF_NONEWFOLDERBUTTON
$browser = New-Object -ComObject Shell.Application
# To make the dialog topmost, you need to supply the Window handle of the current process
[intPtr]$handle = [System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle
# see: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773205(v=vs.85).aspx
$folder = $browser.BrowseForFolder($handle, $Message, $browserForFolderOptions, $InitialDirectory)
$result = $null
if ($folder) {
$result = $folder.Self.Path
}
# Release and remove the used Com object from memory
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($browser) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
return $result
}
$folder = Get-FolderName
if ($folder) { Write-Host "You selected the directory: $folder" }
else { "You did not select a directory." }
The second one uses a bit of C# code for the 'Topmost' feature and System.Windows.Forms
function Get-FolderName {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
[string]$Message = "Please select a directory.",
[System.Environment+SpecialFolder]$InitialDirectory = [System.Environment+SpecialFolder]::MyDocuments,
[switch]$ShowNewFolderButton
)
# To ensure the dialog window shows in the foreground, you need to get a Window Handle from the owner process.
# This handle must implement System.Windows.Forms.IWin32Window
# Create a wrapper class that implements IWin32Window.
# The IWin32Window interface contains only a single property that must be implemented to expose the underlying handle.
$code = #"
using System;
using System.Windows.Forms;
public class Win32Window : IWin32Window
{
public Win32Window(IntPtr handle)
{
Handle = handle;
}
public IntPtr Handle { get; private set; }
}
"#
if (-not ([System.Management.Automation.PSTypeName]'Win32Window').Type) {
Add-Type -TypeDefinition $code -ReferencedAssemblies System.Windows.Forms.dll -Language CSharp
}
# Get the window handle from the current process
# $owner = New-Object Win32Window -ArgumentList ([System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle)
# Or write like this:
$owner = [Win32Window]::new([System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle)
# Or use the the window handle from the desktop
# $owner = New-Object Win32Window -ArgumentList (Get-Process -Name explorer).MainWindowHandle
# Or write like this:
# $owner = [Win32Window]::new((Get-Process -Name explorer).MainWindowHandle)
Add-Type -AssemblyName System.Windows.Forms
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
$dialog.Description = $Message
$dialog.RootFolder = $InitialDirectory
# $dialog.SelectedPath = '' # a folder within the RootFolder to pre-select
$dialog.ShowNewFolderButton = if ($ShowNewFolderButton) { $true } else { $false }
$result = $null
if ($dialog.ShowDialog($owner).ToString() -eq 'OK') {
$result = $dialog.SelectedPath
}
# clear the FolderBrowserDialog from memory
$dialog.Dispose()
return $result
}
$folder = Get-FolderName -InitialDirectory MyPictures
if ($folder) { Write-Host "You selected the directory: $folder" }
else { "You did not select a directory." }
Hope that helps
p.s. See Environment.SpecialFolder Enum for the [System.Environment+SpecialFolder] enumeration values

Copy files which are complete?

While running a command to copy files from source to destination using powershell, I ran into the problem of how to only copy files which do not have any operation running on them?
If there are 3 files A/B/C, logging is still happening on A, while B and C are complete. I only want to copy B and C using powershell.
Any ideas will be helpful.
Here is something you can start with:
function Test-FileNotInUse
{
[cmdletbinding()]
param(
[parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('FullName')] [string] $filePath
)
process {
$inUse = $false
try {
$fileInfo = New-Object System.IO.FileInfo $filePath
$fileStream = $fileInfo.Open([System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
if ($fileStream) {
$fileStream.Close()
}
}
catch {
$inUse = $true
}
if (!$inUse) {
Write-Output $filePath
}
}
}
$source = "C:\source"
$destination = "C:\destination"
dir $source -recurse | Test-FileNotInUse | %{ copy $_ $destination }