上一篇我们已经把duilib嵌入到win32程序里面了,现在把CEF也嵌入进去。

先把编译好的CEF源码存放到项目里面。在cef_binary_138.0.29+gae7ee9b+chromium-138.0.7204.169_windows32uild estscefsimpleDebug路径下把编译好的文件拷贝到我们自己项目程序目录下:

然后右击项目–>属性–>VC++目录添加CEF文件路径:
包含目录:
..cef_binary_138.0.29+gae7ee9b+chromium-138.0.7204.169_windows32
..cef_binary_138.0.29+gae7ee9b+chromium-138.0.7204.169_windows32 ests
库目录:
..cef_binary_138.0.29+gae7ee9b+chromium-138.0.7204.169_windows32uildlibcef_dll_wrapperDebug
..cef_binary_138.0.29+gae7ee9b+chromium-138.0.7204.169_windows32Debug

这样做是为了可以在项目中引用CEF的库文件和头文件。
在framework.h中把cef库文件添加进去:
#pragma comment(lib, “libcef_dll_wrapper.lib”)
#pragma comment(lib, “libcef.lib”)
创建Cef_App.cpp Cef_App.h与Cef_Handler.cpp Cef_Handler.h。里面的内容基本就是cefsimple里simple_app.cc simple_app.h与simple_handler.cc simple_handler.h的内容。
// 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.
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#include"Cef_undef.h"
#include "include/cef_app.h"
#include<string>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;
class CCef_app
: public CefApp
, public CefBrowserProcessHandler
, public CefRenderProcessHandler
{
public:
CCef_app();
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
override {
return this;
}
// 正确实现GetRenderProcessHandler方法这继承必须放到app里面放到handl里会报错
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override {
return this;
}
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) override;
virtual void OnBeforeCommandLineProcessing(
const CefString& process_type,
CefRefPtr<CefCommandLine> command_line);
virtual void OnContextInitialized() override;
private:
IMPLEMENT_REFCOUNTING(CCef_app);
};
#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
// 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 "include/cef_browser.h"
#include <string>
#include<tchar.h>
#include "Cef_app.h"
#include"Cef_Handler.h"
#include "cefsimple/simple_handler.h"
#include "include/cef_command_line.h"
#include "include/wrapper/cef_helpers.h"
using namespace std;
CCef_app::CCef_app()
{
}
void CCef_app::OnBeforeCommandLineProcessing(
const CefString& process_type,
CefRefPtr<CefCommandLine> command_line)
{
command_line->AppendSwitch("in-process-gpu");
}
//这是渲染进程的代码不好调试
void CCef_app::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
}
void CCef_app::OnContextInitialized() {
//只运行在UI线程上
CEF_REQUIRE_UI_THREAD();
}
// 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.
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#include "Cef_App.h"
#include "include/cef_client.h"
#include "include/base/cef_logging.h"
#include <list>
#include "include/base/cef_callback.h"
#include "include/wrapper/cef_closure_task.h"
class Cef_Handler
: public CefClient
, public CefDisplayHandler
, public CefLifeSpanHandler
, public CefRequestHandler
, public CefLoadHandler
{
public:
Cef_Handler();
virtual ~Cef_Handler();
//必须先调用此回调才能使用CefLoadHandler中的回调函数
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override
{
return this;
}
//必须先调用此回调才能使用CefRequestHandler中的回调函数
virtual CefRefPtr<CefRequestHandler> GetRequestHandler() override
{
return this;
}
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() override
{
return this;
}
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override
{
return this;
}
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) override;
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
static Cef_Handler* GetInstance();
// Request that all existing browser windows close.
void CloseAllBrowsers(bool force_close);
CefWindowHandle GetBrowserhWnd() const;
CefRefPtr<CefBrowser> GetBrowser() const;
CefRefPtr<CefFrame> GetMainFrame() const;
int GetIdentifier() const;
HWND GetPaintWindow() const;
HWND m_windowHwnd = NULL;
CefRefPtr<CefBrowser> m_pBrowser;
private:
typedef std::list<CefRefPtr<CefBrowser> > BrowserList;
BrowserList browser_list_;
HWND m_hParentWnd;
private:
IMPLEMENT_REFCOUNTING(Cef_Handler);
};
#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
// 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"framework.h"
#include "Cef_Handler.h"
#include <sstream>
#include <string>
#include "include/base/cef_bind.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
using namespace std;
namespace {
Cef_Handler* g_instance = NULL;
} // namespace
// static
Cef_Handler* Cef_Handler::GetInstance() {
return g_instance;
}
Cef_Handler::Cef_Handler()
{
DCHECK(!g_instance);
g_instance = this;
}
Cef_Handler::~Cef_Handler()
{
g_instance = NULL;
}
void Cef_Handler::OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title)
{
CEF_REQUIRE_UI_THREAD();
CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle();
SetWindowText(hwnd, title.ToWString().c_str());
}
void Cef_Handler::CloseAllBrowsers(bool force_close) {
}
void Cef_Handler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
m_windowHwnd = browser->GetHost()->GetWindowHandle();
m_pBrowser = browser;
// Add to the list of existing browsers.
browser_list_.push_back(browser);
}
void Cef_Handler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
}
CefRefPtr<CefBrowser> Cef_Handler::GetBrowser() const
{
if (!browser_list_.empty())
{
return browser_list_.front();
}
return nullptr;
// return m_pBrowser;
}
CefWindowHandle Cef_Handler::GetBrowserhWnd() const
{
return GetBrowser().get() != NULL ? GetBrowser()->GetHost()->GetWindowHandle() : NULL;
}
CefRefPtr<CefFrame> Cef_Handler::GetMainFrame() const
{
return GetBrowser().get() != NULL ? GetBrowser()->GetMainFrame() : nullptr;
}
int Cef_Handler::GetIdentifier() const
{
return GetBrowser() ? GetBrowser()->GetIdentifier() : -1;
}
HWND Cef_Handler::GetPaintWindow() const
{
return m_hParentWnd;
}
在CEF中如果继承了哪个基类第一步需要先获取该类的指针。
GetBrowserProcessHandler:提供自定义浏览器进程实体控制器的能力。
GetRenderProcessHandler:提供自定义renderer进程实体控制器的能力。
然后在main函数把加载CEF的代码加上试试:
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
//Cef
wstring LocalStoragePath = L"D:\COOKIES";
#if defined(WIN32) && !defined(UNDER_CE)
HRESULT Hr = ::CoInitialize(NULL);
#else
HRESULT Hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
if (FAILED(Hr)) return 0;
CefMainArgs args(hInstance);
//CefEnableHighDPISupport();
//填充这个结构体,定制CEF的行为。
void* sandbox_info = NULL;
CefSettings settings;
CefSettingsTraits::init(&settings);
settings.multi_threaded_message_loop = true;
settings.no_sandbox = true; // 测试环境开启
//settings.windowless_rendering_enabled = true;
CefString(&settings.locale).FromWString(L"zh-CN");
CefString(&settings.cache_path).FromWString(LocalStoragePath.c_str());
#if _DEBUG
CefString(&settings.log_file).FromWString(L"debug.log");
settings.log_severity = LOGSEVERITY_DEFAULT;
#endif
CefString(&settings.user_agent) = L"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.2228.0 Safari/537.36";
//创建CefApp实例
CefRefPtr<CCef_app> app(new CCef_app);
int exitCode = CefExecuteProcess(args, app, sandbox_info);
if (exitCode >= 0)
{
return exitCode;
}
CefInitialize(args, settings, app.get(), sandbox_info);
CPaintManagerUI::SetInstance(hInstance);
CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin"));
CFrameWnd duiFrame(L"frame.xml");
duiFrame.Create(NULL, _T("FrameWnd"), UI_WNDSTYLE_DIALOG | WS_MINIMIZEBOX, WS_EX_WINDOWEDGE);
duiFrame.ShowModal();
}
编译一下试试看:报错了。

解决这错误的方法如下图:

还有一个错误很莫名其妙GetNextSibling()函数报错,但是这是CEF自己的函数按理应该不会,查了资料原来是和windows里面头文件冲突。解决方案就是创建一个头文件Cef_undef.h在Cef_App.h里面引入它就可以了
#pragma once
#ifdef GetNextSibling
#undef GetNextSibling
#endif
#ifdef GetFirstChild
#undef GetFirstChild
#endif
// 其他可能冲突的宏
#ifdef GetNextSibling
#undef GetNextSibling
#endif
终于不报错了,但是大家会不会有个疑问,CEF和Duilib都加入到项目里面了,但是好像它们两并没有关联起来。我们现在就开始关联吧。
首先我们修改frame.xml内容添加:<cefbrowser name=”Cef_Browser”/>
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<Window size="800,600" >
<VerticalLayout bkcolor="#FF87CEEB">
<HorizontalLayout>
<cefbrowser name="Cef_Browser"/>
</HorizontalLayout>
</VerticalLayout>
</Window>
然后在项目里面创建CCefBrowserUI.cpp与CCefBrowserUI.h这个类是继承Duilib里面CControlUI但是是我们自己给UI界面扩展的一个浏览器控件。
#pragma once
#include"framework.h"
#include"Cef_Handler.h"
#ifdef _DEBUG
#define _ITERATOR_DEBUG_LEVEL 0 // speedup iterator operations while debugging
#endif
class CCefBrowserUI : public CControlUI
{
public:
CCefBrowserUI();
~CCefBrowserUI();
virtual LPCTSTR GetClass() const;
virtual LPVOID GetInterface(LPCTSTR pstrName);
//这个一定要设置,不然控件无法显示
virtual void SetPos(RECT rc, bool bNeedInvalidate = true);
//virtual void Move(SIZE szOffset, bool bNeedInvalidate = true);
virtual void SetInternVisible(bool bVisible = true);
//virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled);
virtual bool DoPaint(HDC hDC, const RECT& rcPaint, CControlUI* pStopControl);
CefRefPtr<CefBrowser> GetBrowser()
{
if (m_Browser)
{
return m_Browser->GetBrowser();
}
else
{
CreateBrowser(L"about:blank");
}
return m_Browser->GetBrowser();
}
CefRefPtr<CefFrame> GetMainFrame()
{
if (m_Browser)
{
return m_Browser->GetMainFrame();
}
else
{
CreateBrowser(L"about:blank");
}
return m_Browser->GetMainFrame();
}
void CloseAllBrowser();
void LoadUrl(const CefString& url);
void CreateBrowser(const CefString& szHomePage);
//当界面控件太少时,GetPos第一次获取的大小为0 DoPaint执行少导致cef绘制失败,需要手动设置RECT大小
void CreateBrowser(RECT ct, const CefString& szHomePage);
//protected:
private:
CefRefPtr<Cef_Handler> m_Browser = nullptr;
};
#include "CCefBrowserUI.h"
#include<tchar.h>
CCefBrowserUI::CCefBrowserUI() {
}
CCefBrowserUI::~CCefBrowserUI() {
//m_pManager->RemoveMessageFilter(this);
if (m_Browser != nullptr)
{
m_Browser->GetBrowser()->GetHost()->CloseBrowser(true);
m_Browser = nullptr;
}
}
LPCTSTR CCefBrowserUI::GetClass() const {
return _T("CefBrowserUI");
}
LPVOID CCefBrowserUI::GetInterface(LPCTSTR pstrName)
{
if (_tcsicmp(pstrName, _T("Cef_Browser")) == 0)
{
return static_cast<CCefBrowserUI*>(this);
}
return CControlUI::GetInterface(pstrName);
}
void CCefBrowserUI::SetPos(RECT rc, bool bNeedInvalidate /* = true */)
{
CControlUI::SetPos(rc, bNeedInvalidate);
if (m_Browser.get())
{
CefRefPtr<CefBrowser> browser = m_Browser->GetBrowser();
DuiLib::CDuiRect rc = GetPos();
if (browser && !rc.IsNull())
{
::SetWindowPos(browser->GetHost()->GetWindowHandle(),
NULL, rc.left, rc.top, rc.GetWidth(), rc.GetHeight(), SWP_NOZORDER | SWP_NOACTIVATE);
}
}
}
void CCefBrowserUI::SetInternVisible(bool bVisible)
{
}
bool CCefBrowserUI::DoPaint(HDC hDC, const RECT& rcPaint, CControlUI* pStopControl)
{
if (IsVisible() && GetBrowser().get())
{
HWND hBrowserWnd = m_Browser->GetBrowserhWnd();
::MoveWindow(hBrowserWnd
, m_rcItem.left
, m_rcItem.top
, m_rcItem.right - m_rcItem.left
, m_rcItem.bottom - m_rcItem.top
, true);
if (!::IsWindowVisible(hBrowserWnd))
{
::ShowWindow(hBrowserWnd, IsVisible());
}
}
return __super::DoPaint(hDC, rcPaint, pStopControl);
}
void CCefBrowserUI::CloseAllBrowser()
{
if (m_Browser.get())
{
m_Browser->CloseAllBrowsers(true);
}
}
void CCefBrowserUI::CreateBrowser(const CefString& szHomePage)
{
CefWindowInfo info;
if (m_Browser == NULL)
{
m_Browser = new Cef_Handler();
if (m_Browser != NULL)
{
RECT rt = GetPos();
CefRect Ret(0, 0, 1, 1);
info.SetAsChild(GetManager()->GetPaintWindow(), Ret);
info.style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
CefBrowserSettings browserSettings;
CefRefPtr<CefRequestContext> rc = CefRequestContext::GetGlobalContext();
//browserSettings.image_loading = STATE_DISABLED;
//browserSettings.remote_fonts = STATE_DISABLED;
CefBrowserHost::CreateBrowser(info, m_Browser.get(), szHomePage, browserSettings, nullptr, rc);
}
}
if (m_Browser->GetMainFrame().get())
{
m_Browser->GetMainFrame()->LoadURL(szHomePage.c_str());
}
return;
}
void CCefBrowserUI::CreateBrowser(RECT ct, const CefString& szHomePage)
{
CefWindowInfo info;
if (m_Browser == NULL)
{
m_Browser = new Cef_Handler();
if (m_Browser != NULL)
{
RECT rt = GetPos();
CefRect Ret(rt.left, rt.right, rt.top, rt.bottom);
info.SetAsChild(GetManager()->GetPaintWindow(), Ret);
CefBrowserSettings browserSettings;
CefRefPtr<CefRequestContext> rc = CefRequestContext::GetGlobalContext();
CefBrowserHost::CreateBrowser(info, m_Browser.get(), szHomePage, browserSettings, nullptr, rc);
}
}
else
{
if (m_Browser->GetMainFrame().get())
{
m_Browser->GetMainFrame()->LoadURL(szHomePage.c_str());
}
}
return;
}
void CCefBrowserUI::LoadUrl(const CefString& url)
{
if (m_Browser.get())
{
CefRefPtr<CefFrame> mainframe = m_Browser->GetMainFrame();
if (mainframe)
mainframe->LoadURL(url);
}
}
GetInterface:获取frame.xml中扩展的cefbrowser名字。
CreateBrowser:创建浏览器实例。
LoadUrl:如果已经创建浏览器实例也可以用该函数打开网址。
好了最后一步就是我们要在CFrameWnd.cpp里面创建一个cefbrowser扩展控件这样就把CEF与Duilib界面关联起来了。
CControlUI* CFrameWnd::CreateControl(LPCTSTR pstrClassName)//创建新的控件
{
if (_tcsicmp(pstrClassName, _T("cefbrowser")) == 0)
{
return new CCefBrowserUI;
}
return NULL;
}
编译运行下看看,竟然崩溃了,而且是一个很莫名其妙的崩溃,堆栈也没有任何提示。在libcef.dll报错了。没办法只能到处找解决方案。


终于在CEF官网找到一个解决方案,开启GPU渲染可以解决问题:command_line->AppendSwitch(“in-process-gpu”)
最后编译下试试效果:
