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卻不是個好插件,好男碰到了渣女,不得不一起過日子,必然會造成各種問題。這些問題其實就是一層窗戶紙,解決的時候查了數不清的資料,掉了一把又一把的頭發,但解決方案往往只有幾行代碼。作為一個開發工程師,使用框架或者第三方引擎的時候,不要滿足於淺層的調用,還要去深入學習,才能比較全面又清晰的理解這些框架或引擎。解決問題不至於無從下手,隨着知識和經驗的積累,從而做到舉重若輕。


免責聲明!

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



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