Các lỗi khi thực thi VBA

Euler

Biên Tập Viên
Ở topic trước, chúng ta đã được giới thiệu về .
Trong khuôn khổ topic này, tôi sẽ trình bày các lỗi xảy ra khi thực thi VBA. Chú ý rằng, tôi đã cố gắng để tạo nên một chủ đề hấp dẫn hơn bài gốc.
Vì vậy nếu bạn cũng cảm thấy hơi khó đọc, thì chịu khó đọc. Vì nếu không nắm được, đến lúc nào đó các bạn gặp lỗi, và cứ loay hoay chẳng hiểu lỗi ấy là gì để mà khắc phục thì rất dở. Do đó, chúng ta phải đọc để hiểu bản chất. Thật ra cũng không khó đâu, mình sẽ cố gắng để tạo ra một chủ đề thú vị.
Hãy chú ý các mã lỗi nhé. Để lần sau nếu gặp thì chúng ta hiểu chúng ta đang bị cái gì nha.

1. Không có GoSub đối với Return (Mã lỗi:3)
Mã:
Sub Sample()
    Dim i As Long
    For i = 1 To 10
        Cells(i, 1) = i
    Next i
    Return    ''Nơi trả về của Return không có GoSub
End Sub
Kết quả:
Bạn cần đăng nhập để thấy đính kèm


Đối với Code VBA, không nên sử dụng Goto và GoSub. ( )
Tuy nhiên vẫn có lúc chúng ta phải dùng, để tránh nơi phát sinh lỗi có thể có (mà chúng ta phán đoán được).
Nơi trả về của Return là quay lại nhãn Goto hoặc GoSub.
Và vì trong chương trình không có nhãn Goto hoặc GoSub nên xảy ra lỗi, với mã lỗi là 3 như ở ảnh trên các bạn đã thấy.
Nguồn:
 

tuhocvba

Administrator
Thành viên BQT
2. Lỗi khi gọi thủ tục hoặc tham số truyền vào sai. (Mã lỗi: 5)
Mã:
Sub Sample()
    Dim buf As String
    buf = Left("tuhocvba.net", -1)
End Sub
Với hàm Left(string1,số_ký_tự_cần_lấy) nó sẽ lấy số ký tự từ bên trái, trong đó số_ký_tự_cần_lấy > 0. Ở đây ta truyền vào là số âm, do đó tất nhiên sẽ sinh ra lỗi do tham số sai.
Bạn cần đăng nhập để thấy đính kèm


Nguồn tham khảo:

3. Lỗi vượt quá giới hạn (Mã lỗi: 6)
Mã:
Sub Sample1()
    Dim N As Integer
    N = 32768
End Sub
Kết quả:
Bạn cần đăng nhập để thấy đính kèm


Kiểu số nguyên integer nằm trong giới hạn từ -32768~32767
Do đó khi chúng ta thực hiện phép gán N = 32768 thì chương trình không thực thi được, sẽ sinh ra lỗi.
Chúng ta cũng nên chú ý rằng Excel 2007 trở đi, số dòng là 1048576. Do đó nếu dữ liệu của chúng ta lớn, vượt quá cả giới hạn của integer thì chúng ta phải khai báo là Long, có giá trị tới hơn 2 triệu, đủ sức làm việc với bảng tính Excel. Tuy nhiên, nếu dữ liệu của chúng ta nằm trong giới hạn 30.000 trở về, thì cứ khai báo integer là được. Hãy chú ý nhé.
Nguồn:
 

giaiphapvba

Administrator
Thành viên BQT
4. Bộ nhớ không đủ (Mã lỗi: 7)
Không có chưong trình ví dụ. Cũng không tạo ra được lỗi để lấy ảnh minh họa.
Nguồn:
5. Chỉ mục (Index) nằm ngoài phạm vi (Mã lỗi: 9)
Mã:
Sub Sample1()
    Dim i As Long
    For i = 1 To 4
        Cells(i, 1) = Worksheets(i).Name
    Next i
End Sub
Chú ý file excel của tôi chỉ có 1 sheet thôi nhé. Theo như chương trình trên thì có lẽ file phải có 4 sheet mới thực thi được. (i chạy tới 4 mà).
Do đó sẽ sinh ra lỗi, chúng ta cùng xem nhé.
Bạn cần đăng nhập để thấy đính kèm


Tương tự trường hợp này cũng sinh ra lỗi:
Mã:
Sub Sample2()
    Dim tmp As Variant, i As Long
    tmp = Split("tanaka,suzuki,yamada", ",")
    For i = 1 To 3
        Cells(i, 1) = tmp(i)
    Next i
End Sub
Vì sau khi thực hiện lệnh Split, ta thu được mảng có kích thước như sau:
tmp(0) → "tanaka"
tmp(1) → "suzuki"
tmp(2) → "yamada"
Vì thế không tồn tại tmp(3), do đó sẽ phát sinh lỗi.
Nguồn:
6. Mảng đang bị cố định, nhất thời bị khóa không thể thay đổi (Mã lỗi: 10)
Mã:
Sub Sample1()
    Dim tmp() As String, s As Variant, n As Long
    tmp = Split("tuhocvba,Vietnam,VBA", ",")
    For Each s In tmp
        n = UBound(tmp) + 1
        ReDim Preserve tmp(n) 'Thực hiệ khai báo lại kích thước mảng'
        tmp(n) = s & "THV"
    Next s
End Sub
Bạn cần đăng nhập để thấy đính kèm

Khi chúng ta sử dụng For Each, chúng ta duyệt qua từng đối tượng là phần tử của mảng. Khi For Each chưa kết thúc, thì tạm thời kích thước của mảng sẽ bị khóa nên ta không thể thay đổi, sẽ sinh ra lỗi.
Và do đó, nếu tôi sử dụng For Next thì sẽ tránh được lỗi.
Chương trình dưới đây sẽ không có lỗi:
Mã:
Sub Sample1()
    Dim tmp() As String, s As Variant, n As Long
    Dim i As Integer 'Sua lai code de dung for next
    
    tmp = Split("tuhocvba,Vietnam,VBA", ",")
    For i = LBound(tmp) To UBound(tmp) Step 1 'Sử dụng For next
        n = UBound(tmp) + 1
        ReDim Preserve tmp(n)
        tmp(n) = s & "THV"
    Next i
End Sub
Nguồn:
 

Euler

Biên Tập Viên
7. Lỗi vì chia cho 0 (Mã lỗi 11)
Mã:
Sub Sample1()
    Dim N As Long
    N = 120 / 0
End Sub
Lỗi này đơn giản quá phải không các bạn? :)
Bạn cần đăng nhập để thấy đính kèm


8. Kiểu dữ liệu không thống nhất, mâu thuẫn với nhau (Mã lỗi 13)
Mã:
Sub Sample1()
    Dim N As Long
    N = "tuhocvba.net"
End Sub
Khai báo N là số, ở dưới thực hiện phép gán với ký tự. Trước và sau không thống nhất với nhau. Hãy chú ý mã lỗi 13 này nhé.
@thanhphuongvip
Bạn cần đăng nhập để thấy đính kèm


Lỗi này rất hay gặp. Ta đi vào ví dụ khác nhé:
Mã:
Sub Sample2()
    Dim buf As String
    buf = InputBox("Hay nhap ngay thang nam")
    ActiveCell = DateValue(buf)
End Sub
Bạn cần đăng nhập để thấy đính kèm

Ta không nhập gì, lập tức ấn Cancel. Như thế InputBox sẽ nhận ký tự là rỗng "".
Và như vậy tới đây hàm trả về giá trị ngày DateValue sẽ không thực thi được với đầu vào là string rỗng "". Lập tức lỗi 13 sẽ xuất hiện.
Đối với hàm DateValue, đầu vào như vậy là sai.

Nguy hiểm hơn ở chỗ là nếu các chương trình liên kết với nhau. Khi đó nếu không kiểm soát tốt các kiểu dữ liệu đầu vào, việc gọi hàm hay thủ tục có thể sai. Tức là đứng độc lập thì không sai, nhưng quá trình gọi nhau thì xảy ra lỗi.
Ví dụ:
Mã:
Sub Sample3()
    Call myProc("tuhocvba.net")   ''Dau vao la ky tu
End Sub

Sub myProc(n As Long)       ''Dau vao la so
    ActiveCell = n
End Sub
Tôi chạy thủ tục Sample3 thì lập tức xảy ra lỗi.

Tuy nhiên VBA có một đặc thù là tự động chuyển kiểu dữ liệu mà nó hiểu được. Ta ví dụ:
Mã:
Sub Sample4()
    Dim N As Long, Str As String
    Str = "100"
    N = Str 'Câu lệnh kỳ vọng xảy ra lỗi nhưng thực tế không có lỗi
    MsgBox N 'Câu lệnh kỳ vọng xảy ra lỗi nhưng thực tế không có lỗi
End Sub
Ta khai báo N là số, vậy mà ta lại thực hiện phép gán với ký tự string Str. Nếu là một ngôn ngữ lập trình khắt khe về khai báo, hẳn là sẽ không thể tin được với đoạn code như trên. Ấy thế mà VBA không hề xảy ra lỗi, chương trình vẫn chạy bình thường.
Chú ý rằng tham số của hàm MsgBox được định nghĩa là String. Nhưng ở đây cũng đã không xảy ra lỗi.
Nguồn:
 

tuhocvba

Administrator
Thành viên BQT
9. Khi không phát sinh lỗi thì không thể thực thi Resume (Mã lỗi: 20 )
Đầu tiên là cách sử dụng Resume đúng.
Mã:
Sub Sample1()
    On Error GoTo Error1
    ActiveWorkbook.SaveAs "D:\VBA\a2.xls"
    MsgBox "Da hoan thanh"
    Exit Sub
Error1:
    MsgBox "Khong the save file"
    Err.Clear
    Resume Next
End Sub
Từ workbook mà bạn đang thực thi macro, chúng ta sử dụng phương thức SaveAs, nhưng trước đó chúng ta cho thực thi On Error Goto Error1, có nghĩa là giả như có lỗi phát sinh thì nó sẽ chạy tới nhãn Error1 và thực thi các lệnh ở đó.
Nó sẽ lưu file vào đường dẫn được chỉ định, nhưng giả sử trong folder đó đã có file trùng tên thì ra sao đây?
Chúng ta sẽ cùng nhau xác nhận vấn đề này.
Bạn cần đăng nhập để thấy đính kèm

Nếu chúng ta click vào No thì file sẽ không được save. File không được save thì câu lệnh SaveAs không thể kết thúc, do đó tại câu lệnh SaveAs sẽ phát sinh ra lỗi. Quá trình xử lý ở nhãn Error1 được kích hoạt. Thông báo "Khong the save file" sẽ hiện ra. Ở đây ý nghĩa Resume Next là như vậy, nếu lỗi phát sinh thì nó sẽ thực thi chỉ thị tiếp theo. Do đó thông báo hiển thị như trên là đúng ý đồ người code.
Câu lệnh Resume phải được sử dụng trong khi xử lý lỗi đang xảy ra.
Bây giờ bạn chạy code sau:
Mã:
Sub Sample2()
    ActiveWorkbook.SaveAs "D:\VBA\a3.xls"
    MsgBox "da hoan thanh"
    Resume Next
End Sub
Cứ lưu file bình thường. Cho tới khi chương trình chạy tới câu lệnh Resume Next thì sẽ xảy ra lỗi.
Nội dung lỗi đã rất rõ ràng, Resume khi mà không có lỗi thì là lỗi.
Bạn cần đăng nhập để thấy đính kèm


Nguồn:
 

Euler

Biên Tập Viên
10. Không đủ chỗ cho ngăn nhớ (Nguyên bản từ gốc: Stack) (Mã lỗi: 28)
Mã:
Sub Sample1()
    Call Sample2
End Sub
Sub Sample2()
    Call Sample1
End Sub
Chúng ta có ngay thông báo lỗi như sau:
Bạn cần đăng nhập để thấy đính kèm

Code trên là ví dụ vô cùng dễ hiểu.
Từ một thủ tục, chúng ta gọi một thủ tục khác. Như vậy VBA sẽ phải lục lọi trong ký ức ngăn nhớ của nó xem cái thủ tục nào được gọi, nó ở đâu. Và thủ tục đang đứng để gọi thủ tục khác thì nằm ở ngăn nhớ nào, có như vậy khi thủ tục được gọi kết thúc, nó mới có thể quay về thủ tục ban đầu, đúng không nào?

Tuy nhiên, bộ nhớ của nó cũng có giới hạn. A gọi B, B gọi C, C gọi D, D gọi E,... cứ như thế thì bộ nhớ nào chịu nổi. Và như thế thì lỗi sẽ xảy ra. Code trên, chúng ta cố tình cho chúng gọi lẫn nhau, và kết cục là VBA sẽ bị lỗi, không thể thực thi được.

Ở ví dụ trên, là thủ tục gọi một thủ tục khác. Tuy nhiên với thuật toán đệ quy, tự mình gọi chính mình mới là điều tôi muốn mọi người lưu ý.
Ở ví dụ này, nó sẽ liệt kê toàn bộ thủ tục con trong một folder được chỉ định.
Mã:
Sub Sample3()
    Call Sample4("C:\tuhocvba_net")
End Sub
Sub Sample4(Target As String)
    Dim folder As Object
    Static cnt As Long
    With CreateObject("Scripting.FileSystemObject")
        For Each folder In .GetFolder(Target).SubFolders
            cnt = cnt + 1
            Cells(cnt, 1) = folder.Name
            If folder.SubFolders.Count > 1 Then
                Call Sample4(folder.Path)   ''Tu minh goi chinh minh
            End If
        Next folder
    End With
End Sub
Các thủ tục đệ quy kiểu này chắc chắn làm tốn bộ nhớ lưu trữ lịch sử các lần gọi thủ tục. Nếu đường dẫn folder quá sâu, và được phân cấp đủ lớn, thì bộ nhớ sẽ không đủ, khi đó có thể sẽ xảy ra lỗi, các bạn chú ý.
Diễn đàn tuhocvba.net tham khảo và dịch từ:
 

giaiphapvba

Administrator
Thành viên BQT
11. Lỗi chưa định nghĩa Function (Mã lỗi : 35 )
Bạn cần đăng nhập để thấy đính kèm

Mã:
Sub tuhocvba()
    Call tuhocvba3
End Sub
Sub tuhocvba2()
    MsgBox Now()
End Sub
Khi chúng ta gọi một thủ tục hay một hàm, nếu nó không tồn tại thì sẽ bị báo lỗi như trên.
Thông thường, chúng ta gõ sai tên hàm hay thủ tục, và vì vậy không thể gọi được và bị lỗi như trên.

12. Tên file hoặc số hiệu bất chính(không đúng) (Mã lỗi: 52)
Mã:
Sub Sample1()
    Dim buf As String
    Open "C:\Sample.txt" For Input As #1 'Chú ý ở đây ta gán số hiệu là 1
        Line Input #2, buf      ''←Không phải 2 mà là 1 mới đúng
    Close #1
End Sub
Bạn cần đăng nhập để thấy đính kèm

File đã được mở bằng phương thức Open thì có thể chỉ định số hiệu như Code ở trên. Điều này cũng giống như có nhiều xe máy lưu thông trên đường, để kiểm soát thì ta cần gán số hiệu.
Khi chúng ta nhầm lẫn số hiệu thì sẽ phát sinh lỗi.
Code trên để không có lỗi thì có hai cách sửa:
Hoặc là:
Mã:
Open "C:\Sample.txt" For Input As #2 'Sửa lại định nghĩa số hiệu: 1 => 2
Hoặc là:
Mã:
Line Input #1, buf 'sửa lại 2 => 1
Chúng ta chú ý rằng việc macro xử lý đồng thời nhiều file bằng phương thức Open không phải là hiếm. Đại để mở 1~3 files thì vẫn thường gặp. Và khi đó nếu chúng ta chỉ định nhầm số hiệu và thao tác nhầm data thì rất nguy hiểm, đây là chú ý quan trọng khi các bạn chỉ định số hiệu để mở file và thao tác với data của file đó. Sau đây tôi sẽ định nghĩa hằng số trong trường hợp có quá nhiều file.
Mã:
Sub Sample1()
    Dim buf1 As String, buf2 As String
    Const UserAddress As Long = 1 'Định nghĩa hằng số
    Const UserName As Long = 2
    Const UserData As Long = 3
    Open "C:\Sample1.txt" For Input As UserAddress 'Mở file #1
    Open "C:\Sample2.txt" For Input As UserName 'Mở file #2
    Open "C:\Sample3.txt" For Append As UserData 'Mở file #3
        Do Until EOF(UserAddress)
            Line Input #UserAddress, buf1 'Đọc dữ liệu từng dòng
            Line Input #UserName, buf2
            Print #UserData, buf1 & ":" & buf2
        Loop
    Close UserData
    Close UserName
    Close UserAddress
End Sub
Việc định nghĩa hằng số bằng các tên mà người code có thể hình dung được, sẽ hạn chế bớt sai sót.
Bài viết được tham khảo và dịch từ:
 

tuhocvba

Administrator
Thành viên BQT
13. File không tồn tại (Mã lỗi: 53)
Mã:
Sub Sample1()
    Dim buf As String
    Open "C:\file_khong_ton_tai.txt" For Input As #1
        Line Input #1, buf
    Close #1
    MsgBox buf
End Sub
Bạn cần đăng nhập để thấy đính kèm


Ta dùng code sau để chèn một bức ảnh vào sheet:
Mã:
Sub Sample3()
    ActiveSheet.Pictures.Insert "C:\bientc2.jpg" 'File không tồn tại
End Sub
Bạn cần đăng nhập để thấy đính kèm

Lỗi 1004 xuất hiện thông báo là nó không thể chèn ảnh vào. Tuy nhiên, nguyên nhân của nguyên nhân chính là do ảnh không tồn tại. Các bạn hãy chú ý điều này.
Bài viết được dịch và tham khảo từ:
 

vbano1

Admin
Thành viên BQT
14. File Mode bất chính (không đúng) (Mã lỗi: 54)
Mã:
Sub Sample1()
    Dim buf As String
    Open "D:\VBA\1.txt" For Output As #1
        Line Input #1, buf
    Close #1
    MsgBox buf
End Sub
Bạn cần đăng nhập để thấy đính kèm

Chúng ta mở file để đọc bằng chế độ ghi đè (Output), ngược lại, chúng ta lại chỉ định ghi đè đối với file đã mở ở chế độ chỉ đọc (read only) (Input), như thế thật là mâu thuẫn, và cuối cùng là phát sinh ra lỗi như trên.

15. File đang được mở nên không thể ra lệnh tiếp tục mở (Mã lỗi: 55)
Mã:
Sub Sample2()
    Dim buf As String
    Open "D:\VBA\1.txt" For Input As #1
        Open "D:\VBA\1.txt" For Output As #2
            Line Input #2, buf
        Close #2
    Close #1
    MsgBox buf
End Sub
Bạn cần đăng nhập để thấy đính kèm


File đang được mở mà bây giờ ta lại ra lệnh mở thì sẽ phát sinh lỗi thông báo rằng file đang được mở rồi!
Nguồn tham khảo và dịch:
 

tuhocvba

Administrator
Thành viên BQT
14. File Mode bất chính (không đúng) (Mã lỗi: 54)
Ở ví dụ mã lỗi 54, đưa ra ví dụ như dưới đây thì sẽ hay hơn.
Nhắc lại về việc ghi nội dung vào file text, ta có hai code dưới đây đều có tác dụng ghi nội dung vào file txt:
Mã:
Sub Sample2()
    Dim buf As String
    buf = "tuhocvba.net"
        Open "D:\VBA\1.txt" For Output As #1
            Write #1, buf
        Close #1

    MsgBox buf
End Sub
Sub Sample3()
    Dim buf As String
    buf = "tuhocvba.net"
        Open "D:\VBA\1.txt" For Output As #1
            Print #1, buf
        Close #1

    MsgBox buf
End Sub
Khác nhau giữa Write và Print thì mọi người có thể tự kiểm chứng.
Write sẽ ghi vào là "tuhocvba.net" (có dấu ngoặc kép)
Print thì sẽ ghi vào là tuhocvba.net (không có dấu ngoặc kép)

Bây giờ ta cố tình tạo ra lỗi 54 bằng cách sửa Output thành Input:
Mã:
Sub Sample2()
    Dim buf As String
    buf = "tuhocvba.net"
        Open "D:\VBA\1.txt" For Input As #1
            Write #1, buf
        Close #1

    MsgBox buf
End Sub
Sub Sample3()
    Dim buf As String
    buf = "tuhocvba.net"
        Open "D:\VBA\1.txt" For Input As #1
            Print #1, buf
        Close #1

    MsgBox buf
End Sub
Cả hai thủ tục trên đều sẽ cho ra lỗi 54.
Bạn cần đăng nhập để thấy đính kèm
 

Euler

Biên Tập Viên
16. File trùng tên đã tồn tại nên không thể lưu file được (Mã lỗi: 58)
Tôi có hai file là 2.txt3.txt cùng lưu trong folder D:\VBA
Bây giờ tôi chạy macro với mục đích lưu file 3.txt thành tên 2.txt, nhưng vì file 2.txt đã tồn tại rồi, nên việc lưu dưới dạng tên mới là không thể thực thi.
Mã:
Sub Sample1()
    Name "D:\VBA\3.txt" As "D:\VBA\2.txt"
End Sub
Lỗi 58 sẽ xuất hiện như dưới đây, thông báo rằng file có tên như vậy đã tồn tại rồi.
Bạn cần đăng nhập để thấy đính kèm


Để khắc phục điều này, chúng ta nên có đoạn code kiểm tra file 2.txt đã tồn tại hay chưa trước khi tiến hành lưu dưới tên mới.
Mã:
Sub Sample2()
    If Dir("D:\VBA\2.txt") <> "" Then
        MsgBox "D:\VBA\2.txt da ton tai"
    Else
        Name "D:\VBA\3.txt" As "D:\VBA\2.txt"
    End If
End Sub
Lưu ý rằng, cách kiểm tra file tồn tại bằng hàm Dir có hạn chế là không thể kiểm tra với đường link là tiếng việt có dấu hoặc link chứa khoảng trắng của dấu cách " ". .
 

tuhocvba

Administrator
Thành viên BQT
17. Không có dữ liệu (Mã lỗi: 62)
Tôi có đoạn code sau dùng để đọc từng dòng dữ liệu của file 2.txt, dòng dữ liệu được đọc ra sẽ ghi vào biến buf.
Mã:
Sub Sample1()
    Dim buf As String
    Open "D:\VBA\2.txt" For Input As #1
        Do Until EOF(1)
            Line Input #1, buf
        Loop
        Line Input #1, buf
    Close #1
End Sub
Lỗi 62 sẽ xảy ra.
Bạn cần đăng nhập để thấy đính kèm


Với câu lệnh Line Inut#, nó sẽ đọc từng dòng dữ liệu của file txt. Khi thực thi lệnh Line Inut# , thì con trỏ cũng sẽ di chuyển tới vị trí dòng dữ liệu tiếp theo.
Hàm EOF sẽ phán đoán đâu là dòng lệnh cuối cùng-giá trị trả về là True. Lúc này vị trí con trỏ đã ở cuối file. Tại đây không còn dữ liệu để đọc, nhưng ta lại tiếp tục cho lệnh đọc dữ liệu (tô màu vàng), và lúc này lỗi 62 xảy ra.
Bạn cần đăng nhập để thấy đính kèm


Nguồn:
 

giaiphapvba

Administrator
Thành viên BQT
18. Đường dẫn bị vô hiệu (Mã lỗi: 75)
Tôi có đoạn code sau để ghi nội dung vào file. Tuy nhiên file này tôi lại để thuộc tính là chỉ đọc, không cho ghi nội dung.
Mã:
Sub Sample1()
    ''File de thuoc tinh la chi doc, khong cho ghi noi dung
    ''Doan code duoi day to co y ghi noi dung vao file
    Open "C:\Users\VBA\Openfile.txt" For Append As #1
        Print #1, "tuhocvba.net"
    Close #1
End Sub
Bạn cần đăng nhập để thấy đính kèm


Lỗi 75 sẽ phát sinh vì file này không được phép ghi nội dung, nói cách khác là đường dẫn file bị vô hiệu.
Để tránh lỗi này, tôi sẽ dùng hàm GetAttr truy tìm thuộc tính của file.
Mã:
Sub Sample2()
    ''Truy tim thuoc tinh cua file
    Dim Atr As Long
    Atr = GetAttr("C:\Users\VBA\Openfile.txt")
    If Atr And vbReadOnly Then
        MsgBox "File nay khong duoc phep ghi noi dung"
    Else
        Open "C:\Sample.txt" For Append As #1
            Print #1, "tuhocvba.net"
        Close #1
    End If
End Sub
Nguồn tham khảo và lược dịch:
 
Top