Skip to content

Instantly share code, notes, and snippets.

@treytomes
Created March 3, 2016 16:29
Show Gist options
  • Save treytomes/ecde10ca3132dfed167c to your computer and use it in GitHub Desktop.
Save treytomes/ecde10ca3132dfed167c to your computer and use it in GitHub Desktop.
List directories and check if a directory is empty using Kernel32 calls.
Imports System.IO
Imports System.Runtime.InteropServices
''' <remarks>
''' I don't think this is actually faster than the .NET Directory functions.
''' </remarks>
Public Class FileSystemHelper
Private Shared ReadOnly conInvalidHandleValue As IntPtr = New IntPtr(-1)
<Flags>
Private Enum FileAttributes
FILE_ATTRIBUTE_READONLY = 1
FILE_ATTRIBUTE_HIDDEN = 2
FILE_ATTRIBUTE_SYSTEM = 4
FILE_ATTRIBUTE_DIRECTORY = 16
FILE_ATTRIBUTE_ARCHIVE = 32
FILE_ATTRIBUTE_DEVICE = 64
FILE_ATTRIBUTE_NORMAL = 128
FILE_ATTRIBUTE_TEMPORARY = 256
FILE_ATTRIBUTE_SPARSE_FILE = 512
FILE_ATTRIBUTE_REPARSE_POINT = 1024
FILE_ATTRIBUTE_COMPRESSED = 2048
FILE_ATTRIBUTE_OFFLINE = 4096
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192
FILE_ATTRIBUTE_ENCRYPTED = 16384
FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768
FILE_ATTRIBUTE_VIRTUAL = 65536
FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072
End Enum
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Private Structure WIN32_FIND_DATA
Public dwFileAttributes As UInteger
Public ftCreationTime As ComTypes.FILETIME
Public ftLastAccessTime As ComTypes.FILETIME
Public ftLastWriteTime As ComTypes.FILETIME
Public nFileSizeHighs As UInteger
Public nFileSizeLows As UInteger
Public dwReserved0s As UInteger
Public dwReserved1s As UInteger
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)>
Public cFileName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=14)>
Public cAlternateFileName As String
End Structure
<DllImport("kernel32.dll", CharSet:=CharSet.Auto)>
Private Shared Function FindFirstFile(lpFileName As String, <Out> ByRef lpFindFileData As WIN32_FIND_DATA) As IntPtr
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto)>
Private Shared Function FindNextFile(hFindFile As IntPtr, <Out> ByRef lpFindFileData As WIN32_FIND_DATA) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto)>
Private Shared Function FindClose(hFindFile As IntPtr) As Boolean
End Function
Public Shared Iterator Function GetDirectories(pPath As String) As IEnumerable(Of String)
If String.IsNullOrEmpty(pPath) Then
Throw New ArgumentNullException(pPath)
End If
If Directory.Exists(pPath) Then
If Not pPath.EndsWith(Path.DirectorySeparatorChar.ToString()) Then
pPath &= Path.DirectorySeparatorChar
End If
Dim oFindData As WIN32_FIND_DATA = Nothing
Dim oFindHandle As IntPtr = FindFirstFile(pPath & "*", oFindData)
If oFindHandle <> conInvalidHandleValue Then
Try
Do
If (oFindData.cFileName <> "." AndAlso oFindData.cFileName <> "..") Then
If CBool(oFindData.dwFileAttributes And FileAttributes.FILE_ATTRIBUTE_DIRECTORY) Then
Yield pPath & oFindData.cFileName
End If
End If
Loop Until Not FindNextFile(oFindHandle, oFindData)
Finally
FindClose(oFindHandle)
End Try
End If
End If
End Function
Public Shared Function IsDirectoryEmpty(pPath As String) As Boolean
If String.IsNullOrEmpty(pPath) Then
Throw New ArgumentNullException(pPath)
End If
If Directory.Exists(pPath) Then
If Not pPath.EndsWith(Path.DirectorySeparatorChar.ToString()) Then
pPath &= Path.DirectorySeparatorChar
End If
Dim oFindData As WIN32_FIND_DATA = Nothing
Dim oFindHandle As IntPtr = FindFirstFile(pPath & "*", oFindData)
If oFindHandle <> conInvalidHandleValue Then
Try
Dim blnIsEmpty As Boolean = True
Do
If (oFindData.cFileName <> ".") AndAlso (oFindData.cFileName <> "..") Then
Exit Do
End If
Loop Until Not FindNextFile(oFindHandle, oFindData)
Return blnIsEmpty
Finally
FindClose(oFindHandle)
End Try
End If
Throw New Exception("Failed to get directory first file", Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()))
End If
Throw New DirectoryNotFoundException()
End Function
End Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment