前面我们分析了CefSample的代码,那么现在我们将基于MFC的Dialog做一个简单的CefDemo模型。
CefSample使用的是Cef自己的消息环CefRunMessageLoop。但是在使用MFC的时候,MFC有自己的消息环,所以我们不能再使用CefRunMessageLoop,所以我们需要在MFC的消息环里面处理Cef的消息,代码如下,重写虚函数PumpMessage:
BOOL CCefDemoApp::PumpMessage()
{
auto result = CWinApp::PumpMessage();
//Cef Message
CefDoMessageLoopWork();
return result;
}
因为我们需要创建CEF子窗口,所以我们把CefInitialize放在Dialong::OnInitDialog里面初始化,并且绑定父窗口的句柄,然后在CefApp::OnContextInitialized里面创建浏览器(CefExecuteProcess还是放在入口函数执行即:InitInstance),代码如下:
BOOL CCefDemoDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
...
// TODO: 在此添加额外的初始化代码
if (m_browser_app == nullptr)
{
m_browser_app = new CBrowserApp(GetSafeHwnd());
m_browser_app->Init(AfxGetInstanceHandle());
}
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
///初始化CEFAPP
bool CBrowserApp::Init(HINSTANCE hist)
{
CefEnableHighDPISupport();
CefMainArgs cef_main_args(hist);
CefSettings cef_settings;
cef_settings.multi_threaded_message_loop = true;
cef_settings.no_sandbox = true;
return CefInitialize(cef_main_args, cef_settings, this, NULL);
}
void CBrowserApp::OnContextInitialized()
{
CEF_REQUIRE_UI_THREAD();
RECT rtClient;
::GetClientRect(m_hWndParent, &rtClient);
CefWindowInfo wnd_info;
wnd_info.SetAsChild(m_hWndParent, rtClient);
CefBrowserSettings browser_settings;
CefBrowserHost::CreateBrowser(wnd_info, m_simple_handler, "http://www.baidu.com", browser_settings, NULL);
}
然后就是最重要的一关就是在关闭对话框的时候必须先关闭Cef所有打开的浏览器才行否则会崩溃或者进程驻留,所以我们需要在Dialog的时候关闭所有的浏览器(调用了CloseBrowser后,CEF会调用DoClose,然后会再发一个WM_CLOSE给窗口)cef关闭流程,并且在App退出之前调用CefShutdown(),代码如下:
//关闭对话框之前,关闭所有的Cef浏览器
void CCefDemoDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//尝试关闭浏览器,如果成功则直接返回,拦截当次WM_CLOSE消息,等待CEF关闭完成之后发送的WM_CLOSE的消息
if (m_browser_app && m_browser_app.get() && m_browser_app->TryClose())
return;
CDialogEx::OnClose();
}
//App里面尝试关闭所有的浏览器,如果浏览器已经全部关闭则返回false,否则返回true
bool CBrowserApp::TryClose()
{
if (m_simple_handler && m_simple_handler.get() &&
!m_simple_handler->IsClosing())
{
m_simple_handler->CloseAllBrowsers(true);
return true;
}
return false;
}
//这里是完全仿照CefSanple实现的,
void CBrowserHandler::CloseAllBrowsers(bool force_close)
{
if (!CefCurrentlyOn(TID_UI))
{
// Execute on the UI thread.
// bind ref cef_closure_task
CefPostTask(TID_UI, base::Bind(&CBrowserHandler::CloseAllBrowsers, this,force_close));
return;
}
if (m_browser_list.empty())
return;
std::list<CefRefPtr<CefBrowser> >::const_iterator it = m_browser_list.begin();
for (; it != m_browser_list.end(); ++it)
(*it)->GetHost()->CloseBrowser(force_close);
}
//退出应用程序之前记得释放Cef资源,否则崩溃
int CCefDemoApp::ExitInstance()
{
// TODO: 在此添加专用代码和/或调用基类
CefShutdown();
return CWinApp::ExitInstance();
}
此时基于MFC的CefDemo运行成功,效果如下,但是我们打开其他网页的时候可能弹出了一个新的POP窗口,那么我们怎么实现像浏览器一样的标签页弹出呢?下一章将描述怎么实现一个简单的多标签的Cef浏览器。