处理VB中的自动化错误

时间:2014-12-11 18:01:48

标签: vb6 automation ui-automation

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

下面的示例代码将Adobe 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的当前层被锁定时)
  • 意外的服务器行为

EDIT#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

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

  1. 发生了什么错误?
  2. 是什么电话造成的?
  3. 它发生了什么样的客户端功能?
  4. 目标#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
    

    弱点:

    • 代码变得更大,可读性更低
    • 由于copypasting
    • ,可以指定错误原因

    优点

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

    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不满意,因为CallByName
    • 总是引发自动化错误440
    • 需要拆分PathItems.Add
    • 等表达式
    • 某些类型的自动化接口调用显着(最多3倍)处理速度下降

    优点

    • 紧凑且易于阅读的代码,没有重复的错误陈述

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

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

    可以改进给定的代码吗?

    任何想法都表示赞赏!提前谢谢!

    塞尔

2 个答案:

答案 0 :(得分:2)

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

正确的调试解决方案实际上取决于您尝试解决的问题。如果这是一个临时的错误搜索并且您正在以交互方式工作,那么简单的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://stackoverflow.com/a/3792280/58845 - 它恰好适合。)

事实上,根据您的需要,您最终可能会选择全班,以及#34; wrap"您正在使用的库类的方法。当图书馆出于某种原因有一个不方便的界面时,这种事情实际上很常见。

做的是你的解决方案#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

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

答案 1 :(得分:1)

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

您也可以从调试器开始。

windbg或ntsd(ntsd是一个控制台程序,可能已安装)。两者都来自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 list modules

     

x *!*列出所有模块中的所有功能

     

p步骤

     

!sysinfo machineid

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

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