Notice: This post does not contain any pictures. They were all lost during the import from my old Gonjer.com site. I do apologize for that but I hope that the post will still help out.
I’ve received a request from one of my customers were they wanted a button or an application that could bypass the 255 max char file path length. Some inexperienced users often like the file tree structure so much that they name their folders with a complete essay which will override the max char file path length in a few seconds.
One way to overcome the file path length is to use mapped network drives, i.e. \\contoso.com\dfs\human_resources will be L:\. This method replaces any UNC network path with three letters instead of 33 in the example above.
Now you may start to wonder if this blog post is necessary. Use Group Policy Preference (Drive Maps) to map the UNC network path and manage every thing from a central location. Well of course this works when you have a few network shares and the users build their file tree structure on the width instead on the depth.
With our larger customers we try to use “Network Locations” using “GPP Shortcuts” instead of Drive Maps, the reasons are:
- Drive Maps only have a limited number of letters to use. 22 letters available (A,B, C and D excluded).
- Some Servicedesk conversations can be misinterpreted if you use the same drive letter for two or more locations.
- Application/system owners may use a drive letter for file based application search paths.
Trust med this is an issue with slow WAN-links and when you want change or do some restructure in the file servers.
- Network Locations uses shortcuts to point to a UNC path. When accessed the shortcut translates to the full UNC path.
And of course with Network Locations there are some side effect and that are the max file path length issue when you have some users you like to write. So the reason that i started to write this script is for the end user to use when he or she encounters large file path lengths.
Here is the script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
#requires -Version 3.0 <# .SYNOPSIS Creates a Network Shortcut under the current user Network Places (locations). .DESCRIPTION Creates a Network Shortcut under my the current user Network Places (locations). The TargetPath can be either a UNC path or a local path (i.e 'C:\Folder\Application') when parameter 'Type' is 'Location'. When the parameter 'Type' is 'Drive' the TargetPath must be a valid UNC path. This is script is similar to what the "Add Network Location" and "Map network drive" wizard in Windows (This Computer) does. .EXAMPLE New-PHNetworkShortcut.ps1 -Name 'ApplicationShare' -Target 'C:\Folder\Application' -Type 'Location' Successfully mapped: 'C:\Folder\Application' to network location. .EXAMPLE New-PHNetworkShortcut.ps1 -Name 'ApplicationShare' -Target '\\server01.contoso.com\ApplicationShare' -Type 'Drive' Successfully mapped: '\\server01.contoso.com\ApplicationShare' to drive letter: D: .EXAMPLE New-PHNetworkShortcut.ps1 -Name 'ApplicationShare' -Target '\\server01.contoso.com\ApplicationShare' -Type 'Location' Successfully mapped: '\\server01.contoso.com\ApplicationShare' to network location. .INPUTS String .NOTES Created on: 2016-07-12 12:31 Created by: Philip Haglund Organization: Omnicit AB Filename: New-PHNetworkShortcut.ps1 Version: 1.0 Requirements: Powershell 2.0 Changelog: 2016-07-12 12:31 - Creation of script. .LINK https://omnicit.se #> [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Script' )] param ( # Name of the Network Shortcut. # Example: 'ApplicationShare' [Parameter( ParameterSetName = 'Application' )] [Parameter( ParameterSetName = 'Script', Mandatory, HelpMessage = "Name of the Network Shortcut.`nExample: 'ApplicationShare'" )] [string]$Name, # Target Path for the Network Location Shortcut. # Example 1: '\\server01.contoso.com\ApplicationShare' # Example 2: 'C:\Folder\Application' (Not possible when parameter Type = Drive) [Parameter( Mandatory, HelpMessage = "Target Path for the Network Location Shortcut.`nExample 1: '\\server01.contoso.com\ApplicationShare'`nExample 2: 'C:\Folder\Application (Not possible when parameter Type = Drive)" )] [string]$Target, # Type of network shortcut to map. # Possible values: 'Drive', 'Location' [Parameter( Mandatory, HelpMessage = "Type of network shortcut to map.`nPossible values: 'Drive', 'Location'" )] [ValidateSet('Drive', 'Location')] [string]$Type, # Run as application to view output in Windows.Forms dialog boxes. # Parameter used when script is compiled to .exe to display Forms dialog boxes. [Parameter( ParameterSetName = 'Application' )] [switch]$Application ) begin { function Get-FreeDriveLetter { function Test-FreeDriveLetter { process { if ( -not (Test-Path -Path $_) ) { $_ } } } Get-ChildItem -Name -Path function:[d-z]: | Test-FreeDriveLetter } function Read-MessageBoxDialog { # Thanks to Daniel Schroeder for this function - http://blog.danskingdom.com/author/admin/ [CmdletBinding()] param ( [string]$Message = 'Example Message', [string]$WindowTitle = 'Example Title', [Windows.Forms.MessageBoxButtons]$Buttons = [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]$Icon = [Windows.Forms.MessageBoxIcon]::None ) return [Windows.Forms.MessageBox]::Show($Message, $WindowTitle, $Buttons, $Icon) } if ($PSBoundParameters.ContainsKey('Application')) { Add-Type -AssemblyName System.Windows.Forms $Name = $Target -replace '.*\\' } } process { if ($PSCmdlet.ShouldProcess("$($Target)", "$($PSCmdlet.MyInvocation.InvocationName) -Type $($Type)")) { if ($Type -eq 'Location') { $NetworkShortcuts = [Environment]::GetFolderPath('NetworkShortcuts') $FullName = "$($NetworkShortcuts)\$($Name)" $Ini = "$($FullName)\desktop.ini" $Lnk = "$($FullName)\target.lnk" $IniText = "[.ShellClassInfo]`r`nCLSID2={0AFACED1-E828-11D1-9187-B532F1E9575D}`r`nFlags=2" try { if (Test-Path -Path $FullName -PathType Container) { Remove-Item -Path $FullName -Recurse -Force -ErrorAction Stop } $null = New-Item -Path $FullName -ItemType Directory -ErrorAction Stop Set-ItemProperty -Path $FullName -Name Attributes -Value 1 -ErrorAction Stop Out-File -FilePath $ini -InputObject $IniText -Encoding ascii -ErrorAction Stop Set-ItemProperty -Path $ini -Name Attributes -Value 6 -ErrorAction Stop $Mapped = $true } catch { if ($_.CategoryInfo.Activity -eq 'Remove-Item') { $Mapped = "Unable to remove existing Network Shortcut $($FullName). $($_.Exception.Message)" } elseif ($_.CategoryInfo.Activity -eq 'New-Item') { $Mapped = "Unable to create directory $($FullName). $($_.Exception.Message)" } elseif ($_.CategoryInfo.Activity -eq 'Set-ItemProperty') { $Mapped = "Unable to set attribute $($_.CategoryInfo.TargetName -replace 'int Attributes=') on target directory $($FullName). $($_.Exception.Message)" } elseif ($_.CategoryInfo.Activity -eq 'Out-File') { $Mapped = "Unable to create file $ini with text $($IniText). $($_.Exception.Message)" } elseif ($_.CategoryInfo.Activity -eq 'Test-Path') { $Mapped = "Unable to validate Path. $($_.Exception.Message)" } else { $Mapped = "Unknown error - $($_.Exception.Message)" } } if ($Mapped -eq $true) { try { $shortcut = (New-Object -ComObject WScript.Shell).CreateShortcut($lnk) $shortcut.IconLocation = "$env:windir\System32\imageres.dll,137" $shortcut.TargetPath = $Target $shortcut.Save() } catch { $Mapped = "Unable to create Shortcut file .lnk with target $($Target). $($_.Exception.Message)" } } $TargetString = 'network location.' } elseif ($Type -eq 'Drive') { if (([bool]([Uri]$Target).IsUnc -eq $true) -and (Test-Path -Path $Target) -and $Target -notmatch '.*\\$') { foreach ($l in Get-FreeDriveLetter) { try { (New-Object -ComObject WScript.Network -ErrorAction Stop).MapNetworkDrive($l, $Target) $Mapped = $true break } catch { $Mapped = $false } } } else { $Mapped = "$($Target) is not a valid UNC path or the path is not existing.`nPath may not end with a trailing '\'" } $TargetString = "driveletter:`n$($l)" } else { 'UNKNOWN ERROR' } } } end { if ($PSBoundParameters.ContainsKey('Application')) { if ($Mapped -eq $true) { $Message = Read-MessageBoxDialog -Message "Successfully mapped:`n'$($Target)'`n`nTo $($TargetString)" -WindowTitle "Add Network $($Type)" -Icon Information -Buttons OK } elseif ($Mapped -is [string]) { $Message = Read-MessageBoxDialog -Message "$($Mapped)`n`nContact Servicedesk for assistance." -WindowTitle "Add Network $($Type)" -Icon Error -Buttons OK } else { $Message = Read-MessageBoxDialog -Message "Unable to map:`n'$($Target)'`n`nTo $($TargetString)`n`nContact Servicedesk for assistance." -WindowTitle "Add Network $($Type)" -Icon Warning -Buttons OK } } else { if ($Mapped -eq $true) { Write-Output -InputObject "Successfully mapped: '$($Target)' to $($TargetString)" } elseif ($Mapped -is [string]) { Write-Output -InputObject $Mapped } else { Write-Output -InputObject "Unable to map: '$($Target)' to $($TargetString). Contact Servicedesk for assistance." } } Write-Verbose -Message "$($PSCmdlet.MyInvocation.InvocationName) finished." } |
An issue with this script is that the end users doesn’t use Powershell so I’ve had to come up with a way every user in the organization can bypass the long path file name with their favorite tool (the computer mouse). The first thing I did was to compile the script to executable file using Powershell Studio 2016. Now that I have the .exe file I need some way for the users to run the file using their mouse.
Context menu was the clear way to go.
Picture: Context Menu
The following registry settings are applied to accomplish the picture above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\shell\Gonjer.AddNetworkMenu] "MUIVerb"="Add Folder to Network Drive/Location" "Icon"="shell32.dll,-258" "SubCommands"="Gonjer.NetworkLocation;Gonjer.NetworkDrive" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Gonjer.NetworkDrive] "Icon"="imageres.dll,-33" "MUIVerb"="Network Drive with letter" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Gonjer.NetworkDrive\command] @="\"C:\\Progra~1\\Gonjer\\PHNetworkLocation\\New-PHNetworkLocation.exe\" -Application -Type Drive -Target \"%1\"" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Gonjer.NetworkLocation] "Icon"="imageres.dll,-143" "MUIVerb"="Network Location" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Gonjer.NetworkLocation\command] @="\"C:\\Progra~1\\Gonjer\\PHNetworkLocation\\New-PHNetworkLocation.exe\" -Application -Type Location -Target \"%1\"" |
One problem with the registry settings above is that C: is the SystemDrive disk.
This settings only apply to folders when you right click on them, according to the picture. You will not be able to right click in a folder to map to a network drive / location.
To insert the registry settings and the .exe application on the users computer I’ve created a .msi installer again using Powershell Studio 2016 so we can deploy everything either with GPO or ConfigMgr. I you are interested in the .exe or the .msi just write back to me or create your own using the source code above.
I hope this will help someone with network location or drive maps when using Powershell and a start with context menu while right clicking folders.