20150622 CEF嵌入MFC对话框程序

        最近在学习CEF,发现自己的编程能力实在太弱,看不懂应该怎么使用这个库,也不知道可以向谁请教,尽管官方说提供的cefclient示例程序已经很清楚了啊,但是我看不懂啊,自己一个人慢慢磨真的十分痛苦。最近结合网上的资料,学习了一些些吧,写下这篇日志,希望可以帮到后来的人(不过后来的人应该不会像我这么弱了的吧)。

        这是一个将CEF嵌入MFC对话框的程序,说来惭愧,到现在我都还不会怎么写一个好看的界面,只会在MFC上堆砌各种控件,唉。

        这篇日志主要参考了以下资料:

  • https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md(这里面的资料貌似也很完善,但我还没有看完,主要是里面很多语句不理解,感觉是在给编程已经上道的人写的,看得心塞)
  • l 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 还不熟悉啊,心塞。







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值