【源码下载地址】:http://60.191.21.235:1122/vbwebbrowserhunter.rar关于如何取得浏览器接口的问题大家已经关注很久了,特别是VBAHZ同志,时不时地就贴个帖子出来,呵呵……为了不辜负VBAHZ同志对本人的抬举,偶便决定看看这个有点麻烦的问题……要从指定窗口返回浏览器对象需要解决这么两个问题:
1、得到浏览器对象接口
2、跨进程调用对象
这两个问题用VB实现起来都不是什么轻松的事情。因为涉及到接口编程,所以可能需要用到自定义的类型库,麻烦!跨进程调用COM对象需要进行列集(Marshal)和散集(Unmarshal),更是麻烦!麻烦!不过麻烦归麻烦,问题还是要一个一个解决的。首先,我们来看看如何得到浏览器对象接口。在翻遍了msdn后,最后终于在一个角落里发现了秘诀。其大致是这样描述整个过程的:
1、 发送一个微软没有公布的消息WM_USER+7到指定窗口,获得一个IShellView指针
2、 从这个指针用QueryInterface获得一个IServiceProvider接口
3、 从IServiceProvider接口的QueryService方法得到一个服务ID为SID_STopLevelBrowser的IServiceProvider接口
4、 最后从服务ID为SID_STopLevelBrowser的IServiceProvider接口调用QueryService方法得到IID_IWebBrowser2接口以上这个过程用VC实现起来是非常快的,而用VB的话就头疼了。另外,这些方法需要在指定窗口所在的线程里运行才可以,否则,后果将不可预料……而第二个问题涉及到COM对象的列集(Marshal)和散集(Unmarshal)。很明显,列集的工作肯定也要在目标窗口所在线程中完成,而散集的工作则要在本地程序里来做。因此,综上所述,要解决上述问题,就必须要在目标窗口线程空间里插入代码。代码插入有多种方法,我这次还是跟以往一样用的是直接代码写入,但跟以往有一点不同的是,这次的代码插入后要立即运行。我先想到了CreateRemoteThread,但是后来再想,用这个API创建的是一个新的线程,从这个线程访问另一个线程中的对象,同样需要进行列集和散集的操作,而这样实在太麻烦了。而后我想到了在窗体过程(WindowProc)上安装个钩子,然后马上个发送NULL消息来激活代码,但是,WindowProc是个使用比较频繁的过程,弄不好的话就会把对象窗口弄疯掉,而且,同样的,这样做麻烦阿~~最后,我想到了SetThreadContext,使用这个api我们就可以设定目标线程的eip,hoho,没错,就是他了~~

解决方案 »

  1.   

    实现思路大致就是上面这样,接下来讲一个不使用类型库释放对象的小技巧:'释放IStream对象
    Private Function ReleaseStream(ByVal lpStream As Long) As Boolean
        Dim lpThis As Long
        Dim lpVTable As Long
        
        Dim oTmp As Collection
        
        If lpStream = 0 Then Exit Function
        
        Set oTmp = New Collection
        
        lpThis = ObjPtr(oTmp)
        
        CopyMemory lpVTable, ByVal lpThis, 4
        
        CopyMemory ByVal lpThis, ByVal lpStream, 4
        Set oTmp = Nothing
        CopyMemory ByVal lpThis, lpVTable, 4
        
        Set oTmp = Nothing
        ReleaseStream = True
    End Function在这个CVBWebBrowserHunter类里需要释放所建立的IStream对象,而如果只为这一个释放操作而建立一个类型库就太头大了。所以,我这里就“借用”了一下oTmp这个Collection对象的驱壳,将IStream对象替换进去。然后当我们调用Set oTmp=Nothing的时候,VB就会去调用oTmp对象(现在其实是IStream)的Release方法。这样,我们就实现了不用类型库而直接调用IStream对象的Release方法。
      

  2.   

    最后贴一下要远程执行代码:
    pushad
    pushfd mov ebp,esp
    sub esp,30h mov ebx,123456h;ebx=lpStart
    lea edi,[ebp-08h]
    push edi
    lea edi,[ebx+1Ch];edi=IID_IServiceProvider 
    push edi
    mov eax,[ebx+64h];eax=psb
    mov ecx,[eax]
    push eax
    call [ecx];psb->QueryInterface(IID_IServiceProvider,(void**)&psp)
    cmp eax,0
    jl setokflag lea edi,[ebp-0Ch]
    push edi
    lea edi,[ebx+1Ch]
    push edi
    lea edi,[ebx+2Ch]
    push edi
    mov eax,[ebp-08h];eax=psp
    mov ecx,[eax]
    push eax
    call [ecx+0Ch];psp->QueryService(SID_STopLevelBrowser,IID_IServiceProvider,(void**)&psptb)
    cmp eax,0
    jl releasepsp lea edi,[ebp-10h]
    push edi
    lea edi,[ebx+4Ch]
    push edi
    lea edi,[ebx+3Ch]
    push edi
    mov eax,[ebp-08h];eax=psp
    mov ecx,[eax]
    push eax
    call [ecx+0Ch];psptb->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2,(void**)&pwb);
    cmp eax,0
    jl releasepsptb push 0
    push 0
    push 0
    push [ebp-10h]
    lea edi,[ebx+4Ch]
    push edi
    lea edi,[ebp-14h]
    push edi
    call [ebx];CoGetMarshalSizeMax (&ulSize, IID_IWebBrowser2,pwb, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
    cmp eax,0
    jl setokflag lea edi,[ebp-14h]
    push edi
    push 2
    call [ebx+0Ch];GlobalAlloc (GMEM_MOVEABLE, (DWORD) ulSize);
    cmp eax,0
    je setokflag
    lea ecx,[ebp-18h]
    mov [ecx],eax;hGlobal=GlobalAlloc; lea edi,[ebp-1Ch]
    push edi
    push 1
    mov edi,[ebp-18h]
    push edi
    call [ebx+8h];CreateStreamOnHGlobal (hGlobal, TRUE, &pStream);
    cmp eax,0
    jge go1 mov edi,[ebp-18h]
    push edi
    call [ebx+10h];GlobalFree (hGlobal);
    jmp releasepsptb go1:
    push 0
    push 0
    push 0
    push [ebp-10h]
    lea edi,[ebx+4Ch]
    push edi
    push [ebp-1Ch]
    call [ebx+4];CoMarshalInterface (pStream, IID_IWebBrowser2, pwb, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
    cmp eax,0
    jl releasestream; lea edi,[ebx+5Ch]
    lea esi,[ebp-14h]
    movsd;cbData=ulSize; mov edi,[ebp-18h]
    push edi
    call [ebx+14h]
    mov [ebx+60h],eax;lpData=GlobalLock (hGlobal); mov edi,[ebp-18h]
    push edi
    call [ebx+18h];GlobalUnlock (hGlobal); mov [ebx+68h],dword ptr 1
    loop1:
    cmp dword ptr [ebx+68h],0
    jne loop1releasepsptb:
    mov eax,[ebp-0Ch];eax=psptb
    mov ecx,[eax]
    push eax
    call [ecx+8];psptb->Release();
    releasepsp:
    mov eax,[ebp-08h];eax=psp
    mov ecx,[eax]
    push eax
    call [ecx+8];psp->Release();
    releasestream:
    mov eax,[ebp-1Ch]
    mov ecx,[eax]
    push eax
    call [ecx+8h];pStream->Release ();setokflag:
    mov [ebx+68h],dword ptr 2

    mov esp,ebp
    popfd
    popad push 123456h;push reteip
    ret
      

  3.   

    >发送一个微软没有公布的消息WM_USER+7到指定窗口,获得一个IShellView指针
    这句话有问题吧,返回的是IShellBrowser指针而不是IShellView指针,不过虽然类型错了,像你这样直接当IUnknown用还是可以的。P.S.你的代码没有捕获浏览器的事件,进程外的浏览器控件窗口销毁之后继续访问会出问题的
      

  4.   

    是IShellBrowser,我写错了......进程外浏览器销毁的时候,再次访问就会出现RPC错误,所以我在所带的例子中用了On Error Resume Next,呵呵,人懒没办法
      

  5.   

    此外,连接事件也是很方便的事情,把声明改成private withevents m_oIE as internetexplorer就可以了,然后在OnQuit事件里做个标记说明当前m_oIE已经over...
      

  6.   

    没那么复杂吧..
    请看,http://www.mvps.org/emorcillo/en/code/vb6/iedom.shtml,内容如下The following code is the VB version of the C code in the KB article 249232 - HOWTO: Get IHTMLDocument2 from a HWND(http://support.microsoft.com/default.aspx?kbid=249232). It requires oleacc.dll to be installed on the system (oleacc.dll is part of the Active Accessibility).'
    ' Requires: reference to "Microsoft HTML Object Library"
    'Private Type UUID
       Data1 As Long
       Data2 As Integer
       Data3 As Integer
       Data4(0 To 7) As Byte
    End TypePrivate Declare Function GetClassName Lib "user32" _
       Alias "GetClassNameA" ( _
       ByVal hWnd As Long, _
       ByVal lpClassName As String, _
       ByVal nMaxCount As Long) As LongPrivate Declare Function EnumChildWindows Lib "user32" ( _
       ByVal hWndParent As Long, _
       ByVal lpEnumFunc As Long, _
       lParam As Long) As LongPrivate Declare Function RegisterWindowMessage Lib "user32" _
       Alias "RegisterWindowMessageA" ( _
       ByVal lpString As String) As LongPrivate Declare Function SendMessageTimeout Lib "user32" _
       Alias "SendMessageTimeoutA" ( _
       ByVal hWnd As Long, _
       ByVal msg As Long, _
       ByVal wParam As Long, _
       lParam As Any, _
       ByVal fuFlags As Long, _
       ByVal uTimeout As Long, _
       lpdwResult As Long) As Long
          
    Private Const SMTO_ABORTIFHUNG = &H2Private Declare Function ObjectFromLresult Lib "oleacc" ( _
       ByVal lResult As Long, _
       riid As UUID, _
       ByVal wParam As Long, _
       ppvObject As Any) As LongPrivate Declare Function FindWindow Lib "user32" _
       Alias "FindWindowA" ( _
       ByVal lpClassName As String, _
       ByVal lpWindowName As String) As Long'
    ' IEDOMFromhWnd
    '
    ' Returns the IHTMLDocument interface from a WebBrowser window
    '
    ' hWnd - Window handle of the control
    '
    Function IEDOMFromhWnd(ByVal hWnd As Long) As IHTMLDocument
    Dim IID_IHTMLDocument As UUID
    Dim hWndChild As Long
    Dim lRes As Long
    Dim lMsg As Long
    Dim hr As Long   If hWnd <> 0 Then
          
          If Not IsIEServerWindow(hWnd) Then
          
             ' Find a child IE server window
             EnumChildWindows hWnd, AddressOf EnumChildProc, hWnd
             
          End If
          
          If hWnd <> 0 Then
                
             ' Register the message
             lMsg = RegisterWindowMessage("WM_HTML_GETOBJECT")
                
             ' Get the object pointer
             Call SendMessageTimeout(hWnd, lMsg, 0, 0, _
                     SMTO_ABORTIFHUNG, 1000, lRes)         If lRes Then
                   
                ' Initialize the interface ID
                With IID_IHTMLDocument
                   .Data1 = &H626FC520
                   .Data2 = &HA41E
                   .Data3 = &H11CF
                   .Data4(0) = &HA7
                   .Data4(1) = &H31
                   .Data4(2) = &H0
                   .Data4(3) = &HA0
                   .Data4(4) = &HC9
                   .Data4(5) = &H8
                   .Data4(6) = &H26
                   .Data4(7) = &H37
                End With
                   
                ' Get the object from lRes
                hr = ObjectFromLresult(lRes, IID_IHTMLDocument,_
                         0, IEDOMFromhWnd)
                   
             End If      End If
          
       End IfEnd FunctionPrivate Function IsIEServerWindow(ByVal hWnd As Long) As Boolean
    Dim lRes As Long
    Dim sClassName As String   ' Initialize the buffer
       sClassName = String$(100, 0)
       
       ' Get the window class name
       lRes = GetClassName(hWnd, sClassName, Len(sClassName))
       sClassName = Left$(sClassName, lRes)
       
       IsIEServerWindow = StrComp(sClassName, _
                          "Internet Explorer_Server", _
                          vbTextCompare) = 0
       
    End Function'
    ' Copy this function to a .bas module
    '
    Function EnumChildProc(ByVal hWnd As Long, lParam As Long) As Long
       
       If IsIEServerWindow(hWnd) Then
          lParam = hWnd
       Else
          EnumChildProc = 1
       End If
       
    End Function
      

  7.   

    这段代码的确是可以的,但是它要求系统必须有安装Active Accessibility……