dim objcvbsaxcontenthandler as cvbsaxcontenthandler set objcvbsaxcontenthandler = this
调用有效地获得了 visual basic 包装程序接口。然后我们可以调用包装程序,然后就可以按预期的方式触发事件了。
为了达到这种效果,我们在 cvbsaxcontenthandler 上按“friend”范围定义了的方法。用这种方法,vbsaxxml 组件的内部代码可以访问函数,但是外部客户机甚至无法看到它。该 friend 方法的实现仅仅触发事件、传送所有原始参数,加上 abort 标志(用 byref 传送),因此处理该事件的代码可以设置标志。下面是代码:
friend sub startdocument(abort as boolean) raiseevent startdocument(abort) end sub
在从客户机应用程序(例如窗体)的 startdocument 事件处理程序返回后,cvbsaxcontenthandler 中的 friend startdocument 方法简单地将 abort 参数的值传回 vtable 重载函数。根据 abort 标志的值,vtable 重载可以正确地设置底层 isaxcontenthandler.startelement 方法的 hresult 返回值。
其他处理程序方法也可以按类似的方式重载。下面是重载 startelement 方法的函数原型,它有自己的参数。
public function isaxcontenthandler_startelement(byval this as isaxcontenthandler, 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) as long
friend 等价物类似于:
friend sub startelement(byval namespaceuri as string, byval localname as string, byval qname as string, byval attributes as cvbsaxattributes, abort as boolean) raiseevent startelement(namespaceuri, localname, qname, attributes, abort) end sub
在此函数有自己的函数,它是将 c++ 风格参数映射为 visual basic 友好字符串的 vtable 重载函数。
类型库 hacking
在某些情况下可以发现,编译提供的 xmlsax.idl 文件产生的 xmlsax.tlb 类型库,不仅仅是对 visual basic 不友好,而且实际上在 visual basic 中不能用。特别是在 idl 中定义为 [out] 的方法的参数,对 visual basic 来说是不可接受的。如果参数定义为 [in, out](byref 参数)或者 [out, retval](返回值),那么 visual basic 不允许使用它。这是 sax2 接口中某些方法的状况。
此外,我还在 isaxattributes 接口上调用大多数方法时遇到了麻烦。例如,getlocalname 方法在 visual basic 中产生了一个子程序。调用者将索引传送给 attributes 集合。程序返回整型的 by-now-familiar 数组和数组长度参数。idl 类似于:
hresult getlocalname(
[in] int nindex,
[out] const wchar_t ** ppwchlocalname,
[out] int * pcchlocalname);
我知道 [out] 参数在 visual basic 中是不可接受的。因此,我决定将 idl 中的 [out] 参数更改为 [in, out]。但是我无法确定如何将 const wchar_t ** 数据类型解码。我注意到,hardcore visual basic 一书提供了称为 upointertostring 的函数,好像很有帮助。但是 upointertostring 需要指针(在 vb 中是 long)。因此我决定将数据类型从 const wchar_t ** 改变为 int *。修改后的 idl 类似于:
hresult getlocalname(
[in] int nindex,
[in, out] int * ppwchlocalname,
[in, out] int * pcchlocalname);
结果该格式在 visual basic 中是可接受的。字符串值可以用 upointertostringex(与 upointertostring 相同,但是用字符计数作为参数)成功解码。
isaxattributes 上的方法现在可以作为子程序调用:
call pattributes.getvalue(0, pwchvalue, cchvalue)
上面的调用获得索引位置为 0 的属性值,将指针分配给 pwchvalue 中的值和 cchvalue 中的值长度。然后从下面的代码中返回 vb 字符串(类似于上面的描述)。
sattvalue = string$(cchvalue, 0) copymemory byval strptr(sattvalue), byval pwchvalue, cchvalue * 2
请注意 pwchvalue 前面必须加 byval。
我复制了 xmlsax.idl 文件,称之为 xmlsaxvb.idl,然后在将修改后的副本最终编译为 xmlsaxvb.tlb 之前修改了副本。下载中包括的 activex 组件工程将引用这个修改后的类型库。
当然,不能建议修改 idl 文件为一般规则。但需要建议的是,不要让修改后的类型库离开您开发所用的计算机。如果用“软件包和部署向导”创建安装程序,那么这样的类型库不应该包括在安装软件包中。但是,由于本文中讨论的 msxml 只是技术预览,因此任何人尝试在生产环境中引入该示例都是没有危险的。