# OnError

注册一个当发生未处理错误时, 会自动调用的函数.

    OnError Callback , AddRemove

## 参数

Callback

  类型: 函数对象

  要调用的函数.

  回调接受两个参数, 可以定义如下:

      MyCallback(Thrown, Mode) { ...

  虽然你给参数的名称并不重要, 但是下面的值会依次赋值给它们:

  1. 抛出的值, 通常为 Error 对象.
  2. 错误的模式: Return, Exit 或 ExitApp. 有关详情, 请参阅下面的表格.

  如果不需要相应的信息, 可以从回调参数列表的末尾省略一个或多个参数, 但在这种情况下, 必须指定星号作为最后一个参数, 例如 `MyCallback(Param1, *)`.

  回调可以返回以下值之一(其他值是为将来使用而保留的, 应该避免):

  - `0`, `""` 或没有 Return: 允许错误处理正常进行.
  - `1`: 抑制默认错误对话框和任何剩余的错误回调.
  - `-1`: 同上, 但如果 Mode(第二个参数) 包含单词 Return, 则允许继续执行当前线程.
AddRemove

  类型: 整数

  如果未设置或省略, 则默认为 1(在所有之前注册的回调之后调用该回调). 否则, 指定下列数字之一:

  - 1 = 在所有之前注册的回调之后调用该回调.
  - -1 = 在所有之前注册的回调之前调用该回调.
  - 0 = 不调用该回调.

## Error 模式

模式 | 描述
--- | ---
Return | 抛出的值是一个可继续的运行时错误. 如果回调返回 -1, 线程继续; 否则线程退出.
Exit | 抛出的值是一个不可继续的运行时错误或由脚本 thrown 的值. 线程将退出.
ExitApp | 抛出的值是一个严重的运行时错误, 例如由 DllCall 检测到的损坏. 程序将退出.

## 备注

Callback 只会被调用来处理通常会导致显示错误消息的错误或异常. 加载时的错误无法调用, 因为在加载脚本完毕之前无法调用 OnError.

Callback 在当前线程中被调用, 在线程退出之前(即在调用堆栈展开之前).

## 相关

Try, Catch, Throw, OnExit

## 示例

将脚本产生的错误记录到文本文件, 而不是显示给用户.

    OnError LogError
    i := Integer("cause_error")

    LogError(exception, mode) {
        FileAppend "Error on line " exception.Line ": " exception.Message "`n"
            , "errorlog.txt"
        return true
    }

使用 OnError 来实现其他错误处理方法. 注意: 当 Try 处于活动状态时, OnError 是无效的.

    AccumulateErrors()
    {
        local ea := ErrorAccumulator()
        ea.Start()
        return ea
    }

    class ErrorAccumulator
    {
        Errors := []                        ; 累计错误数组.
        _cb := AccumulateError.Bind(this.Errors)
        Start() => OnError(this._cb, -1)    ; 先注册 cb.
        Stop() => OnError(this._cb, 0)      ; 取消注册 cb.
        Last => this.Errors[-1]             ; 最近的错误.
        Count => this.Errors.Length         ; 累计错误的数目.
        __item[i] => this.Errors[i]         ; 索引.
        __delete() => this.Stop()           ; 用于与函数范围绑定.
    }

    ; 这是 OnError 回调. 'errors' 是通过 Bind() 给定的值.
    AccumulateError(errors, e, mode)
    {
        if mode != "Return" ; 不可继续.
            return
        if e.What = "" ; 表达式缺陷或类似的, 不是内置函数.
            return
        try {
            ; 尝试将错误打印到 stdout.
            FileAppend Format("{1} ({2}) : ({3}) {4}`n", e.File, e.Line, e.What, e.Message), "*"
            if HasProp(e, "extra")
                FileAppend "     Specifically: " e.Extra "`n", "*"
        }
        errors.Push(e)
        return -1 ; 继续.
    }

    RearrangeWindows()
    {
        ; 开始在 'err' 中累积错误.
        local err := AccumulateErrors()

        ; 做一些可能失败的事情...
        MonitorGetWorkArea, &left, &top, &right, &bottom
        width := (right-left)//2, height := bottom-top
        WinMove left, top, width, height, A_ScriptFullPath
        WinMove left+width, top, width, height, "AutoHotkey v2 Help"

        ; 检查是否发生任何错误.
        if err.Count
            MsgBox err.Count " error(s); last error at line #" err.Last.Line
        else
            MsgBox "No errors"

        ; 当变量超出作用域时, Stop 会被自动调用,
        ; 因为只有我们有一个对象的引用. 这将导致 OnError
        ; 被调用来解除回调.
        ; err.Stop()
    }

    ; 调用抑制和累积错误的测试函数.
    RearrangeWindows()
    ; 调用另一个函数来显示恢复正常的错误行为.
    WinMove 0, 0, 0, 0, "non-existent window"

dftg