Cải thiện tốc độ với Window API

Liên hệ QC
Tôi tuân thủ nội quy khi đăng bài

hakhuongdhkt

Gnouhk
Tham gia
26/8/14
Bài viết
42
Được thích
2
Giới tính
Nam
Em có 2 đoạn code dùng để lấy tất cả tên các file trong một folder.
Đối với những folder nhỏ, tốc độ 2 code đều như nhau. Nhưng với folder lớn, nhiều thư mục con thì mã 2 chạy lâu hơn rất nhiều ( code 1 chỉ mất vài giây).
Nhưng, vấn đề là code 1 không hiển thị được tên file có dấu tiếng việt; cách 2 lại hiển thị được.

Vậy mong các bác hướng dẫn cách nào để code 2 chạy nhanh hơn được không ạ?

Code 1: Dùng CMD & Dir
Mã:
Sub GetFilename()
   
Dim fldpath
Application.ScreenUpdating = False
Sheets(1).Range("A6:C" & Rows.Count).Clear
fldpath = "C:\Temp"
If fldpath = False Then
    'MsgBox "Folder Not Selected"
    Exit Sub
End If
    sn = Split(CreateObject("wscript.shell").exec("cmd /c Dir """ & fldpath & """ /s /b /a:-d").StdOut.ReadAll, vbCrLf)
    Sheets(1).Cells(6, 3).Resize(UBound(sn) + 1) = Application.Transpose(sn)
Call splitfilename
Application.ScreenUpdating = True
End Sub

Code 2: Dùng Window API & Đệ quy
Mã:
Option Explicit

Private Declare PtrSafe Function FindClose Lib "kernel32" (ByVal hFindFile As LongPtr) As Long
Private Declare PtrSafe Function FindFirstFileW Lib "kernel32" (ByVal lpFileName As LongPtr, ByVal lpFindFileData As LongPtr) As LongPtr
Private Declare PtrSafe Function FindNextFileW Lib "kernel32" (ByVal hFindFile As LongPtr, ByVal lpFindFileData As LongPtr) As LongPtr

Private Type FILETIME
  dwLowDateTime  As Long
  dwHighDateTime As Long
End Type

Const MAX_PATH  As Long = 260
Const ALTERNATE As Long = 14

' Can be used with either W or A functions
' Pass VarPtr(wfd) to W or simply wfd to A
Private Type WIN32_FIND_DATA
  dwFileAttributes As Long
  ftCreationTime   As FILETIME
  ftLastAccessTime As FILETIME
  ftLastWriteTime  As FILETIME
  nFileSizeHigh    As Long
  nFileSizeLow     As Long
  dwReserved0      As Long
  dwReserved1      As Long
  cFileName        As String * MAX_PATH
  cAlternate       As String * ALTERNATE
End Type

Public arr As Variant
Public n As Long
Private Const FILE_ATTRIBUTE_DIRECTORY As Long = 16 '0x10
Private Const INVALID_HANDLE_VALUE As LongPtr = -1

Public Function searchfile(sfolder As String) As Variant
Dim hFile     As LongPtr
Dim foundItem     As String
Dim folderPath     As String
Dim wfd       As WIN32_FIND_DATA

folderPath = sfolder & "\"
hFile = FindFirstFileW(StrPtr(folderPath & "*"), VarPtr(wfd))

If hFile <> INVALID_HANDLE_VALUE Then
    Do While FindNextFileW(hFile, VarPtr(wfd))
        foundItem = Left$(wfd.cFileName, InStr(wfd.cFileName, vbNullChar) - 1)
        If foundItem = "." Or foundItem = ".." Then
            'Found Directory
        ElseIf wfd.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY Then
            searchfile (folderPath & foundItem)
        ElseIf InStr(1, foundItem, vbTextCompare) > 0 Then 'for performance
            'Debug.Print Left$(wfd.cFileName, InStr(wfd.cFileName, vbNullChar) - 1)
            If IsArray(arr) Then
                ReDim Preserve arr(1 To 3, 1 To n)
            Else
                ReDim arr(1 To 3, 1 To n)
            End If
            arr(1, n) = wfd.cFileName
            arr(2, n) = folderPath   'Left(folderPath, Len(folderPath) - 1)
            arr(3, n) = folderPath & wfd.cFileName
            n = n + 1
        End If
       
    Loop
    FindClose hFile
End If
searchfile = arr
End Function

Sub getfilename()
Application.ScreenUpdating = False
Dim fpath As String
Dim resultarr As Variant
n = 1
fpath = "C:\Temp"
resultarr = searchfile(fpath)
Sheet1.Range("A2:C" & UBound(resultarr, 2) + 1).Value = Application.Transpose(resultarr)
Application.ScreenUpdating = True
End Sub
 
Lần chỉnh sửa cuối:
Chuyện code két viết trên bất cứ Tools gì và ai viết chạy nhanh hay lâu không quan trọng lắm

cái quan trọng nhất là phải thử xem có chạy hay không ... Nếu chạy ra kết quả thì nên lưu lại mà sử dụng khi cần

Thử như sau:

1/ C:\Windows
2/ C:\

Nếu 2 cách trên ok thì nên lưu lại code đó mà dùng hay tham khảo cách Tôi thử



dùng hàm Dir không hổ trợ tiếng Việt có dấu
Bài đã được tự động gộp:

Nếu bạn sử dụng trên VBA thì tìm vài hàm cmd trên này có từ 8 to 10 năm trước mà dùng ... nó sử dụng tốt với yều cầu của bạn
 
Lần chỉnh sửa cuối:
Upvote 0
Chuyện code két viết trên bất cứ Tools gì và ai viết chạy nhanh hay lâu không quan trọng lắm

cái quan trọng nhất là phải thử xem có chạy hay không ... Nếu chạy ra kết quả thì nên lưu lại mà sử dụng khi cần

Thử như sau:

1/ C:\Windows
2/ C:\

Nếu 2 cách trên ok thì nên lưu lại code đó mà dùng hay tham khảo cách Tôi thử



dùng hàm Dir không hổ trợ tiếng Việt có dấu
E test thì cả 2 code đều không bị văng. Code 1 thì chạy 1 lúc ra, code 2 cứ running mãi ngồi đợi cũng oải
 
Upvote 0
Có cách nữa là dùng FSO và đệ quy, code đơn giản hơn nhiều so với cách 2, lại hỗ trợ tiếng Việt.

Cứ cái gì đơn giản mà dùng được thì cứ dùng.
 
Upvote 0
Upvote 0
Có cách nữa là dùng FSO và đệ quy, code đơn giản hơn nhiều so với cách 2, lại hỗ trợ tiếng Việt.

Cứ cái gì đơn giản mà dùng được thì cứ dùng.
Em có thử FSO rồi, nhưng n còn lâu hơn API này . Lục tung google thấy api nhanh, ai dè vẫn không khá khẩm lắm.
 
Upvote 0
Em có thử FSO rồi, nhưng n còn lâu hơn API này . Lục tung google thấy api nhanh, ai dè vẫn không khá khẩm lắm.
Không mấy khi lấy nguyên cả ổ đĩa nên thử cho biết chứ chẳng để làm gì. Người dùng đa số chỉ cần lục lọi 1 thư mục nho nhỏ để lấy file ra xử lý nên cách nào đơn giản, dùng được với dấu tiếng Việt thì nên dùng.
 
Upvote 0
Em có thử FSO rồi, nhưng n còn lâu hơn API này . Lục tung google thấy api nhanh, ai dè vẫn không khá khẩm lắm.
trên này có hết rồi không cần tìm google nữa ... vì nhiều năm qua tôi cũng đã tìm rồi

Hãy tìm vài hàm cmd trên này mà dùng ... nếu thích API thì tìm bài của tôi mà dùng ... còn linh nào thong thả tìm sẽ ra...

Tại sao thế vì quá trình tìm ta có thời gian ngắn ngía và thử nó khi lang thang trên con đường tìm kiếm

xong sẽ thấy cái phù hợp với nhu cầu của mình thì tạm dừng chân
 
Upvote 0
Tự dưng đi đặt điều kiện Windows API có phải là tự giới hạn mình trong một góc nhìn?

Thời buổi công nghệ tiên tiến hiện nay. Chỉ cần xác định yêu cầu. Đường đi để cho bên tư vấn người ta dưa ra các lựa chọn.

ListDir trên nền tảng Windows hiện nay không có cái gì qua PowerShell và Python cả.
1. PowerShell là công cụ của quản lý mạng. Chắn chắn là hiệu quả với Directory, so với VBScript.
2. Python chỉ là script, nhưng các thư viện của nó được viết bằng code C, do các tay tổ lập trình viết cho nên chắc chắn là hiệu quả.
 
Upvote 0
Em có 2 đoạn code dùng để lấy tất cả tên các file trong một folder.
Đối với những folder nhỏ, tốc độ 2 code đều như nhau. Nhưng với folder lớn, nhiều thư mục con thì mã 2 chạy lâu hơn rất nhiều ( code 1 chỉ mất vài giây).
Nhưng, vấn đề là code 1 không hiển thị được tên file có dấu tiếng việt; cách 2 lại hiển thị được.

Vậy mong các bác hướng dẫn cách nào để code 2 chạy nhanh hơn được không ạ?

Code 1: Dùng CMD & Dir
Mã:
Sub GetFilename()
  
Dim fldpath
Application.ScreenUpdating = False
Sheets(1).Range("A6:C" & Rows.Count).Clear
fldpath = "C:\Temp"
If fldpath = False Then
    'MsgBox "Folder Not Selected"
    Exit Sub
End If
    sn = Split(CreateObject("wscript.shell").exec("cmd /c Dir """ & fldpath & """ /s /b /a:-d").StdOut.ReadAll, vbCrLf)
    Sheets(1).Cells(6, 3).Resize(UBound(sn) + 1) = Application.Transpose(sn)
Call splitfilename
Application.ScreenUpdating = True
End Sub

Code 2: Dùng Window API & Đệ quy
Mã:
Option Explicit

Private Declare PtrSafe Function FindClose Lib "kernel32" (ByVal hFindFile As LongPtr) As Long
Private Declare PtrSafe Function FindFirstFileW Lib "kernel32" (ByVal lpFileName As LongPtr, ByVal lpFindFileData As LongPtr) As LongPtr
Private Declare PtrSafe Function FindNextFileW Lib "kernel32" (ByVal hFindFile As LongPtr, ByVal lpFindFileData As LongPtr) As LongPtr

Private Type FILETIME
  dwLowDateTime  As Long
  dwHighDateTime As Long
End Type

Const MAX_PATH  As Long = 260
Const ALTERNATE As Long = 14

' Can be used with either W or A functions
' Pass VarPtr(wfd) to W or simply wfd to A
Private Type WIN32_FIND_DATA
  dwFileAttributes As Long
  ftCreationTime   As FILETIME
  ftLastAccessTime As FILETIME
  ftLastWriteTime  As FILETIME
  nFileSizeHigh    As Long
  nFileSizeLow     As Long
  dwReserved0      As Long
  dwReserved1      As Long
  cFileName        As String * MAX_PATH
  cAlternate       As String * ALTERNATE
End Type

Public arr As Variant
Public n As Long
Private Const FILE_ATTRIBUTE_DIRECTORY As Long = 16 '0x10
Private Const INVALID_HANDLE_VALUE As LongPtr = -1

Public Function searchfile(sfolder As String) As Variant
Dim hFile     As LongPtr
Dim foundItem     As String
Dim folderPath     As String
Dim wfd       As WIN32_FIND_DATA

folderPath = sfolder & "\"
hFile = FindFirstFileW(StrPtr(folderPath & "*"), VarPtr(wfd))

If hFile <> INVALID_HANDLE_VALUE Then
    Do While FindNextFileW(hFile, VarPtr(wfd))
        foundItem = Left$(wfd.cFileName, InStr(wfd.cFileName, vbNullChar) - 1)
        If foundItem = "." Or foundItem = ".." Then
            'Found Directory
        ElseIf wfd.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY Then
            searchfile (folderPath & foundItem)
        ElseIf InStr(1, foundItem, vbTextCompare) > 0 Then 'for performance
            'Debug.Print Left$(wfd.cFileName, InStr(wfd.cFileName, vbNullChar) - 1)
            If IsArray(arr) Then
                ReDim Preserve arr(1 To 3, 1 To n)
            Else
                ReDim arr(1 To 3, 1 To n)
            End If
            arr(1, n) = wfd.cFileName
            arr(2, n) = folderPath   'Left(folderPath, Len(folderPath) - 1)
            arr(3, n) = folderPath & wfd.cFileName
            n = n + 1
        End If
      
    Loop
    FindClose hFile
End If
searchfile = arr
End Function

Sub getfilename()
Application.ScreenUpdating = False
Dim fpath As String
Dim resultarr As Variant
n = 1
fpath = "C:\Temp"
resultarr = searchfile(fpath)
Sheet1.Range("A2:C" & UBound(resultarr, 2) + 1).Value = Application.Transpose(resultarr)
Application.ScreenUpdating = True
End Sub
Nếu chỉ lấy list tên file thì code cũng không phức tạp. Bạn vào Sub GetFileList và sửa lại đường dẫn rồi thử xem xài được không
Mã:
Sub GetFileList()
Dim sArr()
sArr = ShowFileList(ThisWorkbook.path)
MsgBox UBound(sArr)
End Sub

Function ShowFileList(ByVal FolderPath As String)
Dim Res(), k As Long, File As Object
With CreateObject("Scripting.FileSystemObject")
    With .GetFolder(FolderPath)
        For Each File In .Files
            k = k + 1
            ReDim Preserve Res(1 To k)
            Res(k) = File.Name
        Next
    End With
End With
ShowFileList = Res
End Function
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom