Khi lập trình VBA trong Excel, việc phát hiện lỗi là điều không thể tránh khỏi. Điều quan trọng là biết cách xử lý và định rõ nguyên nhân. Câu chuyện dưới đây sẽ hướng dẫn bạn cách sử dụng các lệnh On Error để giải quyết vấn đề.
Làm thế nào người lập trình có thể điều khiển và xử lý lỗi VBA? Đơn giản, sử dụng các lệnh On Error để tận dụng hiệu quả và giải quyết vấn đề một cách chuyên nghiệp.
Bí quyết xử lý lỗi VBA với các lệnh On Error
Trong quá trình thực thi mã, nếu gặp lỗi, chúng ta có 2 cách tiếp cận:
- Cách thứ nhất: Bỏ qua lỗi và tiếp tục thực hiện mã.
- Cách thứ hai: Sử dụng xử lý lỗi tại chỗ để kiểm soát và thực thi khi có lỗi xảy ra.
Cả hai phương pháp này đều đảm bảo người dùng cuối không phải đối mặt với lỗi.
Hơn nữa, chúng ta có khả năng áp dụng một số lệnh On Error để đối phó với lỗi.
1. Lệnh On Error Resume Next
Khi sử dụng lệnh On Error Resume Next, lỗi sẽ bị bỏ qua và mã sẽ tiếp tục thực hiện.
Phương pháp xử lý lỗi này được sử dụng phổ biến, tuy nhiên cần cẩn trọng vì nó bỏ qua lỗi, gây khó khăn trong việc xác định và sửa lỗi.
Ví dụ, nếu chúng ta thực hiện đoạn mã dưới đây, kết quả sẽ là một thông báo lỗi:
Sub AssignValues()
x = 20 / 4
y = 30 / 0
End Sub
Lỗi xảy ra vì chia một số cho 0 không hợp lệ.
Tuy nhiên, nếu chúng ta sử dụng lệnh On Error Resume Next trong đoạn mã, nó sẽ bỏ qua lỗi mà không cung cấp thông báo, điều này có thể làm mất thông tin quan trọng về lỗi cần sửa:
Sub AssignValues()
On Error Resume Next
x = 20 / 4
y = 30 / 0
End Sub
Chú ý: Chỉ nên sử dụng lệnh On Error Resume Next khi chắc chắn về loại lỗi mà mã VBA có thể bỏ qua và có kiểm soát rõ ràng về lỗi đó.
Ví dụ, dưới đây là đoạn mã sự kiện VBA thêm các giá trị ngày và giờ vào ô A1 của sheet mới được chèn (mã này được thêm vào bảng tính chứ không phải module):
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Sh.Range('A1') = Format(Now, 'dd-mmm-yyyy hh:mm:ss')
End Sub
Mặc dù đoạn mã trên hoạt động tốt trong hầu hết các tình huống, nhưng nếu chúng ta thêm một biểu đồ thay vì một sheet, nó sẽ trả về lỗi. Điều này xảy ra vì biểu đồ không chứa các ô như bảng tính.
Nếu sử dụng lệnh On Error Resume Next trong đoạn mã trên, nó sẽ chạy mà không có bất kỳ lỗi nào xảy ra.
Private Sub Workbook_NewSheet(ByVal Sh As Object)
On Error Resume Next
Sh.Range('A1') = Format(Now, 'dd-mmm-yyyy hh:mm:ss')
End Sub
Nếu muốn, chúng ta có thể phân tích lỗi (nếu có) và hiển thị thông báo lỗi liên quan.
Dùng đoạn mã sau để hiện hộp thoại thông báo lỗi và thông báo rằng bảng tính chưa được chèn:
Private Sub Workbook_NewSheet(ByVal Sh As Object)
On Error Resume Next
Sh.Range('A1') = Format(Now, 'dd-mmm-yyyy hh:mm:ss')
If Err.Number <> 0 Then
MsgBox 'Bạn đã chèn một biểu đồ' & vbCrLf & 'Lỗi - ' & Err.Description
End If
End Sub
Trong đó, Err.Number dùng để lấy mã lỗi và Err.Description dùng để lấy mô tả lỗi.
2. Lệnh On Error GoTo 0
Lệnh On Error GoTo 0 sẽ dừng mã tại dòng gây ra lỗi và hiển thị hộp thoại thông báo mô tả lỗi.
Đơn giản, lệnh này giúp kiểm tra hành vi lỗi và hiển thị thông báo lỗi mặc định.
Thường thì chúng ta không cần sử dụng lệnh On Error Goto 0, nhưng có thể kết hợp với On Error Resume Next để có kết quả tốt hơn.
Ví dụ, đoạn mã sau sẽ chọn tất cả các ô trống trong phạm vi:
Sub ChonOTrongCongThuc()
Chon.SpecialCells(xlCellTypeBlanks).Select
End Sub
Trong trường hợp không có ô trống nào trong các ô đã chọn, có thể sẽ xuất hiện lỗi. Để tránh lỗi, chúng ta có thể sử dụng lệnh On Error Resume Next:
Sub ChonOTrongCongThuc()
On Error Resume Next
Chon.SpecialCells(xlCellTypeBlanks).Select
End Sub
Vấn đề có thể xuất hiện khi một phần của mã gặp lỗi, vì thế chúng ta dùng lệnh On Error Resume Next để bỏ qua lỗi và chuyển đến dòng tiếp theo.
Ví dụ khác, đoạn mã dưới đây không gây ra lỗi nào:
Sub ChonOTrongCongThuc()
On Error Resume Next
Chon.SpecialCells(xlCellTypeBlanks).Select
' .. thêm mã có thể chứa lỗi
End Sub
Về cơ bản, đoạn mã trên chứa 2 lỗi. Lỗi đầu tiên xuất hiện trong lệnh chọn tất cả các ô trống (sử dụng Selection.SpecialCells) và lỗi thứ 2 xuất hiện trong phần mã còn lại.
Lỗi đầu tiên có thể bị bỏ qua, nhưng lỗi sau không thể. Trong trường hợp này, chúng ta sử dụng lệnh On Error Goto 0.
Khi sử dụng lệnh này, chúng ta sẽ đặt lại cài đặt lỗi về trạng thái mặc định ban đầu, có nghĩa là lỗi sẽ được hiển thị (nếu có).
Ví dụ, đoạn mã dưới đây sẽ không có lỗi trong trường hợp không có ô trống, nhưng sẽ hiển thị thông báo lỗi với '10/0'.
Sub ChonOTrongCongThuc()
On Error Resume Next
Chon.SpecialCells(xlCellTypeBlanks).Select
On Error GoTo 0
' .. thêm mã có thể chứa lỗi
End Sub
3. Lệnh On Error Goto Nhan Danh
Cả hai lệnh On Error Resume Next và On Error Goto 0 thực tế không sửa lỗi mà chỉ cho phép bỏ qua lỗi và tiếp tục kiểm tra lỗi.
Bằng cách sử dụng lệnh On Error Go [Nhan Danh] để chỉ định hành động khi mã xảy ra lỗi.
Cấu trúc mã sử dụng lệnh này để xử lý lỗi có dạng như sau:
Sub KiemTra()
On Error GoTo NhanDanh:
X = 10 / 0 'dòng này gây lỗi
' .... mã còn lại
Exit Sub
NhanDanh:
' mã xử lý lỗi
End Sub
Chú ý: Trước nhãn Label là Exit Sub. Điều này đảm bảo nếu không có lỗi xảy ra, Sub sẽ thoát và mã Label sẽ không được thực thi. Nếu không sử dụng Exit Sub, mã Label sẽ luôn được thực thi ngay cả khi không có lỗi.
Trong đoạn mã ví dụ sau đây, khi có lỗi xảy ra, mã sẽ nhảy và thực thi phần xử lý lỗi:
Sub XuLyLoi()
On Error GoTo NhanDinh
X = 12
Y = 20 / 0
Z = 30
Exit Sub
NhanDinh:
MsgBox 'Có lỗi xảy ra' & vbCrLf & Err.Description
End Sub
Chú ý: Khi xảy ra lỗi, mã đã chạy và thực thi các dòng trước dòng gây ra lỗi. Trong ví dụ trên, giá trị của X được thiết lập là 12, nhưng do lỗi xảy ra ở dòng tiếp theo, nó không thiết lập giá trị cho Y và Z.
Khi nhảy đến phần xử lý lỗi (trong ví dụ trên là ErrMsg), mã sẽ tiếp tục thực thi tất cả các dòng trong và dưới phần xử lý lỗi, sau đó thoát khỏi Sub.
4. Lệnh On Error Goto -1
Lệnh này hơi phức tạp một chút, và thường ít được sử dụng. Dưới đây là giải thích về cách sử dụng lệnh On Error Goto -1 để xử lý lỗi VBA trong Excel.
Giả sử nếu đoạn mã của bạn đang xảy ra lỗi nhưng đã được khắc phục bằng cách sử dụng trình xử lý lỗi tại chỗ. Tuy nhiên, nếu một lỗi khác xảy ra trong trình xử lý lỗi tại chỗ.
Trong tình huống này, bạn không thể áp dụng trình xử lý lỗi thứ hai vì lỗi đầu tiên vẫn chưa được khắc phục. Do đó, khi xử lý lỗi đầu tiên, lỗi này vẫn tồn tại trong bộ nhớ VBA. Và bộ nhớ VBA chỉ có thể lưu giữ một lỗi duy nhất.
Giải pháp cho trường hợp này là sử dụng lệnh On Error Goto -1. Lệnh này sẽ xóa lỗi và giải phóng bộ nhớ VBA để xử lý lỗi tiếp theo.
Để dễ hình dung, bạn đọc có thể tham khảo một số ví dụ dưới đây:
Đoạn mã sau sẽ hiển thị thông báo lỗi vì không thể chia cho số 0:
Sub XửLýLỗi()
X = 12
Y = 20 / 0
Z = 30
End Sub
Để giải quyết vấn đề này, tôi sử dụng trình xử lý lỗi có tên là ErrMsg như sau:
Sub XửLýLỗi()
On Error GoTo ErrMsg:
X = 12
Y = 20 / 0
Z = 30
Exit Sub
ErrMsg:
MsgBox 'Có lỗi xảy ra' & vbCrLf & Err.Description
End Sub
Ngay sau khi lỗi xuất hiện, trình xử lý lỗi sẽ được kích hoạt và hiển thị thông báo lỗi như sau:
Mở rộng mã để thêm nhiều mã trong hoặc sau trình xử lý lỗi như sau:
Sub XửLýLỗi()
On Error GoTo ErrMsg:
X = 12
Y = 20 / 0
Z = 30
Exit Sub
ErrMsg:
MsgBox 'Có lỗi xảy ra' & vbCrLf & Err.Description
A = 10 / 2
B = 35 / 0
End Sub
Mặc dù lỗi đầu tiên đã được xử lý, nhưng lỗi thứ hai vẫn hiển thị trên màn hình như sau:
Dù vậy, mã vẫn tiếp tục hoạt động theo đúng cách mong muốn. Để xử lý lỗi thứ hai, chúng ta sử dụng một trình xử lý lỗi khác (ErrMsg2).
Sub XửLýLỗi()
On Error GoTo ErrMsg:
X = 12
Y = 20 / 0
Z = 30
Exit Sub
ErrMsg:
MsgBox 'Có lỗi xảy ra' & vbCrLf & Err.Description
On Error GoTo ErrMsg2:
A = 10 / 2
B = 35 / 0
Exit Sub
ErrMsg2:
MsgBox 'Có lỗi xảy ra' & vbCrLf & Err.Description
End Sub
Khi chạy đoạn mã trên, nó vẫn trả về lỗi run-time ngay cả khi chúng ta đã có trình xử lý lỗi thứ hai.
Lỗi này xuất hiện khi chúng ta xóa lỗi đầu tiên khỏi bộ nhớ VBA.
Khi VBA gặp phải lỗi mới, nó vẫn liên kết với lỗi đầu tiên, dẫn đến việc trình xử lý lỗi thứ 2 không được sử dụng. Mã dừng chạy tại dòng gây ra lỗi và hiển thị thông báo lỗi.
Để xóa bộ nhớ VBA và loại bỏ lỗi trước đó, chúng ta sử dụng lệnh On Error Goto -1.
Khi thêm lệnh này vào đoạn mã dưới đây và chạy, mã sẽ hoạt động theo mong đợi:
Sub XửLýLỗi()
On Error GoTo ErrMsg:
X = 12
Y = 20 / 0
Z = 30
Exit Sub
ErrMsg:
MsgBox 'Có lỗi xảy ra' & vbCrLf & Err.Description
On Error GoTo -1
On Error GoTo ErrMsg2:
A = 10 / 2
B = 35 / 0
Exit Sub
ErrMsg2:
MsgBox 'Có lỗi xảy ra' & vbCrLf & Err.Description
End Sub
Lưu ý: Lỗi sẽ tự động bị xóa khi chương trình con (subroutine) kết thúc. Do đó, lệnh On Error Goto -1 hữu ích khi gặp nhiều lỗi trong cùng một chương trình con.
Thông qua bài viết này, Mytour mong rằng bạn sẽ hiểu rõ và biết cách điều khiển lỗi trong VBA với các lệnh On Error. Ngoài ra, còn nhiều vấn đề về lỗi VBA khác mà chúng tôi đã chia sẻ trong bài viết Khám phá cách sửa lỗi VBA trong Excel. Hãy tìm hiểu thêm và không bỏ qua những kiến thức quan trọng này nhé.