0%

CEF-MFC多文档应用架构设计

  1. 创建工程
  2. App架构
    1. 应用程序类
    2. 创建Cef视图(View)
    3. 创建Cef对话框
  3. 浏览器代理核心类:CCefClientHandler
  4. 视图类CCefView和CCefFrame
  5. 对话框类CCefDialog

视图

image-20230321163306470

对话框

image-20230321163402561

内存占用(开了三个视图、三个对话框)

image-20230321163532916

创建工程

  • VS2022(v143)
  • C++ 17

新建MFC工程,创建后应用程序类型选择“多个文档”,将选项卡式文档勾掉(也可以在初始化代码中关掉)

image-20230321104730524

创建完成后按下F5运行

image-20230321110032828

把CEF相关的资源拷贝到exe输出文件夹中

image-20230321110208069

再把CEF的include和lib拷贝到工程根目录下

image-20230321110318519

image-20230321111514858

配置一下工程属性,把include和lib引进来

image-20230321111623104

App架构

在APP类中添加CEF相关的成员变量和成员函数

应用程序类

MFCApplication1.h

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
// ......

#include <include/cef_app.h>
#include <vector>

//......

class CMFCApplication1App : public CWinAppEx
{
//......
private:
////////////////
// CEF相关
bool InitializeCef(); // 初始化CEF
void UninitializeCef(); // 关闭CEF,释放资源

CMultiDocTemplate* m_pCefTemplate = NULL; // CEF多文档模板
bool m_bCefInitialized = false; // 记录是否初始化
bool m_bCefMessageLoopWork = false; // 记录是否开启消息循环
std::vector<CefRefPtr<CefBrowser>> m_cefBrowserList; // 记录正在使用的浏览器

public:
void SetCefMessageLoopWork(bool bWork = true); // 设置是否开启消息循环
void OnCreateCefBrowser(CefRefPtr<CefBrowser> browser); // 通知App创建了新的浏览器
void OnCloseCefBrowser(CefRefPtr<CefBrowser> browser); // 通知App关闭了浏览器窗口
};

MFCApplication1.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

bool CMFCApplication1App::InitializeCef()
{
CefMainArgs mainArgs(m_hInstance);

// CEF 应用包括多个子进程(渲染进程/GPU进程等),这些进程共享一个可执行程序
// 子进程也会调用这个函数,执行到这一步返回值不为-1,则就退出了
int exitCode = CefExecuteProcess(mainArgs, nullptr, nullptr);
if (exitCode >= 0) return false;

CefSettings settings;
settings.no_sandbox = true;
settings.multi_threaded_message_loop = false;

m_bCefInitialized = CefInitialize(mainArgs, settings, nullptr, nullptr);
ASSERT(m_bCefInitialized);

return true;
}

void CMFCApplication1App::UninitializeCef()
{
if (m_bCefInitialized)
{
// 等待所有CEF相关的线程退出
for (int i = 0; i < 10; i++)
{
CefDoMessageLoopWork();
Sleep(50);
}

// 关闭CEF,并释放所有CEF资源
CefShutdown();

m_bCefInitialized = false;
}
}

void CMFCApplication1App::SetCefMessageLoopWork(bool bWork)
{
m_bCefMessageLoopWork = bWork;
}

void CMFCApplication1App::OnCreateCefBrowser(CefRefPtr<CefBrowser> browser)
{
for (auto itr = m_cefBrowserList.begin(); itr != m_cefBrowserList.end(); itr++)
{
if ((*itr)->IsSame(browser))
{
AfxMessageBox(L"浏览器实例已存在,请勿重复添加");
return;
}
}
m_cefBrowserList.push_back(browser);
m_bCefMessageLoopWork = true;
}

void CMFCApplication1App::OnCloseCefBrowser(CefRefPtr<CefBrowser> browser)
{
for (auto itr = m_cefBrowserList.begin(); itr != m_cefBrowserList.end(); itr++)
{
if ((*itr)->IsSame(browser))
{
m_cefBrowserList.erase(itr);
break;
}
}

for (auto itr = m_dlgList.begin(); itr != m_dlgList.end(); itr++)
{
// 如果是对话框中的浏览器,则删除掉
auto dlg = reinterpret_cast<CCefDialog*>(itr->get());
auto dlgBrowser = dlg->browser();
if (dlgBrowser && dlgBrowser->IsSame(browser))
{
m_dlgList.erase(itr);
break;
}
}

if (m_cefBrowserList.empty())
{
m_bCefMessageLoopWork = false;
}
}

BOOL CMFCApplication1App::InitInstance()
{
/// ......

// 初始化Cef环境
if (!InitializeCef())
{
return FALSE;
}

CWinAppEx::InitInstance();

/// ......

// 创建CEF视图模板
m_pCefTemplate = new CMultiDocTemplate(IDR_MFCApplication1TYPE,
RUNTIME_CLASS(CMFCApplication1Doc),
RUNTIME_CLASS(CCefFrame),
RUNTIME_CLASS(CCefView));
AddDocTemplate(m_pCefTemplate);

/// ......
}

int CMFCApplication1App::ExitInstance()
{
/// ......

// 释放Cef资源
UninitializeCef();

return CWinAppEx::ExitInstance();
}

BOOL CMFCApplication1App::PumpMessage()
{
// TODO: 在此添加专用代码和/或调用基类
const auto bResult{ __super::PumpMessage() };

// 使用PeekMessage从消息队列中检索消息并将其从队列中移除,并不会等待新的消息到达
// 在主进程中可以避免阻塞
MSG msg;
if (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE))
return bResult;

if (m_bCefInitialized && m_bCefMessageLoopWork)
CefDoMessageLoopWork();

return bResult;
}

创建Cef视图(View)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void CMFCApplication1App::OnViewOpenCefView()
{
// TODO: 在此添加命令处理程序代码
if (m_pCefTemplate == NULL || !m_bCefInitialized)
return;

// 创建新的子窗口
auto pFrame = reinterpret_cast<CCefFrame*>(m_pCefTemplate->CreateNewFrame(NULL, NULL));
if (pFrame == NULL)
{
AfxMessageBox(L"Cef窗口创建失败");
return;
}
ASSERT_KINDOF(CFrameWnd, pFrame);

// 设置标题
pFrame->SetTitle(L"CEF View");
pFrame->url("http://www.baidu.com");

// 刷新视图
m_pCefTemplate->InitialUpdateFrame(pFrame, NULL);
}

创建Cef对话框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void CMFCApplication1App::OnViewOpenCefDlg()
{
// TODO: 在此添加命令处理程序代码

// 非模态对话框(需要自行管理内存)
auto dlg = std::make_shared<CCefDialog>("http://www.baidu.com");
dlg->Create(IDD_DLG_CEF);
dlg->ShowWindow(SW_SHOWNORMAL);

m_dlgList.push_back(dlg);

//// 模态对话框
//CCefDialog dlg("http://www.baidu.com");
//dlg.DoModal();
}

浏览器代理核心类:CCefClientHandler

CCefClientHandler.h

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#pragma once
#include <include/cef_app.h>

class CCefClientHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler
{
public:
// 实现该接口获取ClientHandler的事件。Delegate类的方法在主线程中调用
class Delegate
{
public:
virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) = 0;
virtual void OnBrowserClosing(CefRefPtr<CefBrowser> browser) = 0;
virtual void OnBrowserClosed(CefRefPtr<CefBrowser> browser) = 0;
virtual void OnSetAddress(const std::string& url) = 0;
virtual void OnSetTitle(const std::string& title) = 0;
virtual void OnSetFullscreen(const bool fullscreen) = 0;
virtual void OnSetLoadingState(const bool isLoading, const bool canGoBack, const bool canGoForward) = 0;

protected:
virtual ~Delegate() {}
};

public:
CCefClientHandler(Delegate* delegate);

CCefClientHandler(const CCefClientHandler&) = delete;
CCefClientHandler& operator=(const CCefClientHandler&) = delete;

// 创建浏览器窗口
bool CreateBrowser(const CefWindowInfo& windowInfo, const CefString& url, const CefBrowserSettings& settings);

// 主动关闭浏览器窗口
void CloseBrowser();

// 与窗口代理分离(不释放内存,仅从类中删除)
void DetachDelegate();

//////////////////////////////////
// CefDisplayHandler方法
CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }

void OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url) override;
void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) override;
void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen) override;

//////////////////////////////////
// CefLifeSpanHandler方法
CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
bool DoClose(CefRefPtr<CefBrowser> browser) override;
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
const CefString& target_frame_name,
WindowOpenDisposition target_disposition,
bool user_gesture,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings,
CefRefPtr<CefDictionaryValue>& extra_info,
bool* no_javascript_access) override;

//////////////////////////////////
// CefLoadHandler方法
CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser, bool isLoading, bool canGoBack, bool canGoForward) override;
void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) override;

private:
Delegate* m_lpDelegate = nullptr;
CefRefPtr<CefBrowser> m_browser;

IMPLEMENT_REFCOUNTING(CCefClientHandler);
};

CCefClientHandler.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "pch.h"
#include "CCefClientHandler.h"
#include <include/wrapper/cef_helpers.h>
#include "MFCApplication1.h"

CCefClientHandler::CCefClientHandler(Delegate* delegate)
: m_lpDelegate(delegate)
{
}

bool CCefClientHandler::CreateBrowser(const CefWindowInfo& windowInfo, const CefString& url, const CefBrowserSettings& settings)
{
// 创建窗口
auto result{ CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, nullptr, nullptr) };

// 开启消息循环
theApp.SetCefMessageLoopWork(true);

return result;
}

void CCefClientHandler::CloseBrowser()
{
if (m_browser != nullptr)
{
::DestroyWindow(m_browser->GetHost()->GetWindowHandle());
m_browser = nullptr;
}
}

void CCefClientHandler::DetachDelegate()
{
m_lpDelegate = nullptr;
}

void CCefClientHandler::OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url)
{
CEF_REQUIRE_UI_THREAD();

if (frame->IsMain())
{
if (m_lpDelegate)
{
m_lpDelegate->OnSetAddress(url.ToString());
}
}
}

void CCefClientHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title)
{
CEF_REQUIRE_UI_THREAD();

if (m_lpDelegate)
{
m_lpDelegate->OnSetTitle(title.ToString());
}
}

void CCefClientHandler::OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen)
{
CEF_REQUIRE_UI_THREAD();

if (m_lpDelegate)
{
m_lpDelegate->OnSetFullscreen(fullscreen);
}
}

void CCefClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();

// 只允许创建一次窗口,有新的弹窗时通过 OnBeforePopup() 控制
ASSERT(m_browser == nullptr);
m_browser = browser;

theApp.OnCreateCefBrowser(browser);

if (m_lpDelegate)
{
m_lpDelegate->OnBrowserCreated(browser);
}
}

bool CCefClientHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();

if (m_lpDelegate)
{
m_lpDelegate->OnBrowserClosing(browser);
}
return false;
}

void CCefClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();

m_browser = nullptr;

theApp.OnCloseCefBrowser(browser);

if (m_lpDelegate)
{
m_lpDelegate->OnBrowserClosed(browser);
}
}

bool CCefClientHandler::OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& target_url, const CefString& target_frame_name, WindowOpenDisposition target_disposition, bool user_gesture, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr<CefClient>& client, CefBrowserSettings& settings, CefRefPtr<CefDictionaryValue>& extra_info, bool* no_javascript_access)
{
// 返回true不创建新窗口
return true;
}

void CCefClientHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> browser, bool isLoading, bool canGoBack, bool canGoForward)
{
CEF_REQUIRE_UI_THREAD();

if (m_lpDelegate)
{
m_lpDelegate->OnSetLoadingState(isLoading, canGoBack, canGoForward);
}
}

void CCefClientHandler::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl)
{
CEF_REQUIRE_UI_THREAD();

// 针对已下载的文件不显示错误信息
if (errorCode == ERR_ABORTED) return;

std::stringstream ss;
ss <<"<html><body bgcolor=\"white\">"
"<h2>Failed to load URL " << failedUrl.ToString() <<
" with error " << errorText.ToString() << "(" << errorCode <<
").</h2></body></html>";
frame->LoadURL(ss.str());
}

视图类CCefView和CCefFrame

CCefView.h

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
41
42
43
44
#pragma once
#include "CCefClientHandler.h"

// CCefView 视图

class CCefView : public CView, public CCefClientHandler::Delegate
{
protected:
CCefView(); // 动态创建所使用的受保护的构造函数
virtual ~CCefView();
DECLARE_DYNCREATE(CCefView)

public:
////////////////////////////
// 窗口代理
void OnBrowserCreated(CefRefPtr<CefBrowser> browser) override;
void OnBrowserClosing(CefRefPtr<CefBrowser> browser) override;
void OnBrowserClosed(CefRefPtr<CefBrowser> browser) override;
void OnSetAddress(const std::string& url) override;
void OnSetTitle(const std::string& title) override;
void OnSetFullscreen(const bool fullscreen) override;
void OnSetLoadingState(const bool isLoading, const bool canGoBack, const bool canGoForward) override;

private:
std::string m_url;
CefRefPtr<CCefClientHandler> m_clientHandler;
CefRefPtr<CefBrowser> m_browser;

public:
virtual void OnDraw(CDC* pDC); // 重写以绘制该视图
#ifdef _DEBUG
virtual void AssertValid() const;
#ifndef _WIN32_WCE
virtual void Dump(CDumpContext& dc) const;
#endif
#endif

protected:
DECLARE_MESSAGE_MAP()
public:
virtual void OnInitialUpdate();
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnClose();
};

CCefView.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// CCefView.cpp: 实现文件
//

#include "pch.h"
#include "MFCApplication1.h"
#include "CCefView.h"
#include "CCefFrame.h"
#include "CCefClientHandler.h"

// CCefView

IMPLEMENT_DYNCREATE(CCefView, CView)

CCefView::CCefView()
{

}

CCefView::~CCefView()
{
if (m_clientHandler != nullptr)
{
m_clientHandler->DetachDelegate();
m_clientHandler = nullptr;
m_browser = nullptr;
}
}

BEGIN_MESSAGE_MAP(CCefView, CView)
ON_WM_SIZE()
ON_WM_CLOSE()
END_MESSAGE_MAP()

void CCefView::OnBrowserCreated(CefRefPtr<CefBrowser> browser)
{
m_browser = browser;
}
void CCefView::OnBrowserClosing(CefRefPtr<CefBrowser> browser)
{
}
void CCefView::OnBrowserClosed(CefRefPtr<CefBrowser> browser)
{
if (m_browser != nullptr && m_browser->IsSame(browser))
{
m_clientHandler->DetachDelegate();
m_clientHandler = nullptr;
m_browser = nullptr;
}
}
void CCefView::OnSetAddress(const std::string& url)
{
m_url = url;
}
void CCefView::OnSetTitle(const std::string& title)
{
::SetWindowText(GetParentFrame()->GetSafeHwnd(), CefString(title).ToWString().c_str());
}
void CCefView::OnSetFullscreen(const bool fullscreen)
{
}
void CCefView::OnSetLoadingState(const bool isLoading, const bool canGoBack, const bool canGoForward)
{

}

// CCefView 绘图

void CCefView::OnDraw(CDC* pDC)
{
CDocument* pDoc = GetDocument();
// TODO: 在此添加绘制代码
}


// CCefView 诊断

#ifdef _DEBUG
void CCefView::AssertValid() const
{
CView::AssertValid();
}

#ifndef _WIN32_WCE
void CCefView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
#endif
#endif //_DEBUG


// CCefView 消息处理程序


void CCefView::OnInitialUpdate()
{
CView::OnInitialUpdate();

// TODO: 在此添加专用代码和/或调用基类
auto frame = reinterpret_cast<CCefFrame*>(GetParentFrame());
m_url = frame->url();
ASSERT(!m_url.empty());

CRect rect;
GetClientRect(&rect);

CefWindowInfo info;
info.SetAsChild(GetSafeHwnd(), CefRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top));

CefBrowserSettings browserSettings;
browserSettings.windowless_frame_rate = 60; // 最低帧率

m_clientHandler = new CCefClientHandler(this);
m_clientHandler->CreateBrowser(info, m_url, browserSettings);
}


void CCefView::OnSize(UINT nType, int cx, int cy)
{
__super::OnSize(nType, cx, cy);

// TODO: 在此处添加消息处理程序代码

if (m_clientHandler != nullptr)
{
if (m_browser != nullptr)
{
auto hWnd = m_browser->GetHost()->GetWindowHandle();
CRect rect;
GetClientRect(&rect);

::SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
}
}


void CCefView::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值

__super::OnClose();
}

CCefFrame.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

// CCefFrame

#include <string>

class CCefFrame : public CMDIChildWndEx
{
DECLARE_DYNCREATE(CCefFrame)

public:
CCefFrame();
virtual ~CCefFrame();

void url(const std::string& url);
std::string url() const;

private:
std::string m_url;

protected:
DECLARE_MESSAGE_MAP()
};

CCefFrame.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
// CCefFrame.cpp: 实现文件
//

#include "pch.h"
#include "MFCApplication1.h"
#include "CCefFrame.h"

// CCefFrame

IMPLEMENT_DYNCREATE(CCefFrame, CMDIChildWndEx)

CCefFrame::CCefFrame()
{
}

CCefFrame::~CCefFrame()
{
}

void CCefFrame::url(const std::string& url)
{
m_url = url;
}

std::string CCefFrame::url() const
{
return m_url;
}

BEGIN_MESSAGE_MAP(CCefFrame, CMDIChildWndEx)
END_MESSAGE_MAP()

对话框类CCefDialog

CCefDialog.h

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
41
42
43
44
45
#pragma once
#include "CCefClientHandler.h"

// CCefDialog 对话框

class CCefDialog : public CDialogEx, public CCefClientHandler::Delegate
{
DECLARE_DYNAMIC(CCefDialog)

public:
CCefDialog(const std::string& url, CWnd* pParent = nullptr); // 标准构造函数
virtual ~CCefDialog();

public:
////////////////////////////////
// CEF 代理
void OnBrowserCreated(CefRefPtr<CefBrowser> browser) override;
void OnBrowserClosing(CefRefPtr<CefBrowser> browser) override;
void OnBrowserClosed(CefRefPtr<CefBrowser> browser) override;
void OnSetAddress(const std::string& url) override;
void OnSetTitle(const std::string& title) override;
void OnSetFullscreen(const bool fullscreen) override;
void OnSetLoadingState(const bool isLoading, const bool canGoBack, const bool canGoForward) override;

CefRefPtr<CefBrowser> browser();

private:
std::string m_url;
CefRefPtr<CCefClientHandler> m_clientHandler;
CefRefPtr<CefBrowser> m_browser;

// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_DLG_CEF };
#endif

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持

DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnClose();
};

CCefDialog.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// CCefDialog.cpp: 实现文件
//

#include "pch.h"
#include "MFCApplication1.h"
#include "CCefDialog.h"


// CCefDialog 对话框

IMPLEMENT_DYNAMIC(CCefDialog, CDialogEx)

CCefDialog::CCefDialog(const std::string& url, CWnd* pParent /*=nullptr*/)
: m_url(url), CDialogEx(IDD_DLG_CEF, pParent)
{

}

CCefDialog::~CCefDialog()
{
if (m_clientHandler != nullptr)
{
m_clientHandler->DetachDelegate();
m_clientHandler = nullptr;
m_browser = nullptr;
}
}

void CCefDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}

void CCefDialog::OnBrowserCreated(CefRefPtr<CefBrowser> browser)
{
m_browser = browser;
}
void CCefDialog::OnBrowserClosing(CefRefPtr<CefBrowser> browser)
{

}
void CCefDialog::OnBrowserClosed(CefRefPtr<CefBrowser> browser)
{
if (m_browser != nullptr && m_browser->IsSame(browser))
{
m_clientHandler->DetachDelegate();
m_clientHandler = nullptr;
m_browser = nullptr;
}
}
void CCefDialog::OnSetAddress(const std::string& url)
{
m_url = url;
}
void CCefDialog::OnSetTitle(const std::string& title)
{
::SetWindowText(GetSafeHwnd(), CefString(title).ToWString().c_str());
}
void CCefDialog::OnSetFullscreen(const bool fullscreen)
{

}
void CCefDialog::OnSetLoadingState(const bool isLoading, const bool canGoBack, const bool canGoForward)
{

}

CefRefPtr<CefBrowser> CCefDialog::browser()
{
return m_browser;
}


BEGIN_MESSAGE_MAP(CCefDialog, CDialogEx)
ON_WM_SIZE()
ON_WM_CLOSE()
END_MESSAGE_MAP()


// CCefDialog 消息处理程序


BOOL CCefDialog::OnInitDialog()
{
__super::OnInitDialog();

// TODO: 在此添加额外的初始化
CRect rect;
GetClientRect(&rect);

CefWindowInfo info;
info.SetAsChild(GetSafeHwnd(), CefRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top));

CefBrowserSettings settings;
settings.windowless_frame_rate = 60; // 最低帧率

m_clientHandler = new CCefClientHandler(this);
m_clientHandler->CreateBrowser(info, m_url, settings);

return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}


void CCefDialog::OnSize(UINT nType, int cx, int cy)
{
__super::OnSize(nType, cx, cy);

// TODO: 在此处添加消息处理程序代码
if (m_clientHandler != nullptr)
{
if (m_browser != nullptr)
{
auto hWnd = m_browser->GetHost()->GetWindowHandle();
CRect rect;
GetClientRect(&rect);

::SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
}
}

void CCefDialog::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值

__super::OnClose();

if (m_clientHandler != nullptr)
{
m_clientHandler->CloseBrowser();
}
}