2013-09-27 50 views
6

Tôi nhận được cuốn sách "Phát triển Excel chuyên nghiệp" của Rob Bovey và nó đang mở mắt.Xử lý lỗi Excel VBA - đặc biệt là trong các chức năng - Phong cách phát triển Excel chuyên nghiệp

Tôi đang sửa mã của mình bằng cách xử lý lỗi. Tuy nhiên, có rất nhiều điều tôi không hiểu. Tôi đặc biệt cần phải biết cách sử dụng nó một cách chính xác trong các chức năng. Tôi sử dụng phiên bản rethrow của Bovey của trình xử lý lỗi (ở dưới cùng). Khi tôi bắt đầu, tôi đã sử dụng phương thức boolean (non-rethrow) cơ bản và chuyển các chương trình con của tôi thành các hàm boolean. (P.S. Tôi đang chuyển về phương thức boolean dựa trên câu trả lời.)

Tôi cần hướng dẫn về cách phù hợp với các chức năng trong lược đồ này. Tôi muốn họ trả lại giá trị thực của họ (một chuỗi hoặc gấp đôi, ví dụ, hoặc -1 nếu chúng thất bại trong một số trường hợp) để tôi có thể lồng chúng vào các chức năng khác và không chỉ trả về lỗi xử lý boolean.

Đây là những gì mà một cuộc gọi thường trình con của bDrawCellBorders (myWS) sẽ giống như trong một điểm vào. Các cuộc gọi phụ dường như hoạt động tốt. (Tức là nó là một chương trình con đã được biến thành một chức năng duy nhất để nó có thể trả về một boolean để chương trình xử lý lỗi.)

Sub UpdateMe() ' Entry Point 

    Const sSOURCE As String = "UpdateMe()" 

    On Error GoTo ErrorHandler 

    Set myWS = ActiveCell.Worksheet 
    Set myRange = ActiveCell 
    myWS.Unprotect 

' lots of code 

    If Not bDrawCellBorders(myWS) Then ERR.Raise glHANDLED_ERROR ' Call subroutine 

' lots of code 

ErrorExit: 
    On Error Resume Next 
    Application.EnableEvents = True 
    myWS.Protect AllowFormattingColumns:=True 
    Exit Sub 

ErrorHandler: 
    If bCentralErrorHandler(msMODULE, sSOURCE,,True) Then ' Call as Entry Point 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 
End Sub 

Tuy nhiên, tôi không biết làm thế nào để mở rộng này để chức năng thực sự. Điều này dựa trên một ví dụ trong cuốn sách được vẽ cho một chương trình con, và tôi chỉ chuyển nó sang một hàm. Câu hỏi: * Làm cách nào để gọi? Có phải đơn giản là x = sngDoSomeMath (17) * Chức năng xử lý lỗi của nó có đúng không? * Đâu là địa điểm hoặc địa điểm phù hợp để gọi quy trình xử lý lỗi với bReThrow = true?

Public Function sngDoSomeMath(ByVal iNum As Integer) As Single 

Dim sngResult As Single 

Const sSOURCE As String = "sngDoSomeMath()" 

On Error GoTo ErrorHandler 

' example 1, input did not pass validation. don't want to 
' go up the error stack but just inform the 
' calling program that they didn't get a good result from this 
' function call so they can do something else 
If iNum <> 42 Then 
    sngResult = -1 'function failed because I only like the number 42 
    GoTo ExitHere 
End If 

' example 2, true error generated 
sngResult = iNum/0 

sngDoSomeMath = lResult 

ExitHere: 
    Exit Function 
ErrorHandler: 

' Run cleanup code 
' ... here if any 

' Then do error handling 

If bCentralErrorHandler(msMODULE, sSOURCE, , , True) Then ' The true is for RETHROW 
    Stop 
    Resume 
End If 

End Function 

Các Lỗi Handler Routine:

' 
' Description: This module contains the central error 
'    handler and related constant declarations. 
' 
' Authors:  Rob Bovey, www.appspro.com 
'    Stephen Bullen, www.oaltd.co.uk 
' 
' Chapter Change Overview 
' Ch# Comment 
' -------------------------------------------------------------- 
' 15 Initial version 
' 
Option Explicit 
Option Private Module 

' ************************************************************** 
' Global Constant Declarations Follow 
' ************************************************************** 
Public Const gbDEBUG_MODE As Boolean = False ' True enables debug mode, False disables it. 
Public Const glHANDLED_ERROR As Long = 9999  ' Run-time error number for our custom errors. 
Public Const glUSER_CANCEL As Long = 18   ' The error number generated when the user cancels program execution. 


' ************************************************************** 
' Module Constant Declarations Follow 
' ************************************************************** 
Private Const msSILENT_ERROR As String = "UserCancel" ' Used by the central error handler to bail out silently on user cancel. 
Private Const msFILE_ERROR_LOG As String = "Error.log" ' The name of the file where error messages will be logged to. 


'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 
' Comments: This is the central error handling procedure for the 
'   program. It logs and displays any run-time errors 
'   that occur during program execution. 
' 
' Arguments: sModule   The module in which the error occured. 
'    sProc   The procedure in which the error occured. 
'    sFile   (Optional) For multiple-workbook 
'        projects this is the name of the 
'        workbook in which the error occured. 
'    bEntryPoint  (Optional) True if this call is 
'        being made from an entry point 
'        procedure. If so, an error message 
'        will be displayed to the user. 
' 
' Returns:  Boolean   True if the program is in debug 
'        mode, False if it is not. 
' 
' Date   Developer  Chap Action 
' -------------------------------------------------------------- 
' 03/30/08  Rob Bovey  Ch15 Initial version 
' 
Public Function bCentralErrorHandler(_ 
     ByVal sModule As String, _ 
     ByVal sProc As String, _ 
     Optional ByVal sFile As String, _ 
     Optional ByVal bEntryPoint As Boolean, _ 
     Optional ByVal bReThrow As Boolean = True) As Boolean 

    Static sErrMsg As String 

    Dim iFile As Integer 
    Dim lErrNum As Long 
    Dim sFullSource As String 
    Dim sPath As String 
    Dim sLogText As String 

    ' Grab the error info before it's cleared by 
    ' On Error Resume Next below. 
    lErrNum = ERR.Number 
    ' If this is a user cancel, set the silent error flag 
    ' message. This will cause the error to be ignored. 
    If lErrNum = glUSER_CANCEL Then sErrMsg = msSILENT_ERROR 
    ' If this is the originating error, the static error 
    ' message variable will be empty. In that case, store 
    ' the originating error message in the static variable. 
    If Len(sErrMsg) = 0 Then sErrMsg = ERR.Description 

    ' We cannot allow errors in the central error handler. 
    On Error Resume Next 

    ' Load the default filename if required. 
    If Len(sFile) = 0 Then sFile = ThisWorkbook.Name 

    ' Get the application directory. 
    sPath = ThisWorkbook.Path 
    If Right$(sPath, 1) <> "\" Then sPath = sPath & "\" 

    ' Construct the fully-qualified error source name. 
    sFullSource = "[" & sFile & "]" & sModule & "." & sProc 

    ' Create the error text to be logged. 
    sLogText = " " & sFullSource & ", Error " & _ 
       CStr(lErrNum) & ": " & sErrMsg 

    ' Open the log file, write out the error information and 
    ' close the log file. 
    iFile = FreeFile() 
    Open sPath & msFILE_ERROR_LOG For Append As #iFile 
    Print #iFile, Format$(Now(), "mm/dd/yy hh:mm:ss"); sLogText 
    If bEntryPoint Or Not bReThrow Then Print #iFile, 
    Close #iFile 

    ' Do not display or debug silent errors. 
    If sErrMsg <> msSILENT_ERROR Then 

     ' Show the error message when we reach the entry point 
     ' procedure or immediately if we are in debug mode. 
     If bEntryPoint Or gbDEBUG_MODE Then 
      Application.ScreenUpdating = True 
      MsgBox sErrMsg, vbCritical, gsAPP_NAME 
      ' Clear the static error message variable once 
      ' we've reached the entry point so that we're ready 
      ' to handle the next error. 
      sErrMsg = vbNullString 
     End If 

     ' The return vale is the debug mode status. 
     bCentralErrorHandler = gbDEBUG_MODE 

    Else 
     ' If this is a silent error, clear the static error 
     ' message variable when we reach the entry point. 
     If bEntryPoint Then sErrMsg = vbNullString 
     bCentralErrorHandler = False 
    End If 

    'If we're using re-throw error handling, 
    'this is not the entry point and we're not debugging, 
    're-raise the error, to be caught in the next procedure 
    'up the call stack. 
    'Procedures that handle their own errors can call the 
    'central error handler with bReThrow = False to log the 
    'error, but not re-raise it. 
    If bReThrow Then 
     If Not bEntryPoint And Not gbDEBUG_MODE Then 
      On Error GoTo 0 
      ERR.Raise lErrNum, sFullSource, sErrMsg 
     End If 
    Else 
     'Error is being logged and handled, 
     'so clear the static error message variable 
     sErrMsg = vbNullString 
    End If 

End Function 

Trả lời

4

Tôi cần thêm một chút trợ giúp về kỹ thuật cụ thể này vì vậy tôi đã đi ngay đến nguồn và ông Bovey đã đủ duyên dáng để trả lời. Anh ấy đã cho phép tôi đăng phản hồi của mình lên cộng đồng StackOverflow.

Hướng dẫn dưới đây đề cập đến phương pháp xử lý lỗi ưa thích của ông đối với các chức năng "kỹ thuật xử lý lỗi boolean" và không áp dụng cho phương pháp "thử lại" thay thế, cả hai được mô tả trong cuốn sách "Phát triển Excel chuyên nghiệp".


Hi Shari,

Trong câu trả lời cho câu hỏi của bạn về xử lý lỗi trong chức năng, có kịch bản xử lý ba lỗi mà bạn có thể có với một hàm trong VBA:

1) Chức năng là quá tầm thường không cần xử lý lỗi. Trong sự kiện không chắc xảy ra lỗi trong một chức năng như thế này, nó sẽ tràn qua vào trình xử lý lỗi của quy trình gọi.

2) Chức năng không tầm thường cần xử lý lỗi và sử dụng hệ thống giá trị Boolean return được mô tả trong sách. Bất kỳ giá trị nào khác mà hàm cần trả về được trả về thông qua đối số ByRef. Trường hợp này bao gồm phần lớn các hàm tôi viết. Có một số điều bạn không thể làm với các chức năng như điều này, cho chúng trực tiếp vào đối số của một hàm khác là một ví dụ , nhưng tôi xem đây là sự cân bằng tốt để đạt được việc xử lý lỗi bằng chứng bullet .

3) Chức năng không tầm thường cần xử lý lỗi và phải trả lại giá trị không liên quan đến trạng thái lỗi của nó. Đây là một tình huống hiếm hoi vì tôi có thể chuyển đổi 99% cộng trong số này thành trường hợp 2 bằng cách tái cơ cấu mã của tôi. Nếu bạn không thể thực hiện điều này, lựa chọn duy nhất của bạn là chọn một giá trị trả lại tùy ý nằm ngoài phạm vi phạm vi giá trị trả về bình thường và sử dụng giá trị này để cho biết lỗi đã xảy ra . Nếu người gọi hàm này thấy cờ lỗi tùy ý giá trị mà nó biết nó không thể tiếp tục.

Rob Bovey Application Chuyên http://www.appspro.com/


Mã Ví dụ (Shari W)


' Show how to call a function using this error handling method. 
Const giBAD_RESULT As Integer = -1 

Function TestMath() ' An Entry Point 

    Dim sngResult As Single 
    Dim iNum As Integer 

    ' Call the function, actual result goes in sngResult but it returns the error handling boolean. 
    ' A true error like Div 0 will go to error handler. 

    ' Set Up Error Handling for Entry Point 
    Application.EnableCancelKey = xlErrorHandler 
    Dim bUserCancel As Boolean 
    Const sSOURCE As String = "TestMath()" 
    On Error GoTo ErrorHandler 
    ' End Error Set Up 

    iNum = 0 ' Try 0 to create error 
    If Not bDoSomeMath(iNum, sngResult) Then ERR.Raise glHANDLED_ERROR 
    ' If function does parameter checking and wants to return a bad input code, check for that. 
    If sngResult = giBAD_RESULT Then 
     MsgBox ("Bad input to bDoSomeMath " & iNum) 
    Else 
     MsgBox ("I believe the answer is " & sngResult) 
    End If 

ErrorExit: 
    On Error Resume Next 
    Exit Function 

ErrorHandler: 
    If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 
End Function 
Function bDoSomeMath(ByVal iNum As Integer, ByRef sngResult As Single) As Boolean 

    ' Error handling Set Up 
    Dim bReturn As Boolean 
    Const sSOURCE As String = "bDoSomeMath()" 
    On Error GoTo ErrorHandler 
    bReturn = True 
    ' End Error Set Up 

    If iNum < 0 Or iNum > 1000 Then 
     sngResult = giBAD_RESULT 'function failed because I only like the numbers 0 to 1000 
     GoTo ErrorExit 
    Else 
     sngResult = 100/iNum ' generate a true error by iNum = 0 
    End If 

ErrorExit: 
    On Error Resume Next 
    bDoSomeMath = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE, , , True) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 
2

một đề nghị để xử lý quản lý trong VBA lỗi có thể được tìm thấy here.

Công cụ rất giống (MZ-Công cụ) và phương pháp (bộ xử lý lỗi chuẩn/chung, có thể được sử dụng để xây dựng hệ thống báo cáo lỗi tự động) sẽ hoạt động với Excel.

+0

MZ-Tools là một công cụ tuyệt vời. Tôi đã thay thế ErrorHandler đơn giản chuẩn của họ bằng phiên bản Rob Bovey (ở trên). Với ONE CLICK tôi có thể dán tiêu đề và xử lý lỗi vào một thói quen, ngay cả khi tôi bỏ bê để bắt đầu với họ.Nhưng tôi cần thêm thông tin cụ thể về xử lý lỗi hàng đầu cho các chức năng! Cảm ơn. –

+0

+ 1 Đề xuất tốt về Công cụ MZ :) –

+0

Cảm ơn các bạn, nhưng tôi vẫn hy vọng một người nào đó sử dụng các phương pháp được mô tả sẽ kêu vang và làm thẳng tôi ra! Các ví dụ trên trang web đó và trang web đi kèm với MZ-Tools quá cơ bản (tôi đã thay thế chúng trong các tùy chọn MZ-Tools.) –

12

Đó là một cuốn sách tuyệt vời của Rob.

tôi hai xu của Xử lý lỗi (Hoặc cho một thủ tục hay hàm) được dựa trên KISS (Giữ nó đơn giản Silly)

Hiểu những gì bạn muốn từ xử lý lỗi của bạn?

này thường là những gì tôi muốn/mong đợi từ xử lý lỗi của tôi ...

  1. dòng mà trên đó các lỗi xảy ra
  2. Error Number
  3. Thông báo lỗi
  4. Thiết lập lại sự kiện nếu có

Cho phép chia nhỏ phần trên. Như bạn bây giờ đã biết cách xử lý lỗi của bạn trông như thế nào, hãy xem xét ví dụ này.

Sub Sample() 
    Dim i As Integer, j As Integer 

    On Error GoTo Whoa 

    Application.ScreenUpdating = False 

    i = 1111111111 

    For j = 1 To i 
     Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
    Next i 

LetsContinue: 
    Exit Sub 
Whoa: 
    MsgBox Err.Description 
    Resume LetsContinue 
End Sub 

Đây là trình xử lý lỗi rất cơ bản nhưng không giúp được gì nhiều. Bây giờ chúng ta hãy tinh chỉnh nó để làm cho nó hữu ích hơn. Nếu bạn chạy mã trên, bạn sẽ nhận được một thông báo lỗi như được hiển thị trong ảnh chụp màn hình dưới đây và nếu bạn nhận thấy, nó không phải là giúp đỡ nhiều.

enter image description here

Bây giờ hãy giải quyết tất cả các điểm mà tôi đã đề cập trong Logic trên

  1. dòng mà trên đó các lỗi xảy ra

Có một tài sản được gọi ERL mà rất ít người biết. Bạn thực sự có thể sử dụng nó để lấy số dòng mã mà lỗi xảy ra. Cho rằng bạn phải đảm bảo bạn số mã của bạn. Xem ví dụ này.

Sub Sample() 
    Dim i As Integer, j As Integer 

10  On Error GoTo Whoa 

20  Application.ScreenUpdating = False 

30  i = 1111111111 

40  For j = 1 To i 
50   Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
60  Next j 

LetsContinue: 
70  Exit Sub 
Whoa: 
80  MsgBox Erl 
90  Resume LetsContinue 
End Sub 

Khi bạn chạy mã trên, bạn sẽ có được điều này

enter image description here

Vì vậy, bây giờ tôi biết rằng lỗi xảy ra trên Line 30 là i = 1111111111

Chuyển sang tiếp theo

  1. Lỗi tê liệt er
  2. Thông báo lỗi

Số lỗi và thông báo lỗi có thể được lấy từ Err.NumberErr.Description tương ứng. Vì vậy, bây giờ hãy kết hợp Erl, Err.NumberErr.Description

Kiểm tra ví dụ này

Sub Sample() 
    Dim i As Integer, j As Integer 

10  On Error GoTo Whoa 

20  Application.ScreenUpdating = False 

30  i = 1111111111 

40  For j = 1 To i 
50   Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
60  Next j 

LetsContinue: 
70  Exit Sub 
Whoa: 
80  MsgBox "The Error Happened on Line : " & Erl & vbNewLine & _ 
      "Error Message : " & Err.Description & vbNewLine & _ 
      "Error Number : " & Err.Number 
90  Resume LetsContinue 
End Sub 

Khi bạn chạy mã này, bạn sẽ nhận được một cái gì đó như thế này.

enter image description here

Bạn có thể chọn để tuỳ chỉnh thêm các thông báo lỗi để làm cho nó thân thiện với người dùng hơn. Ví dụ

'~~> Message you want to deliver to the user in case the error happens 
Const sMsg As String = "Please take a screenshot of this message and contact the developer for a resolution" 
'~~> Title of your message box 
Const sTitle As String = "Oopsie Daisies" 

'~~> Change the above as applicable 

Sub Sample() 
    Dim i As Integer, j As Integer 

10  On Error GoTo Whoa 

20  Application.ScreenUpdating = False 

30  i = 1111111111 

40  For j = 1 To i 
50   Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
60  Next j 

LetsContinue: 
70  Exit Sub 
Whoa: 
80  MsgBox "The Error Happened on Line : " & Erl & vbNewLine & _ 
      "Error Message : " & Err.Description & vbNewLine & _ 
      "Error Number : " & Err.Number & vbNewLine & vbNewLine & _ 
      sMsg, vbCritical, sTitle 
90  Resume LetsContinue 
End Sub 

enter image description here

Bật để một :)

Thiết lập lại sự kiện tiếp theo nếu có

Khi bạn đang làm việc với các sự kiện và một lỗi xảy ra, nếu có không xử lý lỗi, mã bị ngắt. Thật không may là không thiết lập lại các sự kiện. Điều rất quan trọng là bạn đặt lại các sự kiện trong trình xử lý Lỗi.

Nếu bạn nhận thấy trong mã trên, chúng tôi đang đặt Application.ScreenUpdating = False. Khi ngắt mã, sự kiện đó không được đặt lại. Bạn sẽ phải xử lý điều đó trong trình xử lý lỗi LetsContinue trong trường hợp này. Xem ví dụ này.

'~~> Message you want to deliver to the user in case the error happens 
Const sMsg As String = "Please take a screenshot of this message and contact the developer for a resolution" 
'~~> Title of your message box 
Const sTitle As String = "Oopsie Daisies" 

'~~> Change the above as applicable 

Sub Sample() 
    Dim i As Integer, j As Integer 

10  On Error GoTo Whoa 

20  Application.ScreenUpdating = False 

30  i = 1111111111 

40  For j = 1 To i 
50   Debug.Print ThisWorkbook.Sheets(1).Cells(i, 1).Value 
60  Next j 

LetsContinue: 
70  Application.ScreenUpdating = True 
80  Exit Sub 
Whoa: 
90  MsgBox "The Error Happened on Line : " & Erl & vbNewLine & _ 
      "Error Message : " & Err.Description & vbNewLine & _ 
      "Error Number : " & Err.Number & vbNewLine & vbNewLine & _ 
      sMsg, vbCritical, sTitle 
100  Resume LetsContinue 
End Sub 

Giống như Philippe, tôi cũng khuyên bạn nên sử dụng MZ-Tools cho VBA. Tôi đã sử dụng nó ngay bây giờ cho những năm lừa ...

Hy vọng điều này sẽ hữu ích.

+2

câu trả lời thú vị. Nếu tôi, nơi bạn tôi sẽ thực hiện một số đối tượng lỗi và một phương pháp error.add sẽ tự động thêm các lỗi vào một tập tin txt, một cơ sở dữ liệu, hoặc thậm chí là một thư. Điều này sẽ tránh bước 'ảnh chụp màn hình' và sẽ cho phép bạn quản lý các tác vụ gỡ lỗi của bạn như một người chuyên nghiệp! –

+0

True :) Và tôi nghĩ về tùy chọn email nhưng tôi không chắc chắn nếu người dùng có thể có quyền truy cập email trên máy tính đó. Tôi chắc chắn những gì bạn đề xuất có thể dễ dàng kết hợp nếu người dùng muốn. Đề xuất trên của tôi xoay quanh 'KISS'. Một người sử dụng có thể đưa ra đề nghị trên và mang nó đến một mức độ cao hơn nhiều :) –

+0

Siddharth - Bạn là một lực lượng được tính đến trên trang web này! Cảm ơn vì đã giúp đỡ rất nhiều. Câu trả lời của bạn là một giải pháp tuyệt vời cho một mô hình kiểm tra lỗi cơ bản và nhiều người xem xét điều này sẽ không cần phải đi xa hơn nữa. Tuy nhiên, tôi cần một cái gì đó mạnh mẽ hơn cho khách hàng doanh nghiệp của tôi đó là lý do tại sao tôi đang cố gắng để hiểu những điểm tốt của kỹ thuật xử lý lỗi của Bovey. Tôi thích ý tưởng của bản ghi lỗi được gửi qua email cho tôi. –

Các vấn đề liên quan