VB中自动化错误的处理

Posted

技术标签:

【中文标题】VB中自动化错误的处理【英文标题】:Handling of Automation errors in VB 【发布时间】:2014-12-11 18:01:48 【问题描述】:

编辑#1 我正在开发一个 VB6 EXE 应用程序,旨在将一些特殊图形输出到 Adob​​e Illustrator。

下面的示例代码在 Adob​​e Illustrator 中将给定的图形绘制为虚线。

' Proconditions:
' ai_Doc As Illustrator.Document is an open AI document
' Point_Array represented as "array of array (0 to 1)" contains point coordinates
'
Private Sub Draw_AI_Path0(ByRef Point_Array As Variant)
Dim New_Path As Illustrator.PathItem
    Set New_Path = ai_Doc.PathItems.Add
    New_Path.SetEntirePath Point_Array
    New_Path.Stroked = True
    New_Path.StrokeDashes = Array(2, 1)
End Sub

然而,这个简单的代码可能会引发各种运行时自动化错误,原因如下:

不正确的客户端代码(例如,将 Array 以外的值分配给 New_Path.StrokeDashes) 客户端数据不正确(例如,将太大的Point_Array 传递给New_Path.SetEntirePath) 某些服务器功能不可用(例如当 AI 的当前层被锁定时) 意外的服务器行为

编辑#2

不幸的是,由于此类错误是由服务器应用程序(在我们的例子中是 AI)引发的,因此它们的描述通常不充分、糟糕且具有误导性。错误情况可能取决于 AI 版本、安装的应用程序、系统资源等。单个问题可能导致不同的错误。将太大的 Point_Array 传递给 New_Path.SetEntirePath 的示例(Windows XP SP3、Adobe Illustrator CS3):

对于 32767 及以上的数组大小,错误为 -2147024809 (&H80070057) "Illegal Argument" 对于 32000 到 32766 的数组大小,错误为 -2147212801 (&H800421FF)“无法在路径中插入更多段。8191 为最大值”

编辑结束#2

传统的错误处理可用于防止客户端崩溃并显示错误详情,如下所示:

Private Sub Draw_AI_Path1(ByRef Point_Array As Variant)
Dim New_Path As Illustrator.PathItem
On Error GoTo PROCESS_ERROR
    Set New_Path = ai_Doc.PathItems.Add
    New_Path.SetEntirePath Point_Array
    New_Path.Stroked = True
    New_Path.StrokeDashes = Array(2, 1)
    Exit Sub
PROCESS_ERROR:
    MsgBox "Failed somewhere in Draw_AI_Path1 (" & Format(Err.Number) & ")" _
        & vbCrLf & Err.Description
End Sub

如您所见,可以轻松访问错误编号和错误描述。但是,我还需要知道是什么调用导致了错误。这对于包含许多对自动化接口的调用的大型复杂过程非常有用。所以,我需要知道:

    发生了什么错误? 是什么电话引起的? 发生在哪个客户端函数中?

目标#3 可以通过here 描述的技术来实现。所以,让我们关注目标 1 和 2。目前,我可以看到两种方法来检测失败的调用:

1) 通过硬编码描述来“检测”对自动化接口的每次调用:

Private Sub Draw_AI_Path2(ByRef Point_Array As Variant)
Dim New_Path As Illustrator.PathItem
Dim Proc As String
On Error GoTo PROCESS_ERROR
    Proc = "PathItems.Add"
    Set New_Path = ai_Doc.PathItems.Add
    Proc = "SetEntirePath"
    New_Path.SetEntirePath Point_Array
    Proc = "Stroked"
    New_Path.Stroked = True
    Proc = "StrokeDashes"
    New_Path.StrokeDashes = Array(2, 1)
    Exit Sub
PROCESS_ERROR:
    MsgBox "Failed " & Proc & " in Draw_AI_Path2 (" & Format(Err.Number) & ")" _
        & vbCrLf & Err.Description
End Sub

弱点:

代码变大,可读性变差 可能由于复制粘贴而指定了不正确的原因

强项

两个目标都满足 最小的处理速度影响

2) 通过设计一个调用任何自动化接口调用的函数来“检测”所有调用:

Private Function Invoke( _
    ByRef Obj As Object, ByVal Proc As String, ByVal CallType As VbCallType, _
    ByVal Needs_Object_Return As Boolean, Optional ByRef Arg As Variant) _
    As Variant
On Error GoTo PROCESS_ERROR
    If (Needs_Object_Return) Then
        If (Not IsMissing(Arg)) Then
            Set Invoke = CallByName(Obj, Proc, CallType, Arg)
        Else
            Set Invoke = CallByName(Obj, Proc, CallType)
        End If
    Else
        If (Not IsMissing(Arg)) Then
            Invoke = CallByName(Obj, Proc, CallType, Arg)
        Else
            Invoke = CallByName(Obj, Proc, CallType)
        End If
    End If
    Exit Function
PROCESS_ERROR:
    MsgBox "Failed " & Proc & " in Draw_AI_Path3 (" & Format(Err.Number) & ")" _
        & vbCrLf & Err.Description
    If (Needs_Object_Return) Then
        Set Invoke = Nothing
    Else
        Invoke = Empty
    End If
End Function


Private Sub Draw_AI_Path3(ByRef Point_Array As Variant)
Dim Path_Items As Illustrator.PathItems
Dim New_Path As Illustrator.PathItem
    Set Path_Items = Invoke(ai_Doc, "PathItems", VbGet, True)
    Set New_Path = Invoke(Path_Items, "Add", VbMethod, True)
    Call Invoke(New_Path, "SetEntirePath", VbMethod, False, Point_Array)
    Call Invoke(New_Path, "Stroked", VbSet, False, True)
    Call Invoke(New_Path, "StrokeDashes", VbSet, False, Array(2, 1))
End Sub

弱点:

目标 #1 不满足,因为自动化错误 440 总是由 CallByName 引发 需要拆分像PathItems.Add这样的表达式 某些类型的自动化接口调用的处理速度显着下降(最多 3 倍)

强项

简洁易读的代码,没有重复的错误语句

还有其他处理自动化错误的方法吗?

是否有针对 2) 的弱点 #1 的解决方法?

给定的代码可以改进吗?

任何想法都值得赞赏!提前致谢!

谢尔盖

【问题讨论】:

您当然不想拥有大量样板代码,或者将您的代码折磨成可怕的东西,只是为了进行错误处理。你想要的是一个标准的错误处理程序,something like this——注意处理意外错误的部分。要找出导致错误的原因,请使用Double Resume trick。您只需要在开发期间真正需要这种级别的可追溯性,而不是在稳定版本的正常使用期间。 @Jean-François,谢谢!分析错误代码是完美的,我会这样做。但是,这还不够 - 请参阅我的 EDIT#2 对,这就是为什么我建议上面的Double Resume trick。这不适合你吗? 双重简历适用于调试。但是,编译的应用程序运行时我需要错误信息 【参考方案1】:

想想为什么你可能想知道错误是从哪里引发的。一个原因是为了简单的调试目的。另一个更重要的原因是您希望在发生特定错误时执行特定的操作以处理

正确的调试解决方案实际上取决于您要解决的问题。如果这是一个临时的错误搜索并且您正在交互工作,那么简单的Debug.Print 语句可能就是您所需要的。如果您只有几个需要细粒度错误识别的例程,并且您可以容忍弹出消息框,则您的解决方案 #1 很好。但是,就像您说的那样,这有点乏味且容易出错,因此将其作为样板文件或某种“标准做法”是一个坏主意。

但这里真正的危险信号是您的声明,即您有“包含许多对自动化接口的调用的大型复杂过程”,以及需要以精细的方式处理或至少跟踪错误。解决方案始终如一 - 将您的大型复杂程序分解为一组更简单的程序!

例如,您可能有一个例程执行以下操作:

Sub SetEntirePath(New_Path As Illustrator.PathItem, ByRef Point_Array As Variant)
    On Error Goto EH

    New_Path.SetEntirePath Point_Array

    Exit Sub

EH:

    'whatever you need to deal with "set entire path" errors
End Sub

您基本上将大型过程中的逐行错误处理提取到更小、更集中的例程中,然后调用它们。而且您可以免费“追踪”您的错误。 (如果你有某种系统的追踪系统,比如我在这里描述的那个 - https://***.com/a/3792280/58845 - 它正好适合。)

事实上,根据您的需要,您最终可能会使用整个类来“包装”您正在使用的库类的方法。当一个库由于某种原因而有一个不方便的界面时,这种事情实际上很常见。

我会做的是你的解决方案#2。这基本上是为了找出错误发生的位置而扭曲了整个程序。我保证“通用”Invoke 稍后会给您带来问题。使用以下内容会更好:

Private Sub Draw_AI_Path4(ByRef Point_Array As Variant)

    ...

    path_wrapper.SetEntirePath Point_Array
    path_wrapper.Stroked = True
    path_wrapper.StrokeDashes = Array(2, 1)

    ...

End Sub

我可能不会为了调试目的而使用包装类。同样,任何包装器(如果您使用包装器)的目的是解决库接口的一些问题。但是包装器使调试更容易。

【讨论】:

感谢您的完美回答,约翰!我之前想过这样的事情,但我害怕大量的编程和性能问题。但是,当我最近尝试为 PathItem 实现包装类时,我发现处理速度没有显着下降。包装类可以很好地本地化错误处理。它还有助于及早发现this 等陷阱。所有这些都会提高代码的安全性。再次感谢! PS,很抱歉没有给你投票。只要我有足够的声望,我就会去做!【参考方案2】:

可以在 VB6 调试器中运行它。如果在没有优化的情况下编译(如果优化,您将无法识别您的代码),您还可以从 WinDbg 或 WER 获取堆栈跟踪(使用 GFlags 进行设置)。 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug 是存储设置的位置。

您也可以在调试器中启动。

windbg 或 ntsd(ntsd 是一个控制台程序,可能已安装)。两者都来自 Debugging Tools For Windows。

下载并安装 Windows 调试工具

http://msdn.microsoft.com/en-us/windows/hardware/hh852363

安装 Windows SDK,但只需选择调试工具。

在 C:\ 中创建一个名为 Symbols 的文件夹

启动 Windbg。文件菜单-符号文件路径并输入

srv*C:\symbols*http://msdl.microsoft.com/download/symbols

然后

windbg -o -g -G c:\windows\system32\cmd.exe /k batfile.bat

您可以按 F12 停止它,kb 将显示调用堆栈(g 继续程序)。如果有错误,它也会停止并显示它们。

输入 lm 列出加载的模块,输入 x *!* 列出符号,输入 bp symbolname 设置断点

da 显示在该地址找到的 ascii 数据

dda显示指针的值

kv 10 显示最后 10 个堆栈帧

lm 列出模块

x *!* 列出所有模块中的所有函数

p 步骤

!sysinfo 机器ID

如果在 VB6 中编程,则此环境变量 link=/pdb:none 将符号存储在 dll 中,而不是单独的文件中。确保在没有优化的情况下编译程序并勾选 Create Symbolic Debug Info 框。两者都在项目属性的编译选项卡上。

CoClassSyms (microsoft.com/msj/0399/hood/hood0399.aspx) 也可以从类型库中生成符号。

【讨论】:

以上是关于VB中自动化错误的处理的主要内容,如果未能解决你的问题,请参考以下文章

VB6 程序无法打开 Excel 2007,未注册自动化错误库

vb6上的错误“运行时错误-2147319779自动化错误对象库未注册”

VB 宏+mysql解决EXCEL表格实现自动化处理

vb2010 (vb.net )Socket套接字当网络中断时或服务器意外关机,客户端如何自动重新连接服务端

如何在VB中编写自动下载代码?

请问vb如何自动注册ocx文件?