使用NtQueryDirectoryFile遍歷文件/目錄

好久沒寫文章了,今天發個以前寫的一個代碼,本來是VC的今天專門轉換成了VB,希望能給一些有用的人一些幫助。

是高手就沒必要看了,初學者可以學習下VB的指針操作,和NT系列函數的使用方法。

代碼下載地址是:http://p.blog.csdn.net/images/p_blog_csdn_net/chenhui530/EntryImages/20080722/FindFile.jpg

Option Explicit

Private Type LARGE_INTEGER
    lowpart As Long
    highpart As Long
End Type

Private Type UNICODE_STRING
    uLength As Integer
    uMaximumLength As Integer
    pBuffer As Long
End Type

Private Type IO_STATUS_BLOCK
    Status As Long
    uInformation As Long
End Type

Private Type OBJECT_ATTRIBUTES
    Length As Long
    RootDirectory As Long
    ObjectName As Long
    Attributes As Long
    SecurityDescriptor As Long
    SecurityQualityOfService As Long
End Type

Private Type FILE_BOTH_DIRECTORY_INFORMATION
    NextEntryOffset As Long
    FileIndex As Long
    CreationTime As LARGE_INTEGER
    LastAccessTime As LARGE_INTEGER
    LastWriteTime As LARGE_INTEGER
    ChangeTime As LARGE_INTEGER
    EndOfFile As LARGE_INTEGER
    AllocationSize As LARGE_INTEGER
    FileAttributes As Long
    FileNameLength As Long
    EaSize As Long
    ShortNameLength As Byte
    ShortName(23) As Byte
    FileName As Byte '這裏爲了對齊,其實不用這個VB也是按4個字節對齊
    FileName1 As Integer '(519) As Byte
End Type

Private Const FileBothDirectoryInformation = 3
Private Const SYNCHRONIZE = &H100000
Private Const FILE_ANY_ACCESS = 0
Private Const FILE_LIST_DIRECTORY = 1
Private Const FILE_DIRECTORY_FILE = 1
Private Const FILE_SYNCHRONOUS_IO_NONALERT = &H20
Private Const FILE_OPEN_FOR_BACKUP_INTENT = &H4000
Private Const OBJ_CASE_INSENSITIVE = &H40

Private Declare Function NtQueryDirectoryFile Lib "ntdll.dll" (ByVal FileHandle As Long, _
                                ByVal hEvent As Long, _
                                ByVal ApcRoutine As Long, _
                                ByVal ApcContext As Long, _
                                ByRef IoStatusBlock As Any, _
                                FileInformation As Any, _
                                ByVal Length As Long, _
                                ByVal FileInformationClass As Long, _
                                ByVal ReturnSingleEntry As Long, _
                                FileName As Any, _
                                ByVal RestartScan As Long) As Long
Private Declare Function NtClose Lib "ntdll.dll" (ByVal ObjectHandle As Long) As Long
Private Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function NtOpenFile Lib "ntdll.dll" (FileHandle As Long, _
                                                    ByVal DesiredAccess As Long, _
                                                    ObjectAttributes As OBJECT_ATTRIBUTES, _
                                                    IoStatusBlock As IO_STATUS_BLOCK, _
                                                    ByVal ShareAccess As Long, _
                                                    ByVal OpenOptions As Long) As Long
                                                   
Private Declare Sub RtlInitUnicodeString Lib "ntdll.dll" (DestinationString As Any, ByVal SourceString As Long)
Private Declare Sub ZeroMemory Lib "ntdll.dll" Alias "RtlZeroMemory" (dest As Any, ByVal numBytes As Long)
                               
Private Function FindFirstFile(ByVal strDirectory As String, bytBuffer() As Byte) As Long
    Dim strFolder As String
    Dim obAttr As OBJECT_ATTRIBUTES
    Dim objIoStatus As IO_STATUS_BLOCK
    Dim ntStatus As Long
    Dim hFind As Long
    Dim strUnicode As UNICODE_STRING
   
    strFolder = "/??/"
    strFolder = strFolder & strDirectory
    RtlInitUnicodeString strUnicode, StrPtr(strFolder) '初始化Unicode字符串
    obAttr.Length = LenB(obAttr) '初始化OBJECT_ATTRIBUTES結構
    obAttr.Attributes = OBJ_CASE_INSENSITIVE
    obAttr.ObjectName = VarPtr(strUnicode) '需要打開的文件夾路徑
    obAttr.RootDirectory = 0
    obAttr.SecurityDescriptor = 0
    obAttr.SecurityQualityOfService = 0
    '獲取文件夾句柄
    ntStatus = NtOpenFile(hFind, _
                        FILE_LIST_DIRECTORY Or SYNCHRONIZE Or FILE_ANY_ACCESS, _
                        obAttr, _
                        objIoStatus, _
                        3, _
                        FILE_DIRECTORY_FILE Or FILE_SYNCHRONOUS_IO_NONALERT Or FILE_OPEN_FOR_BACKUP_INTENT)
                       
    If ntStatus = 0 And hFind <> -1 Then
        &apos;獲取文件夾文件/目錄信息,其實這個函數是Kernel32裏的FindFirstFile的封裝
        ntStatus = NtQueryDirectoryFile(hFind, _
                                     0, _
                                     0, _
                                     0, _
                                     objIoStatus, _
                                     bytBuffer(0), _
                                     UBound(bytBuffer), _
                                     FileBothDirectoryInformation, _
                                     1, _
                                     ByVal 0&, _
                                     0)
        If ntStatus = 0 Then &apos;Nt系列函數一般返回大於零表示成功,而NtQueryDirectoryFile返回零表示成功
            FindFirstFile = hFind
        Else
            NtClose hFind
        End If
    End If
End Function

Private Function FindNextFile(ByVal hFind As Long, bytBuffer() As Byte) As Boolean
    Dim ntStatus As Long
    Dim objIoStatus As IO_STATUS_BLOCK
    &apos;這是Kernel32 FindNextFile的封裝,只是是一次把所有文件/目錄都取出來了
    ntStatus = NtQueryDirectoryFile(hFind, _
                                 0, _
                                 0, _
                                 0, _
                                 objIoStatus, _
                                 bytBuffer(0), _
                                 UBound(bytBuffer), _
                                 FileBothDirectoryInformation, _
                                 0, _
                                 ByVal 0&, _
                                 0)
    If ntStatus = 0 Then
        FindNextFile = True
    Else
        FindNextFile = False
    End If
End Function

Private Sub cmdEnum_Click()
    Dim pDir As FILE_BOTH_DIRECTORY_INFORMATION
    Dim hFind As Long
    Dim bytBuffer() As Byte
    Dim bytName() As Byte
    Dim strPath As String
    Dim strFileName As String * 520
    Dim dwFileNameOffset As Long
    Dim dwDirOffset As Long
   
    Me.lstFile.Clear
    ReDim bytBuffer(LenB(pDir) + 260 * 2 - 3) &apos;定義一個緩存,返回數據用的
    strPath = txtPath.Text
    If Right(strPath, 1) <> "/" Then strPath = strPath & "/"
    &apos;這裏傳字節數據方便處理些,本來打算直接使用FILE_BOTH_DIRECTORY_INFORMATION結構的但是發現VB在處理這方面比較差
    &apos;尤其是在處理在遍歷緩存中的所有文件和目錄的時候更加麻煩了,因爲FILE_BOTH_DIRECTORY_INFORMATION的長度其實是可
    &apos;變的長度是根據LenB(FILE_BOTH_DIRECTORY_INFORMATION)+FileNameLength,這樣直接用結構在VB中就無法定義了
    hFind = FindFirstFile(strPath, bytBuffer) &apos;獲取第一個文件/目錄對象
    CopyMemory pDir, bytBuffer(0), LenB(pDir) &apos;獲取FILE_BOTH_DIRECTORY_INFORMATION結構,目的是獲取FileNameLength和NextEntryOffset數據
    ReDim bytName(pDir.FileNameLength - 1) &apos;定義個和文件名一樣大的緩存來存放文件名
    dwFileNameOffset = VarPtr(bytBuffer(&H5E)) &apos;文件名偏移是&H5E
    CopyMemory bytName(0), ByVal dwFileNameOffset, pDir.FileNameLength
    strFileName = strPath & CStr(bytName)
    Me.lstFile.AddItem strFileName
    Erase bytBuffer
    ReDim bytBuffer((LenB(pDir) + CLng(260 * 2 - 3)) * CLng(&H2000))
    If FindNextFile(hFind, bytBuffer) Then
        dwDirOffset = 0
        &apos;遍歷緩存
        Do While 1
            ZeroMemory pDir, LenB(pDir) &apos;把FILE_BOTH_DIRECTORY_INFORMATION置0
            CopyMemory pDir, ByVal VarPtr(bytBuffer(dwDirOffset)), LenB(pDir) &apos;得到FILE_BOTH_DIRECTORY_INFORMATION結構
            Erase bytName
            ReDim bytName(pDir.FileNameLength - 1)
            dwFileNameOffset = dwDirOffset + &H5E
            dwFileNameOffset = VarPtr(bytBuffer(dwFileNameOffset))
            CopyMemory bytName(0), ByVal dwFileNameOffset, pDir.FileNameLength
            strFileName = strPath & CStr(bytName)
            Me.lstFile.AddItem strFileName &apos;這裏如果是目錄可以遞歸遍歷目錄下所有文件/目錄,自己封裝一個遞歸函數吧
            If pDir.NextEntryOffset = 0 Then Exit Do
            dwDirOffset = dwDirOffset + pDir.NextEntryOffset &apos;這裏指向下一個FILE_BOTH_DIRECTORY_INFORMATION結構在內存中的位置
        Loop
    End If
    NtClose hFind
    Me.lblMsg.Caption = "文件目錄總數是:" & Me.lstFile.ListCount
End Sub

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章