1. 问题描述
Qt项目打包交付后在一台客户机上面出现了打开界面就停止运行
由于在客户机上没有代码调试环境,不能联网,不能重装系统,就直接用该客户机器上的vs2010对exe界面程序进行调试
发现中断进程中提示“下面的框架可能不正确或缺失,没有为ntdll.dll加载符号”,再看下面的进程,有对Qt5WebEngineWidgets.dll,Qt5WebEngineCore.dll,Qt5WebChannel.dll的提示,这三个动态库是项目使用了QWebEngineView控件才加进去的,是通过间接调用QtWebEngineProcess.exe来进行,然后,在打包的软件的路径下双击QtWebEngineProcess.exe发现也是会停止运行,在别的客户机上双击这个exe是一闪而过,也就是该机器系统不支持这个库,当前qt版本是5.14.2,后来又测试了以下方法都没有在该2机器上解决这个问题:
- 用qt msvc2017_32编译软件,在该机器上同样的问题
- 替换Qt版本为5.14.0,在该机器上同样的问题
- 替换其他同型号客户机上可用的ntdll.dll等系统库(不要轻易尝试,可能会让系统奔溃),该机器上测试失败
- Qt bug列表网发现说是可能是机器显卡和显卡驱动不匹配导致,然后重装该机器显卡驱动(联想机器,在官网下载的专用显卡驱动,没有测试其他通用显卡驱动),还是没有解决
2. 问题定位
由于该客户机对解决问题的局限比较大,经过各种方法测试,后在工程中去掉QWebEngineView控件的使用,重新编译发现在该机器可以正常运行,但是少了用到QWebEngineView的部分功能
将问题定位为QWebEngineView(QtWebEngineProcess.exe)控件对部分旧机器或系统兼容性不好
3. 问题解决方案(选用Qt别的web控件)
在不同QT版本中使用的类和方法不同:
-
Qt4中使用webkit模块;
-
Qt5 ~Qt5.5使用webkitwidgets模块;
-
Qt5.6以上MSVC版本使用webenginewidgets模块,Qt5.6以后,移除了QtWebkit这个组件
-
Qt5.6以后的mingw版本,不能使用QtWebEngine,只能使用QAxWidget控件
项目当前使用的是第三种方式出现问题,排除第三种方式,第一种是Qt4也排除掉,可选第二种或者第四种,这里选择用QAxWidget的方案,不用降低Qt版本。
先引用下几种方式的使用方法:
1)Qt4版本
Qt4使用webkit模块。
1. 创建一个Widget类的QT工程,在pro工程文件中添加 webkit network :
QT += core gui webkit network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = web
TEMPLATE = app
SOURCES += main.cpp\
widget.cpp
HEADERS += widget.h
FORMS += widget.ui
2. 编辑widget.h文件,在Widget类中添加一个指针成员,该指针指向一个QWebView类:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtWebKit/QWebView>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
QWebView *view;
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
3. 修改widget.cpp,实现下面两个成员函数
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
Widget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
view = new QWebView(this);
view->load(QUrl("http://www.baidu.com"));
view.showMaximized();
}
Widget::~Widget()
{
delete view;
delete ui;
}
2) Qt5 ~ Qt5.5版本
Qt5版本之后,将QWebView、QWebFrame、QWebPage、QWebInspector等类被单独移到了webkitwidgets模块,不再在QtWebKit模块中,因此需使用webkitwidgets模块。
和Qt4的区别仅在于pro文件中添加的模块不同和widget.h文件中包含的头文件不同。
QT += core gui webkitwidgets network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtWebKitWidgets/QWebView>
3) Qt5.6及以上版本
Qt5.5.1是最后一个支持webkitwidgets的版本,在5.6版本以及之后的版本,Qt将webkitwidgets模块移除,并用一个新的模块webenginewidgets代替。
1. 创建一个Widget类的QT工程,在pro工程文件中添加 webenginewidgets:
QT += core gui webenginewidgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = web
TEMPLATE = app
SOURCES += \
main.cpp \
widget.cpp
HEADERS += \
widget.h
FORMS += \
widget.ui
RESOURCES +=
2. 编辑widget.h文件,包含QWebEngineView头文件,并在Widget类中添加一个指针成员,该指针指向一个QWebEngineView类:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QWebEngineView>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
QWebEngineView *view;
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
3. 修改widget.cpp,实现下面两个成员函数
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
Widget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
view = new QWebEngineView(this);
view->load(QUrl(QStringLiteral("http://www.baidu.com")));
view.showMaximized();
}
Widget::~Widget()
{
delete view;
delete ui;
}
4)Qt5.6以后的mingw版本,使用 QAxWidget 显示网页
使用QAxWidget前需要注意两点:
1.QAxContainer.lib,QAxServer.lib库不可缺。
2.开发机本身具有相应的IE游览器(注册表文件一致)
第①种 QAxContainer.lib,QAxServer.lib引用方式:
关于注册表文件:
QAxWidget *flash = new QAxWidget(0, 0); //QAxWidget使用的是ActiveX插件
flash->resize(600, 400); //设置该控件的初始大小
flash->setControl(QString::fromUtf8("{8856F961-340A-11D0-A96B-00C04FD705A2}"));//注册组件ID
flash->setProperty("DisplayAlerts", false);//不显示警告信息
flash->setProperty("DisplayScrollBars", true);//不显示滚动条
QString webstr = QString("www.baidu.com");//设置要打开的网页
flash->dynamicCall("Navigate(const QString&)", webstr);//显示网页
flash->show();
第②种 在pro文件里加入一句:
QT += axcontainer
显示网页的代码如下:
this->ui->axWidget->setControl(QString::fromUtf8("{8856F961-340A-11D0-A96B-00C04FD705A2}"));//注册组件ID
this->ui->axWidget->setProperty("DisplayAlerts",false);//不显示警告信息
this->ui->axWidget->setProperty("DisplayScrollBars",true);//不显示滚动条
QString webstr=QString("github.com");//设置要打开的网页
this->ui->axWidget->dynamicCall("Navigate(const QString&)",webstr);//显示网页
webWidget->dynamicCall("Navigate(const QString&)", "file:///"+QCoreApplication::applicationDirPath()+"/drawChart/report.html"); //加载本地文件
5)使用 QAxWidget 的注意点
这样显示普通的JavaScript脚本是没啥问题的,但是当引用jQuery等js的封装库的时候会无法解析,提示如下:
原因是该控件默认使用的版本是IE7的兼容模式,对jq不怎么支持,需要指定控件加载的IE内核版本来解决这个问题,有修改注册表,HTML中强制指定等方法,详细可参考:
WebBrowser控件默认使用IE9,IE10的方法
我的用途是加载本地HTML文件,所以选用里面最简便的方法是在.html文件添加下面的指定:
在html头 加标签 强制使用最新的ie渲染
<meta http-equiv="X-UA-Compatible" content="IE=edge">
或者强制使用最新的ie11渲染
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE11"/>
然后可以正常显示本地HTML页面了。