目录
1.应用背景
随着 HTML5 的崛起和 IE 的淘汰,微软 Web Browser 控件越来越不合时宜,CEF (Chromium Embedded Framework) 越来越受欢迎。但 CEF 使用起来比 Web Browser 复杂的多,而且它的官方例子 cefsimple 和 cefclient 都是使用 Win32 API 开发的,这给大家学习使用 CEF 带来了很大不便,很多人更需要一个使用 MFC 开发的例子。
CEF版本:cef_binary_3.2704.1434.gec3e9ed_windows32
开发工具:visual studio 2017
2.下载CEF
获得 CEF 有两种方法,一种是下载源码自己编译,另一种是直接下载编译好的二进制文件。自己编译 CEF 源码太麻烦了,可谓困难重重,直接pass。
只有第二种方法了,我用的版本是cef_binary_3.2704.1434.gec3e9ed_windows32.7z
3.编译 libcef_dll_wrapper
解压好之后里面默认含libcef.dll 提供的是 C 接口。我们既然都已经使用 MFC 了,当然更想用 C++ 接口,但C++接口需要自己编译,
我们用CMake编译,下载地址:cmake-3.8.2-win32-x86.msi ,安装CMake时需要选择“添加到环境变量”
编译好后,打开工程,编译 libcef_dll_wrapper项目。
至此准备工作全部完成。
4.新建MFC对话框程序
3.1属性中添加包含目录,库目录。
3.2属性中,"C/C++"->"预编译头"->"不使用预编译头"。
3.3属性中,"链接器"->"输入"->"附加依赖项",填入libcef.lib,libcef_dll_wrapper.lib两个静态库
4.coding
4.1声明CSimpleClient类,如下,常规做法是定义CSimpleClient类,继承自所有的event handling classes。
class CSimpleClient : public CefClient, public CefLifeSpanHandler, public CefDownloadHandler, public CefKeyboardHandler, public CefContextMenuHandler, public CefBrowserProcessHandler, public CefApp
4.2实现CSimpleClient类
#include "SimpleClient.h"
#include <tchar.h>
enum { ID_CMD_REFRESH = 0 ,
ID_SHOW_DEV_TOOLS
};
CSimpleClient::CSimpleClient()
{
hWnd_ = NULL;
}
CSimpleClient::~CSimpleClient()
{
}
void CSimpleClient::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
if (!m_cefBrowser.get())
{
m_cefBrowser = browser;
m_BrowserHwnd = browser->GetHost()->GetWindowHandle();
}
}
//void CSimpleClient::OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line)
//{
//
// command_line->AppendSwitch("no-proxy-server");
// command_line->AppendSwitch("--enable-npapi");
// command_line->AppendSwitch("--disable-web-security");
// command_line->AppendSwitch("allow-outdated-plugins");
//
// //manifest.json中的version
// command_line->AppendSwitchWithValue("ppapi-flash-version","20.0.0.228");
//
// //加载flash插件
// command_line->AppendSwitchWithValue("ppapi-flash-path", "PepperFlash\\pepflashplayer.dll");
//
//}
bool CSimpleClient::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
{
const std::string& messageName = message->GetName();
if (messageName == "login_msg")
{
// extract message
CefRefPtr<CefListValue> args = message->GetArgumentList();
CefString strUser = args->GetString(0);
CefString strPassword = args->GetString(1);
TCHAR szLog[256] = { 0 };
_stprintf_s(szLog, 256, L"BrowserProcess, user - %s, password - %s\r\n", strUser.c_str(), strPassword.c_str());
OutputDebugString(szLog);
//send reply to render process
CefRefPtr<CefProcessMessage> outMsg = CefProcessMessage::Create("login_reply");
// Retrieve the argument list object.
CefRefPtr<CefListValue> replyArgs = outMsg->GetArgumentList();
// Populate the argument values.
replyArgs->SetSize(1);
replyArgs->SetInt(0, 0);
// Send the process message to the renderer process.
browser->SendProcessMessage(PID_RENDERER, outMsg);
return true;
}
return false;
}
bool CSimpleClient::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,
bool* no_javascript_access)
{
switch (target_disposition)
{
case WOD_NEW_FOREGROUND_TAB:
case WOD_NEW_BACKGROUND_TAB:
case WOD_NEW_POPUP:
case WOD_NEW_WINDOW:
browser->GetMainFrame()->LoadURL(target_url);
return true; //cancel create
}
return false;
}
void CSimpleClient::OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback)
{
callback->Continue(download_item->GetURL(), true);
}
void CSimpleClient::OnDownloadUpdated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
CefRefPtr<CefDownloadItemCallback> callback)
{
if (download_item->IsComplete())
{
//MessageBox.Show("下载成功");
OutputDebugString(L"下载成功");
if (browser->IsPopup() && !browser->HasDocument())
{
//browser->GetHost()->ParentWindowWillClose();
browser->GetHost()->CloseBrowser(true);
}
}
else
{
//如果取消应该跳转到一个网页
browser->GoBack();
}
}
bool CSimpleClient::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
const CefKeyEvent& event,
CefEventHandle os_event,
bool* is_keyboard_shortcut)
{
if (event.type != KEYEVENT_RAWKEYDOWN)
return false;
if (event.windows_key_code == 116)//F5刷新
browser->Reload();
else if (event.windows_key_code == VK_F12)
{
ShowDevelopTools(browser, CefPoint(0, 0));
}
return false;
}
bool CSimpleClient::OnKeyEvent(CefRefPtr<CefBrowser> browser,
const CefKeyEvent& event,
CefEventHandle os_event) {
return false;
}
//右键菜单
void CSimpleClient::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model)
{
if (model->GetCount() > 0) {// 刷新菜单
model->AddSeparator();
model->AddItem(ID_CMD_REFRESH, __TEXT("刷新"));
model->AddItem(ID_SHOW_DEV_TOOLS, __TEXT("开发者选项"));
}
}
bool CSimpleClient::OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
int command_id, EventFlags event_flags)
{
switch (command_id)
{
case ID_CMD_REFRESH:
browser->Reload();
break;
case ID_SHOW_DEV_TOOLS:
{
ShowDevelopTools(browser, CefPoint(0,0));
break;
}
default:
break;
}
return false;
}
//开发选项
void CSimpleClient::ShowDevelopTools(CefRefPtr<CefBrowser> browser, const CefPoint& inspect_element_at)
{
CefWindowInfo windowInfo;
CefBrowserSettings settings;
windowInfo.SetAsPopup(browser->GetHost()->GetWindowHandle(), "DevTools");
browser->GetHost()->ShowDevTools(windowInfo, this, settings, CefPoint());
}
void CSimpleClient::CloseDevelopTools(CefRefPtr<CefBrowser> browser)
{
browser->GetHost()->CloseDevTools();
}
bool CSimpleClient::DoClose(CefRefPtr<CefBrowser> browser)
{
同一个browser可能有多个浏览器窗口(在新的子窗口打开链接,而不是在当前窗口跳转时会出现.),此时返回值可能会被用来做一些特别处理.想了解请参考官方demo.
//if (CefCurrentlyOn(TID_UI))
//{
// return true;
//}
return false; //返回true取消关闭
}
void CSimpleClient::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
if (m_BrowserHwnd == browser->GetHost()->GetWindowHandle())
{// 浏览器窗口被销毁的话,browser指针置为NULL.
m_cefBrowser = NULL;
}
}
5.在主Dlg的OnInitDialog()函数中
// TODO: 在此添加额外的初始化代码
CefRefPtr<CSimpleClient> client(new CSimpleClient());
m_simpleClient = client;
CefRefPtr<ClientAppRender> app(new ClientAppRender());
CefSettings settings;
CefSettingsTraits::init(&settings);
settings.multi_threaded_message_loop = true;
settings.remote_debugging_port = 8088;//如果不定义,则不能运行调试工具
settings.single_process = false;
CefMainArgs mainArgs;
CefRefPtr<CefApp> cefApp;
cefApp = app;
CefInitialize(mainArgs, settings, cefApp, NULL);
RECT rect;
GetClientRect(&rect);
RECT rectnew = rect;
rectnew.top = rect.top + 50;
rectnew.bottom = rect.bottom;
rectnew.left = rect.left;
rectnew.right = rect.right;
CefWindowInfo winInfo;
winInfo.SetAsChild(GetSafeHwnd(), rectnew);
std::wstring domain = GetInstallPath() + "test.html";
//CefPostTask(TID_IO, NewCefRunnableMethod(manager.get(), &CefCookieManager::SetCookie, CefString(domain.c_str()), cookie, callback));
CefBrowserSettings browserSettings;
CefBrowserHost::CreateBrowser(winInfo, m_simpleClient, domain.c_str(), browserSettings, NULL);
5.运行程序
5.1需要将D:\cef_binary_3.2704.1434.gec3e9ed_windows32\Debug下面所有文件都拷贝到demo的Debug目录下,
还需要将D:\cef_binary_3.2704.1434.gec3e9ed_windows32\cefclient\Debug目录下的所有文件都拷贝到demo的Debug目录下
6.OK,运行效果如下:
参考链接:
https://blog.csdn.net/blackwoodcliff/article/details/74276848
https://blog.csdn.net/mushao999/article/details/37606189
欢迎加入我们的qq讨论群:184821652