eric0hn 2017-03-12
微信、qq等很多windows客户端都用到了libcef.dll,从现在开始准备学习学习。
1.什么是libcef
Chromium嵌入式框架对于嵌入chromium浏览器的其他应用程序来说是一个简单的框架。。其遵循建立于2008年Marshall Greenblatt 的BSD许可协议。基于Google Chromium工程。不像Chromium工程本身,主要集中开发Google Chrome应用程序,CEF聚焦于使用第三方嵌入浏览器的应用程序, 通过提供稳定的API,CEF将chromium底层以及复杂的Blink代码隔离开来,分支发布紧跟chromium发布,以及二进制发布。大多数CEF的特性都有默认的实现,其功能了丰富的功能,较少甚至没有集成工作需要去做。目前全球有超过1亿CEF运行实例,其嵌入在广泛公司和企业的嵌入式产品中。
2.如何下载libcef
https://cefbuilds.com/
从这个网站下载你要的版本,我下载的是windows 32位。
下载并解压,得到了如下文件夹:
3.如何从下载文件中得到想要的解决方案
这个时候你就会迷茫了,网上很多说在文件夹中找到对应vs版本的解决方案。但是如上图所示,干嘛没有.sln文件。
这个时候就需要阅读以下文档了:
https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial
看到下面一段话:
Using CMake
Starting with 2171 branch the CEF3 sample applications can be built using CMake, a cross-platform open-source build system that can generate project files in many different formats. See the comments at the top of the CMakeLists.txt file included with the binary distribution for platform-specific CMake usage instructions.
这个时候,需要下载CMake了,这是什么鬼?
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
下载地址:
https://cmake.org/
下载后安装,没有什么特别的地方。
接下来就是使用cmake了:
Browse Source选择刚刚解压的libcef文件夹,最外面的奥!!
Browse Build选择要把生成的工程放在哪里。
点击Generate,然后选择现在使用的VS版本,就可完成。
接下来就可以打开cef.sln了,然后可以运行里面的例子。
得到的工程文件夹中文件的作用。
include – 这个文件夹里面放CEF客户应用程序所需的头文件
libcef – 此文件夹存放CEF的静态库
libcef_dll – 此文件夹CEF的动态拉链库
tests – 此文件夹存放测试的例子
cefclient – 一个简单的客户程序
cefsimple–一个简单的例子
unittests – CEF界面单元测试
下面看一下:
下面就是运行cefsimple的界面了:
下面就是windows的入口函数:
#include <windows.h>
#include "cefsimple/simple_app.h"
#include "include/cef_sandbox_win.h"
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
// to the CMake command-line to disable use of the sandbox.
// Uncomment this line to manually enable sandbox support.
// #define CEF_USE_SANDBOX 1
#if defined(CEF_USE_SANDBOX)
// The cef_sandbox.lib static library is currently built with VS2013. It may not
// link successfully with other VS versions.
#pragma comment(lib, "cef_sandbox.lib")
#endif
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
void* sandbox_info = NULL;
#if defined(CEF_USE_SANDBOX)
// Manage the life span of the sandbox information object. This is necessary
// for sandbox support on Windows. See cef_sandbox_win.h for complete details.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
// SimpleApp implements application-level callbacks. It will create the first
// browser instance in OnContextInitialized() after CEF has initialized.
CefRefPtr<SimpleApp> app(new SimpleApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, app.get(), sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
// Specify CEF global settings here.
CefSettings settings;
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
// Initialize CEF.
CefInitialize(main_args, settings, app.get(), sandbox_info);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
Linux Entry Point
// cefsimple_linux.cpp
#include "cefsimple/simple_app.h"
#include <X11/Xlib.h>
#include "include/base/cef_logging.h"
namespace {
int XErrorHandlerImpl(Display *display, XErrorEvent *event) {
LOG(WARNING)
<< "X error received: "
<< "type " << event->type << ", "
<< "serial " << event->serial << ", "
<< "error_code " << static_cast<int>(event->error_code) << ", "
<< "request_code " << static_cast<int>(event->request_code) << ", "
<< "minor_code " << static_cast<int>(event->minor_code);
return 0;
}
int XIOErrorHandlerImpl(Display *display) {
return 0;
}
} // namespace
// Entry point function for all processes.
int main(int argc, char* argv[]) {
// Provide CEF with command-line arguments.
CefMainArgs main_args(argc, argv);
// SimpleApp implements application-level callbacks. It will create the first
// browser instance in OnContextInitialized() after CEF has initialized.
CefRefPtr<SimpleApp> app(new SimpleApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, app.get(), NULL);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
// Specify CEF global settings here.
CefSettings settings;
// Install xlib error handlers so that the application won't be terminated
// on non-fatal errors.
XSetErrorHandler(XErrorHandlerImpl);
XSetIOErrorHandler(XIOErrorHandlerImpl);
// Initialize CEF for the browser process.
CefInitialize(main_args, settings, app.get(), NULL);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
现在就来讲讲如何在自己的win32程序中搭建libcef运行环境。
首先就是建一个空的win32项目,例如名字为TestLibCef。
1. cefsimple目录(注意是拷贝文件夹)拷贝到新工程下并包含在项目中(注意,是TestLibCef\TestLibCef文件夹下)
2. 并在TestLibCef\TestLibCef文件夹下,新建一个dll文件夹
源码把Debug目录下的文件全部拷贝到该文件夹下()
3. 把resource目录下的文件全部拷贝到该文件夹下(TestLibCef\TestLibCef\dll)
4.把include文件夹拷贝到该文件夹下(注意是拷贝文件夹)(TestLibCef\TestLibCef\dll)
5.把out\Debug\lib\libcef_dll_wrapper.lib文件拷贝到该文件夹下(TestLibCef\TestLibCef\dll)
(如果你要发布你的应用程序了,那么你就应该拷贝相应的release目录下的文件)
这个时候,你的dll文件夹是有这些文件:
6.在工程中添加一些头文件和源文件,如下图:
7.接下来就是修改工程的属性了,在解决方案下TestLibCef上右键,属性。
常规页,设置如下:
8. C/C++下的常规的“附加包含目录”添加如下:
9. C/C++下的预处理器进行修改,加入如下文件:
_DEBUG
V8_DEPRECATION_WARNINGS
BLINK_SCALE_FILTERS_AT_RECORD_TIME
_WIN32_WINNT=0x0602
WINVER=0x0602
WIN32
_WINDOWS
NOMINMAX
PSAPI_VERSION=1
_CRT_RAND_S
CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
WIN32_LEAN_AND_MEAN
_ATL_NO_OPENGL
_HAS_EXCEPTIONS=0
_SECURE_ATL
CHROMIUM_BUILD
TOOLKIT_VIEWS=1
USE_AURA=1
USE_ASH=1
USE_DEFAULT_RENDER_THEME=1
USE_LIBJPEG_TURBO=1
USE_MOJO=1
ENABLE_ONE_CLICK_SIGNIN
ENABLE_REMOTING=1
ENABLE_WEBRTC=1
ENABLE_PEPPER_CDMS
ENABLE_CONFIGURATION_POLICY
ENABLE_INPUT_SPEECH
ENABLE_NOTIFICATIONS
ENABLE_HIDPI=1
ENABLE_EGLIMAGE=1
__STD_C
_CRT_SECURE_NO_DEPRECATE
_SCL_SECURE_NO_DEPRECATE
NTDDI_VERSION=0x06020000
_USING_V110_SDK71_
ENABLE_TASK_MANAGER=1
ENABLE_EXTENSIONS=1
ENABLE_PLUGIN_INSTALLATION=1
ENABLE_PLUGINS=1
ENABLE_SESSION_SERVICE=1
ENABLE_THEMES=1
ENABLE_AUTOFILL_DIALOG=1
ENABLE_BACKGROUND=1
ENABLE_AUTOMATION=1
ENABLE_GOOGLE_NOW=1
CLD_VERSION=2
ENABLE_FULL_PRINTING=1
ENABLE_PRINTING=1
ENABLE_SPELLCHECK=1
ENABLE_CAPTIVE_PORTAL_DETECTION=1
ENABLE_APP_LIST=1
ENABLE_SETTINGS_APP=1
ENABLE_MANAGED_USERS=1
ENABLE_MDNS=1
ENABLE_SERVICE_DISCOVERY=1
USING_CEF_SHARED
__STDC_CONSTANT_MACROS
__STDC_FORMAT_MACROS
DYNAMIC_ANNOTATIONS_ENABLED=1
WTF_USE_DYNAMIC_ANNOTATIONS=1
10. C/C++下代码生成中,运行库改为“多线程调试MTD”
11. 链接器,常规,附加库目录为:
因为是VS2015,这个会与VS2013的配置有区别
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10150.0\\ucrt\\x86 %(AdditionalLibraryDirectories)
12. 链接器,输入,内容如下:
wininet.lib
dnsapi.lib
version.lib
msimg32.lib
ws2_32.lib
usp10.lib
psapi.lib
dbghelp.lib
winmm.lib
shlwapi.lib
kernel32.lib
gdi32.lib
winspool.lib
comdlg32.lib
advapi32.lib
shell32.lib
ole32.lib
oleaut32.lib
user32.lib
uuid.lib
odbc32.lib
odbccp32.lib
delayimp.lib
credui.lib
netapi32.lib
comctl32.lib
rpcrt4.lib
opengl32.lib
glu32.lib
D:\test\TestLibCef\TestLibCef\dll\libcef_dll_wrapper.lib
D:\test\TestLibCef\TestLibCef\dll\libcef.lib
13. 链接器,高级,如下图:
14. 编译生成项目,将dll文件夹中的所有内容拷贝到debug或release文件夹下。
最后完成。
不出意外,你的程序不会生成成功,会提示C2220错误,你可能Google或是百度,很多人告诉你把警告等级调低,这根本还是解决不了问题。而且良好的编程习惯都是把警告当做错误看待的。
其实可以这样解决问题:
关闭VS2015;
打开VS2015软件(不点击任何解决方案);
选择 文件 ->打开 ->项目, 找到之前建立的TestLibCef的sln文件。
成功!!
至于原因:
原来文件的代码页为英文,而我们使用的是中文系统。仅此而已。
现在还是在win32项目的基础上,对libcef进行简单的剖析。注意是针对WinMain函数中libcef的类以及方法的介绍。
首先上一段代码:
#include <windows.h>
#include "cefsimple/simple_app.h"
// Entry point function for all processes.
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Enable High-DPI support on Windows 7 or newer.
CefEnableHighDPISupport();
void* sandbox_info = NULL;
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
// SimpleApp implements application-level callbacks. It will create the first
// browser instance in OnContextInitialized() after CEF has initialized.
CefRefPtr<SimpleApp> app(new SimpleApp);
// Specify CEF global settings here.
CefSettings settings;
settings.no_sandbox = true;
// Initialize CEF.
CefInitialize(main_args, settings, app.get(), sandbox_info);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
下面就对上面代码中的内容进行简要介绍。
一、UNREFERENCED_PARAMETER的作用
作用:告诉编译器,已经使用了该变量,不必检测警告!
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
二、 高清适配函数CefEnableHighDPISupport()
可以参考链接:http://www.ubergizmo.com/how-to/how-to-google-chrome-hidpi-support-windows/
// Enable High-DPI support on Windows 7 or newer.
CefEnableHighDPISupport();
三、CefMainArgs和CefExecuteProcess函数(判断子线程)
提供一个命令行的方式,暂时不知道咋回事呢!
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
在CEF3和Chromium中许多特性可以使用命令行参数进行配置。这些参数采用”–some-argument[=optional-param]”形式,并通过CefExecuteProcess()和CefMainArgs结构(参考下面的”应用资源布局”章节)传递给CEF。在传递CefSettings结构给CefInitialize()之前,我们可以设置CefSettings.command_line_args_disabled为false来禁用对命令行参数的处理。如果想指定命令行参数传入主应用程序,实现CefApp::OnBeforeCommandLineProcessing()方法。
四、CefRefPtr
创建SimpleApp的实例,在这个类中保存process-level callbacks.
初始化CEF并开启消息循环。
CefRefPtr<SimpleApp> app(new SimpleApp);
五 、定制你的CEF(CefWindowInfo和settings)
5.1.全局设置
// Specify CEF global settings here.
CefSettings settings;
5.2.本地windows信息
// Information used when creating the native window.
5.3.浏览器设置
// Specify CEF browser settings here.
可以在这里配置的参数有很多
比如:
DefaultEncoding(用于所有网页内容的编码方式,默认为ISO-8859-1)
UserStyleSheetLocation(用于所有网页的样式,应该按照这样的格式设置这个字段:data:text/css;charset=utf-8;base64,[csscontent])
RemoteFonts(用于所有网页的字体)
JavaScript(用于所有网页是否可以执行JS脚本)
JavaScriptOpenWindows(用于所有网页是否可以通过JS来打开窗口)
六 、初始化CEF
// Initialize CEF.
CefInitialize(main_args, settings, app.get(), sandbox_info);
当CEF初始化完毕以后, SimpleApp::OnContextInitialized()会被调用。在这个方法中:
创建一个单例的SimpleHandler
由CefBrowserHost::CreateBrowserSync()创建一个浏览器窗口
所有的浏览器共享同一个在SimpleHandler。SimpleHandler负责定制浏览器的行为并保存browser-related callbacks(loading状态,标题行为等)
当浏览器窗口被关闭的时候, SimpleHandler::OnBeforeClose() 被调用。当所有浏览器窗口被关闭,CEF消息循环退出。
七 、消息循环
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
八 、关闭CEF
// Shut down CEF.
CefShutdown();
九 、创建浏览器窗口
CreateBrowser是一个静态函数
// Create the first browser window.
================================================================
应用程序通过调用CefBrowser和CefFrame的方法来处理浏览器控件事件:
Back, Forward, Reload and Stop Load。控件浏览器的导航
Undo, Redo, Cut, Copy, Paste, Delete, Select All.控件目标框架的选取
Print。打印目标框架
Get Source。以字符串的形式来获取目标框架的HTML源码
View Source. 用缓存文件来保存目的框架的HTML源码,并且用系统默认的文本查看器打开
Load URL.加载特定的URL到目标框架
Load String. 加载一个特定的字符串到目标框架,通过一个随意指定的虚拟URL
Load Stream. 加载一个特定的二进制文件到目标框架,通过一个随意指定的虚拟URL
Load Request, 加载一个特定的请求到目标框架
Execute JavaScript: 在目标框架里面执行一个特定的Javscript命令
Zoom。 缩放特定框架的网页内容
框架接口:
CefBrowser是主要的浏览器窗口类,可以用静态的函数CreateBrowser() 和CreateBrowserSync() 来创建一个新的浏览器窗口。
CefFrame 代表一个浏览器窗口的框架,每个浏览器窗口有一个顶层的主框架,而这个主框架可以用GetMainFrame() 方法得到。
CefClient是主浏览器窗口的代表接口,这个接口做为参数传递给CreateBrowser()
CefRequest 代表URL,方法,发送数据和头文件等这样的请求。
CefSchemeHandleFactory 类是被用来处理像myscheme://mydomain类似客户计划的请求
CefReadHandler和CefWriteHandle是一个读写数据的简单接口。
CefV8Handler,CefV8Value和CefV8Context是被用来创建和访问JavaScript对象。
今天就通过一个简单的例子,在windows程序中使用libcef。
现在再重新写一下如何搞?直接在源代码上搞起!
1 打开源码cefclient解决方案
2 确保cefclient例子可以完美运行
3 在cefclient中,除了util.h之外,全部移除
4 manifests 和 resources文件也可以移除(you must remember to remove the additional manifest files from the Visual Studio project file.)
5 libcef_dll_wrapper 是静态链接,所以我们需要更改项目为动态链接。
接下来就要干我们的事儿了:
1 创建一个自己的头文件ExampleCefApp.h, 在这里新建一个类,继承自CefApp:
CefApp 负责所有的工作, 但是这是一个抽象类,需要计数实现
这样, 我们继承自CefApp创建了一个傀儡类,实际上也什么都没做
#include "include/cef_app.h"
class ExampleCefApp : public CefApp
{
public:
ExampleCefApp ()
{
}
virtual ~ExampleCefApp ()
{
}
private:
IMPLEMENT_REFCOUNTING (ExampleCefApp);
};
2创建一个ExampleCefHandler.h文件,这里面实现一个类继承自所有的event handling classes
#pragma once
#include "include/cef_client.h"
#include "cefclient/util.h"
class ExampleCefHandler : public CefClient,
public CefContextMenuHandler,
public CefDisplayHandler,
public CefDownloadHandler,
public CefDragHandler,
public CefGeolocationHandler,
public CefKeyboardHandler,
public CefLifeSpanHandler,
public CefLoadHandler,
public CefRequestHandler
{
public:
ExampleCefHandler();
virtual ~ExampleCefHandler();
CefRefPtr<CefBrowser> GetBrowser();
#pragma region CefClient
// since we are letting the base implementations handle all of the heavy lifting,
// these functions just return the this pointer
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler () OVERRIDE;
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler () OVERRIDE;
virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler () OVERRIDE;
virtual CefRefPtr<CefDragHandler> GetDragHandler () OVERRIDE;
virtual CefRefPtr<CefGeolocationHandler> GetGeolocationHandler () OVERRIDE;
virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler () OVERRIDE;
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler () OVERRIDE;
virtual CefRefPtr<CefLoadHandler> GetLoadHandler () OVERRIDE;
virtual CefRefPtr<CefRequestHandler> GetRequestHandler () OVERRIDE;
#pragma endregion // CefClient
#pragma region CefDownloadHandler
// 这个函数为虚函数,我们必须实现它。但是我们什么也没做,所以下载文件的操作不会工作
virtual void OnBeforeDownload (CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback);
#pragma endregion // CefDownloadHandler
#pragma region CefLifeSpanHandler
// 缓存一个指向browser的引用
virtual void OnAfterCreated (CefRefPtr<CefBrowser> browser) OVERRIDE;
// 释放browser引用
virtual void OnBeforeClose (CefRefPtr<CefBrowser> browser) OVERRIDE;
#pragma endregion // CefLifeSpanHandler
protected:
// the browser reference
CefRefPtr<CefBrowser> browser;
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING (ExampleCefHandler);
// Include the default locking implementation.
IMPLEMENT_LOCKING (ExampleCefHandler);
};
3.创建一个源文件 ExampleCefHandler.cc:
#include "cefclient/ExampleCefHandler.h"
// defined in main.cppp
extern void AppQuitMessageLoop ();
ExampleCefHandler::ExampleCefHandler ()
{
}
ExampleCefHandler::~ExampleCefHandler ()
{
}
CefRefPtr<CefBrowser> ExampleCefHandler::GetBrowser ()
{
return browser;
}
CefRefPtr<CefContextMenuHandler> ExampleCefHandler::GetContextMenuHandler ()
{
return this;
}
CefRefPtr<CefDisplayHandler> ExampleCefHandler::GetDisplayHandler ()
{
return this;
}
CefRefPtr<CefDownloadHandler> ExampleCefHandler::GetDownloadHandler ()
{
return this;
}
CefRefPtr<CefDragHandler> ExampleCefHandler::GetDragHandler ()
{
return this;
}
CefRefPtr<CefGeolocationHandler> ExampleCefHandler::GetGeolocationHandler ()
{
return this;
}
CefRefPtr<CefKeyboardHandler> ExampleCefHandler::GetKeyboardHandler ()
{
return this;
}
CefRefPtr<CefLifeSpanHandler> ExampleCefHandler::GetLifeSpanHandler ()
{
return this;
}
CefRefPtr<CefLoadHandler> ExampleCefHandler::GetLoadHandler ()
{
return this;
}
CefRefPtr<CefRequestHandler> ExampleCefHandler::GetRequestHandler ()
{
return this;
}
void ExampleCefHandler::OnBeforeDownload (CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name, CefRefPtr<CefBeforeDownloadCallback> callback)
{
UNREFERENCED_PARAMETER (browser);
UNREFERENCED_PARAMETER (download_item);
callback->Continue (suggested_name, true);
}
void ExampleCefHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
REQUIRE_UI_THREAD();
AutoLock lock_scope (this);
this->browser = browser;
CefLifeSpanHandler::OnAfterCreated (browser);
}
void ExampleCefHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
REQUIRE_UI_THREAD();
AutoLock lock_scope (this);
browser = NULL;
AppQuitMessageLoop();
CefLifeSpanHandler::OnBeforeClose (browser);
}
4. 最后,创建一个主函数:
#include "cefclient/ExampleCefApp.hpp"
#include "cefclient/ExampleCefHandler.hpp"
#include "cefclient/util.h"
#include <windows.h>
#define BROWSER_WINDOW_CLASS TEXT("BrowserWindowClass")
#define INVALID_HWND (HWND)INVALID_HANDLE_VALUE
#define MESSAGE_WINDOW_CLASS TEXT("MessageWindowClass")
#define QUIT_CEF_EXAMPLE 0xABAD1DEA
namespace
{
CefRefPtr<ExampleCefHandler> example_cef_handler;
HWND application_message_window_handle = INVALID_HWND;
}
LRESULT CALLBACK BrowserWindowWndProc (HWND, UINT, WPARAM, LPARAM);
void CreateBrowserWindow (HINSTANCE instance_handle, int show_minimize_or_maximize)
{
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof (wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = BrowserWindowWndProc;
wcex.hInstance = instance_handle;
wcex.hCursor = LoadCursor (NULL, IDC_ARROW);
wcex.hbrBackground = WHITE_BRUSH;
wcex.lpszClassName = BROWSER_WINDOW_CLASS;
RegisterClassEx (&wcex);
HWND window_handle (CreateWindow (BROWSER_WINDOW_CLASS, BROWSER_WINDOW_CLASS,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0, NULL, NULL, instance_handle, NULL));
ShowWindow (window_handle, show_minimize_or_maximize);
UpdateWindow (window_handle);
}
LRESULT CALLBACK MessageWindowWndProc (HWND, UINT, WPARAM, LPARAM);
HWND CreateMessageWindow (HINSTANCE instance_handle)
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof (wcex);
wcex.lpfnWndProc = MessageWindowWndProc;
wcex.hInstance = instance_handle;
wcex.lpszClassName = MESSAGE_WINDOW_CLASS;
RegisterClassEx (&wcex);
return CreateWindow (MESSAGE_WINDOW_CLASS, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, instance_handle, 0);
}
// Program entry point function.
int APIENTRY wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
UNREFERENCED_PARAMETER (hPrevInstance);
UNREFERENCED_PARAMETER (lpCmdLine);
int result (0);
CefMainArgs main_args (hInstance);
CefRefPtr<ExampleCefApp> app (new ExampleCefApp);
// CefExecuteProcess returns -1 for the host process
if (CefExecuteProcess(main_args, app.get()) == -1)
{
CefSettings settings;
settings.multi_threaded_message_loop = true;
CefInitialize (main_args, settings, app.get ());
CreateBrowserWindow (hInstance, nCmdShow);
application_message_window_handle = CreateMessageWindow (hInstance);
MSG msg;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
result = static_cast<int>(msg.wParam);
DestroyWindow (application_message_window_handle);
application_message_window_handle = INVALID_HWND;
// disabled due to https://code.google.com/p/chromiumembedded/issues/detail?id=755
// CefShutdown ();
UnregisterClass (BROWSER_WINDOW_CLASS, hInstance);
UnregisterClass (MESSAGE_WINDOW_CLASS, hInstance);
}
return result;
}
LRESULT CALLBACK BrowserWindowWndProc (HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param)
{
LRESULT result (0);
switch (message)
{
case WM_CREATE:
{
example_cef_handler = new ExampleCefHandler();
RECT rect = { 0 };
GetClientRect (window_handle, &rect);
CefWindowInfo info;
info.SetAsChild(window_handle, rect);
CefBrowserSettings settings;
CefBrowserHost::CreateBrowser(info, example_cef_handler.get(),
CefString ("http://www.google.com"), settings, NULL);
}
break;
case WM_SIZE:
{
// from the cefclient example, do not allow the window to be resized to 0x0 or the layout will break;
// also be aware that if the size gets too small, GPU acceleration disables
if ((w_param != SIZE_MINIMIZED)
&& (example_cef_handler.get ())
&& (example_cef_handler->GetBrowser ()))
{
CefWindowHandle hwnd (example_cef_handler->GetBrowser ()->GetHost ()->GetWindowHandle ());
if (hwnd)
{
RECT rect = { 0 };
GetClientRect (window_handle, &rect);
HDWP hdwp = BeginDeferWindowPos (1);
hdwp = DeferWindowPos (hdwp, hwnd, NULL,rect.left,
rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
EndDeferWindowPos (hdwp);
}
}
}
break;
case WM_ERASEBKGND:
{
if ((example_cef_handler.get ())
&& (example_cef_handler->GetBrowser ()))
{
CefWindowHandle hwnd (example_cef_handler->GetBrowser()->GetHost()->GetWindowHandle());
// from the cefclient example, don't erase the background
// if the browser window has been loaded to avoid flashing
result = hwnd ? 1 : DefWindowProc (window_handle, message, w_param, l_param);
}
}
break;
case WM_ENTERMENULOOP:
{
if (!w_param)
{
CefSetOSModalLoop (true);
}
result = DefWindowProc (window_handle, message, w_param, l_param);
}
break;
case WM_EXITMENULOOP:
{
if (!w_param)
{
CefSetOSModalLoop (false);
}
result = DefWindowProc (window_handle, message, w_param, l_param);
}
break;
case WM_DESTROY:
break;
default:
{
result = DefWindowProc (window_handle, message, w_param, l_param);
}
break;
}
return result;
}
LRESULT CALLBACK MessageWindowWndProc (HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param)
{
LRESULT result (0);
switch (message)
{
case WM_COMMAND:
{
if (w_param == QUIT_CEF_EXAMPLE)
{
PostQuitMessage(0);
}
}
break;
default:
{
result = DefWindowProc (window_handle, message, w_param, l_param);
}
break;
}
return result;
}
void AppQuitMessageLoop ()
{
if (application_message_window_handle != INVALID_HWND)
{
PostMessage(application_message_window_handle, WM_COMMAND, QUIT_CEF_EXAMPLE, 0);
}
}
很久没写关于libcef的文章了,因为自己理解的非常浅薄。
我们知道浏览器有记住密码功能,就是登陆后,再次输入域名就可以直接登陆。很多时候是通过cookie来实现的。
对于一个没接触过web的人,也许不理解何为cookie?
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。
比如说,我们create一个browser,并导航到http://blog.csdn.net/网站,然后可以进行登陆等操作。
但是如果我们给browser事先设置好一些cookie,比如username userinfo等。这样再进行访问http://blog.csdn.net/的时候,cef浏览器就会读取cookie中的信息,然后显示的http://blog.csdn.net/主页是登录状态的。
所以,接下来的问题就是如何为cef设置cookie?
是在createbrowser前还是createbrowser之后呢?
答案是之前。
首先假设 cookie的格式:
username = xidada
直接上代码了:
std::wstring username_key = L"username";
std::wstring username_value = L"xidada";
std::wstring domain = L"blog.csdn.net"
CefRefPtr<CefCookieManager> manager = CefCookieManager::GetGlobalManager();
CefCookie cookie;
CefString(&cookie.name).FromWString(username_key.c_str());
CefString(&cookie.value).FromWString(username_value.c_str());
CefString(&cookie.domain).FromWString(domain.c_str());
CefString(&cookie.path).FromASCII("/");
cookie.has_expires = false;
domain = L"https://" + domain;
CefPostTask(TID_IO,
NewCefRunnableMethod(manager.get(),
&CefCookieManager::SetCookie,
CefString(domain.c_str()),
cookie));
//创建浏览器
CefBrowserHost::CreateBrowser(info, g_web_browser_client.get(),
domain.c_str(), browserSettings, NULL);
这里需要注意的是,cookie.domain是不带”https://”的,而CefString(domain.c_str())中的domain是带”https://“的,一定要注意。
下面看看setcookie的英文文档:
SetCookie
public virtualbool SetCookie( const CefString& url,
const CefCookie& cookie,
CefRefPtr< CefSetCookieCallback > callback )= 0;
如果你是初学者,你很可能会跟我一样,遇到这样的问题,即如何给cef browser设置url,或是改变cef browser的url。
对于创建一个browser,你可能很轻松的驾驭,并再创建browser的时候,传递一个url。比如导航到”www.baidu.com”。但是接下来,你想导航到”www.google.com”,你会怎么做呢?
也许你会重新create一个browser,并成功导航至”www.google.com”。但此时,你打开任务管理器看一下,可以明显的看到有两个browser在运行。
所以,我要说的就是在同一个browser上如何seturl或是loadrul。
createbrowser:
std::wstring url = L"www.baidu.com"; CefBrowserHost::CreateBrowser(info, g_web_browser_client.get(),
接下来,首先是获得一个浏览器的指针,然后通过GetMainFram函数,然后调用LoadURL,进行访问自己想要的url。
std::wstring url2 = "www.google.com"; CefRefPtr<CefBrowser> browser = g_web_browser_client->GetBrowser(); browser->GetMainFrame()->LoadURL(CefString(url2));
GetMainFrame
public virtual CefRefPtr< CefFrame > GetMainFrame()= 0;
Returns the main (top-level) frame for the browser window.
LoadURL
public virtual void LoadURL( const CefString& url )= 0;
Load the specified |url|.