From 9de6fbcb623feb6b3efefb3e6f30049c2510fb02 Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Thu, 6 Mar 2025 05:44:00 +0300 Subject: [PATCH 01/11] Fix wildcard & confirm override CreateSymLinkFile.ps1 --- CreateSymLinkFile.ps1 | 86 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/CreateSymLinkFile.ps1 b/CreateSymLinkFile.ps1 index 9bbb73e..80e46c8 100644 --- a/CreateSymLinkFile.ps1 +++ b/CreateSymLinkFile.ps1 @@ -1,17 +1,55 @@ +Add-Type -AssemblyName PresentationFramework # Function to check for elevated privileges function Test-Admin { $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) return $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } +function Confirm-Overwrite { + param ($linkPath) + + # Presume that it's not a folder + $isDirectory = $false + + if (-not [string]::IsNullOrWhiteSpace($linkPath)) { + # If the input box isn't empty, check if it's a valid path + if (Test-Path $linkPath -PathType Leaf) { + # if the path points to a file, it is not a folder + $isDirectory = $false + } + elseif (Test-Path $linkPath -PathType Container) { + # if the path points to a folder, it is a folder + $isDirectory = $true + } + } + + $message = "A $(if ($isDirectory) { 'folder' } else { 'file' }) with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" + $result = [System.Windows.MessageBox]::Show( + $message, + "Confirm Overwrite", + [System.Windows.MessageBoxButton]::YesNoCancel, + [System.Windows.MessageBoxImage]::Warning + ) + return $result +} + +function Escape-WildcardCharacters { + param ( + [string]$path + ) + $escapedPath = $path -replace '([*?\[\]])', '``$1' + return $escapedPath +} + # Save the initial working directory -$initialWorkingDirectory = (Get-Location).Path +$initialWorkingDirectory = [System.IO.Directory]::GetCurrentDirectory() +$escapedInitialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory if (-not (Test-Admin)) { Write-Output "Please run this script as an administrator. Let's try rerunning automatically..." # Re-run the script with elevated privileges, passing the initial working directory as an argument - Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$initialWorkingDirectory`"" -Verb RunAs + Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$escapedInitialWorkingDirectory`"" -Verb RunAs Exit # Exit the non-elevated instance } @@ -27,11 +65,14 @@ function Show-FilePickerDialog { $dialog.Filter = "All files (*.*)|*.*" if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { return $dialog.FileName - } else { + } + else { return $null } } +# Function to escape wildcard characters in a path + # Main script $selectedPath = Show-FilePickerDialog @@ -40,35 +81,46 @@ if ($selectedPath) { $linkName = Split-Path -Leaf $selectedPath $linkPath = Join-Path -Path $currentDir -ChildPath $linkName + # Escape wildcard characters in the paths + $escapedLinkPath = Escape-WildcardCharacters -path $linkPath + $escapedSelectedPath = Escape-WildcardCharacters -path $selectedPath + Write-Output "Original Working Directory: $originalWorkingDirectory" Write-Output "Current Directory: $currentDir" Write-Output "Selected Path: $selectedPath" + Write-Output "Escaped Selected Path: $escapedSelectedPath" Write-Output "Link Path: $linkPath" + Write-Output "Escaped Link Path: $escapedLinkPath" - # Check if selected item is a file, and then check if we already have a file with that name and offer to overwrite if we do - if (Test-Path -Path $selectedPath -PathType Leaf) { - if (Test-Path -Path $linkPath) { - $choice = Confirm-Overwrite -linkPath $linkPath + # Check if selected item is a file, and then check if we already have a file with that name and offer to overwrite if we do + if (Test-Path -Path $escapedSelectedPath -PathType Leaf) { + if (Test-Path -Path $escapedLinkPath) { + $choice = Confirm-Overwrite -linkPath $escapedLinkPath if ($choice -eq 'Yes') { # Remove the existing symlink - Remove-Item -Path $linkPath -Force + Remove-Item -Path $escapedLinkPath -Force # Create a new file symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop - Write-Output "Overwritten file symbolic link: $linkPath -> $selectedPath" - } elseif ($choice -eq 'No') { + New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop + Write-Output "Overwritten file symbolic link: $escapedLinkPath -> $escapedSelectedPath" + } + elseif ($choice -eq 'No') { Write-Output "Symbolic link (or file/folder with that name) already existed! Operation canceled by user by not overwriting." - } else { + } + else { Write-Output "Operation canceled by user." } - } else { + } + else { # Create a new file symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop - Write-Output "Created file symbolic link: $linkPath -> $selectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop + Write-Output "Created file symbolic link: $escapedLinkPath -> $escapedSelectedPath" } - } else { + } + else { Write-Output "The selected path is not a file." } -} else { +} +else { Write-Output "No file was selected." } From 4f05291e83ee0499c0254e285a3ae3e49b3ef969 Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Thu, 6 Mar 2025 06:45:42 +0300 Subject: [PATCH 02/11] fix for wildcard along with newer folder pickup CreateSymLinkFolder.ps1 --- CreateSymLinkFolder.ps1 | 247 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 225 insertions(+), 22 deletions(-) diff --git a/CreateSymLinkFolder.ps1 b/CreateSymLinkFolder.ps1 index 5d6366d..d3fbe59 100644 --- a/CreateSymLinkFolder.ps1 +++ b/CreateSymLinkFolder.ps1 @@ -1,17 +1,203 @@ +$source = @' +// https://stackoverflow.com/a/66823582/21149029 +using System; +using System.Diagnostics; +using System.Reflection; +using System.Windows.Forms; +/// +/// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions +/// +#pragma warning disable 0219, 0414, 0162 +public class FolderSelectDialog { + private string _initialDirectory; + private string _title; + private string _message; + private string _fileName = ""; + + public string InitialDirectory { + get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; } + set { _initialDirectory = value; } + } + public string Title { + get { return _title ?? "Select a folder"; } + set { _title = value; } + } + public string Message { + get { return _message ?? _title ?? "Select a folder"; } + set { _message = value; } + } + public string FileName { get { return _fileName; } } + + public FolderSelectDialog(string defaultPath="MyComputer", string title="Select a folder", string message=""){ + InitialDirectory = defaultPath; + Title = title; + Message = message; + } + + public bool Show() { return Show(IntPtr.Zero); } + + /// Handle of the control or window to be the parent of the file dialog + /// true if the user clicks OK + public bool Show(IntPtr? hWndOwnerNullable=null) { + IntPtr hWndOwner = IntPtr.Zero; + if(hWndOwnerNullable!=null) + hWndOwner = (IntPtr)hWndOwnerNullable; + if(Environment.OSVersion.Version.Major >= 6){ + try{ + var resulta = VistaDialog.Show(hWndOwner, InitialDirectory, Title, Message); + _fileName = resulta.FileName; + return resulta.Result; + } + catch(Exception){ + var resultb = ShowXpDialog(hWndOwner, InitialDirectory, Title, Message); + _fileName = resultb.FileName; + return resultb.Result; + } + } + var result = ShowXpDialog(hWndOwner, InitialDirectory, Title, Message); + _fileName = result.FileName; + return result.Result; + } + + private struct ShowDialogResult { + public bool Result { get; set; } + public string FileName { get; set; } + } + + private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title, string message) { + var folderBrowserDialog = new FolderBrowserDialog { + Description = message, + SelectedPath = initialDirectory, + ShowNewFolderButton = true + }; + var dialogResult = new ShowDialogResult(); + if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK) { + dialogResult.Result = true; + dialogResult.FileName = folderBrowserDialog.SelectedPath; + } + return dialogResult; + } + + private static class VistaDialog { + private const string c_foldersFilter = "Folders|\n"; + + private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly; + private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog"); + private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags); + private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags); + private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags); + private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags); + private readonly static uint s_fosPickFoldersBitFlag = (uint) s_windowsFormsAssembly + .GetType("System.Windows.Forms.FileDialogNative+FOS") + .GetField("FOS_PICKFOLDERS") + .GetValue(null); + private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly + .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents") + .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null); + private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise"); + private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise"); + private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show"); + + public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title, string description) { + var openFileDialog = new OpenFileDialog { + AddExtension = false, + CheckFileExists = false, + DereferenceLinks = true, + Filter = c_foldersFilter, + InitialDirectory = initialDirectory, + Multiselect = false, + Title = title + }; + + var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { }); + s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog }); + s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint) s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag }); + var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U }; + s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken); + + try { + int retVal = (int) s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle }); + return new ShowDialogResult { + Result = retVal == 0, + FileName = openFileDialog.FileName + }; + } + finally { + s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] }); + } + } + } + + // Wrap an IWin32Window around an IntPtr + private class WindowWrapper : IWin32Window { + private readonly IntPtr _handle; + public WindowWrapper(IntPtr handle) { _handle = handle; } + public IntPtr Handle { get { return _handle; } } + } + + public string getPath(){ + if (Show()){ + return FileName; + } + return ""; + } +} +'@ # Function to check for elevated privileges function Test-Admin { $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) return $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } +function Confirm-Overwrite { + param ( + $linkPath + ) + Add-Type -AssemblyName PresentationFramework + + # Presume that it's not a folder + $isDirectory = $false + + if (-not [string]::IsNullOrWhiteSpace($linkPath)) { + # If the input box isn't empty, check if it's a valid path + if (Test-Path $linkPath -PathType Leaf) { + # if the path points to a file, it is not a folder + $isDirectory = $false + } + elseif (Test-Path $linkPath -PathType Container) { + # if the path points to a folder, it is a folder + $isDirectory = $true + } + } + + $message = "A $(if ($isDirectory) { 'folder' } else { 'file' }) with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" + $result = [System.Windows.MessageBox]::Show( + $message, + "Confirm Overwrite", + [System.Windows.MessageBoxButton]::YesNoCancel, + [System.Windows.MessageBoxImage]::Warning + ) + return $result +} + +function Escape-WildcardCharacters { + param ( + [string]$path + ) + $escapedPath = $path -replace '([*?\[\]])', '``$1' + return $escapedPath +} + # Save the initial working directory -$initialWorkingDirectory = (Get-Location).Path +$initialWorkingDirectory = [System.IO.Directory]::GetCurrentDirectory() +$escapedInitialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory if (-not (Test-Admin)) { Write-Output "Please run this script as an administrator. Let's try rerunning automatically..." # Re-run the script with elevated privileges, passing the initial working directory as an argument - Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$initialWorkingDirectory`"" -Verb RunAs + Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$escapedInitialWorkingDirectory`"" -Verb RunAs Exit # Exit the non-elevated instance } @@ -21,15 +207,21 @@ Set-Location -Path $originalWorkingDirectory # Function to show folder picker dialog function Show-FolderPickerDialog { - Add-Type -AssemblyName System.Windows.Forms - $dialog = New-Object System.Windows.Forms.FolderBrowserDialog - $dialog.Description = "Select a folder to create a symbolic link" - $dialog.SelectedPath = [System.IO.Directory]::GetCurrentDirectory() - if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { - return $dialog.SelectedPath - } else { + + Add-Type -Language CSharp -TypeDefinition $source -ReferencedAssemblies ("System.Windows.Forms", "System.ComponentModel.Primitives") + + $path = [System.IO.Directory]::GetCurrentDirectory() + $title = "Select Folder" + $description = "Select a folder to create a symbolic link" + + $out = [FolderSelectDialog]::new($path, $title, $description).getPath() + + if ($out -eq "") { return $null } + else { + return $out + } } # Main script @@ -40,34 +232,45 @@ if ($selectedPath) { $linkName = Split-Path -Leaf $selectedPath $linkPath = Join-Path -Path $currentDir -ChildPath $linkName + # Escape wildcard characters in the paths + $escapedLinkPath = Escape-WildcardCharacters -path $linkPath + $escapedSelectedPath = Escape-WildcardCharacters -path $selectedPath + Write-Output "Original Working Directory: $originalWorkingDirectory" Write-Output "Current Directory: $currentDir" Write-Output "Selected Path: $selectedPath" + Write-Output "Escaped Selected Path: $escapedSelectedPath" Write-Output "Link Path: $linkPath" + Write-Output "Escaped Link Path: $escapedLinkPath" - if (Test-Path -Path $selectedPath -PathType Container) { - if (Test-Path -Path $linkPath) { - $choice = Confirm-Overwrite -linkPath $linkPath + if (Test-Path -Path $escapedSelectedPath -PathType Container) { + if (Test-Path -Path $escapedLinkPath) { + $choice = Confirm-Overwrite -linkPath $escapedLinkPath if ($choice -eq 'Yes') { # Remove the existing symlink - Remove-Item -Path $linkPath -Force + Remove-Item -Path $escapedLinkPath -Force # Create a new directory symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop - Write-Output "Overwritten directory symbolic link: $linkPath -> $selectedPath" - } elseif ($choice -eq 'No') { + New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop + Write-Output "Overwritten directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" + } + elseif ($choice -eq 'No') { Write-Output "Symbolic link (or file/folder with that name) already existed! Operation canceled by user by not overwriting." - } else { + } + else { Write-Output "Operation canceled by user." } - } else { + } + else { # Create a new directory symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop - Write-Output "Created directory symbolic link: $linkPath -> $selectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop + Write-Output "Created directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" } - } else { + } + else { Write-Output "The selected path is not a folder." } -} else { +} +else { Write-Output "No folder was selected." } From 5752ddb116cb319adea297e997bc480f9c930286 Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Thu, 6 Mar 2025 06:46:01 +0300 Subject: [PATCH 03/11] Syntax Update CreateSymLinkFile.ps1 --- CreateSymLinkFile.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CreateSymLinkFile.ps1 b/CreateSymLinkFile.ps1 index 80e46c8..798810a 100644 --- a/CreateSymLinkFile.ps1 +++ b/CreateSymLinkFile.ps1 @@ -1,4 +1,3 @@ -Add-Type -AssemblyName PresentationFramework # Function to check for elevated privileges function Test-Admin { $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) @@ -6,7 +5,10 @@ function Test-Admin { } function Confirm-Overwrite { - param ($linkPath) + param ( + $linkPath + ) + Add-Type -AssemblyName PresentationFramework # Presume that it's not a folder $isDirectory = $false From 7f96a5635425f01aaa4d699e200c52540ce319ea Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Fri, 7 Mar 2025 19:28:50 +0300 Subject: [PATCH 04/11] Update CreateSymLinkFolder.ps1 --- CreateSymLinkFolder.ps1 | 206 +++------------------------------------- 1 file changed, 15 insertions(+), 191 deletions(-) diff --git a/CreateSymLinkFolder.ps1 b/CreateSymLinkFolder.ps1 index d3fbe59..5bc614d 100644 --- a/CreateSymLinkFolder.ps1 +++ b/CreateSymLinkFolder.ps1 @@ -1,149 +1,3 @@ -$source = @' -// https://stackoverflow.com/a/66823582/21149029 -using System; -using System.Diagnostics; -using System.Reflection; -using System.Windows.Forms; -/// -/// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions -/// -#pragma warning disable 0219, 0414, 0162 -public class FolderSelectDialog { - private string _initialDirectory; - private string _title; - private string _message; - private string _fileName = ""; - - public string InitialDirectory { - get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; } - set { _initialDirectory = value; } - } - public string Title { - get { return _title ?? "Select a folder"; } - set { _title = value; } - } - public string Message { - get { return _message ?? _title ?? "Select a folder"; } - set { _message = value; } - } - public string FileName { get { return _fileName; } } - - public FolderSelectDialog(string defaultPath="MyComputer", string title="Select a folder", string message=""){ - InitialDirectory = defaultPath; - Title = title; - Message = message; - } - - public bool Show() { return Show(IntPtr.Zero); } - - /// Handle of the control or window to be the parent of the file dialog - /// true if the user clicks OK - public bool Show(IntPtr? hWndOwnerNullable=null) { - IntPtr hWndOwner = IntPtr.Zero; - if(hWndOwnerNullable!=null) - hWndOwner = (IntPtr)hWndOwnerNullable; - if(Environment.OSVersion.Version.Major >= 6){ - try{ - var resulta = VistaDialog.Show(hWndOwner, InitialDirectory, Title, Message); - _fileName = resulta.FileName; - return resulta.Result; - } - catch(Exception){ - var resultb = ShowXpDialog(hWndOwner, InitialDirectory, Title, Message); - _fileName = resultb.FileName; - return resultb.Result; - } - } - var result = ShowXpDialog(hWndOwner, InitialDirectory, Title, Message); - _fileName = result.FileName; - return result.Result; - } - - private struct ShowDialogResult { - public bool Result { get; set; } - public string FileName { get; set; } - } - - private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title, string message) { - var folderBrowserDialog = new FolderBrowserDialog { - Description = message, - SelectedPath = initialDirectory, - ShowNewFolderButton = true - }; - var dialogResult = new ShowDialogResult(); - if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK) { - dialogResult.Result = true; - dialogResult.FileName = folderBrowserDialog.SelectedPath; - } - return dialogResult; - } - - private static class VistaDialog { - private const string c_foldersFilter = "Folders|\n"; - - private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly; - private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog"); - private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags); - private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags); - private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags); - private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags); - private readonly static uint s_fosPickFoldersBitFlag = (uint) s_windowsFormsAssembly - .GetType("System.Windows.Forms.FileDialogNative+FOS") - .GetField("FOS_PICKFOLDERS") - .GetValue(null); - private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly - .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents") - .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null); - private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise"); - private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise"); - private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show"); - - public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title, string description) { - var openFileDialog = new OpenFileDialog { - AddExtension = false, - CheckFileExists = false, - DereferenceLinks = true, - Filter = c_foldersFilter, - InitialDirectory = initialDirectory, - Multiselect = false, - Title = title - }; - - var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { }); - s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog }); - s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint) s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag }); - var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U }; - s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken); - - try { - int retVal = (int) s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle }); - return new ShowDialogResult { - Result = retVal == 0, - FileName = openFileDialog.FileName - }; - } - finally { - s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] }); - } - } - } - - // Wrap an IWin32Window around an IntPtr - private class WindowWrapper : IWin32Window { - private readonly IntPtr _handle; - public WindowWrapper(IntPtr handle) { _handle = handle; } - public IntPtr Handle { get { return _handle; } } - } - - public string getPath(){ - if (Show()){ - return FileName; - } - return ""; - } -} -'@ # Function to check for elevated privileges function Test-Admin { $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) @@ -151,27 +5,10 @@ function Test-Admin { } function Confirm-Overwrite { - param ( - $linkPath - ) + param ($linkPath) Add-Type -AssemblyName PresentationFramework - # Presume that it's not a folder - $isDirectory = $false - - if (-not [string]::IsNullOrWhiteSpace($linkPath)) { - # If the input box isn't empty, check if it's a valid path - if (Test-Path $linkPath -PathType Leaf) { - # if the path points to a file, it is not a folder - $isDirectory = $false - } - elseif (Test-Path $linkPath -PathType Container) { - # if the path points to a folder, it is a folder - $isDirectory = $true - } - } - - $message = "A $(if ($isDirectory) { 'folder' } else { 'file' }) with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" + $message = "A folder with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" $result = [System.Windows.MessageBox]::Show( $message, "Confirm Overwrite", @@ -182,9 +19,7 @@ function Confirm-Overwrite { } function Escape-WildcardCharacters { - param ( - [string]$path - ) + param ([string]$path) $escapedPath = $path -replace '([*?\[\]])', '``$1' return $escapedPath } @@ -207,21 +42,15 @@ Set-Location -Path $originalWorkingDirectory # Function to show folder picker dialog function Show-FolderPickerDialog { - - Add-Type -Language CSharp -TypeDefinition $source -ReferencedAssemblies ("System.Windows.Forms", "System.ComponentModel.Primitives") - - $path = [System.IO.Directory]::GetCurrentDirectory() - $title = "Select Folder" - $description = "Select a folder to create a symbolic link" - - $out = [FolderSelectDialog]::new($path, $title, $description).getPath() - - if ($out -eq "") { + Add-Type -AssemblyName System.Windows.Forms + $dialog = New-Object System.Windows.Forms.FolderBrowserDialog + $dialog.Description = "Select a folder to create a symbolic link" + $dialog.SelectedPath = [System.IO.Directory]::GetCurrentDirectory() + if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + return $dialog.SelectedPath + } else { return $null } - else { - return $out - } } # Main script @@ -252,25 +81,20 @@ if ($selectedPath) { # Create a new directory symbolic link New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop Write-Output "Overwritten directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" - } - elseif ($choice -eq 'No') { + } elseif ($choice -eq 'No') { Write-Output "Symbolic link (or file/folder with that name) already existed! Operation canceled by user by not overwriting." - } - else { + } else { Write-Output "Operation canceled by user." } - } - else { + } else { # Create a new directory symbolic link New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop Write-Output "Created directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" } - } - else { + } else { Write-Output "The selected path is not a folder." } -} -else { +} else { Write-Output "No folder was selected." } From 52db00c66d45441105de3c656f854acdf2d5f8fb Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Fri, 7 Mar 2025 19:30:17 +0300 Subject: [PATCH 05/11] Update CreateSymLinkFile.ps1 --- CreateSymLinkFile.ps1 | 48 ++++++++++--------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/CreateSymLinkFile.ps1 b/CreateSymLinkFile.ps1 index 798810a..e5bca9f 100644 --- a/CreateSymLinkFile.ps1 +++ b/CreateSymLinkFile.ps1 @@ -5,27 +5,10 @@ function Test-Admin { } function Confirm-Overwrite { - param ( - $linkPath - ) + param ($linkPath) Add-Type -AssemblyName PresentationFramework - # Presume that it's not a folder - $isDirectory = $false - - if (-not [string]::IsNullOrWhiteSpace($linkPath)) { - # If the input box isn't empty, check if it's a valid path - if (Test-Path $linkPath -PathType Leaf) { - # if the path points to a file, it is not a folder - $isDirectory = $false - } - elseif (Test-Path $linkPath -PathType Container) { - # if the path points to a folder, it is a folder - $isDirectory = $true - } - } - - $message = "A $(if ($isDirectory) { 'folder' } else { 'file' }) with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" + $message = "A file with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" $result = [System.Windows.MessageBox]::Show( $message, "Confirm Overwrite", @@ -35,10 +18,9 @@ function Confirm-Overwrite { return $result } +# Function to escape wildcard characters in a path function Escape-WildcardCharacters { - param ( - [string]$path - ) + param ([string]$path) $escapedPath = $path -replace '([*?\[\]])', '``$1' return $escapedPath } @@ -67,13 +49,11 @@ function Show-FilePickerDialog { $dialog.Filter = "All files (*.*)|*.*" if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { return $dialog.FileName - } - else { + } else { return $null } } -# Function to escape wildcard characters in a path # Main script $selectedPath = Show-FilePickerDialog @@ -94,35 +74,29 @@ if ($selectedPath) { Write-Output "Link Path: $linkPath" Write-Output "Escaped Link Path: $escapedLinkPath" - # Check if selected item is a file, and then check if we already have a file with that name and offer to overwrite if we do if (Test-Path -Path $escapedSelectedPath -PathType Leaf) { if (Test-Path -Path $escapedLinkPath) { - $choice = Confirm-Overwrite -linkPath $escapedLinkPath + $choice = Confirm-Overwrite -linkPath $linkPath if ($choice -eq 'Yes') { # Remove the existing symlink Remove-Item -Path $escapedLinkPath -Force # Create a new file symbolic link New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop Write-Output "Overwritten file symbolic link: $escapedLinkPath -> $escapedSelectedPath" - } - elseif ($choice -eq 'No') { + } elseif ($choice -eq 'No') { Write-Output "Symbolic link (or file/folder with that name) already existed! Operation canceled by user by not overwriting." - } - else { + } else { Write-Output "Operation canceled by user." } - } - else { + } else { # Create a new file symbolic link New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop Write-Output "Created file symbolic link: $escapedLinkPath -> $escapedSelectedPath" } - } - else { + } else { Write-Output "The selected path is not a file." } -} -else { +} else { Write-Output "No file was selected." } From b87f5a148187aa8d7a149c59325a333eb03d5ee4 Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Fri, 7 Mar 2025 19:38:27 +0300 Subject: [PATCH 06/11] Update CreateSymLinkFile.ps1 --- CreateSymLinkFile.ps1 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CreateSymLinkFile.ps1 b/CreateSymLinkFile.ps1 index e5bca9f..b60859d 100644 --- a/CreateSymLinkFile.ps1 +++ b/CreateSymLinkFile.ps1 @@ -65,24 +65,23 @@ if ($selectedPath) { # Escape wildcard characters in the paths $escapedLinkPath = Escape-WildcardCharacters -path $linkPath - $escapedSelectedPath = Escape-WildcardCharacters -path $selectedPath + $selectedPath = Escape-WildcardCharacters -path $selectedPath Write-Output "Original Working Directory: $originalWorkingDirectory" Write-Output "Current Directory: $currentDir" Write-Output "Selected Path: $selectedPath" - Write-Output "Escaped Selected Path: $escapedSelectedPath" Write-Output "Link Path: $linkPath" Write-Output "Escaped Link Path: $escapedLinkPath" - if (Test-Path -Path $escapedSelectedPath -PathType Leaf) { + if (Test-Path -Path $selectedPath -PathType Leaf) { if (Test-Path -Path $escapedLinkPath) { $choice = Confirm-Overwrite -linkPath $linkPath if ($choice -eq 'Yes') { # Remove the existing symlink Remove-Item -Path $escapedLinkPath -Force # Create a new file symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop - Write-Output "Overwritten file symbolic link: $escapedLinkPath -> $escapedSelectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop + Write-Output "Overwritten file symbolic link: $escapedLinkPath -> $selectedPath" } elseif ($choice -eq 'No') { Write-Output "Symbolic link (or file/folder with that name) already existed! Operation canceled by user by not overwriting." } else { @@ -90,8 +89,8 @@ if ($selectedPath) { } } else { # Create a new file symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop - Write-Output "Created file symbolic link: $escapedLinkPath -> $escapedSelectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop + Write-Output "Created file symbolic link: $escapedLinkPath -> $selectedPath" } } else { Write-Output "The selected path is not a file." From 8d38d1631c721019db943559f3a07bb171058d2d Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Fri, 7 Mar 2025 19:38:40 +0300 Subject: [PATCH 07/11] Update CreateSymLinkFolder.ps1 --- CreateSymLinkFolder.ps1 | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/CreateSymLinkFolder.ps1 b/CreateSymLinkFolder.ps1 index 5bc614d..013fd02 100644 --- a/CreateSymLinkFolder.ps1 +++ b/CreateSymLinkFolder.ps1 @@ -44,7 +44,7 @@ Set-Location -Path $originalWorkingDirectory function Show-FolderPickerDialog { Add-Type -AssemblyName System.Windows.Forms $dialog = New-Object System.Windows.Forms.FolderBrowserDialog - $dialog.Description = "Select a folder to create a symbolic link" + $dialog.Description = "Select a folder to create a symbolic link to" $dialog.SelectedPath = [System.IO.Directory]::GetCurrentDirectory() if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { return $dialog.SelectedPath @@ -63,24 +63,23 @@ if ($selectedPath) { # Escape wildcard characters in the paths $escapedLinkPath = Escape-WildcardCharacters -path $linkPath - $escapedSelectedPath = Escape-WildcardCharacters -path $selectedPath + $selectedPath = Escape-WildcardCharacters -path $selectedPath Write-Output "Original Working Directory: $originalWorkingDirectory" Write-Output "Current Directory: $currentDir" Write-Output "Selected Path: $selectedPath" - Write-Output "Escaped Selected Path: $escapedSelectedPath" Write-Output "Link Path: $linkPath" Write-Output "Escaped Link Path: $escapedLinkPath" - if (Test-Path -Path $escapedSelectedPath -PathType Container) { + if (Test-Path -Path $selectedPath -PathType Container) { if (Test-Path -Path $escapedLinkPath) { $choice = Confirm-Overwrite -linkPath $escapedLinkPath if ($choice -eq 'Yes') { # Remove the existing symlink Remove-Item -Path $escapedLinkPath -Force # Create a new directory symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop - Write-Output "Overwritten directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop + Write-Output "Overwritten directory symbolic link: $escapedLinkPath -> $selectedPath" } elseif ($choice -eq 'No') { Write-Output "Symbolic link (or file/folder with that name) already existed! Operation canceled by user by not overwriting." } else { @@ -88,8 +87,8 @@ if ($selectedPath) { } } else { # Create a new directory symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop - Write-Output "Created directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop + Write-Output "Created directory symbolic link: $escapedLinkPath -> $selectedPath" } } else { Write-Output "The selected path is not a folder." From 01b1a2a3b052a2baf3e822dcd169e53072365d67 Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Fri, 7 Mar 2025 23:22:12 +0300 Subject: [PATCH 08/11] Update CreateSymLinkFile.ps1 --- CreateSymLinkFile.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CreateSymLinkFile.ps1 b/CreateSymLinkFile.ps1 index b60859d..24cb060 100644 --- a/CreateSymLinkFile.ps1 +++ b/CreateSymLinkFile.ps1 @@ -27,13 +27,13 @@ function Escape-WildcardCharacters { # Save the initial working directory $initialWorkingDirectory = [System.IO.Directory]::GetCurrentDirectory() -$escapedInitialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory +$initialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory if (-not (Test-Admin)) { Write-Output "Please run this script as an administrator. Let's try rerunning automatically..." # Re-run the script with elevated privileges, passing the initial working directory as an argument - Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$escapedInitialWorkingDirectory`"" -Verb RunAs + Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$initialWorkingDirectory`"" -Verb RunAs Exit # Exit the non-elevated instance } From 1ddb19521c915727148e3e58bdbba71f36112af6 Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Fri, 7 Mar 2025 23:22:20 +0300 Subject: [PATCH 09/11] Update CreateSymLinkFolder.ps1 --- CreateSymLinkFolder.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CreateSymLinkFolder.ps1 b/CreateSymLinkFolder.ps1 index 013fd02..58e278d 100644 --- a/CreateSymLinkFolder.ps1 +++ b/CreateSymLinkFolder.ps1 @@ -26,13 +26,13 @@ function Escape-WildcardCharacters { # Save the initial working directory $initialWorkingDirectory = [System.IO.Directory]::GetCurrentDirectory() -$escapedInitialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory +$initialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory if (-not (Test-Admin)) { Write-Output "Please run this script as an administrator. Let's try rerunning automatically..." # Re-run the script with elevated privileges, passing the initial working directory as an argument - Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$escapedInitialWorkingDirectory`"" -Verb RunAs + Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$initialWorkingDirectory`"" -Verb RunAs Exit # Exit the non-elevated instance } From 899b96a777d7dc063acd27fe0a93b0085eb251c8 Mon Sep 17 00:00:00 2001 From: Arda Eren <70160402+DeCEll-1@users.noreply.github.com> Date: Fri, 7 Mar 2025 23:31:47 +0300 Subject: [PATCH 10/11] Update CreateSymLinkFile.ps1 --- CreateSymLinkFile.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/CreateSymLinkFile.ps1 b/CreateSymLinkFile.ps1 index 24cb060..dfb54bd 100644 --- a/CreateSymLinkFile.ps1 +++ b/CreateSymLinkFile.ps1 @@ -54,7 +54,6 @@ function Show-FilePickerDialog { } } - # Main script $selectedPath = Show-FilePickerDialog From 30c692f6ca55068eefba5d07f82cdcff9bb8fd44 Mon Sep 17 00:00:00 2001 From: Arda Eren Date: Sat, 17 May 2025 11:50:41 +0300 Subject: [PATCH 11/11] d --- CreateSymLink.reg | Bin 2378 -> 2138 bytes CreateSymLinkFile.ps1 | 8 ++-- CreateSymLinkFolder.ps1 | 104 +++++++++++++++++++++++++++++++++------- 3 files changed, 92 insertions(+), 20 deletions(-) diff --git a/CreateSymLink.reg b/CreateSymLink.reg index 5ec920383de57917ae484e8c77504a52359a1ca8..41caba57c8a757da089028988435f0fc281438a9 100644 GIT binary patch delta 89 zcmX>lbW32vB4%M11}labC@f+~Wk_czp4`Z$yjg?g8zVN!&5dk|OxPrVx>P4?aVh`+ D+v^m^ delta 329 zcmca5a7t*yBIe{^1}labDD-CtVF+MwWN=|nV2A?J3Jk$OkuV@Dh`|%C%9$Y)Ebhf`bu@O H6^8-<6-_yU diff --git a/CreateSymLinkFile.ps1 b/CreateSymLinkFile.ps1 index dfb54bd..dfc2b30 100644 --- a/CreateSymLinkFile.ps1 +++ b/CreateSymLinkFile.ps1 @@ -26,8 +26,8 @@ function Escape-WildcardCharacters { } # Save the initial working directory -$initialWorkingDirectory = [System.IO.Directory]::GetCurrentDirectory() -$initialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory +# $initialWorkingDirectory = [System.IO.Directory]::GetCurrentDirectory() +$initialWorkingDirectory = (Get-Location).Path if (-not (Test-Admin)) { Write-Output "Please run this script as an administrator. Let's try rerunning automatically..." @@ -36,6 +36,7 @@ if (-not (Test-Admin)) { Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$initialWorkingDirectory`"" -Verb RunAs Exit # Exit the non-elevated instance } +$initialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory # Retrieve the working directory from arguments $originalWorkingDirectory = $args[0] @@ -66,6 +67,7 @@ if ($selectedPath) { $escapedLinkPath = Escape-WildcardCharacters -path $linkPath $selectedPath = Escape-WildcardCharacters -path $selectedPath + Write-Output "Initial Working Directory: $initialWorkingDirectory" Write-Output "Original Working Directory: $originalWorkingDirectory" Write-Output "Current Directory: $currentDir" Write-Output "Selected Path: $selectedPath" @@ -101,4 +103,4 @@ if ($selectedPath) { # Keep the window open #Read-Host -Prompt "Press Enter to exit" Write-Output "`n`nThis window will autoclose soon" -Start-Sleep -Seconds 1 +Start-Sleep -Seconds 1 \ No newline at end of file diff --git a/CreateSymLinkFolder.ps1 b/CreateSymLinkFolder.ps1 index 58e278d..f1b1078 100644 --- a/CreateSymLinkFolder.ps1 +++ b/CreateSymLinkFolder.ps1 @@ -1,3 +1,53 @@ +$source = @' +using System; +using System.Reflection; +using System.Windows.Forms; +public class MyFolderSelector +{ + public string DefaultPath, Title, Message; + public MyFolderSelector(string defaultPath = "MyComputer", string title = "Select a folder", string message = "") + { + DefaultPath = defaultPath; + Title = title; + Message = message; + } + public string GetPath() + { + // filter for which methods we want to get + BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + // get the assembly of the file dialog so we can access its internal types + var fileDialogAssembly = typeof(FileDialog).Assembly; + // get the iFile so we can use its functions to put our settings and show our dialog + var iFileDialog = fileDialogAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog"); + var openFileDialog = new OpenFileDialog + { // our dialog options + AddExtension = false, + CheckFileExists = false, + DereferenceLinks = true, + Filter = "Folders|\n", + InitialDirectory = DefaultPath, + Multiselect = false, + Title = Title + }; + // create vista dialog + var ourDialog = (typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags)).Invoke(openFileDialog, new object[] { }); + // attatch our options + typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags).Invoke(openFileDialog, new[] { ourDialog }); + iFileDialog.GetMethod("SetOptions", c_flags).Invoke(ourDialog, new object[] { (uint)(0x20000840 | 0x00000020 /*defaultOptions | folderPickerOption*/) }); + // generate update event constructor so the file name updates after selection + var vistaDialogEventsConstructorInfo = fileDialogAssembly + .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents") + .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null); + var adviseParametersWithOutputConnectionToken = new[] { vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U }; + // add the event to the dialog + iFileDialog.GetMethod("Advise").Invoke(ourDialog, adviseParametersWithOutputConnectionToken); + // show the dialog to user and get response + int retVal = (int)iFileDialog.GetMethod("Show").Invoke(ourDialog, new object[] { IntPtr.Zero }); + // returns the file name if the user made a selection + return retVal == 0 ? openFileDialog.FileName : ""; + } +} +'@ # Function to check for elevated privileges function Test-Admin { $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) @@ -8,7 +58,21 @@ function Confirm-Overwrite { param ($linkPath) Add-Type -AssemblyName PresentationFramework - $message = "A folder with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" + # Presume that it's not a folder + $isDirectory = $false + + if (-not [string]::IsNullOrWhiteSpace($linkPath)) { + # If the input box isn't empty, check if it's a valid path + if (Test-Path $linkPath -PathType Leaf) { + # if the path points to a file, it is not a folder + $isDirectory = $false + } elseif (Test-Path $linkPath -PathType Container) { + # if the path points to a folder, it is a folder + $isDirectory = $true + } + } + + $message = "A $(if ($isDirectory) { 'folder' } else { 'file' }) with the name '$(Split-Path $linkPath -Leaf)' already exists. Overwrite?" $result = [System.Windows.MessageBox]::Show( $message, "Confirm Overwrite", @@ -26,13 +90,13 @@ function Escape-WildcardCharacters { # Save the initial working directory $initialWorkingDirectory = [System.IO.Directory]::GetCurrentDirectory() -$initialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory +$escapedInitialWorkingDirectory = Escape-WildcardCharacters -path $initialWorkingDirectory if (-not (Test-Admin)) { Write-Output "Please run this script as an administrator. Let's try rerunning automatically..." # Re-run the script with elevated privileges, passing the initial working directory as an argument - Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$initialWorkingDirectory`"" -Verb RunAs + Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" `"$escapedInitialWorkingDirectory`"" -Verb RunAs Exit # Exit the non-elevated instance } @@ -42,14 +106,19 @@ Set-Location -Path $originalWorkingDirectory # Function to show folder picker dialog function Show-FolderPickerDialog { - Add-Type -AssemblyName System.Windows.Forms - $dialog = New-Object System.Windows.Forms.FolderBrowserDialog - $dialog.Description = "Select a folder to create a symbolic link to" - $dialog.SelectedPath = [System.IO.Directory]::GetCurrentDirectory() - if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { - return $dialog.SelectedPath - } else { + + Add-Type -Language CSharp -TypeDefinition $source -ReferencedAssemblies ("System.Windows.Forms") + + $path = $originalWorkingDirectory + $title = "Select Folder" + $description = "Select a folder to create a symbolic link" + + $out = [MyFolderSelector]::new($path, $title, $description).GetPath() + + if ($out -eq "") { return $null + } else { + return $out } } @@ -63,23 +132,24 @@ if ($selectedPath) { # Escape wildcard characters in the paths $escapedLinkPath = Escape-WildcardCharacters -path $linkPath - $selectedPath = Escape-WildcardCharacters -path $selectedPath + $escapedSelectedPath = Escape-WildcardCharacters -path $selectedPath Write-Output "Original Working Directory: $originalWorkingDirectory" Write-Output "Current Directory: $currentDir" Write-Output "Selected Path: $selectedPath" + Write-Output "Escaped Selected Path: $escapedSelectedPath" Write-Output "Link Path: $linkPath" Write-Output "Escaped Link Path: $escapedLinkPath" - if (Test-Path -Path $selectedPath -PathType Container) { + if (Test-Path -Path $escapedSelectedPath -PathType Container) { if (Test-Path -Path $escapedLinkPath) { $choice = Confirm-Overwrite -linkPath $escapedLinkPath if ($choice -eq 'Yes') { # Remove the existing symlink Remove-Item -Path $escapedLinkPath -Force # Create a new directory symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop - Write-Output "Overwritten directory symbolic link: $escapedLinkPath -> $selectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop + Write-Output "Overwritten directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" } elseif ($choice -eq 'No') { Write-Output "Symbolic link (or file/folder with that name) already existed! Operation canceled by user by not overwriting." } else { @@ -87,8 +157,8 @@ if ($selectedPath) { } } else { # Create a new directory symbolic link - New-Item -ItemType SymbolicLink -Path $linkPath -Target $selectedPath -ErrorAction Stop - Write-Output "Created directory symbolic link: $escapedLinkPath -> $selectedPath" + New-Item -ItemType SymbolicLink -Path $linkPath -Target $escapedSelectedPath -ErrorAction Stop + Write-Output "Created directory symbolic link: $escapedLinkPath -> $escapedSelectedPath" } } else { Write-Output "The selected path is not a folder." @@ -100,4 +170,4 @@ if ($selectedPath) { # Keep the window open #Read-Host -Prompt "Press Enter to exit" Write-Output "`n`nThis window will autoclose soon" -Start-Sleep -Seconds 1 +Start-Sleep -Seconds 1 \ No newline at end of file