- 编译示例测试
- 编译MD静态库
- 正确且简单的做法
- 为啥要这么做?
- 编译
- 测试
- 需要拷贝的文件
- Hello World
- 显示一个网页窗口
- 优化:将主程序抽象为App类,窗口由其管理控制
- App类
- WindowDelegate类
- main.cpp
- 最终效果
参考:CEF 桌面软件开发实战 - 刘晓伦liulun - 掘金小册 (juejin.cn)
相关链接
编译示例测试
编译环境:
将CMakeList.txt
拖入CMake,创建输出目录后,即可直接Configure + Generate(遇到一个找不到Doxygen的错误,没管)
最后点击“Open Project”,用VS2022打开工程
右键cefsimple解决方案,生成,启动新实例,即可检验是否编译生成成功

直接执行默认打开google的首页,可以在simple_app.cc
文件中修改默认链接地址

打开后界面如下

生成后的exe保存到了构建输出目录的tests\cefsimple\Debug
文件夹下(解决方案配置选择Release则在tests\cefsimple\Release
下),这里面包含了一个完整的CEF软件,打开cefsample.exe
,可以感受一下启动并渲染网页的速度(吊打Electron)。

编译MD静态库
原本想着把库编译成dll就可以减小libcel.dll的体积,后来发现根本没法减少,天真了哈哈哈。
正确且简单的做法
chromiumembedded / cef / wiki / LinkingDifferentRunTimeLibraries — Bitbucket
在CMake构建阶段,把CEF_RUNTIME_LIBRARY_FLAG
改成/MD
即可。

然后把USE_SANDBOX
勾掉

构建,生成,打开VS。然后在批生成里面,把libcef_dll_wrapper
的两个配置都生成一下,分别得到Debug和Release的MD的.lib文件,完成。
下面都不对!!
为啥要这么做?
现有工程用的MD,就这么简单。
根本没法减少libcef.dll的体积,那里面有个chrome,减个屁。
在源码生成的工程中,解决方案都默认采用了MT的运行库,采用MT的好处在于:打包生成的dll中包含了所有软件运行时需要的环境支撑,不再需要引入诸如MSVCRxxx.dll
等VC环境,看着相对简洁且易于拷贝,适用于独立应用。其缺点在于,该部分dll体积就要大得多(默认使用的libcef.dll
在Release模式下达到了180M以上)。
使用MD运行库的好处在于,CEF生成的dll仅包含了自身的函数接口,其余的系统函数调用仍需要依赖MSVCRxxx.dll
、VCRUNTIME.dll
等dll,这种时候编译的dll体积就会小很多,适用于嵌入到已有的工程中。
由于想在MFC应用中嵌入CEF,因此选择将CEF编译为动态库。
如果需要构建使用MT运行库的CEF应用,可以完全参考解决方案cefsimple
的工程配置以及其中的cefsimple_win.cc
文件,包括头文件引入目录、lib链接、预定义等。
编译
只需要将CEF_USE_SANDBOX
删掉即可,因为沙盒用的是MT,不这么干会报错。
如何将cef静态库(/MT)编译为动态库(/MD)cef库峰峰小筑的博客-CSDN博客
下面开始编译
右键libcef_dll_wrapper
属性
将配置类型改为.dll,点击应用
在“高级”里面,将目标文件扩展名改为.dll
将MFC的使用改成“在共享DLL中使用MFC”
在C/C++,代码生成,将运行库改成MD
在C/C++,将预处理器定义里面的CEF_USE_SANDBOX
删掉
在链接器-输入-附加依赖项,将cefsimple
该位置处的所有内容拷贝过来,并且删掉两处:libcef_dll_wrapper.lib
和cef_sandbox.lib
(若在后续开发中需要打开互联网上的网页,则建议手动编译一个带sandbox的dll,或者使用MT。这里是因为只打算加载自己的页面,以及sandbox不太好编译,故直接放弃)
配置完成后,点击 生成-批生成,将libcef_dll_wrapper
Debug和Release勾上,即可生成dll
为什么没有lib?因为没有选择导出函数,导出函数需要在函数定义处加上__declspec(dllexport)
,这里暂时不这么做,后续需要时考虑按需导出。
测试
。。。

需要拷贝的文件
编译完成后,源码工程中除了编译成果,还有一些文件需要直接拷贝至运行目录才能保证cef正确运行:


最后得到应用程序的大体结构:

Hello World
项目配置:在自定工程创建cef
文件夹,将源码目录下的include文件夹拷贝到该文件夹下;创建cef/lib/Debug
和cef/lib/Release
,拷贝相应的.lib
文件,如下所示:

引用目录就写../cef
,库目录写../cef/lib/$(Configuration)/*.lib
。
显示一个网页窗口
用最少的代码,调用CEF,显示一个网页窗口(但有很多不合理的地方以及运行时会出BUG)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <include/cef_app.h>
int main() { CefEnableHighDPISupport();
CefMainArgs mainArgs;
int exitCode = CefExecuteProcess(mainArgs, nullptr, nullptr); if (exitCode >= 0) return exitCode;
CefSettings settings; settings.no_sandbox = true;
CefInitialize(mainArgs, settings, nullptr, nullptr);
CefWindowInfo winInfo; CefBrowserSettings browserSettings; browserSettings.windowless_frame_rate = 60; CefBrowserHost::CreateBrowser(winInfo, nullptr, "http://baidu.com", browserSettings, nullptr, nullptr);
CefRunMessageLoop(); CefShutdown(); return 0; }
|
优化:将主程序抽象为App类,窗口由其管理控制
App类
作为整个应用程序的入口,用于管理一个或多个窗口
App.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #pragma once #include <include/cef_app.h>
class App : public CefApp, public CefBrowserProcessHandler { public: App() = default; CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override { return this; } void OnContextInitialized() override;
private: IMPLEMENT_REFCOUNTING(App); };
|
App.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include "App.h" #include <include/wrapper/cef_helpers.h> #include <include/views/cef_browser_view.h> #include <include/views/cef_window.h> #include "WindowDelegate.h"
void App::OnContextInitialized() { CEF_REQUIRE_UI_THREAD();
auto url = "http://baidu.com"; CefBrowserSettings settings; CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView( nullptr, url, settings, nullptr, nullptr, nullptr);
CefWindow::CreateTopLevelWindow(new WindowDelegate(browser_view)); }
|
WindowDelegate类
用于代理浏览窗口视图,可以响应窗口的创建、销毁、尺寸变化、鼠标键盘点击等事件。
WindowDelegate.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #pragma once #include <include/views/cef_window.h> #include <include/views/cef_browser_view.h>
class WindowDelegate :public CefWindowDelegate { public: explicit WindowDelegate(CefRefPtr<CefBrowserView> browser_view) : _browser_view(browser_view) { }
WindowDelegate(const WindowDelegate&) = delete; WindowDelegate& operator=(const WindowDelegate&) = delete;
void OnWindowCreated(CefRefPtr<CefWindow> window) override; void OnWindowDestroyed(CefRefPtr<CefWindow> window) override; CefRect GetInitialBounds(CefRefPtr<CefWindow> window) override;
private: CefRefPtr<CefBrowserView> _browser_view;
IMPLEMENT_REFCOUNTING(WindowDelegate); };
|
WindowDelegate.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include "WindowDelegate.h" #include <include/cef_app.h> #include <include/views/cef_display.h>
void WindowDelegate::OnWindowCreated(CefRefPtr<CefWindow> window) { window->AddChildView(_browser_view); window->Show();
_browser_view->RequestFocus(); window->SetTitle(L"Hello World"); }
void WindowDelegate::OnWindowDestroyed(CefRefPtr<CefWindow> window) { _browser_view = nullptr; CefQuitMessageLoop(); }
CefRect WindowDelegate::GetInitialBounds(CefRefPtr<CefWindow> window) { CefRefPtr<CefDisplay> display = CefDisplay::GetPrimaryDisplay(); CefRect rect = display->GetBounds();
int width = 1366; int height = 768;
rect.x = static_cast<int>((rect.width - width) / 2); rect.y = static_cast<int>((rect.height - height) / 2); rect.width = width; rect.height = height;
return rect; }
|
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <include/cef_app.h> #include "App.h"
int main() { CefEnableHighDPISupport();
CefMainArgs mainArgs;
int exitCode = CefExecuteProcess(mainArgs, nullptr, nullptr); if (exitCode >= 0) return exitCode;
CefSettings settings; settings.no_sandbox = true;
CefRefPtr<App> app(new App()); CefInitialize(mainArgs, settings, app.get(), nullptr);
CefRunMessageLoop(); CefShutdown(); return 0; }
|
最终效果
