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