上一篇我们编译了vs2017版本的Cef3,记得那一步的需要修改成/MD,编译出Debug版本和Release版本。
我们还是以cef_binary_75.0.1+gb5e74dd+chromium-75.0.3770.80_windows64.tar.bz2这个版本为准。(原因见上一篇博客)。
我们修改simple_app.cc中的url为"http://www.baidu.com",否则会发现打开cefsimple.exe时半天显示不了网页。(原链接为"http://www.google.com",不科学上网,半天出现不了。)
我们以编译的x64 Release版本为例:
在VS2017中,设置cefsimple为启动项目,或者在在build文件夹下,双击cefsimple.exe打开。
注意观察这个目录的文件结构。
在CMake的输出窗口可以看到:
我把它稍微排版了一下:
CEF Binary files:
chrome_elf.dll;
d3dcompiler_47.dll;
libcef.dll;
libEGL.dll;
libGLESv2.dll;
natives_blob.bin;
snapshot_blob.bin;
v8_context_snapshot.bin;swiftshader
CEF Resource files:
cef.pak;
cef_100_percent.pak;
cef_200_percent.pak;
cef_extensions.pak;
devtools_resources.pak;
icudtl.dat;
locales
这些都是我们将来编译的exe同级目录中需要的文件,有CEF二进制文件和CEF资源文件。
运行效果:
我们现在将把这个例子在Qt中实现出来。
我们新建一个Qt Widgets Application工程,保留ui文件。
就是一个布局加一个QWidget,命令为contentWidget
新看看工程的目录结构
这个include文件夹就
lib存放我们编译出来的Debug版本库和Release库
其实我们编译的时候
这个cef_sandbox.lib在我们这个demo中用不到。
cef.pri是我们建立的一个文本文件,内容:
INCLUDEPATH += $$PWD
INCLUDEPATH += $$PWD/include
win32:CONFIG(debug, debug|release) {
LIBS += $$PWD/lib/debug/libcef.lib \
$$PWD/lib/debug/libcef_dll_wrapper.lib \
user32.lib
}
else:win32:CONFIG(release, debug|release) {
LIBS += $$PWD/lib/release/libcef.lib \
$$PWD/lib/release/libcef_dll_wrapper.lib \
user32.lib
}
这样我们就可以Debug版本和Release版本分别链接不同的lib库。
把vs工程中的
simple_app.h
simple_app.cc
simple_handler.h
simple_handler.cc
都拷贝到Qt工程中
注意修改:
在simple_handler.h中添加getCefBrowerList函数
simple_handler.cpp中添加函数定义:
std::list<CefRefPtr<CefBrowser> > SimpleHandler::getCefBrowerList()
{
return browser_list_;
}
simple_handler.cc中有些地方需要注释掉,我们看看这个文件的全部代码:
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "simple_handler.h"
#include <sstream>
#include <string>
#include "include/base/cef_bind.h"
#include "include/cef_app.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
namespace {
SimpleHandler* g_instance = NULL;
} // namespace
SimpleHandler::SimpleHandler(bool use_views)
: use_views_(use_views), is_closing_(false) {
DCHECK(!g_instance);
g_instance = this;
}
SimpleHandler::~SimpleHandler() {
g_instance = NULL;
}
// static
SimpleHandler* SimpleHandler::GetInstance() {
return g_instance;
}
void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) {
CEF_REQUIRE_UI_THREAD();
if (use_views_) {
// Set the title of the window using the Views framework.
CefRefPtr<CefBrowserView> browser_view =
CefBrowserView::GetForBrowser(browser);
if (browser_view) {
CefRefPtr<CefWindow> window = browser_view->GetWindow();
if (window)
window->SetTitle(title);
}
} else {
// Set the title of the window using platform APIs.
// PlatformTitleChange(browser, title);
}
}
void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Add to the list of existing browsers.
browser_list_.push_back(browser);
}
bool SimpleHandler::DoClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Closing the main window requires special handling. See the DoClose()
// documentation in the CEF header for a detailed destription of this
// process.
if (browser_list_.size() == 1) {
// Set a flag to indicate that the window close should be allowed.
is_closing_ = true;
}
// Allow the close. For windowed browsers this will result in the OS close
// event being sent.
return false;
}
void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Remove from the list of existing browsers.
BrowserList::iterator bit = browser_list_.begin();
for (; bit != browser_list_.end(); ++bit) {
if ((*bit)->IsSame(browser)) {
browser_list_.erase(bit);
break;
}
}
if (browser_list_.empty()) {
// All browser windows have closed. Quit the application message loop.
CefQuitMessageLoop();
}
}
void SimpleHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) {
CEF_REQUIRE_UI_THREAD();
// Don't display an error for downloaded files.
if (errorCode == ERR_ABORTED)
return;
// Display a load error message.
std::stringstream ss;
ss << "<html><body bgcolor=\"white\">"
"<h2>Failed to load URL "
<< std::string(failedUrl) << " with error " << std::string(errorText)
<< " (" << errorCode << ").</h2></body></html>";
frame->LoadString(ss.str(), failedUrl);
}
void SimpleHandler::CloseAllBrowsers(bool force_close) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::Bind(&SimpleHandler::CloseAllBrowsers, this,
force_close));
return;
}
if (browser_list_.empty())
return;
BrowserList::const_iterator it = browser_list_.begin();
for (; it != browser_list_.end(); ++it)
(*it)->GetHost()->CloseBrowser(force_close);
}
std::list<CefRefPtr<CefBrowser> > SimpleHandler::getCefBrowerList()
{
return browser_list_;
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include "simple_handler.h"
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget() override;
protected:
void resizeEvent(QResizeEvent *event) override;
private:
Ui::Widget *ui;
CefRefPtr<SimpleHandler> m_browserHander;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <WinUser.h>
#include <QDesktopWidget>
#include <QMessageBox>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
HWND wnd = (HWND)ui->contentWidget->winId();
QString indexUrl = "www.baidu.com";
RECT winRect;
QRect messageRect(0, 0, 800, 600);
winRect.left = messageRect.left();
winRect.top = messageRect.top();
winRect.right = messageRect.right();
winRect.bottom = messageRect.bottom();
CefWindowInfo cefWndInfo;
cefWndInfo.SetAsChild(wnd, winRect); //将cef界面嵌入qt界面中
CefBrowserSettings cefBrowSetting;
m_browserHander = CefRefPtr<SimpleHandler>(new SimpleHandler(false));
bool browser = CefBrowserHost::CreateBrowser(cefWndInfo, m_browserHander, CefString(indexUrl.toStdWString()), cefBrowSetting, nullptr, nullptr);
if (!browser)
{
QMessageBox::information(NULL, "warning", "brower create failed!");
}
}
void Widget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
if (!m_browserHander.get())
{
return;
}
std::list<CefRefPtr<CefBrowser> > cefBrowerList = m_browserHander->getCefBrowerList();
if (cefBrowerList.size() == 0)
{
qDebug() << "cefBrowerList.size() == 0"; //第一次resizeEvent的时候,这个为0。Qt中resizeEvent只有在窗口显示出来后,才能获取到正确的窗口尺寸值。
return;
}
CefRefPtr<CefBrowser> pBrower = cefBrowerList.front();
if (pBrower.get())
{
HWND hwnd_ = pBrower->GetHost()->GetWindowHandle();
if (hwnd_)
{
QSize browserSize = ui->contentWidget->size();
::MoveWindow(hwnd_, 0, 0, browserSize.width(), browserSize.height(), TRUE);
}
}
}
Widget::~Widget()
{
delete ui;
}
最重要的是
main.cpp
#include "widget.h"
#include "simple_app.h"
#include "simple_handler.h"
#include <QApplication>
void QCefInitSettings(CefSettings & settings)
{
//std::string cache_path = AppGetWorkingDirectory().toStdString() + "/.cache";//缓存地址
// CefString(&settings.cache_path) = CefString(cache_path);
settings.multi_threaded_message_loop = true;//多线程消息循环
settings.log_severity = LOGSEVERITY_DISABLE;//日志
// settings.single_process = true;
settings.no_sandbox = true;//沙盒
}
int QCefInit(int& argc, char** argv)
{
HINSTANCE hInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
CefMainArgs mainArgs(hInstance);
CefRefPtr<SimpleApp> app(new SimpleApp); //CefApp实现,用于处理进程相关的回调。
int exit_code = CefExecuteProcess(mainArgs, app.get(), nullptr);
if (exit_code >= 0) {
return exit_code;
}
CefSettings settings;
QCefInitSettings(settings);
CefInitialize(mainArgs, settings, app, nullptr);
return -1;
}
void CefQuit()
{
CefShutdown();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
int result = QCefInit(argc, argv);
if (result >= 0) {
return result;
}
Widget w;
w.show();
a.exec();
CefQuit();
return 0;
}
编译成功,运行不了。-_-||
需要将Cef需要的二进制文件和资源文件全部拷贝到Qt编译目录中(区分Debug和Release)
再运行:
我会将这个demo放上来,让大家下载。
链接:https://pan.baidu.com/s/1lo3SJxpJBBwH8cP3uU6oyg
提取码:5q4r
如果运行不起来,我无法一一解答,因为这里编译的的动态库版本的cef3,可能每个人的运行库不同,还有windows系统安装的一些库不同等都有差别,包括编译使用的windows SDK的版本也是不同的。需要自己手动编译cef3,自行摸索。