Last active
August 8, 2019 23:23
-
-
Save jborean93/832346fc51eba9e4acb406d9581d47f4 to your computer and use it in GitHub Desktop.
Deletes files and folders that exceed max path
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add-Type -TypeDefinition @' | |
using Microsoft.Win32.SafeHandles; | |
using System; | |
using System.IO; | |
using System.Runtime.ConstrainedExecution; | |
using System.Runtime.InteropServices; | |
namespace FileIO | |
{ | |
public class NativeHelpers | |
{ | |
public enum GET_FILEEX_INFO_LEVELS | |
{ | |
GetFileExInfoStandard, | |
GetFileExMaxInfoLevel | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct FILETIME | |
{ | |
internal UInt32 dwLowDateTime; | |
internal UInt32 dwHighDateTime; | |
public static implicit operator Int64(FILETIME v) { return ((Int64)v.dwHighDateTime << 32) + v.dwLowDateTime; } | |
public static explicit operator DateTimeOffset(FILETIME v) { return DateTimeOffset.FromFileTime(v); } | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct WIN32_FILE_ATTRIBUTE_DATA | |
{ | |
public FileAttributes dwFileAttributes; | |
public FILETIME ftCreationTime; | |
public FILETIME ftLastAccessTime; | |
public FILETIME ftLastWriteTime; | |
public UInt32 nFileSizeHigh; | |
public UInt32 nFileSizeLow; | |
} | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
public struct WIN32_FIND_DATA | |
{ | |
public FileAttributes dwFileAttributes; | |
public FILETIME ftCreationTime; | |
public FILETIME ftLastAccessTime; | |
public FILETIME ftLastWriteTime; | |
public UInt32 nFileSizeHigh; | |
public UInt32 nFileSizeLow; | |
public UInt32 dwReserved0; | |
public UInt32 dwReserved1; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; | |
} | |
} | |
public class NativeMethods | |
{ | |
[DllImport("Kernel32.dll", SetLastError = true)] | |
public static extern bool DeleteFileW( | |
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName); | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
public static extern bool GetFileAttributesExW( | |
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName, | |
NativeHelpers.GET_FILEEX_INFO_LEVELS fInfoLevelId, | |
out NativeHelpers.WIN32_FILE_ATTRIBUTE_DATA lpFileInformation); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool FindClose( | |
IntPtr hFindFile); | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
public static extern SafeFindHandle FindFirstFileW( | |
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName, | |
out NativeHelpers.WIN32_FIND_DATA lpFindFileData); | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
public static extern bool FindNextFileW( | |
SafeFindHandle hFindFile, | |
out NativeHelpers.WIN32_FIND_DATA lpFindFileData); | |
[DllImport("Kernel32.dll", SetLastError = true)] | |
public static extern bool RemoveDirectoryW( | |
[MarshalAs(UnmanagedType.LPWStr)] string lpPathName); | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
public static extern bool SetFileAttributesW( | |
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName, | |
FileAttributes dwFileAttributes); | |
} | |
public class SafeFindHandle : SafeHandleMinusOneIsInvalid | |
{ | |
public SafeFindHandle() : base(true) { } | |
public SafeFindHandle(IntPtr handle) : base(true) { this.handle = handle; } | |
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] | |
protected override bool ReleaseHandle() | |
{ | |
return NativeMethods.FindClose(handle); | |
} | |
} | |
} | |
'@ | |
Function Get-Win32ErrorMessage { | |
Param ( | |
[Parameter(Mandatory=$true)] | |
[Int32] | |
$ErrorCode | |
) | |
$exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $ErrorCode | |
return ("{0} (Win32 Error Code {1} - 0x{1:X8})" -f $exp.Message, $ErrorCode) | |
} | |
Function Get-FileAttributes { | |
Param ( | |
[Parameter(Mandatory=$true)] | |
[String] | |
$Path | |
) | |
$data = New-Object -TypeName FileIO.NativeHelpers+WIN32_FILE_ATTRIBUTE_DATA | |
$res = [FileIO.NativeMethods]::GetFileAttributesExW( | |
$Path, | |
[FileIO.NativeHelpers+GET_FILEEX_INFO_LEVELS]::GetFileExInfoStandard, | |
[Ref]$data | |
) | |
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() | |
if (-not $res) { | |
if ($err -eq 32) { # ERROR_SHARING_VIOLATION | |
# Special system files like pagefile.sys return this error. Use FindFirstFileW instead. | |
$data = New-Object -TypeName FileIO.NativeHelpers+WIN32_FIND_DATA | |
$find_handle = [FileIO.NativeMethods]::FindFirstFileW($Path, [Ref]$data) | |
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() | |
if ($find_handle.IsInvalid) { | |
throw "Failed to get file attributes for '$Path': $(Get-Win32ErrorMessage -ErrorCode $err)" | |
} | |
try { | |
$attrs = $data.dwFileAttributes | |
} finally { | |
$find_handle.Dispose() | |
} | |
} else { | |
throw "Failed to get file attributes for '$Path': $(Get-Win32ErrorMessage -ErrorCode $err)" | |
} | |
} else { | |
$attrs = $data.dwFileAttributes | |
} | |
return $attrs | |
} | |
Function Remove-ReadOnlyAndHiddenAttribute { | |
Param ( | |
[Parameter(Mandatory=$true)] | |
[String] | |
$Path | |
) | |
$attrs = Get-FileAttributes -Path $Path | |
$changed = $false | |
'ReadOnly', 'Hidden' | ForEach-Object -Process { | |
if ($attrs.HasFlag([System.IO.FileAttributes]$_)) { | |
$attrs = $attrs -bxor [System.IO.FileAttributes]$_ | |
$changed = $true | |
} | |
} | |
if ($changed) { | |
$res = [FileIO.NativeMethods]::SetFileAttributesW($Path, $attrs) | |
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() | |
if (-not $res) { | |
throw "Failed to remove the ReadOnly or Hidden attribute for deletion on '$Path': $(Get-Win32ErrorMessage -ErrorCode $err)" | |
} | |
} | |
} | |
Function Remove-File { | |
Param ( | |
[Parameter(Mandatory=$true)] | |
[String] | |
$Path | |
) | |
Remove-ReadOnlyAndHiddenAttribute -Path $Path | |
$res = [FileIO.NativeMEthods]::DeleteFileW($Path) | |
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() | |
if (-not $res) { | |
throw "Failed to delete file '$Path': $(Get-Win32ErrorMessage -ErrorCode $err)" | |
} | |
} | |
Function Remove-Directory { | |
Param ( | |
[Parameter(Mandatory=$true)] | |
[String] | |
$Path, | |
[Switch] | |
$Recurse | |
) | |
if ($Recurse) { | |
$find_path = [System.IO.Path]::Combine($Path, "*") | |
$find_data = New-Object -TypeName FileIO.NativeHelpers+WIN32_FIND_DATA | |
$find_handle = [FileIO.NativeMethods]::FindFirstFileW( | |
$find_path, [ref]$find_data | |
) | |
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() | |
if ($find_handle.IsInvalid) { | |
throw "Failed to enumerate directory with search pattern '$find_path': $(Get-Win32ErrorMessage -ErrorCode $err)" | |
} | |
try { | |
do { | |
if ($find_data.cFileName -in @('.', '..')) { | |
continue | |
} | |
$file_path = [System.IO.Path]::Combine($Path, $find_data.cFileName) | |
if ($find_data.dwFileAttributes.HasFlag([System.IO.FileAttributes]::Directory)) { | |
$is_link = $find_data.dwFileAttributes.HasFlag([System.IO.FileAttributes]::ReparsePoint) | |
Remove-Directory -Path $file_path -Recurse:(-not $is_link) | |
} else { | |
Remove-File -Path $file_path | |
} | |
} while ([FileIO.NativeMethods]::FindNextFileW($find_handle, [ref]$find_data)) | |
} finally { | |
$find_handle.Dispose() | |
} | |
} | |
Remove-ReadOnlyAndHiddenAttribute -Path $Path | |
$res = [FileIO.NativeMethods]::RemoveDirectoryW($Path) | |
$err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() | |
if (-not $res) { | |
throw "Failed to delete directory '$Path': $(Get-Win32ErrorMessage -ErrorCode $err)" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment