Eigenverft.Manifested.Drydock.DriveMapping.ps1
|
function Set-CleanNetworkDrive { <# .SYNOPSIS Remaps a network drive letter after cleaning cached Explorer mount point data. .DESCRIPTION Reads the current mapping of a given drive letter, removes WScript.Network and SMB mappings, deletes HKCU:\Network\<DriveLetter> and related MountPoints2 entries, then maps the drive again via WScript.Network so File Explorer sees the change. Finally, it sends SHChangeNotify to nudge Explorer to refresh drive info. .PARAMETER DriveLetter Single drive letter without colon (for example Z). .PARAMETER RemotePath UNC path to map to (for example \\server\share). .PARAMETER NonPersistent If set, the mapping will not be stored persistently in the user profile. .EXAMPLE Set-CleanNetworkDrive -DriveLetter Z -RemotePath '\\hi-ma-uts.de.bosch.com\srv$' .EXAMPLE Set-CleanNetworkDrive -DriveLetter Z -RemotePath '\\hi230077\fkt' #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [ValidatePattern('^[A-Za-z]$')] [string]$DriveLetter, [Parameter(Mandatory)] [ValidatePattern('^\\\\')] [string]$RemotePath, [switch]$NonPersistent ) $drive = ($DriveLetter.TrimEnd(':') + ':').ToUpper() # COM object for Explorer-visible mapping $nw = New-Object -ComObject WScript.Network # Read current SMB mapping before we touch anything $currentMapping = Get-SmbMapping -LocalPath $drive -ErrorAction SilentlyContinue if ($PSCmdlet.ShouldProcess($drive, "Reset mapping and clean Explorer cache")) { # 1) Remove COM / profile mapping (if any) try { $nw.RemoveNetworkDrive($drive, $true, $true) } catch { # ignore } # 2) Remove SMB mapping if ($currentMapping) { $currentMapping | Remove-SmbMapping -Force -ErrorAction SilentlyContinue } # 3) Remove HKCU:\Network\<DriveLetter> $networkKey = "HKCU:\Network\{0}" -f $DriveLetter.TrimEnd(':') Remove-Item $networkKey -Recurse -Force -ErrorAction SilentlyContinue # 4) Clean MountPoints2 entries related to this drive / UNC $mpBase = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2' # MountPoints2 encodes UNC as "##server#share..." $oldSanitized = $null if ($currentMapping -and $currentMapping.RemotePath) { $oldSanitized = '##' + $currentMapping.RemotePath.TrimStart('\').Replace('\', '#') } $newSanitized = '##' + $RemotePath.TrimStart('\').Replace('\', '#') if (Test-Path $mpBase) { Get-ChildItem $mpBase -ErrorAction SilentlyContinue | ForEach-Object { $childName = $_.PSChildName $matchDriveKey = $childName -eq $drive -or $childName -eq $DriveLetter.TrimEnd(':') $matchOld = $false if ($oldSanitized) { $matchOld = $childName -like "$oldSanitized*" } $matchNew = $childName -like "$newSanitized*" if ($matchDriveKey -or $matchOld -or $matchNew) { Remove-Item $_.PsPath -Recurse -Force -ErrorAction SilentlyContinue } } } # 5) Map new drive via COM (Explorer-compatible) $persistentFlag = -not $NonPersistent $nw.MapNetworkDrive($drive, $RemotePath, $persistentFlag) # 6) Optional: force the visible label to match the UNC try { $shell = New-Object -ComObject Shell.Application $folder = $shell.NameSpace("$drive\") if ($folder -and $folder.Self) { $folder.Self.Name = $RemotePath } } catch { # If this fails, mapping is still correct; only label refresh might lag. } # 7) Shell notify: nudge Explorer to refresh drive info try { if (-not ('ExplorerRefresh' -as [type])) { Add-Type @' using System; using System.Runtime.InteropServices; public static class ExplorerRefresh { const uint SHCNE_ASSOCCHANGED = 0x08000000; const uint SHCNE_UPDATEDIR = 0x00001000; const uint SHCNF_IDLIST = 0x0000; const uint SHCNF_PATHW = 0x0005; [DllImport("shell32.dll", CharSet = CharSet.Unicode)] public static extern void SHChangeNotify(uint wEventId, uint uFlags, string dwItem1, string dwItem2); public static void RefreshDrive(string path) { // Global-ish refresh SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, null, null); // Targeted refresh for the drive path ("Z:\") if (!string.IsNullOrEmpty(path)) { SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATHW, path, null); } } } '@ } [ExplorerRefresh]::RefreshDrive("$drive\") } catch { # Ignore notify failures; mapping itself is already correct. } } } function Remove-NetworkDrive { <# .SYNOPSIS Removes a network drive and cleans related Explorer cache. .DESCRIPTION Removes the mapping for a given drive letter using WScript.Network and SMB, deletes HKCU:\Network\<DriveLetter> and related MountPoints2 entries, and notifies Explorer via SHChangeNotify so the UI updates accordingly. .PARAMETER DriveLetter Single drive letter without colon (for example Z). .EXAMPLE Remove-NetworkDrive -DriveLetter Z #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [ValidatePattern('^[A-Za-z]$')] [string]$DriveLetter ) $drive = ($DriveLetter.TrimEnd(':') + ':').ToUpper() # COM object for Explorer-visible mapping $nw = New-Object -ComObject WScript.Network # Read current SMB mapping before we touch anything $currentMapping = Get-SmbMapping -LocalPath $drive -ErrorAction SilentlyContinue if ($PSCmdlet.ShouldProcess($drive, "Remove network drive and clean Explorer cache")) { # 1) Remove COM / profile mapping (if any) try { $nw.RemoveNetworkDrive($drive, $true, $true) } catch { # ignore } # 2) Remove SMB mapping if ($currentMapping) { $currentMapping | Remove-SmbMapping -Force -ErrorAction SilentlyContinue } # 3) Remove HKCU:\Network\<DriveLetter> $networkKey = "HKCU:\Network\{0}" -f $DriveLetter.TrimEnd(':') Remove-Item $networkKey -Recurse -Force -ErrorAction SilentlyContinue # 4) Clean MountPoints2 entries related to this drive / old UNC $mpBase = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2' # MountPoints2 encodes UNC as "##server#share..." $oldSanitized = $null if ($currentMapping -and $currentMapping.RemotePath) { $oldSanitized = '##' + $currentMapping.RemotePath.TrimStart('\').Replace('\', '#') } if (Test-Path $mpBase) { Get-ChildItem $mpBase -ErrorAction SilentlyContinue | ForEach-Object { $childName = $_.PSChildName $matchDriveKey = $childName -eq $drive -or $childName -eq $DriveLetter.TrimEnd(':') $matchOld = $false if ($oldSanitized) { $matchOld = $childName -like "$oldSanitized*" } if ($matchDriveKey -or $matchOld) { Remove-Item $_.PsPath -Recurse -Force -ErrorAction SilentlyContinue } } } # 5) Shell notify: nudge Explorer to refresh drive info try { if (-not ('ExplorerRefresh' -as [type])) { Add-Type @" using System; using System.Runtime.InteropServices; public static class ExplorerRefresh { const uint SHCNE_ASSOCCHANGED = 0x08000000; const uint SHCNE_UPDATEDIR = 0x00001000; const uint SHCNF_IDLIST = 0x0000; const uint SHCNF_PATHW = 0x0005; [DllImport("shell32.dll", CharSet = CharSet.Unicode)] public static extern void SHChangeNotify(uint wEventId, uint uFlags, string dwItem1, string dwItem2); public static void RefreshDrive(string path) { // Global-ish refresh SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, null, null); // Targeted refresh for the drive path ("Z:\") if (!string.IsNullOrEmpty(path)) { SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATHW, path, null); } } } "@ } [ExplorerRefresh]::RefreshDrive("$drive\") } catch { # Ignore notify failures; the drive is already removed. } } } |