I am struggling to try and get a simple piece of code working as a very novice programmer, here's what I want:
Recursively search a directory(for example D:\Site_Data), find all files with a given extension(for example .xrl) and delete them.
The reason I want to use the Win32API's is because I'm dealing with files buried in directorys over 260 char's deep and have searched high and low and cannot find a solution. VB.NET is what I've been learning so that's what I want to stick to.
I've managed to copy some code and adjust it to delete a "single" file using the Kernel32.dll DeleteFile function, but that only does 1 file, it can't accept wildcard's, here's what I got:
Imports System
Imports System.Runtime.InteropServices
Imports System.IO
Public Class Form1
Public Property delFile As String
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=False, SetLastError:=True)> _
Public Shared Function DeleteFile(ByVal path As String) As Boolean
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
delFile = "D:\Site_Data\Test.JPG"
Try
Dim boolResult As Boolean
Dim delName As String = "\\?\" + delFile
boolResult = DeleteFile(delName)
Debug.WriteLine("Result: " & boolResult.ToString)
Catch ex As Exception
Throw ex
End Try
End Sub
End Class
I understand what I'm looking for is the "FindFirstFile" function but have no idea how to write the code to implement it in VB.NET.
If anyone can help it would be greatly appreciated.
UPDATE 2, WORKING CODE:
Imports System.IO
Imports System.Runtime.InteropServices
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim longFolderName As String = "\\?\c:\users\user1\desktop\pics\"
Dim extensionsToDelete As String = ".jpg"
Dim filenames As List(Of String) = VeryLongFilenameHandler.GetFilenames(longFolderName)
For Each filename As String In filenames
If filename.ToLower.EndsWith(extensionsToDelete) Then
VeryLongFilenameHandler.DeleteFile(longFolderName + filename)
Debug.WriteLine("Result: " & longFolderName, filename)
End If
Next
End Sub
End Class
Class VeryLongFilenameHandler
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Structure WIN32_FIND_DATA
Public dwFileAttributes As UInteger
Public ftCreationTime As System.Runtime.InteropServices.ComTypes.FILETIME
Public ftLastAccessTime As System.Runtime.InteropServices.ComTypes.FILETIME
Public ftLastWriteTime As System.Runtime.InteropServices.ComTypes.FILETIME
Public nFileSizeHigh As UInteger
Public nFileSizeLow As UInteger
Public dwReserved0 As UInteger
Public dwReserved1 As UInteger
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> Public cFileName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=14)> Public cAlternateFileName As String
End Structure
<DllImport("kernel32", CharSet:=CharSet.Unicode)>
Public Shared Function FindFirstFile(lpFileName As String, ByRef lpFindFileData As WIN32_FIND_DATA) As IntPtr
End Function
<DllImport("kernel32", CharSet:=CharSet.Unicode)>
Public Shared Function FindNextFile(hFindFile As IntPtr, ByRef lpFindFileData As WIN32_FIND_DATA) As Boolean
End Function
<DllImport("kernel32.dll")>
Public Shared Function FindClose(ByVal hFindFile As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=False, SetLastError:=True)>
Public Shared Function DeleteFile(ByVal path As String) As Boolean
End Function
Public Shared Function GetFilenames(folderName As String) As List(Of String)
Dim filenames As New List(Of String)
Dim findData As New WIN32_FIND_DATA
Dim findHandle As New IntPtr
Dim INVALID_HANDLE_VALUE As New IntPtr(-1)
' Add wildcard to foldername to get all files
folderName += "*"
findHandle = FindFirstFile(folderName, findData)
If findHandle <> INVALID_HANDLE_VALUE Then
Do
If findData.cFileName <> "." AndAlso findData.cFileName <> ".." AndAlso (findData.dwFileAttributes And FileAttributes.Directory) <> FileAttributes.Directory Then
filenames.Add(findData.cFileName)
End If
Loop While (FindNextFile(findHandle, findData))
FindClose(findHandle)
End If
Return filenames
End Function
End Class
So the above works for deleting all files inside the folder with extension .JPG, but does not do it recursively.
UPDATE 3:
Again thanks for the help theduck, greatly appreciated, I think I'm almost there, I've managed to separate the 2 functions into 2 buttons, 1 button can list all files with .jpg, the other button can list all directories, now it's a case of combining them, so I managed to do the below with the second button but the Debug output isn't showing the files it is "suppose" to delete, this is what I've got so far, getting a little confused as to how it's supposed to work:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim longFolderName As String = "\\?\c:\users\Administrator\Desktop\TestDir\"
Dim foldernames As List(Of String) = VeryLongFilenameHandler.GetFoldernames(longFolderName)
For Each foldername As String In foldernames
Dim longFolderName1 As String = (longFolderName + foldername)
Dim extensionsToDelete As String = ".jpg"
Dim filenames As List(Of String) = VeryLongFilenameHandler.GetFilenames(longFolderName1)
MsgBox(longFolderName1)
For Each filename As String In filenames
If filename.ToLower.EndsWith(extensionsToDelete) Then
'VeryLongFilenameHandler.DeleteFile(longFolderName + filename)
Debug.WriteLine("Deleted: " & longFolderName1 + filename)
End If
Next
Next
End Sub
The MsgBox pops up with both directories as it should, the "VeryLongFilenameHandler.GetFoldernames" is the function adjustment you provided below for getting back just the directories. I basically copied the Public Shared Function and made the necessary adjustments.
UPDATE: Working Ok so it work's now, thanks to theduck, that last edit you made on the sample code is perfect, to link it to a button, I simply moved the code from inside the Module, into a button. Of course I'm not blind to the obvious, you basically wrote the entire code block for me, to which I am very thankful, I haven't seen help like that in a long time. As I'm only a novice programmer the way I learn quicker is by finding working code, and breaking down how that works, I grasp that much quicker than reading MSDN articles.
So once again thank you, here's my final code(consists of one Form and one Button) Imports System.IO Imports System.Runtime.InteropServices Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim longFolderName As String = "\\?\c:\users\administrator\Desktop\TestDir\"
Dim extensionToDelete As String = ".jpg"
RecursiveDelete(longFolderName, extensionToDelete)
End Sub
Sub RecursiveDelete(path As String, extensionToDelete As String)
If Not path.EndsWith("\") Then path += "\"
' Handle all folders below this one
Dim folders As List(Of String) = VeryLongFilenameHandler.GetFolders(path)
For Each folder As String In folders
RecursiveDelete(path + folder, extensionToDelete)
Next
' Delete any applicable files
Dim filenames As List(Of String) = VeryLongFilenameHandler.GetFilenames(path)
For Each filename As String In filenames
If filename.ToLower.EndsWith(extensionToDelete) Then
VeryLongFilenameHandler.DeleteFile(path + filename)
End If
Next
End Sub
End Class
Class VeryLongFilenameHandler
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Structure WIN32_FIND_DATA
Public dwFileAttributes As UInteger
Public ftCreationTime As System.Runtime.InteropServices.ComTypes.FILETIME
Public ftLastAccessTime As System.Runtime.InteropServices.ComTypes.FILETIME
Public ftLastWriteTime As System.Runtime.InteropServices.ComTypes.FILETIME
Public nFileSizeHigh As UInteger
Public nFileSizeLow As UInteger
Public dwReserved0 As UInteger
Public dwReserved1 As UInteger
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> Public cFileName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=14)> Public cAlternateFileName As String
End Structure
<DllImport("kernel32", CharSet:=CharSet.Unicode)>
Public Shared Function FindFirstFile(lpFileName As String, ByRef lpFindFileData As WIN32_FIND_DATA) As IntPtr
End Function
<DllImport("kernel32", CharSet:=CharSet.Unicode)>
Public Shared Function FindNextFile(hFindFile As IntPtr, ByRef lpFindFileData As WIN32_FIND_DATA) As Boolean
End Function
<DllImport("kernel32.dll")>
Public Shared Function FindClose(ByVal hFindFile As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=False, SetLastError:=True)>
Public Shared Function DeleteFile(ByVal path As String) As Boolean
End Function
Public Shared Function GetFilenames(folderName As String) As List(Of String)
Return GetNames(folderName, False)
End Function
Public Shared Function GetFolders(folderName As String) As List(Of String)
Return GetNames(folderName, True)
End Function
Private Shared Function GetNames(folderName As String, getDirectories As Boolean) As List(Of String)
Dim names As New List(Of String)
Dim findData As New WIN32_FIND_DATA
Dim findHandle As New IntPtr
Dim INVALID_HANDLE_VALUE As New IntPtr(-1)
If Not folderName.EndsWith("\") Then folderName += "\"
' Add wildcard to foldername to get all files
folderName += "*"
findHandle = FindFirstFile(folderName, findData)
If findHandle <> INVALID_HANDLE_VALUE Then
Do
If findData.cFileName <> "." AndAlso findData.cFileName <> ".." Then
Dim isDirectory As Boolean = (findData.dwFileAttributes And FileAttributes.Directory) = FileAttributes.Directory
If (getDirectories AndAlso isDirectory) Or (Not getDirectories AndAlso Not isDirectory) Then
names.Add(findData.cFileName)
End If
End If
Loop While (FindNextFile(findHandle, findData))
FindClose(findHandle)
End If
Return names
End Function
End Class