1. 程式人生 > 其它 >CEF3開發者系列之與Flash外掛糾纏不清的那些問題

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