告别白屏!手把手教你用VS2019和MFC搞定CEF92.0集成(附完整源码和避坑清单)
深度解析VS2019MFC与CEF92.0无缝集成的实战指南CEFChromium Embedded Framework作为将Chromium浏览器内核嵌入应用程序的强大工具在现代桌面应用开发中扮演着重要角色。本文将带领C开发者深入探索如何在VS2019环境下通过MFC框架实现CEF92.0版本的完美集成解决开发过程中常见的白屏、链接错误等棘手问题。1. 环境准备与基础配置1.1 CEF92.0资源获取与目录规划首先需要从CEF官方站点获取对应版本的二进制包cef_binary_92.0.27g274abcfchromium-92.0.4515.159_windows32。解压后建议按照以下目录结构组织项目资源项目根目录/ ├── cefLib/ │ ├── bin/ │ │ ├── x86/ │ │ │ ├── Debug/ │ │ │ └── Release/ │ ├── lib/ │ │ ├── x86/ │ │ │ ├── Debug/ │ │ │ └── Release/ │ └── src/ │ ├── include/ │ ├── libcef_dll/ │ └── tests/关键文件部署说明将下载包中的include、libcef_dll、tests等目录复制到cefLib/src下Debug版本的libcef.lib、cef_sandbox.lib放入cefLib/lib/x86/DebugRelease版本的对应文件放入cefLib/lib/x86/Release编译生成的libcef_dll_wrapper.lib和.pdb文件分别放入对应的bin目录1.2 VS2019项目初始设置创建MFC对话框项目后需要进行以下基础配置项目属性 高级 MFC的使用选择在静态库中使用MFCVC目录 包含目录添加$(ProjectDir)cefLib\srcC/C 预处理器 预处理器定义Debug环境下必须添加_HAS_ITERATOR_DEBUGGING0C/C 代码生成 运行库Debug多线程调试(/MTd)Release多线程(/MT)特别注意预处理器定义_HAS_ITERATOR_DEBUGGING0是解决Debug环境下链接错误的关键缺少此项会导致_ITERATOR_DEBUG_LEVEL不匹配的编译错误。2. 链接器与清单文件配置2.1 链接器设置在链接器 输入 附加依赖项中添加以下库文件根据配置选择对应版本libCEF\lib\x86\Debug\libcef.lib libCEF\lib\x86\Debug\libcef_dll_wrapper.lib libCEF\lib\x86\Debug\cef_sandbox.lib或者直接在代码中通过#pragma comment引入#ifdef _DEBUG #pragma comment(lib, libCEF\\lib\\x86\\Debug\\libcef.lib) #pragma comment(lib, libCEF\\lib\\x86\\Debug\\libcef_dll_wrapper.lib) #pragma comment(lib, libCEF\\lib\\x86\\Debug\\cef_sandbox.lib) #endif2.2 清单文件配置在项目根目录创建my.manifest文件内容如下?xml version1.0 encodingutf-8? assembly xmlnsurn:schemas-microsoft-com:asm.v1 manifestVersion1.0 compatibility xmlnsurn:schemas-microsoft-com:compatibility.v1 application supportedOS Id{1f676c76-80e1-4239-95bb-83d0f6d0da78}/ supportedOS Id{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}/ /application /compatibility /assembly然后在清单工具 输入和输出 附加清单文件中添加my.manifest。经验分享缺少正确的清单文件配置是导致CEF白屏问题的常见原因之一特别是在Windows 8.1及更高版本系统中。这个问题曾经困扰我整整一天时间直到发现这个隐藏的兼容性设置。3. CEF核心类实现3.1 CCefBrowserApp类CCefBrowserApp是CEF应用的入口类继承自CefApp和CefBrowserProcessHandler// CCefBrowserApp.h #pragma once #include include/cef_app.h class CCefBrowserApp : public CefApp, public CefBrowserProcessHandler { public: CCefBrowserApp(); virtual ~CCefBrowserApp(); // CefApp方法 virtual CefRefPtrCefBrowserProcessHandler GetBrowserProcessHandler() override { return this; } // CefBrowserProcessHandler方法 virtual void OnContextInitialized() override; private: IMPLEMENT_REFCOUNTING(CCefBrowserApp); };对应的实现文件// CCefBrowserApp.cpp #include pch.h #include CCefBrowserApp.h CCefBrowserApp::CCefBrowserApp() {} CCefBrowserApp::~CCefBrowserApp() {} void CCefBrowserApp::OnContextInitialized() { // 浏览器上下文初始化完成后的处理 }3.2 CCefBrowserEventHandler类CCefBrowserEventHandler处理浏览器事件是CEF与MFC交互的核心// CCefBrowserEventHandler.h #pragma once #include include/cef_client.h #include list class CCefBrowserEventHandler : public CefClient, public CefDisplayHandler, public CefLifeSpanHandler, public CefLoadHandler { public: explicit CCefBrowserEventHandler(bool use_views); ~CCefBrowserEventHandler(); static CCefBrowserEventHandler* GetInstance(); // CefClient方法 CefRefPtrCefDisplayHandler GetDisplayHandler() override { return this; } CefRefPtrCefLifeSpanHandler GetLifeSpanHandler() override { return this; } CefRefPtrCefLoadHandler GetLoadHandler() override { return this; } void CloseAllBrowsers(bool force_close); bool IsClosing() const { return is_closing_; } bool DoClose(CefRefPtrCefBrowser browser) override; void OnBeforeClose(CefRefPtrCefBrowser browser) override; void OnAfterCreated(CefRefPtrCefBrowser browser) override; static bool IsChromeRuntimeEnabled(); private: const bool use_views_; typedef std::listCefRefPtrCefBrowser BrowserList; BrowserList browser_list_; bool is_closing_; IMPLEMENT_REFCOUNTING(CCefBrowserEventHandler); };实现文件中需要特别注意浏览器生命周期管理// CCefBrowserEventHandler.cpp #include pch.h #include CCefBrowserEventHandler.h // 其他必要的include... CCefBrowserEventHandler* CCefBrowserEventHandler::GetInstance() { return g_instance; } bool CCefBrowserEventHandler::DoClose(CefRefPtrCefBrowser browser) { CEF_REQUIRE_UI_THREAD(); if (browser_list_.size() 1) { is_closing_ true; } return false; } void CCefBrowserEventHandler::OnBeforeClose(CefRefPtrCefBrowser browser) { CEF_REQUIRE_UI_THREAD(); // 从浏览器列表中移除 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()) { CefQuitMessageLoop(); } } void CCefBrowserEventHandler::OnAfterCreated(CefRefPtrCefBrowser browser) { CEF_REQUIRE_UI_THREAD(); browser_list_.push_back(browser); }4. CEF初始化与窗口集成4.1 CEF初始化在MFC应用的InitInstance()方法中进行CEF初始化BOOL CYourMFCApp::InitInstance() { // CEF初始化 void* sandbox_info nullptr; CefMainArgs main_args(m_hInstance); CefRefPtrCCefBrowserApp app(new CCefBrowserApp); int exit_code CefExecuteProcess(main_args, nullptr, sandbox_info); if (exit_code 0) { return exit_code; } CefSettings settings; settings.no_sandbox true; settings.multi_threaded_message_loop true; settings.ignore_certificate_errors true; settings.command_line_args_disabled true; if (!CefInitialize(main_args, settings, app.get(), sandbox_info)) { return FALSE; } // ...其他初始化代码... // 在返回前添加CEF清理 CefQuitMessageLoop(); CefShutdown(); return TRUE; }4.2 MFC窗口集成在对话框资源中添加一个Picture Control控件设置ID为IDC_STATIC_BODY类型为Rectangle初始状态为不可见。在对话框的OnInitDialog()方法中添加浏览器集成代码BOOL CYourMFCDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 获取占位控件的位置和大小 CRect rtBody; GetDlgItem(IDC_STATIC_BODY)-GetWindowRect(rtBody); ScreenToClient(rtBody); // 调整位置考虑边框和标题栏 RECT rcBody { rtBody.left, rtBody.top, rtBody.right, rtBody.bottom }; // 设置CEF窗口信息 CefWindowInfo window_info; window_info.SetAsChild(GetSafeHwnd(), rcBody); // 浏览器设置 CefBrowserSettings browser_settings; // 创建浏览器实例 CefRefPtrCCefBrowserEventHandler handler(new CCefBrowserEventHandler(false)); CefBrowserHost::CreateBrowser( window_info, handler, https://example.com, // 初始URL browser_settings, nullptr, nullptr ); return TRUE; }5. 常见问题解决方案5.1 白屏问题排查清单清单文件验证确保my.manifest文件存在且内容正确DPI感知设置在stdafx.h中添加DPI感知声明#pragma comment(linker, \/manifestdependency:typewin32 \ nameMicrosoft.Windows.Common-Controls version6.0.0.0 \ processorArchitecture* publicKeyToken6595b64144ccf1df language*\)资源路径检查确认所有CEF资源文件特别是libcef.dll位于可执行文件的子目录中控制台输出启用CEF日志记录以获取更多调试信息CefSettings settings; settings.log_severity LOGSEVERITY_VERBOSE;5.2 调试符号警告处理Debug模式下运行时可能出现未加载libcef.dll.pdb警告这是正常现象不影响功能。如需消除警告从CEF二进制包中获取对应的.pdb文件将其放在与libcef.dll相同的目录中或在VS2019中设置符号路径调试 选项 调试 符号 添加CEF的pdb文件路径5.3 内存泄漏检测CEF与MFC集成时可能会触发误报的内存泄漏检测可在stdafx.h中添加以下代码抑制#ifdef _DEBUG #define _CRTDBG_MAP_ALLOC #include stdlib.h #include crtdbg.h #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW #endif并在应用退出时调用_CrtDumpMemoryLeaks();6. 高级功能扩展6.1 浏览器控制功能实现在对话框类中添加浏览器控制方法// 刷新页面 void CYourMFCDlg::OnBnClickedButtonRefresh() { if (CCefBrowserEventHandler::GetInstance() !CCefBrowserEventHandler::GetInstance()-IsClosing()) { auto browser CCefBrowserEventHandler::GetInstance()-GetBrowser(); if (browser) { browser-Reload(); } } } // 导航到指定URL void CYourMFCDlg::NavigateTo(const CString url) { if (CCefBrowserEventHandler::GetInstance() !CCefBrowserEventHandler::GetInstance()-IsClosing()) { auto browser CCefBrowserEventHandler::GetInstance()-GetBrowser(); if (browser) { CefString cefUrl(url); browser-GetMainFrame()-LoadURL(cefUrl); } } } // 前进/后退 void CYourMFCDlg::OnBnClickedButtonGoBack() { if (CCefBrowserEventHandler::GetInstance() !CCefBrowserEventHandler::GetInstance()-IsClosing()) { auto browser CCefBrowserEventHandler::GetInstance()-GetBrowser(); if (browser browser-CanGoBack()) { browser-GoBack(); } } }6.2 JavaScript与C交互在CCefBrowserEventHandler中添加CefRenderProcessHandler支持virtual CefRefPtrCefRenderProcessHandler GetRenderProcessHandler() override { return this; } // 处理进程消息 virtual bool OnProcessMessageReceived( CefRefPtrCefBrowser browser, CefRefPtrCefFrame frame, CefProcessId source_process, CefRefPtrCefProcessMessage message) override { const CefString message_name message-GetName(); if (message_name my_message) { // 处理来自JavaScript的消息 return true; } return false; }在JavaScript中发送消息window.cefQuery({ request: JSON.stringify({type: my_message, data: Hello from JS}), onSuccess: function(response) { console.log(response); }, onFailure: function(error_code, error_message) { console.error(error_message); } });6.3 自适应布局处理在对话框的OnSize()方法中添加浏览器窗口大小调整逻辑void CYourMFCDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); if (CCefBrowserEventHandler::GetInstance() !CCefBrowserEventHandler::GetInstance()-IsClosing()) { auto browser CCefBrowserEventHandler::GetInstance()-GetBrowser(); if (browser GetDlgItem(IDC_STATIC_BODY)) { CRect rect; GetDlgItem(IDC_STATIC_BODY)-GetWindowRect(rect); ScreenToClient(rect); CefWindowHandle hwnd browser-GetHost()-GetWindowHandle(); if (hwnd) { ::SetWindowPos(hwnd, NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER); } } } }7. 性能优化与最佳实践7.1 资源加载优化CefBrowserSettings browser_settings; // 禁用插件减少内存占用 browser_settings.plugins STATE_DISABLED; // 禁用Java browser_settings.java STATE_DISABLED; // 禁用WebGL browser_settings.webgl STATE_DISABLED; // 设置缓存路径 CefString(settings.cache_path).FromString(cache);7.2 多进程模型配置// 设置子进程路径 CefString(settings.browser_subprocess_path).FromString(subprocess.exe); // 在InitInstance()中处理命令行参数 CefMainArgs main_args(m_hInstance); if (CefExecuteProcess(main_args, nullptr, sandbox_info) 0) { return 0; // 子进程已退出 }7.3 安全配置建议// 禁用远程字体加载 browser_settings.remote_fonts STATE_DISABLED; // 禁用数据库 browser_settings.databases STATE_DISABLED; // 禁用本地存储 browser_settings.local_storage STATE_DISABLED; // 启用同源策略 settings.remote_debugging_port 0;在实际项目中我发现CEF92.0版本对Windows 10的兼容性最好而在Windows 7上可能需要额外的兼容性设置。对于需要支持多语言的项目建议在CEF初始化时设置accept_language_list参数确保浏览器正确识别系统语言设置。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2566888.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!