#include
#include "App.h"
//整个应用的入口函数
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow)
{
CefEnableHighDPISupport();
CefMainArgs main_args(hInstance);
CefSettings settings;
int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
if (exit_code >= 0) {
return exit_code;
}
CefRefPtr<App> app(new App());
CefInitialize(main_args, settings, app.get(), nullptr);
CefRunMessageLoop();
CefShutdown();
return 0;
}
#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);
};
#ifndef _FileA
#define _FileA
// code
#endif
//App.cpp
#include "App.h"
#include "include/cef_browser.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_helpers.h"
#include "WindowDelegate.h"
//CEF主进程上下文环境初始化成功
void App::OnContextInitialized() {
CEF_REQUIRE_UI_THREAD();
auto url = "https://www.baidu.com";
CefBrowserSettings settings;
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(nullptr, url, settings, nullptr, nullptr, nullptr);
CefWindow::CreateTopLevelWindow(new WindowDelegate(browser_view));
}
// WindowDelegate.h
#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) {};
void OnWindowCreated(CefRefPtr<CefWindow> window) override;
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override;
CefRect GetInitialBounds(CefRefPtr<CefWindow> window) override;
WindowDelegate(const WindowDelegate&) = delete;
WindowDelegate& operator=(const WindowDelegate&) = delete;
private:
CefRefPtr<CefBrowserView> browser_view_;
IMPLEMENT_REFCOUNTING(WindowDelegate);
};
//WindowDelegate.cpp
#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"这是我的窗口标题");
//window->CenterWindow(CefSize(800, 600));
}
//窗口销毁成功
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();
rect.x = (rect.width - 800) / 2;
rect.y = (rect.height - 600) / 2;
rect.width = 800;
rect.height = 600;
return rect;
}
在这一节我们通过创建一个精简的 CEF 应用程序,来带领你熟悉 CEF 框架的运作机制,可总结为如下:
为了解决加载本地页面的问题, CEF 提供了 CefSchemeHandlerFactory 和 CefResourceHandler 等类型和方法支持用户自定义协议。下面我们就介绍第一个方案,如何使用 CEF 内置的协议加载本地页面。
注册内置协议处理工厂
void App::OnContextInitialized() {
CEF_REQUIRE_UI_THREAD();
//下面两行代码为新增代码
CefRegisterSchemeHandlerFactory("https", "bread", new HttpSchemeFactory());
std::string url = "https://bread/index.html?a=123";
CefBrowserSettings settings;
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(nullptr, url, settings, nullptr, nullptr, nullptr);
CefWindow::CreateTopLevelWindow(new WindowDelegate(browser_view));
}
//文件名为:HttpSchemeFactory.h
#pragma once
#include "include/cef_app.h"
class HttpSchemeFactory : public CefSchemeHandlerFactory
{
public:
HttpSchemeFactory() = default;
//删除拷贝函数
HttpSchemeFactory(const HttpSchemeFactory&) = delete;
//删除赋值函数
HttpSchemeFactory& operator=(const HttpSchemeFactory&) = delete;
//处理请求的方法定义
CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& scheme_name, CefRefPtr<CefRequest> request) override;
private:
IMPLEMENT_REFCOUNTING(HttpSchemeFactory);
};
//文件名为:HttpSchemeFactory.cpp
#include "HttpSchemeFactory.h"
#include "include/wrapper/cef_helpers.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include
#include
#include
//处理请求的方法实现
CefRefPtr<CefResourceHandler> HttpSchemeFactory::Create(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& scheme_name,
CefRefPtr<CefRequest> request)
{
CEF_REQUIRE_IO_THREAD();
std::string url = request->GetURL().ToString();
std::string urlPrefix = "https://bread/";
url.erase(0, urlPrefix.size());
size_t paramIndex = url.find_first_of('?');
if (paramIndex != std::string::npos) {
url.erase(paramIndex);
}
TCHAR buffer[MAX_PATH] = { 0 };
GetModuleFileName(NULL, buffer, MAX_PATH);
std::filesystem::path targetPath(buffer);
targetPath = targetPath.parent_path().append("html").append(url);
if (!std::filesystem::exists(targetPath)) {
DLOG(INFO) << L"试图加载:" << targetPath.generic_wstring() << L",但找不到这个文件";
}
std::string ext = targetPath.extension().generic_string();
std::string mime_type_ = ext == ".html"? "text/html":"*";
auto stream = CefStreamReader::CreateForFile(targetPath.generic_wstring());
return new CefStreamResourceHandler(mime_type_, stream);
};
<html>
<head>
<title>第一个本地页面title>
<meta charset="utf-8" />
head>
<body>
这是第一个本地页面<br />
它的地址是:
<script src="/a.js">script>
body>
html>
document.write(location.href);
被 HttpSchemeFactory 工厂类处理的请求除了要匹配 https scheme 之外,还要匹配我们注册的域名: bread 。另外注册的 HTTPS 协议处理工厂只对当前应用生效,不会影响用户操作系统内的其他应用,不用担心应用的兼容性和安全性问题。所以,这个方案适应性还是比较广泛的。
前面介绍了如何使用内置的 HTTPS 协议加载本地页面,实际上所有的内置协议 FTP、 Data 等都可以使用类似的方式加载本地页面。但这也仅仅局限在内置协议的范畴,如果我们要注册一个完全自定义的协议,这种做法就行不通了。本讲中我就带领你注册一个完全自定义的协议。
分离进程处理类
CefMainArgs main_args(hInstance);
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(::GetCommandLineW());
CefRefPtr<CefApp> app;
if (!command_line->HasSwitch("type")) {
app = new App();
}
else if (command_line->GetSwitchValue("type").ToString() == "renderer") {
app = new Renderer();
}
else {
app = new Other();
}
int exit_code = CefExecuteProcess(main_args, app, nullptr);
void App::OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar) {
registrar->AddCustomScheme("my", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
}
[scheme]://[username]:[password]@[host]:[port]/[url-path]
my://bread/index.html?a=123
void App::OnContextInitialized() {
CEF_REQUIRE_UI_THREAD();
CefRegisterSchemeHandlerFactory("my", "bread", new HttpSchemeFactory());
std::string url = "my://bread/index.html?a=123";
CefBrowserSettings settings;
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(nullptr, url, settings, nullptr, nullptr, nullptr);
CefWindow::CreateTopLevelWindow(new WindowDelegate(browser_view));
}
CefRefPtr<CefResourceHandler> CustomSchemeFactory::Create(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& scheme_name,
CefRefPtr<CefRequest> request)
{
CEF_REQUIRE_IO_THREAD();
return new CustomSchemeHandler();
};
// CustomSchemeHandler.h
#pragma once
#include "include/cef_app.h"
#include "include/cef_resource_handler.h"
#include "include/wrapper/cef_helpers.h"
class CustomSchemeHandler : public CefResourceHandler
{
public:
CustomSchemeHandler() : offset_(0) {}
CustomSchemeHandler(const CustomSchemeHandler&) = delete;
CustomSchemeHandler& operator=(const CustomSchemeHandler&) = delete;
//请求发生时触发的事件
bool Open(CefRefPtr<CefRequest> request, bool& handle_request, CefRefPtr<CefCallback> callback) override;
//发送响应头之前触发的事件
void GetResponseHeaders(CefRefPtr<CefResponse> response, int64& response_length, CefString& redirectUrl) override;
//请求被取消时触发的事件
void Cancel() override { CEF_REQUIRE_IO_THREAD(); }
//响应数据时触发的事件
bool Read(void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr<CefResourceReadCallback> callback) override;
private:
std::string data_;
size_t offset_;
IMPLEMENT_REFCOUNTING(CustomSchemeHandler);
};
// CustomSchemeHandler.cpp
#include "CustomSchemeHandler.h"
#include "include/wrapper/cef_helpers.h"
#include
#include
#include
//请求发生时触发的事件
bool CustomSchemeHandler::Open(CefRefPtr<CefRequest> request, bool& handle_request, CefRefPtr<CefCallback> callback) {
DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
handle_request = true;
this->data_ = "这是我自己的页面 这是我自己的内容";
return true;
}
//发送响应头之前触发的事件
void CustomSchemeHandler::GetResponseHeaders(CefRefPtr<CefResponse> response, int64& response_length, CefString& redirectUrl) {
CEF_REQUIRE_IO_THREAD();
DCHECK(!data_.empty());
response->SetMimeType("text/html");
response->SetStatus(200);
response_length = data_.length();
}
//响应数据时触发的事件
bool CustomSchemeHandler::Read(void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr<CefResourceReadCallback> callback) {
DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
bool has_data = false;
bytes_read = 0;
if (offset_ < data_.length()) {
int transfer_size = std::min(bytes_to_read, static_cast<int>(data_.length() - offset_));
memcpy(data_out, data_.c_str() + offset_, transfer_size);
offset_ += transfer_size;
bytes_read = transfer_size;
has_data = true;
}
return has_data;
}