美文网首页
一步一步将CEF3嵌入到MFC视图中

一步一步将CEF3嵌入到MFC视图中

作者: simulationer | 来源:发表于2017-04-02 21:37 被阅读2770次

    一、CEF简介

    Chromium Embedded Framework (CEF) 是一个开源项目,允许在第三方应用中集成谷歌浏览器。原生支持C和C++,通过非官方扩展支持其他语言,如C#, Java, Delphi 或 Python。本文较详细的记录了将CEF集成到MFC单文档程序的过程。

    使用环境为:windows10 64位系统,visual studio 2017,按照32位编译。

    二、准备工作

    下载CEF,最新版本在http://cefbuilds.com/下载,但好像被墙了,我一直没法登陆,后来在下载链接下载到cef_binary_3.2171.1901_windows32

    Paste_Image.png

    解压到任意目录,用VS2017打开cefclient2010.sln,升级到VS2017格式。编译其中的libcef_dll_wrapper项目,会出现以下错误。

    Paste_Image.png

    产生原因为:SDK中的solution文件是用VS2010产生的,我的VS版本为VS2017,转换后,有些Project编译选项中,Treat Warnings As Errors(把警告看作错误来处理)选项开启了。按照下图,把此选项关闭,就可以正常编译了。


    Paste_Image.png

    编译成功后,在\out\Debug\lib 这个目录里会生成一个文件:libcef_dll_wrapper.lib,这个文件在开发我们项目的时候会使用。

    三、新建MFC项目

    新建一个MFC SDI项目

    设置为MFC标准,在静态库中使用MFC。


    新建MFC项目

    准备CEF文件

    • cef_binary_3.2171.1901_windows32中的include目录拷贝到CEFDemo文件夹中。
    • cef_binary_3.2171.1901_windows32目录下Debug中的dll文件拷贝到CEFDemo的Debug目录中。
    • libcef.lib和上面生成的libcef_dll_wrapper.lib拷贝到项目文件夹下的lib目录(新建)中。
    • 配置项目属性
    • C/C++ ->常规附加包含目录$(ProjectDir)lib\;$(ProjectDir)include\;
    • 链接器->输入添加附加依赖项$(ProjectDir)lib\libcef.lib;$(ProjectDir)lib\libcef_dll_wrapper.lib;
    • Debug目录下新建一个html文件夹,并添加一个index.html用来在程序界面中呈现。

    添加必要的类

    添加ClientHandler类,具体如下:

    #pragma once
    
    #include "include/base/cef_lock.h"
    #include "include/cef_client.h"
    
    class ClientHandler : public CefClient,
        public CefDisplayHandler,
        public CefLifeSpanHandler,
        public CefLoadHandler
    {
    public:
        // Implement this interface to receive notification of ClientHandler
        // events. The methods of this class will be called on the main thread.
        class Delegate
        {
        public:
            // Called when the browser is created.
            virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) = 0;
    
            // Called when the browser is closing.
            virtual void OnBrowserClosing(CefRefPtr<CefBrowser> browser) = 0;
    
            // Called when the browser has been closed.
            virtual void OnBrowserClosed(CefRefPtr<CefBrowser> browser) = 0;
    
            // Set the window URL address.
            virtual void OnSetAddress(std::string const & url) = 0;
    
            // Set the window title.
            virtual void OnSetTitle(std::string const & title) = 0;
    
            // Set fullscreen mode.
            virtual void OnSetFullscreen(bool const fullscreen) = 0;
    
            // Set the loading state.
            virtual void OnSetLoadingState(bool const isLoading,
                bool const canGoBack,
                bool const canGoForward) = 0;
    
        protected:
            virtual ~Delegate() {}
        };
    
    public:
        ClientHandler(Delegate* delegate);
        ~ClientHandler();
    
        void CreateBrowser(CefWindowInfo const & info, CefBrowserSettings const & settings, CefString const & url);
    
        // CefClient methods:
        virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
        virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
        virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
    
        // CefDisplayHandler methods:
        virtual void OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url) override;
        virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) override;
        virtual void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen);
    
        // CefLifeSpanHandler methods:
        virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
        virtual bool DoClose(CefRefPtr<CefBrowser> browser) override;
        virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
    
        // CefLoadHandler methods:
        virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
            bool isLoading,
            bool canGoBack,
            bool canGoForward) override;
    
        virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
            CefRefPtr<CefFrame> frame,
            ErrorCode errorCode,
            const CefString& errorText,
            const CefString& failedUrl) override;
    
        // This object may outlive the Delegate object so it's necessary for the
        // Delegate to detach itself before destruction.
        void DetachDelegate();
    
    private:
    
        // Include the default reference counting implementation.
        IMPLEMENT_REFCOUNTING(ClientHandler);
        // Include the default locking implementation.
        IMPLEMENT_LOCKING(ClientHandler);
    
    private:
        Delegate* m_delegate;
    };
    
    
    #include "stdafx.h"
    #include "ClientHandler.h"
    
    
    #include <sstream>
    #include <string>
    
    #include "include/base/cef_bind.h"
    #include "include/cef_app.h"
    #include "include/wrapper/cef_closure_task.h"
    #include "include/wrapper/cef_helpers.h"
    
    ClientHandler::ClientHandler(Delegate* delegate)
        : m_delegate(delegate)
    {
    }
    
    ClientHandler::~ClientHandler()
    {
    }
    
    void ClientHandler::CreateBrowser(CefWindowInfo const & info, CefBrowserSettings const & settings, CefString const & url)
    {
        CefBrowserHost::CreateBrowser(info, this, url, settings, nullptr);
    }
    
    void ClientHandler::OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url)
    {
        CEF_REQUIRE_UI_THREAD();
    
        // Only update the address for the main (top-level) frame.
        if (frame->IsMain())
        {
            if (m_delegate != nullptr)
                m_delegate->OnSetAddress(url);
        }
    }
    
    void ClientHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title)
    {
        CEF_REQUIRE_UI_THREAD();
    
        if (m_delegate != nullptr)
            m_delegate->OnSetTitle(title);
    }
    
    void ClientHandler::OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen)
    {
        CEF_REQUIRE_UI_THREAD();
    
        if (m_delegate != nullptr)
            m_delegate->OnSetFullscreen(fullscreen);
    }
    
    void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
    {
        CEF_REQUIRE_UI_THREAD();
    
        if (m_delegate != nullptr)
            m_delegate->OnBrowserCreated(browser);
    }
    
    bool ClientHandler::DoClose(CefRefPtr<CefBrowser> browser)
    {
        CEF_REQUIRE_UI_THREAD();
    
        if (m_delegate != nullptr)
            m_delegate->OnBrowserClosing(browser);
    
        return false;
    }
    
    void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
    {
        CEF_REQUIRE_UI_THREAD();
    
        if (m_delegate != nullptr)
            m_delegate->OnBrowserClosed(browser);
    }
    
    void ClientHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        ErrorCode errorCode,
        const CefString& errorText,
        const CefString& failedUrl)
    {
        CEF_REQUIRE_UI_THREAD();
    
        // Don't display an error for downloaded files.
        if (errorCode == ERR_ABORTED)
            return;
    
        // Display a load error message.
        std::stringstream ss;
        ss << "<html><body bgcolor=\"white\">"
            "<h2>Failed to load URL " << std::string(failedUrl) <<
            " with error " << std::string(errorText) << " (" << errorCode <<
            ").</h2></body></html>";
        frame->LoadString(ss.str(), failedUrl);
    }
    
    void ClientHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> browser, bool isLoading, bool canGoBack, bool canGoForward)
    {
        CEF_REQUIRE_UI_THREAD();
    
        if (m_delegate != nullptr)
            m_delegate->OnSetLoadingState(isLoading, canGoBack, canGoForward);
    }
    
    void ClientHandler::DetachDelegate()
    {
        m_delegate = nullptr;
    }
    

    改造MFC类

    • CCEFDemoDoc 不需改动
    • CMFCCEFView
      • 添加头文件CClientHandler.hMFCCEFDoc.h
      • 添加继承public ClientHandler::Delegate,并添加其虚函数
        virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) override;
    
        // Called when the browser is closing.
        virtual void OnBrowserClosing(CefRefPtr<CefBrowser> browser) override;
    
        // Called when the browser has been closed.
        virtual void OnBrowserClosed(CefRefPtr<CefBrowser> browser) override;
    
        // Set the window URL address.
        virtual void OnSetAddress(std::string const & url) override;
    
        // Set the window title.
        virtual void OnSetTitle(std::string const & title) override;
    
        // Set fullscreen mode.
        virtual void OnSetFullscreen(bool const fullscreen) override;
    
        // Set the loading state.
        virtual void OnSetLoadingState(bool const isLoading,
            bool const canGoBack,
            bool const canGoForward) override;
    
    void CCEFDemoView::OnBrowserCreated(CefRefPtr<CefBrowser> browser)
    {
        m_browser = browser;
    }
    
    void CCEFDemoView::OnBrowserClosing(CefRefPtr<CefBrowser> browser)
    {
    }
    
    void CCEFDemoView::OnBrowserClosed(CefRefPtr<CefBrowser> browser)
    {
        if (m_browser != nullptr &&
            m_browser->GetIdentifier() == browser->GetIdentifier())
        {
            m_browser = nullptr;
            m_clientHandler->DetachDelegate();
        }
    }
    
    void CCEFDemoView::OnSetAddress(std::string const & url)
    {
        auto main = static_cast<CMainFrame*>(m_wndMain);
        if (main != nullptr)
        {
            auto newurl = CString{ url.c_str() };
            if (newurl.Find(m_startUrl) >= 0)
                newurl = "";
    
            //main->SetUrl(newurl);
        }
    }
    
    void CCEFDemoView::OnSetTitle(std::string const & title)
    {
        ::SetWindowText(m_hWnd, CefString(title).ToWString().c_str());
    }
    
    void CCEFDemoView::OnSetFullscreen(bool const fullscreen)
    {
    }
    
    void CCEFDemoView::OnSetLoadingState(bool const isLoading,
        bool const canGoBack,
        bool const canGoForward)
    {
    }
    
    void CCEFDemoView::OnInitialUpdate()
    {
        CView::OnInitialUpdate();
    
        InitStartUrl();
    
        auto rect = RECT{ 0 };
        GetClientRect(&rect);
    
        CefWindowInfo info;
        info.SetAsChild(GetSafeHwnd(), rect);
    
        CefBrowserSettings browserSettings;
        browserSettings.web_security = STATE_DISABLED;
    
        m_clientHandler = new ClientHandler(this);
        m_clientHandler->CreateBrowser(info, browserSettings, CefString(m_startUrl));
    }
    
    void CCEFDemoView::OnSize(UINT nType, int cx, int cy)
    {
        CView::OnSize(nType, cx, cy);
    
        if (m_clientHandler != nullptr)
        {
            if (m_browser != nullptr)
            {
                auto hwnd = m_browser->GetHost()->GetWindowHandle();
                auto rect = RECT{ 0 };
                GetClientRect(&rect);
    
                ::SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
            }
        }
    }
    
    
    void CCEFDemoView::InitStartUrl()
    {
        TCHAR path_buffer[_MAX_PATH] = { 0 };
        TCHAR drive[_MAX_DRIVE] = { 0 };
        TCHAR dir[_MAX_DIR] = { 0 };
        TCHAR fname[_MAX_FNAME] = { 0 };
        TCHAR ext[_MAX_EXT] = { 0 };
    
        ::GetModuleFileName(NULL, path_buffer, sizeof(path_buffer));
        auto err = _tsplitpath_s(path_buffer, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
        if (err != 0) {}
    
        auto s = CString{ dir };
        s += _T("html");
        err = _tmakepath_s(path_buffer, _MAX_PATH, drive, (LPCTSTR)s, _T("index"), _T("html"));
        if (err != 0) {}
    
        m_startUrl = CString{ path_buffer };
        m_startUrl.Replace(_T('\\'), _T('/'));
        m_startUrl = CString{ _T("file:///") } +m_startUrl;
    }
    
    • 重写CView的OnActivateViewPreTranslateMessage函数
    void CCEFDemoView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
    {
        m_wndMain = AfxGetMainWnd();
    
        return CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
    }
    
    //截获键盘F5消息,刷新页面
    BOOL CCEFDemoView::PreTranslateMessage(MSG* pMsg)
    {
        if (pMsg->message == WM_KEYDOWN)
        {
            if (pMsg->wParam == VK_F5)
            {
                m_browser->Reload();
            }
        }
        return CView::PreTranslateMessage(pMsg);
    }
    
    • 添加OnInitialUpdateOnSize函数
    • 添加私有函数InitStartUrl和如下变量
        CefRefPtr<ClientHandler> m_clientHandler;
        CefRefPtr<CefBrowser> m_browser;
        CWnd* m_wndMain = nullptr;
        CString m_startUrl;
    
    • CCEFDemoView.cpp中添加消息映射ON_WM_SIZE()
    • 析构函数如下:
        if (m_clientHandler != nullptr)
            m_clientHandler->DetachDelegate();
    

    修改CCEFDemoApp

    • 修改头文件
      • 添加引用include/cef_base.hinclude/cef_app.h
      • 重写函数ExitInstance()PumpMessage()
    int CCEFDemoApp::ExitInstance()
    {
        AfxOleTerm(FALSE);
        UninitializeCef();
        return CWinApp::ExitInstance();
    }
    
    BOOL CCEFDemoApp::PumpMessage()
    {
        auto result = CWinApp::PumpMessage();
        CefDoMessageLoopWork();
        return result;
    }
    
    • 添加私有函数和变量:InitializeCef()UninitializeCef(),和CefRefPtr<CefApp> m_app
    void CCEFDemoApp::InitializeCef()
    {
        CefMainArgs mainargs(m_hInstance);
        CefSettings settings;
        settings.multi_threaded_message_loop = false;
        CefInitialize(mainargs, settings, m_app, nullptr);
    }
    void CCEFDemoApp::UninitializeCef()
    {
        CefShutdown();
    }
    
    • 修改源文件
      • 添加头文件引用CEFDemoView.h
      • InitInstanceCWinApp::InitInstance();之前添加InitializeCef();初始化CEF
      • 添加公有函数void SetUrl(CString const & url)
      • 添加消息映射afx_msg void OnClose()
      • 添加私有成员CCEFDemoView* GetView()

    复制资源文件

    cef_binary_3.2171.1901_windows32\Resources目录下的文件,拷贝到程序的Debug目录

    Paste_Image.png

    运行生成的程序,可得如下界面,说明成功。

    Paste_Image.png

    相关文章

      网友评论

          本文标题:一步一步将CEF3嵌入到MFC视图中

          本文链接:https://www.haomeiwen.com/subject/yfxnottx.html