天天看点

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

为什么

为了显示微信登录的二维码,引入了整套QtWebEngine,占用非常大的空间。微信登录二维码有自动刷新动作,这些定义在页面脚本中,所以不能直接自绘二维码方式实现,浏览器少不了。

主要原理

通过COM组件可以调用系统自带的浏览器,当然是IE啦。Qt提供QAxObject和QAxWidget对象帮助访问COM,(与其说帮助,还不如说越帮越忙)

怎么做

请注意接下来描述的每一步,在完全理解前,不要擅自调整

1. 创建或者基于已有的Qt设计师界面类,就是有.ui文件的那种。但是奇葩来了,完整的设置通过“设计”

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

是搞不出来的。所以随便拉个QAxWidget吧。

2. 通过用文本编辑器的方式打开对应的ui文件

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

3. 找到<customwidgets>,往里面插入一组提升设置

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

4. 把界面上的QAxWidget找到,改成如下样式,objectName会在每次用"设计"改ui的时候被覆盖掉(悲剧),control里面的guid是对应IWebBrowser2接口的,固定就那么写。

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

5. 照着模样做一个WebAxWidget.h。注意,集成QAxWidget的类不能声明Q_OBJECT,会导致activeqt机制里面moc无效,表现看这里stackoverflow

#ifndef WEBAXWIDGET_H
#define WEBAXWIDGET_H
#include <ActiveQt/QAxWidget>
#include "windows.h"

class WebAxWidget : public QAxWidget
{
public:

    WebAxWidget(QWidget* parent = 0, Qt::WindowFlags f = 0)
        : QAxWidget(parent, f)
    {
    }
protected:
    bool translateKeyEvent(int message, int keycode) const Q_DECL_OVERRIDE
    {
        if (message >= WM_KEYFIRST && message <= WM_KEYLAST)
            return true;
        else
            return QAxWidget::translateKeyEvent(message, keycode);
    }
};
#endif // WEBAXWIDGET_H      

6. 在你的ui文件对应的界面类里面,声明槽,注意命名形如 on_WebBrowser_ProgressChange. 其中WebBrowser为前面objectName所写,ProgressChange是回调函数的名字,它的出处是MSDN里面IWebBrowser的声明(好吧,我也是蒙的,在第5中所说的错误状态下,可以即时看到QObject::receivers: No such signal mis::WebAxBrowser::DownloadComplete()这样的提示,就是说你的命名对了,槽就会被调用,而不需要手动去连接).稳妥的办法是去qt sample:webbrowser里面抄几个.

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

7. 让槽函数转调一个专门处理它的类,我创建了一个WebAxWrapper,把回调处理后转成类似QWebEngineView的信号:loadFinished和loadProgress

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

8.当然还有load()和url()

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

9. 最后值得一提的是拿页面源码。找了很多帖子,期间想到Javascript法,结果IHTMLWindow2里面的execScript方法不能拿到返回值,网上文章一大堆,还有反汇编C# InvokeScript()方法的.最后找到了流化方案。我做了一个像QWebEnginePage的toPlainText方法

void WebAxWrapper::toPlainText(void (*f)(const QString &))
{
    char *output = NULL;
    QAxObject *document = WebBrowser->querySubObject("Document");
    if(document)
    {
        IHTMLDocument2 *doc2 = nullptr;
        document->queryInterface(QUuid(IID_IHTMLDocument2), (void**)&doc2);
        if (doc2)
        {
            IPersistStreamInit *pPSI=NULL;
            IStream *pStream=NULL;
            HGLOBAL hHTMLText;
            if (SUCCEEDED(doc2->QueryInterface(&pPSI)))
            {
                const int MaxBuffSize =64*1024;
                hHTMLText = GlobalAlloc(GMEM_FIXED, MaxBuffSize);
                CreateStreamOnHGlobal(hHTMLText, TRUE, &pStream);
                pPSI->Save(pStream, FALSE);
                LARGE_INTEGER li;
                li.QuadPart = 0;
                pStream->Seek(li,0,NULL);
                output = new char[MaxBuffSize];
                output[MaxBuffSize-1] = 0;
                ULONG readed = 0;
                pStream->Read((void*)output,MaxBuffSize-1,&readed);
                output[readed] = 0;
                pStream->Release();
                //GlobalFree(hHTMLText);
                pPSI->Release();
            }
            doc2->Release();
        }
    }

    if(output)
    {
        qDebug()<<output;
        f(QString::fromUtf8(output));
        delete[] output;
    }
    else
    {
        f(QString());
    }

}      

需要windows的头文件

#include <ExDisp.h>

#include <shlguid.h>

#include <MsHTML.h>

#include <QUuid>

10. 二维码,其实最后这个不是重点了,直接把我们的页面(http://passport.timelink.cn/wechat/show)显示出来,当发生loadFinished的时候,获取一下当前url,如果不同了,分析一下页面源码,我们的项目在这一步服务器返回一段json,解析并处理就可以了。

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

最后

ie浏览器右键菜单真是又多又杂...

QT通过QAxWidget嵌入IE浏览器并获取页面源码 为什么 主要原理 怎么做 最后

欢迎访问我们的轻录课项目,以及分课网 http://fenke.timelink.cn

继续阅读