Click here to Skip to main content
Click here to Skip to main content

A Simple Windows Example Using the Chromium Embedded Framework 3

, 13 Jun 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
This is a simple Windows example using the Chromium embedded framework 3.

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"
    
    // CefApp does all of the work, but it is an abstract base class that needs reference counting implemented;
    // thus, we create a dummy class that inherits off of CefApp but does nothing
    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
          // 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
          // this function is virtual and must be implemented; we do nothing in it, so downloading files won't work as the callback function isn't invoked
          virtual void OnBeforeDownload (CefRefPtr<CefBrowser> browser, CefRefPtr<CefDownloadItem> download_item, const CefString& suggested_name, CefRefPtr<CefBeforeDownloadCallback> callback);
    #pragma endregion // CefDownloadHandler 
    
    #pragma region CefLifeSpanHandler
          // cache a reference to the browser
          virtual void OnAfterCreated (CefRefPtr<CefBrowser> browser) OVERRIDE;
          // release the browser reference
          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);
    };
  • 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"
    
    // 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);
    }
  • 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);
    }
    
    // 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);
       }
    }

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jason Stern2

United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 3 PinmemberMember 1114902423-Oct-14 3:24 
QuestionI can not compile in visual studio 2010 PinmemberLucas Teixeira6-Aug-14 16:52 
QuestionI can not compile in visual studio 2010 PinmemberLucas Teixeira6-Aug-14 14:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 13 Jun 2014
Article Copyright 2014 by Jason Stern2
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid