Vba:'出错'声明安全

时间:2017-03-29 13:12:17

标签: vba excel-vba error-handling excel

我制作了一个记录的宏,然后更改了本地设置(在我的例子中是小数点分隔符)。在宏的末尾,它将恢复设置。

如果出现错误,我会让程序使用'on error'语句恢复本地设置。 (该程序的简化示例如下)

到目前为止,我没有遇到任何问题;但是,由于我现在计划将该计划转移给我的同事,我真的希望不要干涉他们并覆盖他们自己的本地设置。

On error语句在这里是否足够安全并确保设置已恢复?

是否存在程序可能遇到On error无法重定向到错误处理程序的错误的情况?

PS:我已经知道我可以使用String = Replace(Number, ",", ".")转换我的所有数字但由于某些原因我无法承担宏的所有许多变量。

示例代码:

Private Declare Function GetUserDefaultLCID% Lib "kernel32" ()
Private Declare Function GetLocaleInfoA Lib "kernel32" (ByVal Locale As Long, ByVal LCType As Long, ByVal lpLCData As String, ByVal cchData As Long) As Long
Private Declare Function SetLocaleInfoA Lib "kernel32" (ByVal Locale As Long, ByVal LCType As Long, ByVal lpLCData As String) As Boolean

Sub test()
  ' what to do on error
  On Error GoTo ErrorHandler:
  ' define a number (Your local settings are set up to
  Dim MyNumber As Single
  MyNumber = 0.03
  MsgBox ("Number is 0,03 ->" & MyNumber)
  ' record the settings in the variable LocalSettingsDecimal
  Dim LocalSettingsDecimal As String
  Dim Buffer As String
  Buffer = String(256, 0)
  Dim le As Integer
  le = GetLocaleInfoA(GetUserDefaultLCID(), 14, Buffer, Len(Buffer))
  LocalSettingsDecimal = Left(Buffer, le - 1)

  ' force decimal settings to '.'
  Call SetLocaleInfoA(GetUserDefaultLCID(), 14, ".")

  ' Now is the program body
  MsgBox ("Number is now 0.03 ->" & MyNumber)
  ' an unfortunate error:
  MyNumber = "aa"

  Call SetLocaleInfoA(GetUserDefaultLCID(), 14, LocalSettingsDecimal)
  MsgBox ("Number should be back 0,03 ->" & MyNumber)
  Exit Sub
  ErrorHandler:
  Call SetLocaleInfoA(GetUserDefaultLCID(), 14, LocalSettingsDecimal)
  MsgBox ("There was an error but it's ok, it should be back to 0,03 ->" & MyNumber)
End Sub

2 个答案:

答案 0 :(得分:3)

VBA代码有很多种方法可以被中断,有些甚至不会涉及一个聪明的用户,它会在MsgBox上破坏并点击 Stop 按钮:如果主机崩溃的话与您的代码完全无关(一些KB更新会浮现在脑海中),在程序执行过程中,您的错误处理程序将不会被跳入,并且您无法阻止它。

请勿篡改用户的本地设置。修改你的代码。

  

我已经知道我可以使用String = Replace(Number, ",", ".")

转换我的所有号码

您的代码将数字视为字符串,反之亦然。如果数字来自工作表中的单元格,则可以将其读入Double,而无需考虑用户的小数分隔符。

Dim myNumber As Double
With ActiveSheet
    If IsNumeric(.Range("A1").Value) And Not IsError(.Range("A1").Value) Then myNumber = CDbl(.Range("A1").Value)
    'myNumber contains a valid numeric value
End With

如果数字来自您制作的UserForm上的文本框,并且您允许用户在代码要求为点时输入逗号,那么您需要修复数据输入代码并添加一些输入验证防止这种情况一种方法是处理接收用户输入的KeyPress控件的TextBox事件 - 并“吞下”任何无效的键:

Private Sub txtInput_KeyPress(ByVal keyAscii As MSForms.ReturnInteger)
    If Not IsValidKeyAscii(keyAscii, txtInput.Value) Then keyAscii = 0
End Sub

Private Function IsValidKeyAscii(ByVal keyAscii As Integer, ByVal value As String) As Boolean
'returns true if specified keyAscii is a number, or if it's a dot and value doesn't already contain one
    IsValidKeyAscii = (keyAscii = vbKeyDot And InStr(1, value, Chr$(vbKeyDot)) = 0) Or (keyAscii >= vbKey0 And keyAscii <= vbKey9)
End Function

如果数字来自InputBox,您将无法阻止用户输入他们想要的内容,但您仍然可以验证输入并防止错误输入进一步传播到您的代码中。

Private Function PromptForNumber() As Double
    Dim isValid As Boolean
    Dim userInput As String
    Do
        userInput = InputBox("Enter a decimal number:")
        isValid = IsNumeric(userInput) And InStr(userInput, ",") = 0
        If Not isValid Then MsgBox "Value '" & userInput & "' is not numeric. Please make sure to use [en-US] decimal separator."
    While Not isValid
    PromptForNumber = CDbl(userInput)
End Function

没有理由搞乱用户的控制面板设置。

答案 1 :(得分:1)

如果在代码中的任何地方遇到End命令,那么这将影响您的计划。

看一下下面的例子。如果A调用B然后没有问题,则会处理错误,当控件返回A时,处理程序仍将执行您的区域设置重置。但取消注释C的调用并再次运行代码。执行只是停止,控制永远不会返回A,因此不会执行重置。

因此,您的方案是将工作簿发送给您的同事,然后他们添加一些使用End命令的逻辑,无论出于何种原因。

Option Explicit

Sub A()
    On Error GoTo ErrHandler

    Dim foo As Long

    'call B
    B
    ' call C - uncomment to see impact
    'C

ErrHandler:
    Debug.Print "Error occurred in A"
    Debug.Print "Fixing locale stuff..."

End Sub

Sub B()
    On Error GoTo ErrHandler

    Dim foo As Long

    'cause an error
    foo = "bar"

ErrHandler:
    Debug.Print "Error occurred in B"
    Debug.Print Err.Description

End Sub

Sub C()
    On Error GoTo ErrHandler

    Dim foo As Long

    'cause an error
    foo = "bar"

ErrHandler:
    Debug.Print "Error occurred in C"
    Debug.Print Err.Description
    End

End Sub