Introduction
Chromium Embedded Framework 3 is amazing. However, there are very few examples readily available on the web. The one that comes with CEF3 builds is incredibly feature dense and has a lot of cross-operating system code. The following code snippets will help Windows developers get up and running.
Background
Make sure to read through the Chromium Embedded Framework documentation, as the provided code snippet supplements this and is not a replacement.
This example replaces the cefclient
example project that comes with precompiled builds of the Chromium Embedded Framework. While future versions should be drop-in compatible, this example has been tested against version 3.1650.1562. Precompiled builds of the Chromium Embedded Framework can be found at the Chromium Embedded Framework (CEF) Downloads page.
Using the Code
- Open the cefclient solution file that comes with precompiled builds of the Chromium Embedded Framework.
- Ensure the
cefclient
example project is functional, then completely strip everything except for util.hpp
out of it. The manifests and resources are no longer needed and can also be removed; however, you must remember to remove the additional manifest files from the Visual Studio project file.
libcef_dll_wrapper
is set to statically link against the Microsoft Visual Studio C++ runtime. This will likely cause linker errors. As such, the project should be modified to dynamically link against the Microsoft Visual Studio C++ runtime. However, doing this will also cause linker errors due to the "treat warnings as errors" flag being asserted, as warnings about trying to export classes like std::exception
across DLL boundaries. To solve this, just add the warning to the list of warnings to ignore.
- Create a header file named ExampleCefApp.hpp for the
CefApp
deriving class. Populate it with:
#pragma once
#include "include/cef_app.h"
class ExampleCefApp : public CefApp
{
public:
ExampleCefApp ()
{
}
virtual ~ExampleCefApp ()
{
}
private:
IMPLEMENT_REFCOUNTING (ExampleCefApp);
};
- Create a header file named ExampleCefHandler.hpp for the class that derives off of all of the default event handling classes. Populate it with:
#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
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
#pragma region CefDownloadHandler
virtual void OnBeforeDownload (CefRefPtr<CefBrowser> browser, CefRefPtr<CefDownloadItem> download_item, const CefString& suggested_name, CefRefPtr<CefBeforeDownloadCallback> callback);
#pragma endregion
#pragma region CefLifeSpanHandler
virtual void OnAfterCreated (CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose (CefRefPtr<CefBrowser> browser) OVERRIDE;
#pragma endregion
protected:
CefRefPtr<CefBrowser> browser;
IMPLEMENT_REFCOUNTING (ExampleCefHandler);
IMPLEMENT_LOCKING (ExampleCefHandler);
};
- Create the source file named ExampleCefHandler.cpp for the class that derives off of all of the default event handling classes. Populate it with:
#include "cefclient/ExampleCefHandler.hpp"
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);
}
- Finally, create a source file named main.cpp for the application entry point and to host the Windows message pump and the respective
WndProc
functions. Populate it with:
#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);
}
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);
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;
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:
{
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());
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);
}
}
You should now have a very simple multithreaded Windows application with Chromium Embedded Framework 3 embedded, and the hooks set up for you to implement the rest of your application.
Points of Interest
As of CEF3 3.1650.1562, there are two outstanding, but non-blocking issues in Chromium with multithreading enabled that need to be addressed:
Again, these are only issues if you intend to run in multithreaded mode. However, if you intend to embed CEF into an actual Windows application, odds are that you will want to run in multithreaded mode for performance reasons.
If your application terminates, make sure to check the Output window in the Visual Studio debugger for possible indications as to why the application terminated. Also, make sure that the locales folder is present and populated with any locales the application intends to support. As a minimum, en-US.pak needs to be present.