201506022 CEF嵌入MFC對話框程序


        最近在學習CEF,發現自己的編程能力實在太弱,看不懂應該怎么使用這個庫,也不知道可以向誰請教,盡管官方說提供的cefclient示例程序已經很清楚了啊,但是我看不懂啊,自己一個人慢慢磨真的十分痛苦。最近結合網上的資料,學習了一些些吧,寫下這篇日志,希望可以幫到后來的人(不過后來的人應該不會像我這么弱了的吧)。

        這是一個將CEF嵌入MFC對話框的程序,說來慚愧,到現在我都還不會怎么寫一個好看的界面,只會在MFC上堆砌各種控件,唉。

        這篇日志主要參考了以下資料:

  • https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md(這里面的資料貌似也很完善,但我還沒有看完,主要是里面很多語句不理解,感覺是在給編程已經上道的人寫的,看得心塞)
  • http://mickeymickstechblog.blogspot.com/2014/08/how-to-use-webkit-cef-in-mfc-project.html
  • https://github.com/acristoffers/CEF3SimpleSample

        再說一件慚愧的事情,下面說的程序也只是結合了上面的資料堆砌而成,一些API為什么要這么用,我也不清楚(好希望有人可以帶我裝逼帶我飛)。

 

    預備工作:在http://www.magpcss.net/cef_downloads/中下載Windows版本的CEF3庫,本文下載的是cef_binary_3.2171.1979_windows32.7z

        下面正式開始。

1. 項目建立和配置

        首先建立一個MFC基於對話框程序。注意要選上“在靜態庫中使用MFC”。如果不慎沒選,可以在“項目屬性->配置屬性->常規->MFC的使用”重新配置(這時可能還需要手動將“項目屬性->配置屬性->C/C++->代碼生成->運行庫”配置為“多線程調試(/MTd)”)。

        在項目文件夾的代碼文件夾里(這里就是cefinmfcdialog/cefinmfcdialog中)建立一個CEF3文件夾,將項目要用到的和CEF3相關的頭文件和庫放在這個目錄中。

 

        解壓cef_binary_3.2171.1979_windows32.7z,進入到解壓后的目錄(這里假設解壓到了cef_binary_3.2171.1979_windows32),打開cefclient2010.sln,將其中的項目libcef_dll_wrapperDebug生成方案(默認就是)編譯生成一次。生成后,在目錄里會多出來一個out目錄,里面是生成的文件。

   

        下面將編譯項目需要的CEF3頭文件和庫拷貝到我們的項目文件夾中。

  1. cef_binary_3.2171.1979_windows32/include文件夾拷貝到項目代碼目錄/CEF3中;

  2. cef_binary_3.2171.1979_windows32/out/Debug中的lib目錄拷貝到項目代碼目錄/CEF3中;

  3. cef_binary_3.2171.1979_windows32/Debug/libcef.lib拷貝到項目代碼目錄/CEF3/lib中(按理說libcef.lib也是應該可以用官方提供的CEF3項目文件重新生成一個的,但我還沒有找到方法,這里只好拷貝官方生成好的了)。

 

 

        將CEF3目錄添加到項目的附加包含目錄中,將CEF3/lib/Debug目錄添加到項目的附加庫目錄中。附加依賴項中添加libcef.liblibcef_dll_wrapper.lib

2. 創建新的類

   

    根據https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-application-structure的描述,CEF3的程序的一般結構包括以下部分:

  • 初始化CEF,並且運行子進程可執行邏輯(sub-process executable logic,不知道具體是什么鬼,大概就是自己做一個子進程來自己管理消息循環的意思?)或者CEF消息循環;

  • 提供一個CefApp的實現用來處理進程相關的回調(什么鬼!);

  • 提供一個CefClient的實現用來處理瀏覽器實例相關的回調(什么鬼!);

  • 調用CefBrowserHost::CreateBrowser()來創建瀏覽器實例,以及使用CefLifeSpanHandler管理瀏覽器的生命周期(什么鬼!)。

   

    下面是創建新的類,代碼都是照搬https://github.com/acristoffers/CEF3SimpleSample中的了。基本上我找不到文檔指導怎么繼承CEF3的類來創建自己要求的類的,自己又找不到方法,唉。

    創建一個類ClientV8ExtensionHandler,繼承類CefV8Handler

    ClientV8ExtensionHandler.h

    /************************************************************************************************ 
    *   Copyright (c) 2013 Álan Crístoffer 
    * 
    *   Permission is hereby granted, free of charge, to any person obtaining a copy 
    *   of this software and associated documentation files (the "Software"), to deal 
    *   in the Software without restriction, including without limitation the rights 
    *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
    *   of the Software, and to permit persons to whom the Software is furnished to do so, 
    *   subject to the following conditions: 
    * 
    *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
    *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
    *   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
    *   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
    *   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    *   DEALINGS IN THE SOFTWARE. 
    ************************************************************************************************/  
      
    #ifndef __CEF3SimpleSample__ClientV8ExtensionHandler__  
    #define __CEF3SimpleSample__ClientV8ExtensionHandler__  
      
    #include "include/cef_app.h"  
      
    struct ClientV8ExtensionHandler : public CefV8Handler {  
        ClientV8ExtensionHandler(CefRefPtr<CefApp> app);  
      
        bool Execute(const CefString &name, CefRefPtr<CefV8Value> object, const CefV8ValueList &arguments, CefRefPtr<CefV8Value> &retval, CefString &exception) OVERRIDE;  
      
    private:  
        CefRefPtr<CefApp> app;  
      
        IMPLEMENT_REFCOUNTING(ClientV8ExtensionHandler);  
    };  
      
    #endif /* defined(__CEF3SimpleSample__ClientV8ExtensionHandler__) */  

     ClientV8ExtensionHandler.cpp:

    /************************************************************************************************ 
    *   Copyright (c) 2013 Álan Crístoffer 
    * 
    *   Permission is hereby granted, free of charge, to any person obtaining a copy 
    *   of this software and associated documentation files (the "Software"), to deal 
    *   in the Software without restriction, including without limitation the rights 
    *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
    *   of the Software, and to permit persons to whom the Software is furnished to do so, 
    *   subject to the following conditions: 
    * 
    *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
    *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
    *   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
    *   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
    *   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    *   DEALINGS IN THE SOFTWARE. 
    ************************************************************************************************/  
    #include "stdafx.h"  
    #include "ClientV8ExtensionHandler.h"  
      
    ClientV8ExtensionHandler::ClientV8ExtensionHandler(CefRefPtr<CefApp> app)  
    {  
        this->app = app;  
    }  
      
    bool ClientV8ExtensionHandler::Execute(const CefString &name, CefRefPtr<CefV8Value> object, const CefV8ValueList &arguments, CefRefPtr<CefV8Value> &retval, CefString &exception)  
    {  
        if ( name == "ChangeTextInJS" ) {  
            if ( (arguments.size() == 1) && arguments[0]->IsString() ) {  
                CefString           text   = arguments[0]->GetStringValue();  
                CefRefPtr<CefFrame> frame  = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();  
                std::string         jscall = "ChangeText('";  
                jscall += text;  
                jscall += "');";  
                frame->ExecuteJavaScript(jscall, frame->GetURL(), 0);  
                /* 
                 * If you want your method to return a value, just use retval, like this: 
                 * retval = CefV8Value::CreateString("Hello World!"); 
                 * you can use any CefV8Value, what means you can return arrays, objects or whatever you can create with CefV8Value::Create* methods 
                 */  
                return true;  
            }  
        }  
      
        return false;  
    }  

    創建類ClientHandler,繼承類CefClient和類CefLifeSpanHandler。這里有幾個方法是父類中的抽象方法,必須在實現類中給出實現。

    ClientHandler.h

    /************************************************************************************************ 
    *   Copyright (c) 2013 Álan Crístoffer 
    * 
    *   Permission is hereby granted, free of charge, to any person obtaining a copy 
    *   of this software and associated documentation files (the "Software"), to deal 
    *   in the Software without restriction, including without limitation the rights 
    *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
    *   of the Software, and to permit persons to whom the Software is furnished to do so, 
    *   subject to the following conditions: 
    * 
    *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
    *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
    *   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
    *   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
    *   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    *   DEALINGS IN THE SOFTWARE. 
    ************************************************************************************************/  
      
    #ifndef __CEFSimpleSample__ClientHandler__  
    #define __CEFSimpleSample__ClientHandler__  
      
    #include "include/cef_render_process_handler.h"  
    #include "include/cef_client.h"  
    #include "include/cef_v8.h"  
    #include "include/cef_browser.h"  
      
    class ClientHandler : public CefClient, public CefLifeSpanHandler {  
    public:  
        ClientHandler();  
      
        CefRefPtr<CefBrowser> GetBrowser()  
        {  
            return m_Browser;  
        }  
      
        CefWindowHandle GetBrowserHwnd()  
        {  
            return m_BrowserHwnd;  
        }  
      
        // CefClient methods  
        virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE  
        {  
            return this;  
        }  
      
        // Virutal on CefLifeSpanHandler  
        virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;  
        virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;  
        virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;  
      
    protected:  
        // The child browser window  
        CefRefPtr<CefBrowser> m_Browser;  
      
        // The child browser window handle  
        CefWindowHandle m_BrowserHwnd;  
      
        // /  
        // Macro that provides a reference counting implementation for classes extending  
        // CefBase.  
        // /  
        IMPLEMENT_REFCOUNTING(ClientHandler);  
    };  
      
    #endif /* defined(__CEFSimpleSample__ClientHandler__) */  

 

    ClientHandler.cpp

 

    /************************************************************************************************ 
    *   Copyright (c) 2013 Álan Crístoffer 
    * 
    *   Permission is hereby granted, free of charge, to any person obtaining a copy 
    *   of this software and associated documentation files (the "Software"), to deal 
    *   in the Software without restriction, including without limitation the rights 
    *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
    *   of the Software, and to permit persons to whom the Software is furnished to do so, 
    *   subject to the following conditions: 
    * 
    *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
    *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
    *   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
    *   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
    *   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    *   DEALINGS IN THE SOFTWARE. 
    ************************************************************************************************/  
    #include "stdafx.h"  
    #include "ClientHandler.h"  
      
    #include "include/cef_app.h"  
    #include "include/cef_base.h"  
    #include "include/cef_client.h"  
    #include "include/cef_command_line.h"  
    #include "include/cef_frame.h"  
    #include "include/cef_runnable.h"  
    #include "include/cef_web_plugin.h"  
      
    ClientHandler::ClientHandler()  
    {  
    }  
      
    bool ClientHandler::DoClose(CefRefPtr<CefBrowser> browser)  
    {  
        return false;  
    }  
      
    void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)  
    {  
        if ( !m_Browser.get() ) {  
            // We need to keep the main child window, but not popup windows  
            m_Browser     = browser;  
            m_BrowserHwnd = browser->GetHost()->GetWindowHandle();  
        }  
    }  
      
    void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)  
    {  
        if ( m_BrowserHwnd == browser->GetHost()->GetWindowHandle() ) {  
            // Free the browser pointer so that the browser can be destroyed  
            m_Browser = NULL;  
        }  
    }  

 

    創建類ClientApp,繼承類CefApp

    ClientApp.h

 

    /************************************************************************************************  
    *   Copyright (c) 2013 Álan Crístoffer  
    *  
    *   Permission is hereby granted, free of charge, to any person obtaining a copy  
    *   of this software and associated documentation files (the "Software"), to deal  
    *   in the Software without restriction, including without limitation the rights  
    *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies  
    *   of the Software, and to permit persons to whom the Software is furnished to do so,  
    *   subject to the following conditions:  
    *  
    *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,  
    *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR  
    *   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE  
    *   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR  
    *   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER  
    *   DEALINGS IN THE SOFTWARE.  
    ************************************************************************************************/  
      
    #ifndef __CEF3SimpleSample__ClientHandler__  
    #define __CEF3SimpleSample__ClientHandler__  
      
    #include "include/cef_app.h"  
    #include "include/cef_client.h"  
      
    class ClientApp : public CefApp, public CefRenderProcessHandler {  
    public:  
        ClientApp();  
      
        CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE  
        {  
            return this;  
        }  
      
        void OnWebKitInitialized() OVERRIDE;  
      
        IMPLEMENT_REFCOUNTING(ClientApp);  
    };  
      
    #endif /* defined(__CEF3SimpleSample__ClientHandler__) */  

 

    ClientApp.cpp

    /************************************************************************************************ 
    *   Copyright (c) 2013 Álan Crístoffer 
    * 
    *   Permission is hereby granted, free of charge, to any person obtaining a copy 
    *   of this software and associated documentation files (the "Software"), to deal 
    *   in the Software without restriction, including without limitation the rights 
    *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
    *   of the Software, and to permit persons to whom the Software is furnished to do so, 
    *   subject to the following conditions: 
    * 
    *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
    *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
    *   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
    *   FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
    *   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    *   DEALINGS IN THE SOFTWARE. 
    ************************************************************************************************/  
    #include "stdafx.h"  
    #include "ClientApp.h"  
      
    #include "ClientHandler.h"  
    #include "ClientV8ExtensionHandler.h"  
      
    ClientApp::ClientApp()  
    {  
    }  
      
    void ClientApp::OnWebKitInitialized()  
    {  
        /*std::string app_code = 
            "var app;" 
            "if (!app)" 
            "    app = {};" 
            "(function() {" 
            "    app.ChangeTextInJS = function(text) {" 
            "        native function ChangeTextInJS();" 
            "        return ChangeTextInJS(text);" 
            "    };" 
            "})();;"; 
     
        CefRegisterExtension( "v8/app", app_code, new ClientV8ExtensionHandler(this) );*/  
    }  

3. 初始化和運行CEF

    在cefinmfcdialogDlg.cpp中包含頭文件ClientApp.hClientHandler.h

    在類CcefinmfcdialogDlgCcefinmfcdialogDlg::OnInitDialog()方法中添加如下代碼:

 

           CefMainArgs main_args(theApp.m_hInstance);  
      
    CefRefPtr<ClientApp> app(new ClientApp);  
      
    int exit_code = CefExecuteProcess(main_args, app.get(), NULL);  
    if (exit_code >= 0){  
        exit(exit_code);  
    }  
      
    RECT rect;  
    GetDlgItem(IDC_BROWSER)->GetClientRect(&rect);  
      
    CefSettings settings;  
    CefSettingsTraits::init(&settings);  
    settings.multi_threaded_message_loop = true;  
    CefInitialize(main_args, settings, app.get(), NULL);  
      
    CefWindowInfo info;  
    CefBrowserSettings b_settings;  
    CefRefPtr<CefClient> client(new ClientHandler);  
      
    std::string site = "https://docs.python.org/2/c-api/";  
      
    info.SetAsChild(GetDlgItem(IDC_BROWSER)->GetSafeHwnd(), rect);  
    CefBrowserHost::CreateBrowser(info, client.get(), site, b_settings, NULL);  

 

這里要執行CefExecuteProcess函數必須傳遞一個類CefMainArgs的實例,該實例在Windows中是使用主程序的句柄來初始化的。 CefSettings.multi_threaded_message_loop = true的設置時WindowsCEF3庫中特有的,設置后會使CEF新建一個線程執行消息循環。std:string site變量用於指定程序運行時要打開的網頁。

        給類CcefinmfcdialogDlg添加ON_DESTORY消息的處理函數,在處理函數中添加如下代碼:

 

CefShutdown();  

 

之后執行生成,應該可以無錯生成程序的。

4. 拷貝運行依賴的資源

        再次進入cef_binary_3.2171.1979_windows32目錄,將Resources目錄下的所有內容(不清楚有什么用)以及Debug目錄下的libcef.dllpdf.dll(是的,不知道為什么也要這個dll,從名字看這個dll應該和pdf文件瀏覽相關吧,如果不拷貝過去,程序會瀏覽不了網頁,而且也會在程序所在文件夾中創建一個叫pdf.dll的目錄)拷貝到項目目錄的Debug文件夾中(就是程序生成所在的文件夾)。之后可以調試和運行了:

 

5. 還存在的問題

        在VS中調試程序的話,會在關閉程序后,會連續提示好幾個中斷,原因還不明白。

        當然還有對CEF還不熟悉啊,心塞。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM