Chương 12 - Excel VBA Programming For Dummies: On Error - Kỹ thuật xử lý lỗi

Liên hệ QC

handung107

Thành viên gắn bó
Thành viên danh dự
Tham gia
30/5/06
Bài viết
1,630
Được thích
17,436
Nghề nghiệp
Bác sĩ
Chương 12: Kỹ thuật xử lý lỗi

Bài dịch của Workman​

Khi làm việc với vba, bạn nên lường trước 2 nhóm lỗi: lỗi lập trình và lỗi run-time (tôi sẽ đề cập đến phần lỗi lạp trình, thường được gọi là bug trong chương kế tiếp). Một chương trình viết hoàn thiện sẽ xử lý lỗi như Fred Astaire nhảy vậy: rất duyên dáng. May mắn thay, VBA bao gồm khá nhiều công cụ để giúp ta xác định lỗi-và xử lý chúng một cách duyên dáng.

Các kiểu lỗi

Nếu bạn đã thử các thí dụ trong cuốn sách này, bạn chắc chắn sẽ thấy một vài thông báo lỗi. Một số lỗi là do viết VBA sai. Ví dụ: bạn có thể đánh vần sai từ khóa, hoặc gõ sai câu lệnh và lỗi cú pháp. Nếu bạn mắc những lỗi đó, bạn thậm chí không thể chạy được thủ tục cho đến khi bạn sửa chúng.

Chương này không làm việc với những kiểu lỗi đó. Thay vào đó, tôi sẽ nói về lỗi run-time - một lỗi xảy ra trong khi Excel chạy đoạn mã VBA của bạn. Đặc biệt hơn, chương này sẽ bao gồm các phần sau;

- Xác định lỗi
- Làm vài việc về những lỗi xảy ra
- Phục hồi từ các lỗi
- Thiết lập các lỗi cố ý (Đúng thê, đôi khi lỗi lại là một điều tốt).

Mục tiêu cuối cùng của việc xử lý lỗi là để viết mã trong đó tránh việc hiển thị lỗi Excel càng nhiều càng tốt. nói cách khác, bạn muốn đặt mình vào những lỗi có thể xảy ra trước khi Excel có cơ hội gầm gừ những tiêu đề xấu xí với những câu báo lỗi mà chẳng ai hiểu gì cả (thường là thế).

Một ví dụ về lỗi

Để bắt đầu, tôi lập một Macro VBA ngắn. Bạn hãy vào VBE, chọn insert module, sau đó gõ vào đoạn mã sau:

Sub TinhCanBacHai()

Dim Num as Double
‘ Thông báo lấy một số
Num=Inputbox(“Hay go mot gia tri”)
‘ Tinh can bac hai
Activecell.Value=SQR(Num)
End sub

Đây là thủ tục tính căn bậc hai

Như thể hiện trong hình 12-1, thủ tục này sẽ hỏi người dùng một giá trị. Sau đó nó sẽ nhập căn bậc hai của giá trị trên vào ô hiện tại.
Bạn có thể chạy thủ tục này trực tiếp từ VBE bằng cách nhấn phím F5. Thay vào đó, bạn cũng có thể làm một nút lệnh trong bảng tính (hãy dùng Form Toolbar để làm, và sau đó gán Macro cho nút lệnh này. (Excel sẽ hỏi bạn gán macro nào). Sau đó bạn có thể chạy thủ tục bằng cách đơn giản là nhấn vào nút lệnh trên.
 
MACRO THựC RA KHÔNG HOÀN HảO

Bạn làm thử đoạn mã trên xem sao. Nó chạy khá tốt đấy chứ? Bây giờ bạn thử nhập một số âm khi được yêu cầu nhập một giá trị. Ơ kìa! Tính căn bậc hai của một số âm là điều không thể trên thế gian này. Excel sẽ báo rằng “Run time error ‘5’ – “Invalid procedure call or aguement”, báo rằng bạn bị lỗi run-time error. Bây giờ bạn hãy click vào nút End. Hoặc bấm vào nút Debug; Excel sẽ tạm dừng macro để bạn sử dụng được công cụ debug. (tôi sẽ trình bày cái công cụ debug trong chương 13).

Phần lớn trong chúng ta không thấy những câu báo lỗi trong Excel hữu ích cho lắm (ví dụ Invalid procedure call or aguement). Để hoàn thiện thủ tục, bạn cần phải tham gia vào việc xử lý lỗi một cách uyển chuyển hơn.

Đây là một đoạn mã đã được chỉnh sửa

Sub TinhCanBacHai2()

Dim Num as Double
‘ Thông báo lấy một số
Num=Inputbox(“Hay go mot gia tri”)

‘đảm bảo số là không âm
If Num<0 then
Msgbox “Ban phai nhap so duong”
Exit sub
End if

‘ Tinh can bac hai
Activecell.Value=SQR(Num)
End sub

một cấu trúc If-then sẽ kiểm tra giá trị của biến Num. Nếu biến Num nhỏ hơn 0, thủ tục sẽ hiện lên bảng thông báo cung cấp những thông tin mà người bình thường hiểu được. Thủ tục sẽ chấm dứt với lệnh “Exit sub”, do đó lỗi không thể nào xảy ra
 
MACRO VẫN KHÔNG HOÀN HảO

Chà, đoạn mã được chỉnh sửa hoạt động hoàn hảo quá phải không? Không hẳn như vậy. Hãy thử nhập vào một dữ liệu dạng text xem, hoặc bấm vào nút “Cancel” trong ô “Input Box”. Trong cả 2 trường hợp thủ tục đều báo lỗi (Type mismatch)

Đoạn mã chỉnh sửa sau sẽ sử dụng hàm IsNumeric để đảm bảo rằng Num chủ có thể là giá trị số. Nếu người sử dụng không nhập số, thủ tục sẽ báo lỗi và dừng lại. Hơn nữa, lưu ý rằng biến Num được định nghĩa dưới dạng “Biến”. Nếu ta định nghĩa Num là “Double” thủ tục sẽ báo lỗi nếu ta gõ một dữ liệu không phải dạng số vào ô “Input Box”.

Sub TinhCanBacHai3()

Dim Num as Variant
‘ Thông báo lấy một số
Num=Inputbox(“Hay go mot gia tri”)

‘đảm bảo Num là số
If IsNumeric (Num) then
Msgbox “Ban phai nhap mot so”
Exit sub
End if

‘đảm bảo số là không âm
If Num<0 then
Msgbox “Ban phai nhap so duong”
Exit sub
End if

‘ Tinh can bac hai
Activecell.Value=SQR(Num)
End sub

Macro đã hoàn hảo chưa?

Bây giờ thì đoạn mã đã vô cùng hoàn hảo! Chưa chắc. Bạn hãy thử chạy thủ tục trong một sheet là một đồ thì. Như thể hiện trong hình 12-3, Excel sẽ đưa ra một thông báo mù mờ như những thông báo mà bạn đã từng thấy. lỗi này xảy ra vì không có ô hiện hành trong một sheet đồ thị.

Đoạn mã sau sử dụng hàm TypeName để đảm bảo bạn đang ở một vùng. Nếu bạn không ở một vùng, thủ tục sẽ thông báo và thoát.

Sub TinhCanBacHai4()

Dim Num as Variant

‘đảm bảo là worksheet đang hoạt động
If TypeName (Selection) <> “Range” then
Msgbox “Ban hay chon mot vung”
Exit sub
End if

‘ Thông báo lấy một số
Num=Inputbox(“Hay go mot gia tri”)

‘đảm bảo Num là số
If IsNumeric (Num) then
Msgbox “Ban phai nhap mot so”
Exit sub
End if

‘đảm bảo số là không âm
If Num<0 then
Msgbox “Ban phai nhap so duong”
Exit sub
End if

‘ Tinh can bac hai
Activecell.Value=SQR(Num)
End sub

Hoàn hảo ơi! Chào thua

Bây giờ thủ tục của chúng ta chắc chắn phải hoàn hảo rồi. Nghĩ lại đi, đồng chí. Worksheet bị protect (bạn sử dụng Tool -> Protection -> Protectsheet) và sau đó chạy thử đoạn mã trên. Hừ hừ, Worksheet bị protect lại “sản xuất” ra một lỗi khác. Và tôi thú thực tôi không đủ sức nghĩ đến tất cả các lỗi có thể xảy ra. Hãy đọc tiếp để xem một cách khác xử lý lỗi – ngay cả những lỗi bạn không thể lường trước.
 
Một cách khác để xử lý lỗi

Làm thế nào bạn có thể xử lý tất cả các lỗi có thể xảy ra? Câu trả lời thường là bạn không thể. May mắn thay, VBA cung cấp cho chúng ta một phương pháp khác để xử lý lỗi.

Hãy quay trở lại với thủ tục TinhCanBacHai

Hãy xem đoạn mã sau. Tôi đã sửa lại đoạn mã ở phần trên bằng cách thêm vào một câu lệnh “On Error” để “bẫy” mọi lỗi và sau đó kiểm tra xem InputBox có bị Cancel không.

Sub TinhCanBacHai5()

Dim Num as Variant
Dim Msg as String

‘thiết lập xử lý lỗi
On Error GoTo BadEntry

‘ Thông báo lấy một số
Num=Inputbox(“Hay go mot gia tri”)

‘Thoát nếu nhấn “Cancel”
If Num = “” then Exit sub

‘ Tinh can bac hai
Activecell.Value=SQR(Num)
Exit Sub

BadEntry:

Msg = “Phat hien loi” & vbNewLine
Msg = Msg & “Hay chon mot vung “
Msg = Msg & “va ban phai nhap so khong am”
Msgbox Msg

End sub

Chú ý: On Error không chạy?
Nếu câu lệnh On Error không chạy, bạn nên thay đổi phần điều khiển
1. Kích hoạt VBE
2. Chọn Tool->Options command
3. Click vào thẻ General
4. Bạn cần xem mục “Break On All Errors”, đảm bảo không đánh dấu phần này.

Nếu phần này được đánh dấu, Excel sẽ bỏ qua tất cả các câu lệnh On Error.


Đoạn mã này bẫy tất cả các loại run-time error. Sau khi bẫy được một run-time error, thủ tục TinhCanBacHai mới sẽ hiện ra một hình thông báo như hình 12-4

Phat hien loi
Hay chon mot vung va ban phai nhap so khong am
 
Nói về câu lệnh On Error

Sử dụng câu lệnh On Error trong VBA làm cho Excel bỏ qua những thủ tục xử lý lỗi có sẵn và sử dụng mã xử lý lỗi riêng của bạn. trong phần trước, một run-time error sẽ buộc macro nhảy ngay đến đoạn nhãn BadEntry. Kết quả là, bạn có thể tránh được những câu thông báo lệnh cực ký thiếu thân thiện của Excel và bạn có thể đưa ra những câu thông báo lỗi của riêng mình.

Lưu ý rằng trong ví dụ nêu trên chúng ta sử dụng câu lệnh Exit Sub ngay trước nhãn BadEntry. Câu lệnh này là cần thiết vì bạn không muốn kích hoạt đoạn mã xử lý lỗi nếu lỗi không xảy ra.

Xử lý lỗi : Chi tiết

Bạn có thể sử dụng câu lệnh On Error bằng 3 cách, như trình bày trong bảng 12-1

Chuong12.jpg


Quay lại sau một lỗi

Trong một vài trường hợp, bạn đơn giản muốn thủ tục của mình chấm dứt một cách êm ái khi một lỗi phát sinh. Ví dụ, bạn có thể làm cho xuất hiện một thông báo mô tả lỗi và sau đó thoát khỏi thủ tục. thủ tục TinhCanBacHai5 là ví dụ về việc sử dụng kỹ thuật này). Trong các trường hợp khác, bạn muốn phục hồi nếu có lỗi (nếu được)

Để phục hồi, bạn phải sử dụng câu lệnh Resume. Câu lệnh này xóa điều kiện phát sinh lỗi và cho phép bạn tiếp tục chạy ở một vị trí khác. Bạn có thể sử dụng câu lệnh Resume bằng 3 cách

Chuong12a.jpg


Ví dụ sau sử dụng câu lệnh Resume sau khi có lỗi phát sinh:

Sub TinhCanBacHai6()

Dim Num as Variant
Dim Msg as String
Dim Ans as Integer

TryAgain:
‘thiết lập xử lý lỗi
On Error GoTo BadEntry

‘ Thông báo lấy một số
Num=Inputbox(“Hay go mot gia tri”)

‘Thoát nếu nhấn “Cancel”
If Num = “” then Exit sub

‘ Tinh can bac hai
Activecell.Value=SQR(Num)
Exit Sub

BadEntry:

Msg = “Phat hien loi. Ban co muon lam lai khong?” & vbNewLine
Ans = Msgbox (Msg,vbYesNo)
If Ans = vbYes then Resume TryAgain

End sub

Thủ tục này có thêm một nhãn mới: TryAgain. Nếu một lỗi phát sinh, thủ tục sẽ tiếp tục kích hoạt nhãn BadEntry và thủ tục sẽ hiển thị một thông báo như hình 12-5. nếu người sử dụng click vào nút Yes, thì câu lệnh Resume sẽ được kích hoạt và nhảy ngược về nhãn TryAgain. Nếu người dùng click No là hết chuyện, thủ tục dừng lại.

Nên nhớ rằng câu lệnh Resume xóa mọi điều kiện phát sinh lỗi trước khi tiếp tục. Cụ thể hơn, thử thay câu lệnh áp chót như sau:

If Ans = vbYes then GoTo TryAgain

Đoạn mã này không hoạt động nếu bạn sử dụng GoTo thay vì Resume. Để minh họa, bạn hãy gõ vào một số âm. Bạn sẽ thấy báo lỗi. Click Yes để thử lại lần nữa và sau đó lại nhập một số âm nữa. lỗi số 2 sẽ không được bẫy vì các điều kiện của lỗi đầu tiên chưa được xóa.
 
Gói gọn xử lý lỗi

Để bạn nắm rõ cách thức xử lý lỗi, tôi làm một bản tóm tắt ngắn gọn và … khó nhớ. một thủ tục xử lý lỗi có các đặc điểm sau:

• Được thực hiện ngay sau nhãn được xác định trong câu lệnh On Error
• Chỉ nên được kích hoạt khi macro của bạn phát sinh lỗi. điều này có nghĩa là bạn phải sử dụng một câu lệnh như Exit Sub hoặc Exit Function ngay lập tức trước khi đến nhãn xử lý lỗi.
• Có thể bạn cần câu lệnh Resume. Nếu bạn không muốn chấm dứt thủ tục khi phát sinh lỗi, bạn phải kích hoạt câu lệnh Resume trước khi quay về thủ tục chính.

Biết khi nào thì nên bỏ qua lỗi

Trong một vài trường hợp, chúng ta hoàn toàn có thể bỏ qua lỗi. khi đó ta sử dụng câu lệnh Resume Next.

Ví dụ sau chạy qua tất cả các ô trong một vùng được chọn và khai căn bậc hai tất cả các giá trị. Thủ tục này sẽ báo lỗi nếu gặp bất kỳ một ô nào chứa số âm.

Sub SelelectionSqrt()
Dim cell As Range
If TypeName(Selection) <> "Range" Then Exit Sub
For Each cell In Selection
cell.Value = Sqr(cell.Value)
Next cell
End Sub

Trong trường hợp này, bạn có thể muốn đơn giản là bỏ qua tất cả những ô chứa giá trị mà bạn không thể khai căn. Bạn có thể liệt kê tất cả những lỗi phát sinh bằng cách sử dụng If-Then, nhưng bạn có thể đưa ra một phương pháp tốt hơn bằng cách đơn giản là bỏ qua những lỗi này.

Đoạn mã sau thể hiện điều này thông qua việc sử dụng On Error Resume Next:

Sub SelelectionSqrt()
Dim cell As Range
If TypeName(Selection) <> "Range" Then Exit Sub
On Error Resume Next
For Each cell In Selection
cell.Value = Sqr(cell.Value)
Next cell
End Sub

Một cách tổng quát, bạn có thể sử dụng On Error Resume Next nếu bạn thấy rằng lỗi không ảnh hưởng đến mục tiêu của bạn.

Xác định lỗi cụ thể

Không phải mọi lỗi đều như nhau. Có những loại lỗi nghiêm trọng, có những loại ít nghiêm trọng hơn. Mặc dù bạn có thể bỏ qua lỗi mà bạn cho là không quan trọng, bạn vẫn phải xử lý những lỗi còn lại (nếu thằng lỗi nghiêm trọng ấy). trong một vài trường hợp bạn cần xác định những lỗi cụ thế phát sinh.

Khi một lỗi phát sinh, Excel lưu mã số lỗi vào một Object lỗi gọi là Err. Thuộc tính Number của Object này chứa mã số lỗi. bạn có thể tìm được diễn giải của lỗi này thông qua hàm VBA Error. Ví dụ, câu lệnh sau hiển thị mã lỗi và diễn giải:

MsgBox Err.Number & ": " & Error(Err.Number)

Hình 12-6 thể hiện ví dụ trên. Nên nhớ rằng thông báo lỗi của Excel không hữu ích gì cho lắm.

Thủ tục sau minh họa làm thế nào để xác định lỗi nào phát sinh. Trong trường hợp này, bạn có thể yên tâm bỏ qua những lỗi do gặp phải khi cố khai căn số âm (error 5), hoặc những lỗi gặp phải do khai căn các ký tự (error 13). Nói cách khác, bạn cần phải thông báo cho người dùng biết nếu Worksheet bị protect và vùng được lựa chọn có những ô bị khóa. (Nếu không người dùng có thể nghĩ là macro đã làm xong việc trong khi thực tế nó không làm). Sự kiện này gây ra lỗi 1004

Sub SelelectionSqrt()
Dim cell As Range
Dim ErrMsg As String
If TypeName(Selection) <> "Range" Then Exit Sub
On Error GoTo ErrorHandler
For Each cell In Selection
cell.Value = Sqr(cell.Value)
Next cell
Exit Sub

ErrorHandler:
Select Case Err
Case 5 'So Am
Resume Next
Case 13 'Sai loai. Vi du nhu ky tu chu
Resume Next
case1004 'O bi khoa, sheet bi protect
MsgBox "O bi khoa roi ban oi. Thu lai lan nua di"
Exit Sub
Case Else
ErrMsg = Error(Err.Number)
MsgBox "ERROR: " & ErrMsg
Exit Sub
End Select
End Sub

Khi lỗi run-time error phát sinh, thủ tục sẽ nhảy đến nhãn ErrorHandler. Cấu trúc Select Case duyệt qua 3 lỗi thông dụng. nếu lỗi là 5 hoặc 13, thủ tục sẽ quay lại xử lý ô kế tiếp (nói cách khác, lỗi được bỏ qua). Nếu lỗi là 1004, thủ tục sẽ thông báo cho người dùng và ngưng lại. trường hợp cuối cùng, xử lý mọi trường hợp lỗi còn lại, thủ tục sẽ bẫy mọi lỗi và hiển thị thông báo lỗi cụ thể.

Lỗi cố ý

Đôi khi bạn sử dụng lỗi để làm những việc mình muốn. ví dụ bạn có một macro chỉ chạy được khi có một Workbook nào đó đang mở. làm sao bạn biết được Workbook đó đã được mở chưa? Có lẽ giải pháp tốt nhất là viết một hàm tổng quát nhận một tham số (là tên Workbook) và trả lại giá trị True nếu Workbook ấy đang mở, false nếu Workbook ấy đang đóng.

Đây là hàm nói trên :

Function WorkBookOpen(book As String) As Boolean
Dim WBName As String
On Error GoTo notopen
WBName = Workbooks(book).Name
WorkBookOpen = True
Exit Function

notopen:
WorkBookOpen = False

End Function

Hàm này lợi dùng đặc điểm là Excel sẽ báo lỗi nếu bạn tham chiếu đến một Workbook chưa được mở. ví dụ câu lệnh dưới đây sẽ báo lỗi nếu Workbook tên là “MyWorkbook.xls” không được mở:

WBName = Workbooks(MyWorkbook.xls).Name

Trong hàm WorkBookOpen, câu lệnh On Error yêu cầu VBA nhảy đến notopen nếu có lỗi phát sinh. Do đó, cứ có lỗi thì có nghĩa là Workbook chưa được mở, và hàm sẽ trả lại giá trị False. Nếu Workbook đang mở, không có lỗi và hàm này sẽ trả lại giá trị True.

Đây lại là một biến thể khác của hàm WorkBookOpen. PHiên bản này sử dụng On Error Resume Next để bỏ qua những lỗi phát sinh. Nhưng đoạn mã lại kiểm tra thuộc tính của mã Err. Nếu thuộc tính Err.Number là 0 tức là không có lỗi và Workbook đang được mở. nếu Err.Number là bất cứ số nào khác, có nghĩa là lỗi đã phát sinh (và Workbook hiện không ở trạng thái mở)

Function WorkBookOpen(book As String) As Boolean

Dim WBName As String
On Error Resume Next
WBName = Workbooks(book).Name
If Err.Number = 0 Then WorkBookOpen = True_
Else: WorkBookOpen = False

End Function

Ví dụ dưới đây minh họa cách sử dụng hàm này trong một thủ tục:

Sub macro1()
If not workbookopen(“Prices.xls”) then
Msgbox “Hay mo Workbook Price”
Exit sub
End if

‘ Các đoạn mã khác ….

End sub

Macro1 (phải nằm trong cùng một project với WorkbookOpen) gọi hàm WorkbookOpen và lấy tên của Workbook (Price.xls) chuyển làm tham số cho hàm. Hàm WorkbookOpen sẽ trả lại hai giá trị True hoặc False. Do đó, nếu Workbook chưa được mở, thủ tục sẽ báo lại người dùng biết. nếu Workbook đang mở, macro sẽ tiếp tục chạy.

Xử lý lỗi là một vấn đề hắc búa, có rất nhiều lỗi phát sinh mà bạn không thể lường trước. nhìn chung, bạn nên bẫy lỗi và xử lý các tình huống trước khi Excel xen vào (nếu có thể được). viết những đoạn mã bẫy lỗi yêu cầu cần phải có một nội công thâm hậu về Excel và một vốn hiểu biết rõ ràng về việc VBA xử lý lỗi như thế nào. Chương kế tiếp sẽ bao gồm những ví dụ về việc xử lý lỗi.
 
Web KT
Back
Top Bottom