• 从FireFox中抓取当前网页内容


    FireFox采用Gecko内核,不同于IE内核,不能直接通过HWND像处理IE内核那样获取IHTMLDocument2。幸好Mozilla为Gecko做了一层映射,使得Gecko支持MSAA接口,可以通过HWND间接获得IHTMLDocument2(实际上是ISimpleDOMDocument,和IHTMLDocument2同样继承自IUnknown)。

    网上有篇文章《基于IE和Gecko内核的网页内容获取与分析研究》,可惜这篇文章中提到的方法只对旧版的FireFox有效。搜了好久都没发现有关新版本的中文资料,许多文章都是针对FireFox3.x,并且不太靠谱。无耐之下只得研究Mozilla官网文档。

    Mozilla官网文章:

    Supported MSAA Interfaces》:讲了对Gecko做了映射,以支持MSAA接口。

    MSAA Implementation Features》:讲如何如何获取网页文档信息,但不全是我们想要的,我们要做的是通过HWND的到当前标签页的网页文档信息。

    Find the Window and Load the Document》:告诉我们FireFox将变成一个单窗口的应用程序。最终查找顶层UI窗口并加载网页文档(ISimpleDOMDocument)的唯一方法是使用“accessible relation NAVRELATION_EMBEDS”。我使用SPY++查看FireFox9的窗口时确实如此,只有主界面一个窗口,窗口类为MozillaWindowClass。这篇文章修改日期是“20:42, 4 Mar 2008”,因此这个方法在很早的版本中就已经采用了,可是文中资料却鲜有提及。

    MSAA Relations》:定义了上面提到的NAVRELATION_EMBEDS的值。

    参考上面三篇文章和其他一些参考资料有了下面获取网页文档的方法:

    1、从Mozilla Developer Center下载三个文件ISimpleDOMNode.idl、ISimpleDOMText.idl 、ISimpleDOMDocument.idl,《MSAA Implementation Features》下有提供下载链接。

    2、使用微软的IDL编译器即MIDL(位于VC6安装目录下的VC98\Bin中)来生成Windows平台下的头文件,命令格式如下: 
      MIDL ISimpleDOMNode.idl 
      MIDL ISimpleDOMText.idl 
      MIDL ISimpleDOMDocument.idl

    然后我们就得到了9个文件,其中只需要6个:ISimpleDOMNode.h、ISimpleDOMNode_i.c、ISimpleDOMText.h、ISimpleDOMText_i.c、ISimpleDOMDocument.h、ISimpleDOMDocument_i.c

    3、假设已经得到到FireFox窗口句柄了,以获取网页URL为例,代码如下:

    #include <windows.h>
    #include <objbase.h>
    #include <atlbase.h>
    #include <oleacc.h>
    #include <comutil.h>

    #include "ISimpleDOMNode.h"
    #include "ISimpleDOMText.h"
    #include "ISimpleDOMDocument.h"

    ////////////////////////////////////////////////////////////////////////////////
    //

    // NAVRELATION_EMBEDS 要自己定义,其值在《MSAA Relations》中给出
    #ifndef NAVRELATION_EMBEDS
    #define NAVRELATION_EMBEDS 0x1009
    #endif

    ////////////////////////////////////////////////////////////////////////////////
    //
    bool GetUrl_Gecko(HMODULE hOleAccDll, HWND hWndBrowser, LPTSTR pszUrl, size_t stBytes)
    {
    if (NULL == hOleAccDll || NULL == hWndBrowser)
    {
    return false;
    }

    TCHAR szClassName[_MAX_PATH] = {0};
    ::GetClassName(hWndBrowser, szClassName, sizeof(szClassName));
    if (_tcsncmp(szClassName, TEXT("MozillaWindowClass"), sizeof(szClassName)) != 0)
    {
    return false;
    }

    IAccessible* pAccBrowser = NULL;

    LPFNACCESSIBLEOBJECTFROMWINDOW pfAccessibleObjectFromWindow
    = (LPFNACCESSIBLEOBJECTFROMWINDOW)::GetProcAddress(
    hOleAccDll, "AccessibleObjectFromWindow");

    if (NULL == pfAccessibleObjectFromWindow)
    {
    return false;
    }

    HRESULT hr = pfAccessibleObjectFromWindow(
    hWndBrowser, OBJID_CLIENT, IID_IAccessible, (void**)&pAccBrowser);
    if (FAILED(hr) || NULL == pAccBrowser)
    {
    return false;
    }

    VARIANT vtStart;
    VARIANT vtResult;

    vtStart.vt = VT_I4;
    vtResult.lVal = CHILDID_SELF;

    pAccBrowser->accNavigate(NAVRELATION_EMBEDS, vtStart, &vtResult);

    IDispatch* pDisp = vtResult.pdispVal;
    if (NULL == pDisp)
    {
    return false;
    }

    IAccessible* pAccDoc = NULL;
    hr = pDisp->QueryInterface(IID_IAccessible, (void**)&pAccDoc);
    if (FAILED(hr) || NULL == pAccDoc)
    {
    return false;
    }

    IServiceProvider *pServProv = NULL;
    hr = pAccDoc->QueryInterface(IID_IServiceProvider, (void**)&pServProv);
    if (FAILED(hr) || NULL == pServProv)
    {
    return false;
    }

    const GUID refguid = {0x0c539790, 0x12e4, 0x11cf, 0xb6, 0x61,
    0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};

    ISimpleDOMNode* pNode = NULL;
    hr = pServProv->QueryService(refguid, IID_ISimpleDOMNode, (void**)&pNode);
    if (FAILED(hr) || NULL == pNode)
    {
    return false;
    }

    ISimpleDOMDocument* pDoc = NULL;
    hr = pNode->QueryInterface(IID_ISimpleDOMDocument, (void**)&pDoc);
    if (FAILED(hr) || NULL == pDoc)
    {
    return false;
    }

    BSTR bstrUrl = NULL;
    hr = pDoc->get_URL(&bstrUrl);
    if (FAILED(hr) || NULL == bstrUrl)
    {
    return false;
    }
    _tcsncmp(pszUrl, _bstr_t(bstrUrl), stBytes);

    return true;
    }

    ////////////////////////////////////////////////////////////////////////////////
    //
    bool GetWebPageUrl(HWND hWndBrowser, LPTSTR pszUrl, size_t stBytes)
    {
    if (!::IsWindow(hWndBrowser))
    {
    return false;
    }

    ::CoInitialize(NULL);

    // 显示加载 MSAA 以便确定是否已安装
    HMODULE hOleAccDll = ::LoadLibrary(TEXT("OLEACC.DLL"));
    if (NULL == hOleAccDll)
    {
    ::CoUninitialize();
    return false;
    }

    bool bSucceeded = ::GetUrl_Gecko(hOleAccDll, hWndBrowser, pszUrl, stBytes);

    ::FreeLibrary(hOleAccDll);
    ::CoUninitialize();

    return bSucceeded;
    }
  • 相关阅读:
    atitit 提升数据库死锁处理总结
    CoreJava_线程并发(堵塞队列):在某个目录下搜索含有某keyword的文件
    HDU 4389——X mod f(x)(数位DP)
    POJ 1182 (经典食物链 /并查集扩展)
    【iOS-Android开发对照】 之 APP入口
    《Pro Android Graphics》读书笔记之第四节
    Android多个Module统一配置相同jar或库的版本号
    教你上传代码到码云(与github一样)
    解决本地项目推送到码云(github),上提示:failed to push some refs to ...
    android adb常用指令
  • 原文地址:https://www.cnblogs.com/wxxweb/p/2317094.html
Copyright © 2020-2023  润新知