XML 简单接口 (SAX2)用Visual Basic 实现的示例[2]

[入库:2005年8月18日] [更新:2007年3月25日]

本文简介:选择自 coolstar 的 blog

  • 观察 immediate 窗口。
  • option explicit
    
    private declare sub copymemory lib "kernel32" alias "rtlmovememory" (pdest
      as any, psource as any, byval bytelen as long)
    
    implements xmlsax.isaxcontenthandler
    
    private sub command1_click()
    
    call parse
    
    end sub
    
    private function parse() as boolean
    
    dim i() as integer
    dim str as string
    
    dim sax as saxxmlreader30
    
    set sax = new saxxmlreader30
    
    call sax.putcontenthandler(me)
    
    str = app.path & "\" & "test.xml"
    
    ' 调整数组大小为字符的数目
    redim i(len(str))
    
    ' 复制内存,注意其大小是字符数的两倍,
    ' 因为它是 unicode(双字节)字符串 
    copymemory i(0), byval strptr(str), len(str) * 2
    
    ' 忽略下面中的第一个矩阵项
    sax.parseurl i(0), len(str)
    
    end function
    
    private sub isaxcontenthandler_characters(pwchchars as integer, byval
      cchchars as long)
    
    '什么也不做
    
    end sub
    
    private sub isaxcontenthandler_enddocument()
    
    '什么也不做
    
    
    end sub
    
    private sub isaxcontenthandler_endelement(pwchnamespaceuri as integer,
      byval cchnamespaceuri as long, pwchlocalname as integer, byval
      cchlocalname as long, pwchqname as integer, byval cchqname as long)
    
    '什么也不做
    
    end sub
    
    private sub isaxcontenthandler_endprefixmapping(pwchprefix as integer,
      byval cchprefix as long)
    
    '什么也不做
    
    
    end sub
    
    private sub isaxcontenthandler_ignorablewhitespace(pwchchars as integer,
      byval cchchars as long)
    
    '什么也不做
    
    
    end sub
    
    private sub isaxcontenthandler_processinginstruction(pwchtarget as
      integer, byval cchtarget as long, pwchdata as integer, byval cchdata
      as long)
    
    '什么也不做
    
    
    end sub
    
    private sub isaxcontenthandler_putdocumentlocator(byval plocator as
      xmlsax.isaxlocator)
    
    '什么也不做
    
    end sub
    
    private sub isaxcontenthandler_skippedentity(pwchname as integer, byval
      cchname as long)
    
    '什么也不做
    
    
    end sub
    
    private sub isaxcontenthandler_startdocument()
    
    end sub
    
    private sub isaxcontenthandler_startelement(pwchnamespaceuri as integer,
      byval cchnamespaceuri as long, pwchlocalname as integer, byval
      cchlocalname as long, pwchqname as integer, byval cchqname as long,
      byval pattributes as xmlsax.isaxattributes)
    
    dim str as string
    
    ' 调整字符串大小为正确的字符数
    str = string$(cchlocalname, 0)
    
    ' 复制这些值。请注意大小是字符数的两倍,
    ' 因为它是 unicode(双字节)字符串
    copymemory byval strptr(str), pwchlocalname, cchlocalname * 2
    
    debug.print str
    
    end sub
    
    private sub isaxcontenthandler_startprefixmapping(pwchprefix as integer,
      byval cchprefix as long, pwchuri as integer, byval cchuri as long)
    
    '什么也不做
    
    
    end sub
    

    将它包装起来

    在克服了用 visual basic 编写 sax2 程序的最初困难后,我决定,最好长期封装所有 visual basic 內部 sax2 介面包装类別中棘手的要素。

    工作结果可以在本文的下载地点看到。它虽不完整,但是演示了所有要点。该示例代码可以编译为 activex® 组件 (vbxmlsax),然后重复使用(在 visual basic、vbscript、jscript® 或者甚至 visual c++ 中),并不需知道实现的细节。对那些希望了解某些实现问题的人来说,请阅读它们。

    停止分析器,我想离开!

    基于事件的分析器(如 sax2)的强大功能之一,是能在与用户定义条件匹配时停止分析。例如,您可能希望在 xml 文档中遇到称为“uninteresting”的元素时停止分析。sax2 处理程序接口允许用户停止分析。我们尝试将处理程序接口上的方法表示为自己的包装程序类中的 visual basic 事件,使得事件接收方(例如 vb 窗体对象)可以控制分析的终止。

    对接口的处理程序家族文档的观察告诉我们,处理程序方法通过将返回值 (hresult) 设置为 err_fail 符号值,来表示需要停止分析。只有一个问题:vb 隐藏了 hresult,因此无法在自定义处理程序代码中设置该值。

    实际上是可以设置该值的,但是必须跳过某些环节。该技术称为“vtable 修改”,并被描述在 bruce mckinney 的书 hardcore visual basic 中。在运行时,有可能将调用重新定向,从 visual basic 所提供事件处理程序定向为用户自己的函数。该技术的关键是使用 addressof 操作符来获得重载的函数地址,然后使用 copymemory 来覆盖相应接口的 vtable 项。由于重载函数是由用户(不是 visual basic)定义的,因此返回值是可控制的。

    让我们来看一个示例。isaxcontenthandler 接口提供称为 startdocument 的方法。如果我们创建实现 isaxcontenthandler (cvbsaxcontenthandler) 的 visual basic 类包装程序,那么 visual basic 将提供与下面类似的处理程序原型:

    private sub isaxcontenthandler_startdocument()
    

    在后面的函数原型实际上是:

    public function isaxcontenthandler_startdocument(byval this as
      isaxcontenthandler) as long
    

    实际上返回类型是 hresult,但是可以使用 visual basic long 数据类型来存储 hresult 值。因此,为了重载 startdocument 方法,我们用 bas 文件中的后一种原型创建了函数。在运行时调用的是这个重载函数而不是 visual basic 类包装程序中的程序。请注意增加的参数 this。它是所调用的 isaxcontenthandler 处理程序的实例。下面是 startdocument 的重载函数:

    public function isaxcontenthandler_startdocument(byval this as isaxcontenthandler) as long
      
    #if idebug = -1 then
      debug.print "vtable replacement for isaxcontenthandler_startdocument"
    #end if
      
      dim booabort as boolean
    
      dim objvbsaxcontenthandler as cvbsaxcontenthandler
      set objvbsaxcontenthandler = this
      
      call objvbsaxcontenthandler.startdocument(booabort)
      
      if booabort then
        isaxcontenthandler_startdocument = e_fail
      else
        isaxcontenthandler_startdocument = s_ok
      end if
    
    end function
    

    使用该技术有一个问题。addressof 操作符只能用于在标准模块(bas 文件)中定义的函数。因此,同样的函数将在特定处理程序接口的所有实例上调用。这样就提出一个问题,如何确定被调用的处理程序包装程序的实例(因为它是触发该事件的包装程序)。

    好在 vtable 重载函数需要的格式包含 this 参数(参见前面的代码示例),它的类型就是被调用的接口。通过该参数,我们可以声明类型为 cvbsaxcontenthandler 的变量,来执行与 queryinterface 等价的功能,然后进行下面的赋值。

    本文关键:SAX2 Visual Basic XML
      相关方案
    Google
     

    本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

    go top