Skip to content

Instantly share code, notes, and snippets.

@jborean93
Last active August 8, 2019 23:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jborean93/832346fc51eba9e4acb406d9581d47f4 to your computer and use it in GitHub Desktop.
Save jborean93/832346fc51eba9e4acb406d9581d47f4 to your computer and use it in GitHub Desktop.
Deletes files and folders that exceed max path
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