• CEF3开发者系列之与Flash插件纠缠不清的那些问题


    概述 

       Flash Player也曾兴盛一时,随着国外各大厂商对Flash插件围攻,短短几年间,从不支持到完全抛弃,Flash Player慢慢从互联网上消失。一方面归咎于Flash自身的不安全、低效率,还有保守封闭,另外一方面HTML5的兴起,也加速了Flash的灭亡。说到底是Flash自己作的孽,但造成的后果有时候需要开发者来背。国内还有很多PC互联网时代留下的产品,比如众多的Flash游戏,还需要使用Flash Player来延续他们的生命和服务。就像一对夫妻不想一起过下去了,却被孩子拴住了,不得不必须在一起将就缠绵了。

        在使用CEF3作为框架的过程中,随着Chromium内核的升级,使用的CEF3也要随之升级。虽然谷歌抛弃了Flash,但我们在使用CEF3框架时,还是需要比较好的支持Flash插件,尽量给用户一个好的体验。最近升级到CEF3-80.1.15,使用的是chromium-80.0.3987.163。支持Flash插件加载时,遇到以下一些问题,特总结出来供参考。

    1、 加载旧版本Flash Player插件时,不会主动加载出来,需要点击鼠标右键,手动运行插件。

    2、 加载Flash插件时,会弹出黑色的CMD命令行窗口。

    3、 在Flash内容中的编辑框,无法获取鼠标焦点,且无法切换输入法,即无法输入中文。

    以下都是基于CEF3-80.1.15,chromium-80.0.3987.163,ppapi类型的Flash插件的解决方案。

    一、 如何解决旧版本Flash插件在CEF3中主动加载。

    加载过期Flash插件时,会出现提示:Adobe Flash Player is out of date,必须用户点击,进行主动加载。

    在client_browser.cc中的OnBeforeCommandLineProcessing方法里,加上这么一段,通过命令行的方式,允许加载自定义路径下的过期插件

    command_line->AppendSwitch("--allow-outdated-plugins");  //允许过期插件加载
    command_line->AppendSwitchWithValue("ppapi-flash-path", flashPluginPath);  //Flash插件路径
    command_line->AppendSwitchWithValue("ppapi-flash-version", "29.0.0.171");  //Flash插件版本
    command_line->AppendSwitchWithValue("plugin-policy", "allow"); //允许Flash加载

    如果是加载系统中已经安装的Flash,直接设置下面命令行

    command_line->AppendSwitch("--disable-web-security");//关闭同源策略
    command_line->AppendSwitch("--enable-system-flash"); //使用系统flash
    command_line->AppendSwitch("--load-extension");
    command_line->AppendSwitch("--allow-outdated-plugins");

    但仅仅这样还不够,因为在比较新的chromium内核中,确切说在Chromium76之后,对Flash进行了特殊关照,还需要进一步的设置。在创建Browser进程前,即调用CreateBrowser前:

    CefBrowserHost::CreateBrowser(window_info, client_handler_, client_handler_->startup_url(), settings, extra_info, request_context);

    还需要加上以下加上以下代码,才能允许对旧版本插件进行主动加载

    CefString error;
    CefRefPtr<CefValue> value = CefValue::Create();
    value->SetInt(1);
     
    CefRefPtr<CefValue> valueBool = CefValue::Create();
    valueBool->SetBool(TRUE);
     
    request_context->SetPreference("plugins.allow_outdated", valueBool, error);
    request_context->SetPreference("plugins.run_all_flash_in_allow_mode", valueBool, error);
    request_context->SetPreference("profile.default_content_setting_values.plugins", value, error);
     

    如果做了这些事情,还是不能生效。去看看初始化的时候,CEF3中选用的消息机制是什么。

    例如使用WTL或者MFC框架,自己本身有一套消息处理机制,那么设置settings.multi_threaded_message_loop = true 同时message_loop.reset(new MainMessageLoopStd);否则使用按照CEF3的例子cefclient中进行设置。

     

    二、 如何解决Flash加载启动前,弹出黑色CMD框

    cef3加载flash,会出现弹出命令行窗口,显示not sandboxed,影响使用体验。该问题有以下两个方案解决

    1、 通过Hook的方式,直接拦截黑色弹窗弹出。可以使用easyhook这个第三方库进行hook操作,简单又方便,不用开发者再去写繁杂的hook过程,在https://easyhook.github.io/downloads.html下载 easyhook。具体使用方法见文档和示例。

    主要步骤:由于打开cmd属于启动新的进程,所以hook CreateProcessACreateProcessW,获取进程启动时的命令行,如果命令行中带有“echo NOT SANDBOXED”,则进行拦截

    关键代码如下:

    typedef BOOL(WINAPI *realCreateProcessAPtr)(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, \
                                                LPSTARTUPINFOA lpStartupInfo, \
                                                LPPROCESS_INFORMATION lpProcessInformation);
     
    realCreateProcessAPtr prealCreateProcessA;
      
    void DoHook()
     
    {
        HMODULE hKernel32 = LoadLibrary(L"kernel32.dll");
     
        if (!(prealCreateProcessA = (realCreateProcessAPtr)GetProcAddress(hKernel32, "CreateProcessA")))
     
        {
            return;
        }
     
        if (!(prealCreateProcessW = (realCreateProcessWPtr)GetProcAddress(hKernel32, "CreateProcessW")))
        {
            return;
        }
     
        NTSTATUS resultA = LhInstallHook(prealCreateProcessA, MYCreateProcessA, NULL, &hAHookTrackInfo);
        NTSTATUS resultW = LhInstallHook(prealCreateProcessW, MYCreateProcessW, NULL, &hWHookTrackInfo);
     
        …………………………
     
    }
     
      
     
    BOOL WINAPI MYCreateProcessA(
                                 LPCSTR lpApplicationName,
                                 LPSTR lpCommandLine,
                                 LPSECURITY_ATTRIBUTES lpProcessAttributes,
                                 LPSECURITY_ATTRIBUTES lpThreadAttributes,
                                 BOOL bInheritHandles,
                                 DWORD dwCreationFlags,
                                 LPVOID lpEnvironment,
                                 LPCSTR lpCurrentDirectory,
                                 LPSTARTUPINFOA lpStartupInfo,
                                 LPPROCESS_INFORMATION lpProcessInformation
                                 )
    {
        std::string strCommandLine = lpCommandLine;
     
        if (string::npos != strCommandLine.find("echo NOT SANDBOXED"))
        {
            return TRUE;
        }
        else
        {
            return (prealCreateProcessA)(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
        }
    }
     

    以上只是关键代码,具体代码根据需要进行不全,MYCreateProcessW按照 MYCreateProcessA补全就行

    2、用二进制编辑软件,比如winhex,对flash player的dll文件进行反编译。搜索comspec修改为somspec,(修改的名字只要和comspec不相同即可)修改cmd.exe为cm1.exe (修改的名字只要和cmd.exe不相同即可),然后保存回编译。

     

    三、 如何解决在Flash中无法输入中文的问题

        谷歌在Chrome 63中于2017年底推出了Site Isolation,使其成为企业IT员工的一个选择,他们可以自定义防御工作,以保护工作人员免受外部网站上的威胁。后来,在推出的Chromium 66中,谷歌向普通用户开放了现场测试,一般用户可以通过chrome://flags选项启用站点隔离。谷歌明确表示,网站隔离最终将成为浏览器的默认设置,但该公司首先想要验证修复程序,以解决早期测试中出现的问题。用户可以通过更改选项页面中的一个设置来拒绝参与试用。打开谷歌浏览器 输入chrome://flags/#site-isolation-trial-opt-out 将该项设置为 disabled ,重启即可。

    所以在CEF3 66之后版本,也是默认设置了网站隔离,导致在Flash里的编辑框中,无法获取鼠标焦点,同时无法切换输入法,导致无法输入中文,只能输入英文。既然搞清楚问题了,解决问题就好办了。我们在CEF3中做类似的设置就好。Chromium命令行中有--disable-site-isolation-trials 命令,我们在命令行初始化时,加入即可。在client_browser.cc中的OnBeforeCommandLineProcessing方法里,加上如下代码:

    command_line->AppendSwitch("--disable-site-isolation-trials");
     

    总结

        CEF3是个好框架,但Flash却不是个好插件,好男碰到了渣女,不得不一起过日子,必然会造成各种问题。这些问题其实就是一层窗户纸,解决的时候查了数不清的资料,掉了一把又一把的头发,但解决方案往往只有几行代码。作为一个开发工程师,使用框架或者第三方引擎的时候,不要满足于浅层的调用,还要去深入学习,才能比较全面又清晰的理解这些框架或引擎。解决问题不至于无从下手,随着知识和经验的积累,从而做到举重若轻。

  • 相关阅读:
    1.vue-1
    7.Docker -- 虚拟服务器
    11.Django -- 中间件
    10.Django -- csrf -- 文件上传
    9.djang -- cookie和session
    8.Django --Ajax
    YOLO v1原理详解
    带你一文读懂Faster RCNN论文
    Week13
    Week12
  • 原文地址:https://www.cnblogs.com/guolixiucai/p/15990881.html
Copyright © 2020-2023  润新知