这几天在写HtmlUI的VB6版本,Browser端原来折腾过,并且代码都在,还算好说,Server端用Asynchronous Pluggable Protocols,遍地求代码求不到,最后只得自己研究E文的MSDN,主要是要实现IInternetProtocol接口。
用VB写过COM的同学应该都知道,实现接口时VB是没法直接返回HRESULT的,通常的做法是什么都不做就代表S_OK,用Err.Raise E_XXXX返回错误。
在实现IInternetProtocol时,和往常一样,开始是一大堆的S_OK和一大堆的E_NOTIMPL,执行时发现IInternetProtocol::Read函数一直会被调用,查下MSDN得知如果数据全部发送完毕则返回S_FALSE。于是在函数结尾写上Err.Raise S_FALSE。然后奇怪的问题就来了,从断点命中来看,Start、Read、Terminate都是按顺序执行的,但页面却无法显示。折腾半天未果,遂找了份前人写过的代码比对(http://www.vbgood.com/thread-91065-1-1.html,估计这份代码是全世界唯一的APPVB版公开代码)。
这份代码用的是手工的纯C语言式接口实现,执行正常。断点命中表示Read函数被调用了多次,而我的代码只被调用了一次。联想到昨天实现IClassFactory时Err.Raise pUnknown.QueryInterface(…)的写法莫名其妙地罢工而改成hr = pUnknown.QueryInterface(…) If hr <> 0 Then Err.Raise hr就好了,推测Err.Raise不能用来返回正确的错误码。打开OD,通过内存断点断到Read函数里,在urlmon.dll的返回地址处断下,果然,Err.Raise S_FALSE的返回值并非1,而是0x800A0001。
Google了国内外一圈,没有找到有关S_FALSE的快速解决办法。考虑到既能解决这个问题又能保全VB在COM应用上的方便性,决定用Com Hook解决。
Com Hook,和API Hook类似(导入导出表修改方式,非inline hook),只不过这次Hook的是COM的虚函数表,将原有虚函数表中的函数地址替换成我们的地址,改变程序流程,做点手脚。具体做法请复习32位汇编与com原理,下面贴代码~
Private Sub DoHook_IInternetProtocol_Read() Dim pInternetProtocol As olelib.IInternetProtocol Dim pVTable As Long, pFuncInternetProtocol_Read As Long, dwCode As Long Set pInternetProtocol = Me CopyMemory pVTable, ByVal ObjPtr(pInternetProtocol), 4 CopyMemory pFuncInternetProtocol_Read, ByVal pVTable + 4 * 9, 4 CopyMemory dwCode, ByVal pFuncInternetProtocol_Read, 4 If dwCode <> &H102474FF Then Dim pAlloc As Long, jmpOffset As Long, bCode() As Byte pAlloc = VirtualAlloc(ByVal 0, &H1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE) bCode = Hex2Byte("FF742410FF742410FF742410FF742410E8000000003D01000A807505B801000000C210") CopyMemory ByVal pAlloc, bCode(0), UBound(bCode) + 1 jmpOffset = pFuncInternetProtocol_Read - (pAlloc + 21) CopyMemory ByVal pAlloc + 17, jmpOffset, 4 VirtualProtect ByVal pVTable, &H1000, PAGE_EXECUTE_READWRITE, dwCode CopyMemory ByVal pVTable + 4 * 9, pAlloc, 4 End If End Sub
以上代码在Class_Initialize中调用。
因为仅仅是一个函数遇到这样的问题,个例,所以hook也就只针对一个函数,目前尚未考虑写一个通用的处理函数。先这样放着吧~
博主有联系方式吗,我有个VB中如何仿电子书使用异步可插入协议的问题想向你请教,不知可否?