VB.NET Win32API 查找和删除文件(超过 260 个字符限制)

VB.NET Win32API Find and Delete Files(Over 260 Char Limit)

作为一个非常新手的程序员,我正在努力尝试让一段简单的代码工作,这就是我想要的:

递归搜索目录(例如 D:\Site_Data),找到具有给定扩展名(例如 .xrl)的所有文件并删除它们。

我想使用 Win32API 的原因是因为我正在处理隐藏在超过 260 个字符的目录中的文件,并且搜索了高低,但找不到解决方案。 VB.NET是我一直在学习的,所以这是我想坚持的。

我已经设法复制了一些代码并调整它以使用 Kernel32.dll DeleteFile 函数删除一个 "single" 文件,但这只删除了 1 个文件,它不能接受通配符,这里是我得到了什么:

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

我知道我要找的是 "FindFirstFile" 函数,但不知道如何编写代码来在 VB.NET 中实现它。

如果有人能提供帮助,将不胜感激。

更新 2,工作代码:

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

所以上面的方法适用于删除扩展名为 .JPG 的文件夹中的所有文件,但不会递归执行。

更新 3:

再次感谢theduck的帮助,非常感谢,我想我差不多大功告成了,我已经设法将2个功能分成2个按钮,1个按钮可以列出所有带有.jpg的文件,另一个按钮可以列出所有目录,现在是合并它们的情况,所以我设法用第二个按钮执行了以下操作,但调试输出没有显示要删除的文件 "suppose",这就是我得到的到目前为止,对于它应该如何工作有点困惑:

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 

MsgBox 会弹出两个目录,"VeryLongFilenameHandler.GetFoldernames" 是您在下面提供的功能调整,用于仅返回目录。我基本上复制了 Public 共享函数并进行了必要的调整。

更新:工作中 好的,现在可以使用了,多亏了 theduck,您对示例代码所做的最后一次编辑是完美的,link 它变成了一个按钮,我只是将代码从模块内部移到了一个按钮中。当然我不是盲目的,你基本上为我写了整个代码块,对此我非常感谢,我已经很久没有看到这样的帮助了。因为我只是一个新手程序员,所以我学得更快的方法是找到工作代码,然后分解它的工作原理,我比阅读 MSDN 文章更快地掌握了这一点。

所以再次感谢你,这是我的最终代码(由一个表单和一个按钮组成) 进口 System.IO 进口 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

结束Class

如果您使用循环,您可以让程序继续执行直到满足特定条件(例如,删除所有扩展名为 .example 的文件) 研究 while 循环,它们应该能够执行您希望程序执行的操作。

循环结构: https://msdn.microsoft.com/en-GB/library/ezk76t25.aspx

以下内容可能会有所帮助:

Imports System.IO
Imports System.Runtime.InteropServices

Module Module1

Sub Main()
    Dim longFolderName As String = "\?\c:\temp\"
    Dim extensionToDelete As String = ".xrl"

    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 Module

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

class VeryLongFilenameHandler 有一个 GetFilenames 函数,它将 return 您使用 Win32 API 指定的文件夹中的文件列表。然后您可以遍历这些文件名,检查扩展名并在适当时删除(再次使用 Win32 调用)。